mirror of
https://github.com/curl/curl.git
synced 2024-12-21 06:50:10 +08:00
2c2292ecaf
Small but, instead of sending the initial data though the connection method, send it to the next filter in the chain. While the connection methods accomodates for such use, by ignoring unconnected filters, it is better to follow the filter chain explicitly. Closes #14756
254 lines
6.5 KiB
C
254 lines
6.5 KiB
C
/***************************************************************************
|
|
* _ _ ____ _
|
|
* Project ___| | | | _ \| |
|
|
* / __| | | | |_) | |
|
|
* | (__| |_| | _ <| |___
|
|
* \___|\___/|_| \_\_____|
|
|
*
|
|
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
|
|
*
|
|
* This software is licensed as described in the file COPYING, which
|
|
* you should have received as part of this distribution. The terms
|
|
* are also available at https://curl.se/docs/copyright.html.
|
|
*
|
|
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
|
* copies of the Software, and permit persons to whom the Software is
|
|
* furnished to do so, under the terms of the COPYING file.
|
|
*
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
* KIND, either express or implied.
|
|
*
|
|
* SPDX-License-Identifier: curl
|
|
*
|
|
***************************************************************************/
|
|
|
|
#include "curl_setup.h"
|
|
|
|
#if !defined(CURL_DISABLE_PROXY)
|
|
|
|
#include <curl/curl.h>
|
|
#include "urldata.h"
|
|
#include "cfilters.h"
|
|
#include "cf-haproxy.h"
|
|
#include "curl_trc.h"
|
|
#include "multiif.h"
|
|
|
|
/* The last 3 #include files should be in this order */
|
|
#include "curl_printf.h"
|
|
#include "curl_memory.h"
|
|
#include "memdebug.h"
|
|
|
|
|
|
typedef enum {
|
|
HAPROXY_INIT, /* init/default/no tunnel state */
|
|
HAPROXY_SEND, /* data_out being sent */
|
|
HAPROXY_DONE /* all work done */
|
|
} haproxy_state;
|
|
|
|
struct cf_haproxy_ctx {
|
|
int state;
|
|
struct dynbuf data_out;
|
|
};
|
|
|
|
static void cf_haproxy_ctx_reset(struct cf_haproxy_ctx *ctx)
|
|
{
|
|
DEBUGASSERT(ctx);
|
|
ctx->state = HAPROXY_INIT;
|
|
Curl_dyn_reset(&ctx->data_out);
|
|
}
|
|
|
|
static void cf_haproxy_ctx_free(struct cf_haproxy_ctx *ctx)
|
|
{
|
|
if(ctx) {
|
|
Curl_dyn_free(&ctx->data_out);
|
|
free(ctx);
|
|
}
|
|
}
|
|
|
|
static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter*cf,
|
|
struct Curl_easy *data)
|
|
{
|
|
struct cf_haproxy_ctx *ctx = cf->ctx;
|
|
CURLcode result;
|
|
const char *client_ip;
|
|
struct ip_quadruple ipquad;
|
|
int is_ipv6;
|
|
|
|
DEBUGASSERT(ctx);
|
|
DEBUGASSERT(ctx->state == HAPROXY_INIT);
|
|
#ifdef USE_UNIX_SOCKETS
|
|
if(cf->conn->unix_domain_socket)
|
|
/* the buffer is large enough to hold this! */
|
|
result = Curl_dyn_addn(&ctx->data_out, STRCONST("PROXY UNKNOWN\r\n"));
|
|
else {
|
|
#endif /* USE_UNIX_SOCKETS */
|
|
result = Curl_conn_cf_get_ip_info(cf->next, data, &is_ipv6, &ipquad);
|
|
if(result)
|
|
return result;
|
|
|
|
/* Emit the correct prefix for IPv6 */
|
|
if(data->set.str[STRING_HAPROXY_CLIENT_IP])
|
|
client_ip = data->set.str[STRING_HAPROXY_CLIENT_IP];
|
|
else
|
|
client_ip = ipquad.local_ip;
|
|
|
|
result = Curl_dyn_addf(&ctx->data_out, "PROXY %s %s %s %i %i\r\n",
|
|
is_ipv6? "TCP6" : "TCP4",
|
|
client_ip, ipquad.remote_ip,
|
|
ipquad.local_port, ipquad.remote_port);
|
|
|
|
#ifdef USE_UNIX_SOCKETS
|
|
}
|
|
#endif /* USE_UNIX_SOCKETS */
|
|
return result;
|
|
}
|
|
|
|
static CURLcode cf_haproxy_connect(struct Curl_cfilter *cf,
|
|
struct Curl_easy *data,
|
|
bool blocking, bool *done)
|
|
{
|
|
struct cf_haproxy_ctx *ctx = cf->ctx;
|
|
CURLcode result;
|
|
size_t len;
|
|
|
|
DEBUGASSERT(ctx);
|
|
if(cf->connected) {
|
|
*done = TRUE;
|
|
return CURLE_OK;
|
|
}
|
|
|
|
result = cf->next->cft->do_connect(cf->next, data, blocking, done);
|
|
if(result || !*done)
|
|
return result;
|
|
|
|
switch(ctx->state) {
|
|
case HAPROXY_INIT:
|
|
result = cf_haproxy_date_out_set(cf, data);
|
|
if(result)
|
|
goto out;
|
|
ctx->state = HAPROXY_SEND;
|
|
FALLTHROUGH();
|
|
case HAPROXY_SEND:
|
|
len = Curl_dyn_len(&ctx->data_out);
|
|
if(len > 0) {
|
|
ssize_t nwritten;
|
|
nwritten = Curl_conn_cf_send(cf->next, data,
|
|
Curl_dyn_ptr(&ctx->data_out), len, FALSE,
|
|
&result);
|
|
if(nwritten < 0) {
|
|
if(result != CURLE_AGAIN)
|
|
goto out;
|
|
result = CURLE_OK;
|
|
nwritten = 0;
|
|
}
|
|
Curl_dyn_tail(&ctx->data_out, len - (size_t)nwritten);
|
|
if(Curl_dyn_len(&ctx->data_out) > 0) {
|
|
result = CURLE_OK;
|
|
goto out;
|
|
}
|
|
}
|
|
ctx->state = HAPROXY_DONE;
|
|
FALLTHROUGH();
|
|
default:
|
|
Curl_dyn_free(&ctx->data_out);
|
|
break;
|
|
}
|
|
|
|
out:
|
|
*done = (!result) && (ctx->state == HAPROXY_DONE);
|
|
cf->connected = *done;
|
|
return result;
|
|
}
|
|
|
|
static void cf_haproxy_destroy(struct Curl_cfilter *cf,
|
|
struct Curl_easy *data)
|
|
{
|
|
(void)data;
|
|
CURL_TRC_CF(data, cf, "destroy");
|
|
cf_haproxy_ctx_free(cf->ctx);
|
|
}
|
|
|
|
static void cf_haproxy_close(struct Curl_cfilter *cf,
|
|
struct Curl_easy *data)
|
|
{
|
|
CURL_TRC_CF(data, cf, "close");
|
|
cf->connected = FALSE;
|
|
cf_haproxy_ctx_reset(cf->ctx);
|
|
if(cf->next)
|
|
cf->next->cft->do_close(cf->next, data);
|
|
}
|
|
|
|
static void cf_haproxy_adjust_pollset(struct Curl_cfilter *cf,
|
|
struct Curl_easy *data,
|
|
struct easy_pollset *ps)
|
|
{
|
|
if(cf->next->connected && !cf->connected) {
|
|
/* If we are not connected, but the filter "below" is
|
|
* and not waiting on something, we are sending. */
|
|
Curl_pollset_set_out_only(data, ps, Curl_conn_cf_get_socket(cf, data));
|
|
}
|
|
}
|
|
|
|
struct Curl_cftype Curl_cft_haproxy = {
|
|
"HAPROXY",
|
|
CF_TYPE_PROXY,
|
|
0,
|
|
cf_haproxy_destroy,
|
|
cf_haproxy_connect,
|
|
cf_haproxy_close,
|
|
Curl_cf_def_shutdown,
|
|
Curl_cf_def_get_host,
|
|
cf_haproxy_adjust_pollset,
|
|
Curl_cf_def_data_pending,
|
|
Curl_cf_def_send,
|
|
Curl_cf_def_recv,
|
|
Curl_cf_def_cntrl,
|
|
Curl_cf_def_conn_is_alive,
|
|
Curl_cf_def_conn_keep_alive,
|
|
Curl_cf_def_query,
|
|
};
|
|
|
|
static CURLcode cf_haproxy_create(struct Curl_cfilter **pcf,
|
|
struct Curl_easy *data)
|
|
{
|
|
struct Curl_cfilter *cf = NULL;
|
|
struct cf_haproxy_ctx *ctx;
|
|
CURLcode result;
|
|
|
|
(void)data;
|
|
ctx = calloc(1, sizeof(*ctx));
|
|
if(!ctx) {
|
|
result = CURLE_OUT_OF_MEMORY;
|
|
goto out;
|
|
}
|
|
ctx->state = HAPROXY_INIT;
|
|
Curl_dyn_init(&ctx->data_out, DYN_HAXPROXY);
|
|
|
|
result = Curl_cf_create(&cf, &Curl_cft_haproxy, ctx);
|
|
if(result)
|
|
goto out;
|
|
ctx = NULL;
|
|
|
|
out:
|
|
cf_haproxy_ctx_free(ctx);
|
|
*pcf = result? NULL : cf;
|
|
return result;
|
|
}
|
|
|
|
CURLcode Curl_cf_haproxy_insert_after(struct Curl_cfilter *cf_at,
|
|
struct Curl_easy *data)
|
|
{
|
|
struct Curl_cfilter *cf;
|
|
CURLcode result;
|
|
|
|
result = cf_haproxy_create(&cf, data);
|
|
if(result)
|
|
goto out;
|
|
Curl_conn_cf_insert_after(cf_at, cf);
|
|
|
|
out:
|
|
return result;
|
|
}
|
|
|
|
#endif /* !CURL_DISABLE_PROXY */
|