mirror of
https://github.com/curl/curl.git
synced 2026-04-11 12:01:42 +08:00
Connection filters can now carry the flag CF_TYPE_SETUP, indicating that they are only needed during connection setup, e.g. connect. Once the connection is fully established, those filter are removed again. This frees resources and also makes the filter (call) chains shorter. Closes #21269
247 lines
6.4 KiB
C
247 lines
6.4 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"
|
|
|
|
#ifndef CURL_DISABLE_PROXY
|
|
|
|
#include "urldata.h"
|
|
#include "cfilters.h"
|
|
#include "cf-haproxy.h"
|
|
#include "curl_trc.h"
|
|
#include "select.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;
|
|
curlx_dyn_reset(&ctx->data_out);
|
|
}
|
|
|
|
static void cf_haproxy_ctx_free(struct cf_haproxy_ctx *ctx)
|
|
{
|
|
if(ctx) {
|
|
curlx_dyn_free(&ctx->data_out);
|
|
curlx_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;
|
|
bool 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 = curlx_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 = curlx_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 *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, 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 = curlx_dyn_len(&ctx->data_out);
|
|
if(len > 0) {
|
|
size_t nwritten;
|
|
result = Curl_conn_cf_send(cf->next, data,
|
|
curlx_dyn_uptr(&ctx->data_out), len, FALSE,
|
|
&nwritten);
|
|
if(result) {
|
|
if(result != CURLE_AGAIN)
|
|
goto out;
|
|
result = CURLE_OK;
|
|
nwritten = 0;
|
|
}
|
|
curlx_dyn_tail(&ctx->data_out, len - nwritten);
|
|
if(curlx_dyn_len(&ctx->data_out) > 0) {
|
|
result = CURLE_OK;
|
|
goto out;
|
|
}
|
|
}
|
|
ctx->state = HAPROXY_DONE;
|
|
FALLTHROUGH();
|
|
default:
|
|
curlx_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)
|
|
{
|
|
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 CURLcode 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. */
|
|
return Curl_pollset_set_out_only(
|
|
data, ps, Curl_conn_cf_get_socket(cf, data));
|
|
}
|
|
return CURLE_OK;
|
|
}
|
|
|
|
struct Curl_cftype Curl_cft_haproxy = {
|
|
"HAPROXY",
|
|
CF_TYPE_PROXY | CF_TYPE_SETUP,
|
|
0,
|
|
cf_haproxy_destroy,
|
|
cf_haproxy_connect,
|
|
cf_haproxy_close,
|
|
Curl_cf_def_shutdown,
|
|
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 = curlx_calloc(1, sizeof(*ctx));
|
|
if(!ctx) {
|
|
result = CURLE_OUT_OF_MEMORY;
|
|
goto out;
|
|
}
|
|
ctx->state = HAPROXY_INIT;
|
|
curlx_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 */
|