mirror of
https://github.com/curl/curl.git
synced 2026-01-18 17:21:26 +01:00
build: drop more forward function declarations
Most by moving functions around. Also delete unused ones. Reducing their number from 83 to 33. Remaining ones due to: - circular dependencies. - H3 code, that I did not attempt to update and likely the above applies. - static declarations with attributes (`CURL_PRINTF`, `WARN_UNUSED_RESULT`). - OS400 code. Closes #20321
This commit is contained in:
@@ -259,113 +259,6 @@ static int proxy_h2_client_new(struct Curl_cfilter *cf,
|
||||
return rc;
|
||||
}
|
||||
|
||||
static ssize_t on_session_send(nghttp2_session *h2,
|
||||
const uint8_t *buf, size_t blen,
|
||||
int flags, void *userp);
|
||||
static int proxy_h2_on_frame_recv(nghttp2_session *session,
|
||||
const nghttp2_frame *frame,
|
||||
void *userp);
|
||||
#ifndef CURL_DISABLE_VERBOSE_STRINGS
|
||||
static int proxy_h2_on_frame_send(nghttp2_session *session,
|
||||
const nghttp2_frame *frame,
|
||||
void *userp);
|
||||
#endif
|
||||
static int proxy_h2_on_stream_close(nghttp2_session *session,
|
||||
int32_t stream_id,
|
||||
uint32_t error_code, void *userp);
|
||||
static int proxy_h2_on_header(nghttp2_session *session,
|
||||
const nghttp2_frame *frame,
|
||||
const uint8_t *name, size_t namelen,
|
||||
const uint8_t *value, size_t valuelen,
|
||||
uint8_t flags,
|
||||
void *userp);
|
||||
static int tunnel_recv_callback(nghttp2_session *session, uint8_t flags,
|
||||
int32_t stream_id,
|
||||
const uint8_t *mem, size_t len, void *userp);
|
||||
|
||||
/*
|
||||
* Initialize the cfilter context
|
||||
*/
|
||||
static CURLcode cf_h2_proxy_ctx_init(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
struct cf_h2_proxy_ctx *ctx = cf->ctx;
|
||||
CURLcode result = CURLE_OUT_OF_MEMORY;
|
||||
nghttp2_session_callbacks *cbs = NULL;
|
||||
int rc;
|
||||
|
||||
DEBUGASSERT(!ctx->h2);
|
||||
memset(&ctx->tunnel, 0, sizeof(ctx->tunnel));
|
||||
|
||||
Curl_bufq_init(&ctx->inbufq, PROXY_H2_CHUNK_SIZE, PROXY_H2_NW_RECV_CHUNKS);
|
||||
Curl_bufq_init(&ctx->outbufq, PROXY_H2_CHUNK_SIZE, PROXY_H2_NW_SEND_CHUNKS);
|
||||
|
||||
if(tunnel_stream_init(cf, &ctx->tunnel))
|
||||
goto out;
|
||||
|
||||
rc = nghttp2_session_callbacks_new(&cbs);
|
||||
if(rc) {
|
||||
failf(data, "Could not initialize nghttp2 callbacks");
|
||||
goto out;
|
||||
}
|
||||
|
||||
nghttp2_session_callbacks_set_send_callback(cbs, on_session_send);
|
||||
nghttp2_session_callbacks_set_on_frame_recv_callback(
|
||||
cbs, proxy_h2_on_frame_recv);
|
||||
#ifndef CURL_DISABLE_VERBOSE_STRINGS
|
||||
nghttp2_session_callbacks_set_on_frame_send_callback(cbs,
|
||||
proxy_h2_on_frame_send);
|
||||
#endif
|
||||
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
|
||||
cbs, tunnel_recv_callback);
|
||||
nghttp2_session_callbacks_set_on_stream_close_callback(
|
||||
cbs, proxy_h2_on_stream_close);
|
||||
nghttp2_session_callbacks_set_on_header_callback(cbs, proxy_h2_on_header);
|
||||
|
||||
/* The nghttp2 session is not yet setup, do it */
|
||||
rc = proxy_h2_client_new(cf, cbs);
|
||||
if(rc) {
|
||||
failf(data, "Could not initialize nghttp2");
|
||||
goto out;
|
||||
}
|
||||
|
||||
{
|
||||
nghttp2_settings_entry iv[3];
|
||||
|
||||
iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
|
||||
iv[0].value = Curl_multi_max_concurrent_streams(data->multi);
|
||||
iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
|
||||
iv[1].value = H2_TUNNEL_WINDOW_SIZE;
|
||||
iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
|
||||
iv[2].value = 0;
|
||||
rc = nghttp2_submit_settings(ctx->h2, NGHTTP2_FLAG_NONE, iv, 3);
|
||||
if(rc) {
|
||||
failf(data, "nghttp2_submit_settings() failed: %s(%d)",
|
||||
nghttp2_strerror(rc), rc);
|
||||
result = CURLE_HTTP2;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
rc = nghttp2_session_set_local_window_size(ctx->h2, NGHTTP2_FLAG_NONE, 0,
|
||||
PROXY_HTTP2_HUGE_WINDOW_SIZE);
|
||||
if(rc) {
|
||||
failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)",
|
||||
nghttp2_strerror(rc), rc);
|
||||
result = CURLE_HTTP2;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* all set, traffic will be send on connect */
|
||||
result = CURLE_OK;
|
||||
|
||||
out:
|
||||
if(cbs)
|
||||
nghttp2_session_callbacks_del(cbs);
|
||||
CURL_TRC_CF(data, cf, "[0] init proxy ctx -> %d", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int proxy_h2_should_close_session(struct cf_h2_proxy_ctx *ctx)
|
||||
{
|
||||
return !nghttp2_session_want_read(ctx->h2) &&
|
||||
@@ -1051,6 +944,89 @@ out:
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the cfilter context
|
||||
*/
|
||||
static CURLcode cf_h2_proxy_ctx_init(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
struct cf_h2_proxy_ctx *ctx = cf->ctx;
|
||||
CURLcode result = CURLE_OUT_OF_MEMORY;
|
||||
nghttp2_session_callbacks *cbs = NULL;
|
||||
int rc;
|
||||
|
||||
DEBUGASSERT(!ctx->h2);
|
||||
memset(&ctx->tunnel, 0, sizeof(ctx->tunnel));
|
||||
|
||||
Curl_bufq_init(&ctx->inbufq, PROXY_H2_CHUNK_SIZE, PROXY_H2_NW_RECV_CHUNKS);
|
||||
Curl_bufq_init(&ctx->outbufq, PROXY_H2_CHUNK_SIZE, PROXY_H2_NW_SEND_CHUNKS);
|
||||
|
||||
if(tunnel_stream_init(cf, &ctx->tunnel))
|
||||
goto out;
|
||||
|
||||
rc = nghttp2_session_callbacks_new(&cbs);
|
||||
if(rc) {
|
||||
failf(data, "Could not initialize nghttp2 callbacks");
|
||||
goto out;
|
||||
}
|
||||
|
||||
nghttp2_session_callbacks_set_send_callback(cbs, on_session_send);
|
||||
nghttp2_session_callbacks_set_on_frame_recv_callback(
|
||||
cbs, proxy_h2_on_frame_recv);
|
||||
#ifndef CURL_DISABLE_VERBOSE_STRINGS
|
||||
nghttp2_session_callbacks_set_on_frame_send_callback(cbs,
|
||||
proxy_h2_on_frame_send);
|
||||
#endif
|
||||
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
|
||||
cbs, tunnel_recv_callback);
|
||||
nghttp2_session_callbacks_set_on_stream_close_callback(
|
||||
cbs, proxy_h2_on_stream_close);
|
||||
nghttp2_session_callbacks_set_on_header_callback(cbs, proxy_h2_on_header);
|
||||
|
||||
/* The nghttp2 session is not yet setup, do it */
|
||||
rc = proxy_h2_client_new(cf, cbs);
|
||||
if(rc) {
|
||||
failf(data, "Could not initialize nghttp2");
|
||||
goto out;
|
||||
}
|
||||
|
||||
{
|
||||
nghttp2_settings_entry iv[3];
|
||||
|
||||
iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
|
||||
iv[0].value = Curl_multi_max_concurrent_streams(data->multi);
|
||||
iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
|
||||
iv[1].value = H2_TUNNEL_WINDOW_SIZE;
|
||||
iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
|
||||
iv[2].value = 0;
|
||||
rc = nghttp2_submit_settings(ctx->h2, NGHTTP2_FLAG_NONE, iv, 3);
|
||||
if(rc) {
|
||||
failf(data, "nghttp2_submit_settings() failed: %s(%d)",
|
||||
nghttp2_strerror(rc), rc);
|
||||
result = CURLE_HTTP2;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
rc = nghttp2_session_set_local_window_size(ctx->h2, NGHTTP2_FLAG_NONE, 0,
|
||||
PROXY_HTTP2_HUGE_WINDOW_SIZE);
|
||||
if(rc) {
|
||||
failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)",
|
||||
nghttp2_strerror(rc), rc);
|
||||
result = CURLE_HTTP2;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* all set, traffic will be send on connect */
|
||||
result = CURLE_OK;
|
||||
|
||||
out:
|
||||
if(cbs)
|
||||
nghttp2_session_callbacks_del(cbs);
|
||||
CURL_TRC_CF(data, cf, "[0] init proxy ctx -> %d", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
static CURLcode cf_h2_proxy_connect(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
bool *done)
|
||||
|
||||
@@ -33,9 +33,6 @@
|
||||
#include "select.h"
|
||||
#include "curlx/strparse.h"
|
||||
|
||||
static void cf_cntrl_update_info(struct Curl_easy *data,
|
||||
struct connectdata *conn);
|
||||
|
||||
#ifdef UNITTESTS
|
||||
/* used by unit2600.c */
|
||||
void Curl_cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data)
|
||||
@@ -55,9 +52,6 @@ CURLcode Curl_cf_def_shutdown(struct Curl_cfilter *cf,
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static void conn_report_connect_stats(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data);
|
||||
|
||||
CURLcode Curl_cf_def_adjust_pollset(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
struct easy_pollset *ps)
|
||||
@@ -451,6 +445,51 @@ static CURLcode cf_verboseconnect(struct Curl_easy *data,
|
||||
}
|
||||
#endif
|
||||
|
||||
static CURLcode cf_cntrl_all(struct connectdata *conn,
|
||||
struct Curl_easy *data,
|
||||
bool ignore_result,
|
||||
int event, int arg1, void *arg2)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
size_t i;
|
||||
|
||||
for(i = 0; i < CURL_ARRAYSIZE(conn->cfilter); ++i) {
|
||||
result = Curl_conn_cf_cntrl(conn->cfilter[i], data, ignore_result,
|
||||
event, arg1, arg2);
|
||||
if(!ignore_result && result)
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void cf_cntrl_update_info(struct Curl_easy *data,
|
||||
struct connectdata *conn)
|
||||
{
|
||||
cf_cntrl_all(conn, data, TRUE, CF_CTRL_CONN_INFO_UPDATE, 0, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update connection statistics
|
||||
*/
|
||||
static void conn_report_connect_stats(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
if(cf) {
|
||||
struct curltime connected;
|
||||
struct curltime appconnected;
|
||||
|
||||
memset(&connected, 0, sizeof(connected));
|
||||
cf->cft->query(cf, data, CF_QUERY_TIMER_CONNECT, NULL, &connected);
|
||||
if(connected.tv_sec || connected.tv_usec)
|
||||
Curl_pgrsTimeWas(data, TIMER_CONNECT, connected);
|
||||
|
||||
memset(&appconnected, 0, sizeof(appconnected));
|
||||
cf->cft->query(cf, data, CF_QUERY_TIMER_APPCONNECT, NULL, &appconnected);
|
||||
if(appconnected.tv_sec || appconnected.tv_usec)
|
||||
Curl_pgrsTimeWas(data, TIMER_APPCONNECT, appconnected);
|
||||
}
|
||||
}
|
||||
|
||||
CURLcode Curl_conn_connect(struct Curl_easy *data,
|
||||
int sockindex,
|
||||
bool blocking,
|
||||
@@ -922,23 +961,6 @@ Curl_conn_get_remote_addr(struct Curl_easy *data, int sockindex)
|
||||
return cf ? cf_get_remote_addr(cf, data) : NULL;
|
||||
}
|
||||
|
||||
static CURLcode cf_cntrl_all(struct connectdata *conn,
|
||||
struct Curl_easy *data,
|
||||
bool ignore_result,
|
||||
int event, int arg1, void *arg2)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
size_t i;
|
||||
|
||||
for(i = 0; i < CURL_ARRAYSIZE(conn->cfilter); ++i) {
|
||||
result = Curl_conn_cf_cntrl(conn->cfilter[i], data, ignore_result,
|
||||
event, arg1, arg2);
|
||||
if(!ignore_result && result)
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
CURLcode Curl_conn_ev_data_setup(struct Curl_easy *data)
|
||||
{
|
||||
return cf_cntrl_all(data->conn, data, FALSE, CF_CTRL_DATA_SETUP, 0, NULL);
|
||||
@@ -976,34 +998,6 @@ CURLcode Curl_conn_ev_data_pause(struct Curl_easy *data, bool do_pause)
|
||||
CF_CTRL_DATA_PAUSE, do_pause, NULL);
|
||||
}
|
||||
|
||||
static void cf_cntrl_update_info(struct Curl_easy *data,
|
||||
struct connectdata *conn)
|
||||
{
|
||||
cf_cntrl_all(conn, data, TRUE, CF_CTRL_CONN_INFO_UPDATE, 0, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update connection statistics
|
||||
*/
|
||||
static void conn_report_connect_stats(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
if(cf) {
|
||||
struct curltime connected;
|
||||
struct curltime appconnected;
|
||||
|
||||
memset(&connected, 0, sizeof(connected));
|
||||
cf->cft->query(cf, data, CF_QUERY_TIMER_CONNECT, NULL, &connected);
|
||||
if(connected.tv_sec || connected.tv_usec)
|
||||
Curl_pgrsTimeWas(data, TIMER_CONNECT, connected);
|
||||
|
||||
memset(&appconnected, 0, sizeof(appconnected));
|
||||
cf->cft->query(cf, data, CF_QUERY_TIMER_APPCONNECT, NULL, &appconnected);
|
||||
if(appconnected.tv_sec || appconnected.tv_usec)
|
||||
Curl_pgrsTimeWas(data, TIMER_APPCONNECT, appconnected);
|
||||
}
|
||||
}
|
||||
|
||||
bool Curl_conn_is_alive(struct Curl_easy *data, struct connectdata *conn,
|
||||
bool *input_pending)
|
||||
{
|
||||
|
||||
@@ -69,11 +69,6 @@ struct cpool_bundle {
|
||||
char dest[1]; /* destination of bundle, allocated to keep dest_len bytes */
|
||||
};
|
||||
|
||||
static void cpool_discard_conn(struct cpool *cpool,
|
||||
struct Curl_easy *data,
|
||||
struct connectdata *conn,
|
||||
bool aborted);
|
||||
|
||||
static struct cpool_bundle *cpool_bundle_create(const char *dest)
|
||||
{
|
||||
struct cpool_bundle *bundle;
|
||||
@@ -189,6 +184,53 @@ static void cpool_remove_conn(struct cpool *cpool,
|
||||
}
|
||||
}
|
||||
|
||||
static void cpool_discard_conn(struct cpool *cpool,
|
||||
struct Curl_easy *data,
|
||||
struct connectdata *conn,
|
||||
bool aborted)
|
||||
{
|
||||
bool done = FALSE;
|
||||
|
||||
DEBUGASSERT(data);
|
||||
DEBUGASSERT(!data->conn);
|
||||
DEBUGASSERT(cpool);
|
||||
DEBUGASSERT(!conn->bits.in_cpool);
|
||||
|
||||
/*
|
||||
* If this connection is not marked to force-close, leave it open if there
|
||||
* are other users of it
|
||||
*/
|
||||
if(CONN_INUSE(conn) && !aborted) {
|
||||
CURL_TRC_M(data, "[CPOOL] not discarding #%" FMT_OFF_T
|
||||
" still in use by %u transfers", conn->connection_id,
|
||||
conn->attached_xfers);
|
||||
return;
|
||||
}
|
||||
|
||||
/* treat the connection as aborted in CONNECT_ONLY situations, we do
|
||||
* not know what the APP did with it. */
|
||||
if(conn->connect_only)
|
||||
aborted = TRUE;
|
||||
conn->bits.aborted = aborted;
|
||||
|
||||
/* We do not shutdown dead connections. The term 'dead' can be misleading
|
||||
* here, as we also mark errored connections/transfers as 'dead'.
|
||||
* If we do a shutdown for an aborted transfer, the server might think
|
||||
* it was successful otherwise (for example an ftps: upload). This is
|
||||
* not what we want. */
|
||||
if(aborted)
|
||||
done = TRUE;
|
||||
if(!done) {
|
||||
/* Attempt to shutdown the connection right away. */
|
||||
Curl_cshutdn_run_once(cpool->idata, conn, &done);
|
||||
}
|
||||
|
||||
if(done || !data->multi)
|
||||
Curl_cshutdn_terminate(cpool->idata, conn, FALSE);
|
||||
else
|
||||
Curl_cshutdn_add(&data->multi->cshutdn, conn, cpool->num_conn);
|
||||
}
|
||||
|
||||
void Curl_cpool_destroy(struct cpool *cpool)
|
||||
{
|
||||
if(cpool && cpool->initialised && cpool->idata) {
|
||||
@@ -592,53 +634,6 @@ bool Curl_cpool_find(struct Curl_easy *data,
|
||||
return result;
|
||||
}
|
||||
|
||||
static void cpool_discard_conn(struct cpool *cpool,
|
||||
struct Curl_easy *data,
|
||||
struct connectdata *conn,
|
||||
bool aborted)
|
||||
{
|
||||
bool done = FALSE;
|
||||
|
||||
DEBUGASSERT(data);
|
||||
DEBUGASSERT(!data->conn);
|
||||
DEBUGASSERT(cpool);
|
||||
DEBUGASSERT(!conn->bits.in_cpool);
|
||||
|
||||
/*
|
||||
* If this connection is not marked to force-close, leave it open if there
|
||||
* are other users of it
|
||||
*/
|
||||
if(CONN_INUSE(conn) && !aborted) {
|
||||
CURL_TRC_M(data, "[CPOOL] not discarding #%" FMT_OFF_T
|
||||
" still in use by %u transfers", conn->connection_id,
|
||||
conn->attached_xfers);
|
||||
return;
|
||||
}
|
||||
|
||||
/* treat the connection as aborted in CONNECT_ONLY situations, we do
|
||||
* not know what the APP did with it. */
|
||||
if(conn->connect_only)
|
||||
aborted = TRUE;
|
||||
conn->bits.aborted = aborted;
|
||||
|
||||
/* We do not shutdown dead connections. The term 'dead' can be misleading
|
||||
* here, as we also mark errored connections/transfers as 'dead'.
|
||||
* If we do a shutdown for an aborted transfer, the server might think
|
||||
* it was successful otherwise (for example an ftps: upload). This is
|
||||
* not what we want. */
|
||||
if(aborted)
|
||||
done = TRUE;
|
||||
if(!done) {
|
||||
/* Attempt to shutdown the connection right away. */
|
||||
Curl_cshutdn_run_once(cpool->idata, conn, &done);
|
||||
}
|
||||
|
||||
if(done || !data->multi)
|
||||
Curl_cshutdn_terminate(cpool->idata, conn, FALSE);
|
||||
else
|
||||
Curl_cshutdn_add(&data->multi->cshutdn, conn, cpool->num_conn);
|
||||
}
|
||||
|
||||
void Curl_conn_terminate(struct Curl_easy *data,
|
||||
struct connectdata *conn,
|
||||
bool aborted)
|
||||
|
||||
25
lib/cw-out.c
25
lib/cw-out.c
@@ -101,22 +101,6 @@ struct cw_out_ctx {
|
||||
BIT(errored);
|
||||
};
|
||||
|
||||
static CURLcode cw_out_write(struct Curl_easy *data,
|
||||
struct Curl_cwriter *writer, int type,
|
||||
const char *buf, size_t nbytes);
|
||||
static void cw_out_close(struct Curl_easy *data, struct Curl_cwriter *writer);
|
||||
static CURLcode cw_out_init(struct Curl_easy *data,
|
||||
struct Curl_cwriter *writer);
|
||||
|
||||
const struct Curl_cwtype Curl_cwt_out = {
|
||||
"cw-out",
|
||||
NULL,
|
||||
cw_out_init,
|
||||
cw_out_write,
|
||||
cw_out_close,
|
||||
sizeof(struct cw_out_ctx)
|
||||
};
|
||||
|
||||
static CURLcode cw_out_init(struct Curl_easy *data,
|
||||
struct Curl_cwriter *writer)
|
||||
{
|
||||
@@ -455,6 +439,15 @@ static CURLcode cw_out_write(struct Curl_easy *data,
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
const struct Curl_cwtype Curl_cwt_out = {
|
||||
"cw-out",
|
||||
NULL,
|
||||
cw_out_init,
|
||||
cw_out_write,
|
||||
cw_out_close,
|
||||
sizeof(struct cw_out_ctx)
|
||||
};
|
||||
|
||||
bool Curl_cw_out_is_paused(struct Curl_easy *data)
|
||||
{
|
||||
struct Curl_cwriter *cw_out;
|
||||
|
||||
@@ -70,23 +70,6 @@ struct cw_pause_ctx {
|
||||
size_t buf_total;
|
||||
};
|
||||
|
||||
static CURLcode cw_pause_write(struct Curl_easy *data,
|
||||
struct Curl_cwriter *writer, int type,
|
||||
const char *buf, size_t nbytes);
|
||||
static void cw_pause_close(struct Curl_easy *data,
|
||||
struct Curl_cwriter *writer);
|
||||
static CURLcode cw_pause_init(struct Curl_easy *data,
|
||||
struct Curl_cwriter *writer);
|
||||
|
||||
const struct Curl_cwtype Curl_cwt_pause = {
|
||||
"cw-pause",
|
||||
NULL,
|
||||
cw_pause_init,
|
||||
cw_pause_write,
|
||||
cw_pause_close,
|
||||
sizeof(struct cw_pause_ctx)
|
||||
};
|
||||
|
||||
static CURLcode cw_pause_init(struct Curl_easy *data,
|
||||
struct Curl_cwriter *writer)
|
||||
{
|
||||
@@ -220,6 +203,15 @@ static CURLcode cw_pause_write(struct Curl_easy *data,
|
||||
return result;
|
||||
}
|
||||
|
||||
const struct Curl_cwtype Curl_cwt_pause = {
|
||||
"cw-pause",
|
||||
NULL,
|
||||
cw_pause_init,
|
||||
cw_pause_write,
|
||||
cw_pause_close,
|
||||
sizeof(struct cw_pause_ctx)
|
||||
};
|
||||
|
||||
CURLcode Curl_cw_pause_flush(struct Curl_easy *data)
|
||||
{
|
||||
struct Curl_cwriter *cw_pause;
|
||||
|
||||
427
lib/http2.c
427
lib/http2.c
@@ -124,7 +124,59 @@ struct cf_h2_ctx {
|
||||
#undef CF_CTX_CALL_DATA
|
||||
#define CF_CTX_CALL_DATA(cf) ((struct cf_h2_ctx *)(cf)->ctx)->call_data
|
||||
|
||||
static void h2_stream_hash_free(unsigned int id, void *stream);
|
||||
/**
|
||||
* All about the H2 internals of a stream
|
||||
*/
|
||||
struct h2_stream_ctx {
|
||||
struct bufq sendbuf; /* request buffer */
|
||||
struct h1_req_parser h1; /* parsing the request */
|
||||
struct dynhds resp_trailers; /* response trailer fields */
|
||||
size_t resp_hds_len; /* amount of response header bytes in recvbuf */
|
||||
curl_off_t nrcvd_data; /* number of DATA bytes received */
|
||||
|
||||
char **push_headers; /* allocated array */
|
||||
size_t push_headers_used; /* number of entries filled in */
|
||||
size_t push_headers_alloc; /* number of entries allocated */
|
||||
|
||||
int status_code; /* HTTP response status code */
|
||||
uint32_t error; /* stream error code */
|
||||
CURLcode xfer_result; /* Result of writing out response */
|
||||
int32_t local_window_size; /* the local recv window size */
|
||||
int32_t id; /* HTTP/2 protocol identifier for stream */
|
||||
BIT(resp_hds_complete); /* we have a complete, final response */
|
||||
BIT(closed); /* TRUE on stream close */
|
||||
BIT(reset); /* TRUE on stream reset */
|
||||
BIT(close_handled); /* TRUE if stream closure is handled by libcurl */
|
||||
BIT(bodystarted);
|
||||
BIT(body_eos); /* the complete body has been added to `sendbuf` and
|
||||
* is being/has been processed from there. */
|
||||
BIT(write_paused); /* stream write is paused */
|
||||
};
|
||||
|
||||
static void free_push_headers(struct h2_stream_ctx *stream)
|
||||
{
|
||||
size_t i;
|
||||
for(i = 0; i < stream->push_headers_used; i++)
|
||||
curlx_free(stream->push_headers[i]);
|
||||
Curl_safefree(stream->push_headers);
|
||||
stream->push_headers_used = 0;
|
||||
}
|
||||
|
||||
static void h2_stream_ctx_free(struct h2_stream_ctx *stream)
|
||||
{
|
||||
Curl_bufq_free(&stream->sendbuf);
|
||||
Curl_h1_req_parse_free(&stream->h1);
|
||||
Curl_dynhds_free(&stream->resp_trailers);
|
||||
free_push_headers(stream);
|
||||
curlx_free(stream);
|
||||
}
|
||||
|
||||
static void h2_stream_hash_free(unsigned int id, void *stream)
|
||||
{
|
||||
(void)id;
|
||||
DEBUGASSERT(stream);
|
||||
h2_stream_ctx_free((struct h2_stream_ctx *)stream);
|
||||
}
|
||||
|
||||
static void cf_h2_ctx_init(struct cf_h2_ctx *ctx, bool via_h1_upgrade)
|
||||
{
|
||||
@@ -212,41 +264,6 @@ static CURLcode cf_h2_update_settings(struct cf_h2_ctx *ctx,
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static CURLcode nw_out_flush(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data);
|
||||
|
||||
static CURLcode h2_progress_egress(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data);
|
||||
|
||||
/**
|
||||
* All about the H2 internals of a stream
|
||||
*/
|
||||
struct h2_stream_ctx {
|
||||
struct bufq sendbuf; /* request buffer */
|
||||
struct h1_req_parser h1; /* parsing the request */
|
||||
struct dynhds resp_trailers; /* response trailer fields */
|
||||
size_t resp_hds_len; /* amount of response header bytes in recvbuf */
|
||||
curl_off_t nrcvd_data; /* number of DATA bytes received */
|
||||
|
||||
char **push_headers; /* allocated array */
|
||||
size_t push_headers_used; /* number of entries filled in */
|
||||
size_t push_headers_alloc; /* number of entries allocated */
|
||||
|
||||
int status_code; /* HTTP response status code */
|
||||
uint32_t error; /* stream error code */
|
||||
CURLcode xfer_result; /* Result of writing out response */
|
||||
int32_t local_window_size; /* the local recv window size */
|
||||
int32_t id; /* HTTP/2 protocol identifier for stream */
|
||||
BIT(resp_hds_complete); /* we have a complete, final response */
|
||||
BIT(closed); /* TRUE on stream close */
|
||||
BIT(reset); /* TRUE on stream reset */
|
||||
BIT(close_handled); /* TRUE if stream closure is handled by libcurl */
|
||||
BIT(bodystarted);
|
||||
BIT(body_eos); /* the complete body has been added to `sendbuf` and
|
||||
* is being/has been processed from there. */
|
||||
BIT(write_paused); /* stream write is paused */
|
||||
};
|
||||
|
||||
#define H2_STREAM_CTX(ctx, data) \
|
||||
((struct h2_stream_ctx *)( \
|
||||
data? Curl_uint32_hash_get(&(ctx)->streams, (data)->mid) : NULL))
|
||||
@@ -275,31 +292,6 @@ static struct h2_stream_ctx *h2_stream_ctx_create(struct cf_h2_ctx *ctx)
|
||||
return stream;
|
||||
}
|
||||
|
||||
static void free_push_headers(struct h2_stream_ctx *stream)
|
||||
{
|
||||
size_t i;
|
||||
for(i = 0; i < stream->push_headers_used; i++)
|
||||
curlx_free(stream->push_headers[i]);
|
||||
Curl_safefree(stream->push_headers);
|
||||
stream->push_headers_used = 0;
|
||||
}
|
||||
|
||||
static void h2_stream_ctx_free(struct h2_stream_ctx *stream)
|
||||
{
|
||||
Curl_bufq_free(&stream->sendbuf);
|
||||
Curl_h1_req_parse_free(&stream->h1);
|
||||
Curl_dynhds_free(&stream->resp_trailers);
|
||||
free_push_headers(stream);
|
||||
curlx_free(stream);
|
||||
}
|
||||
|
||||
static void h2_stream_hash_free(unsigned int id, void *stream)
|
||||
{
|
||||
(void)id;
|
||||
DEBUGASSERT(stream);
|
||||
h2_stream_ctx_free((struct h2_stream_ctx *)stream);
|
||||
}
|
||||
|
||||
#ifdef NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE
|
||||
static int32_t cf_h2_get_desired_local_win(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data)
|
||||
@@ -406,6 +398,29 @@ static CURLcode http2_data_setup(struct Curl_cfilter *cf,
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static CURLcode nw_out_flush(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
struct cf_h2_ctx *ctx = cf->ctx;
|
||||
size_t nwritten;
|
||||
CURLcode result;
|
||||
|
||||
if(Curl_bufq_is_empty(&ctx->outbufq))
|
||||
return CURLE_OK;
|
||||
|
||||
result = Curl_cf_send_bufq(cf->next, data, &ctx->outbufq, NULL, 0,
|
||||
&nwritten);
|
||||
if(result) {
|
||||
if(result == CURLE_AGAIN) {
|
||||
CURL_TRC_CF(data, cf, "flush nw send buffer(%zu) -> EAGAIN",
|
||||
Curl_bufq_len(&ctx->outbufq));
|
||||
ctx->nw_out_blocked = 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return Curl_bufq_is_empty(&ctx->outbufq) ? CURLE_OK : CURLE_AGAIN;
|
||||
}
|
||||
|
||||
static void http2_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
|
||||
{
|
||||
struct cf_h2_ctx *ctx = cf->ctx;
|
||||
@@ -464,154 +479,6 @@ static int h2_client_new(struct Curl_cfilter *cf,
|
||||
return rc;
|
||||
}
|
||||
|
||||
static ssize_t send_callback(nghttp2_session *h2,
|
||||
const uint8_t *mem, size_t length, int flags,
|
||||
void *userp);
|
||||
static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
|
||||
void *userp);
|
||||
static int cf_h2_on_invalid_frame_recv(nghttp2_session *session,
|
||||
const nghttp2_frame *frame,
|
||||
int lib_error_code,
|
||||
void *user_data);
|
||||
#ifndef CURL_DISABLE_VERBOSE_STRINGS
|
||||
static int on_frame_send(nghttp2_session *session, const nghttp2_frame *frame,
|
||||
void *userp);
|
||||
#endif
|
||||
static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
|
||||
int32_t stream_id,
|
||||
const uint8_t *mem, size_t len, void *userp);
|
||||
static int on_stream_close(nghttp2_session *session, int32_t stream_id,
|
||||
uint32_t error_code, void *userp);
|
||||
static int on_begin_headers(nghttp2_session *session,
|
||||
const nghttp2_frame *frame, void *userp);
|
||||
static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
|
||||
const uint8_t *name, size_t namelen,
|
||||
const uint8_t *value, size_t valuelen,
|
||||
uint8_t flags,
|
||||
void *userp);
|
||||
#ifndef CURL_DISABLE_VERBOSE_STRINGS
|
||||
static int error_callback(nghttp2_session *session, const char *msg,
|
||||
size_t len, void *userp);
|
||||
#endif
|
||||
static CURLcode cf_h2_ctx_open(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
struct cf_h2_ctx *ctx = cf->ctx;
|
||||
struct h2_stream_ctx *stream;
|
||||
CURLcode result = CURLE_OUT_OF_MEMORY;
|
||||
int rc;
|
||||
nghttp2_session_callbacks *cbs = NULL;
|
||||
|
||||
DEBUGASSERT(!ctx->h2);
|
||||
DEBUGASSERT(ctx->initialized);
|
||||
|
||||
rc = nghttp2_session_callbacks_new(&cbs);
|
||||
if(rc) {
|
||||
failf(data, "Could not initialize nghttp2 callbacks");
|
||||
goto out;
|
||||
}
|
||||
|
||||
nghttp2_session_callbacks_set_send_callback(cbs, send_callback);
|
||||
nghttp2_session_callbacks_set_on_frame_recv_callback(cbs, on_frame_recv);
|
||||
nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(cbs,
|
||||
cf_h2_on_invalid_frame_recv);
|
||||
#ifndef CURL_DISABLE_VERBOSE_STRINGS
|
||||
nghttp2_session_callbacks_set_on_frame_send_callback(cbs, on_frame_send);
|
||||
#endif
|
||||
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
|
||||
cbs, on_data_chunk_recv);
|
||||
nghttp2_session_callbacks_set_on_stream_close_callback(cbs, on_stream_close);
|
||||
nghttp2_session_callbacks_set_on_begin_headers_callback(
|
||||
cbs, on_begin_headers);
|
||||
nghttp2_session_callbacks_set_on_header_callback(cbs, on_header);
|
||||
#ifndef CURL_DISABLE_VERBOSE_STRINGS
|
||||
nghttp2_session_callbacks_set_error_callback(cbs, error_callback);
|
||||
#endif
|
||||
|
||||
/* The nghttp2 session is not yet setup, do it */
|
||||
rc = h2_client_new(cf, cbs);
|
||||
if(rc) {
|
||||
failf(data, "Could not initialize nghttp2");
|
||||
goto out;
|
||||
}
|
||||
ctx->max_concurrent_streams = DEFAULT_MAX_CONCURRENT_STREAMS;
|
||||
|
||||
if(ctx->via_h1_upgrade) {
|
||||
/* HTTP/1.1 Upgrade issued. H2 Settings have already been submitted
|
||||
* in the H1 request and we upgrade from there. This stream
|
||||
* is opened implicitly as #1. */
|
||||
uint8_t binsettings[H2_BINSETTINGS_LEN];
|
||||
ssize_t rclen;
|
||||
size_t binlen; /* length of the binsettings data */
|
||||
|
||||
rclen = populate_binsettings(binsettings, data);
|
||||
|
||||
if(!curlx_sztouz(rclen, &binlen) || !binlen) {
|
||||
failf(data, "nghttp2 unexpectedly failed on pack_settings_payload");
|
||||
result = CURLE_FAILED_INIT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
result = http2_data_setup(cf, data, &stream);
|
||||
if(result)
|
||||
goto out;
|
||||
DEBUGASSERT(stream);
|
||||
stream->id = 1;
|
||||
/* queue SETTINGS frame (again) */
|
||||
rc = nghttp2_session_upgrade2(ctx->h2, binsettings, binlen,
|
||||
data->state.httpreq == HTTPREQ_HEAD,
|
||||
NULL);
|
||||
if(rc) {
|
||||
failf(data, "nghttp2_session_upgrade2() failed: %s(%d)",
|
||||
nghttp2_strerror(rc), rc);
|
||||
result = CURLE_HTTP2;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = nghttp2_session_set_stream_user_data(ctx->h2, stream->id,
|
||||
data);
|
||||
if(rc) {
|
||||
infof(data, "http/2: failed to set user_data for stream %u",
|
||||
stream->id);
|
||||
DEBUGASSERT(0);
|
||||
}
|
||||
CURL_TRC_CF(data, cf, "created session via Upgrade");
|
||||
}
|
||||
else {
|
||||
nghttp2_settings_entry iv[H2_SETTINGS_IV_LEN];
|
||||
size_t ivlen;
|
||||
|
||||
ivlen = populate_settings(iv, data, ctx);
|
||||
rc = nghttp2_submit_settings(ctx->h2, NGHTTP2_FLAG_NONE,
|
||||
iv, ivlen);
|
||||
if(rc) {
|
||||
failf(data, "nghttp2_submit_settings() failed: %s(%d)",
|
||||
nghttp2_strerror(rc), rc);
|
||||
result = CURLE_HTTP2;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
rc = nghttp2_session_set_local_window_size(ctx->h2, NGHTTP2_FLAG_NONE, 0,
|
||||
HTTP2_HUGE_WINDOW_SIZE);
|
||||
if(rc) {
|
||||
failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)",
|
||||
nghttp2_strerror(rc), rc);
|
||||
result = CURLE_HTTP2;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* all set, traffic will be send on connect */
|
||||
result = CURLE_OK;
|
||||
CURL_TRC_CF(data, cf, "[0] created h2 session%s",
|
||||
ctx->via_h1_upgrade ? " (via h1 upgrade)" : "");
|
||||
|
||||
out:
|
||||
if(cbs)
|
||||
nghttp2_session_callbacks_del(cbs);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns nonzero if current HTTP/2 session should be closed.
|
||||
*/
|
||||
@@ -738,29 +605,6 @@ void Curl_http2_ver(char *p, size_t len)
|
||||
(void)curl_msnprintf(p, len, "nghttp2/%s", h2->version_str);
|
||||
}
|
||||
|
||||
static CURLcode nw_out_flush(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
struct cf_h2_ctx *ctx = cf->ctx;
|
||||
size_t nwritten;
|
||||
CURLcode result;
|
||||
|
||||
if(Curl_bufq_is_empty(&ctx->outbufq))
|
||||
return CURLE_OK;
|
||||
|
||||
result = Curl_cf_send_bufq(cf->next, data, &ctx->outbufq, NULL, 0,
|
||||
&nwritten);
|
||||
if(result) {
|
||||
if(result == CURLE_AGAIN) {
|
||||
CURL_TRC_CF(data, cf, "flush nw send buffer(%zu) -> EAGAIN",
|
||||
Curl_bufq_len(&ctx->outbufq));
|
||||
ctx->nw_out_blocked = 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return Curl_bufq_is_empty(&ctx->outbufq) ? CURLE_OK : CURLE_AGAIN;
|
||||
}
|
||||
|
||||
/*
|
||||
* The implementation of nghttp2_send_callback type. Here we write |data| with
|
||||
* size |length| to the network and return the number of bytes actually
|
||||
@@ -2533,6 +2377,125 @@ static CURLcode cf_h2_adjust_pollset(struct Curl_cfilter *cf,
|
||||
return result;
|
||||
}
|
||||
|
||||
static CURLcode cf_h2_ctx_open(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
struct cf_h2_ctx *ctx = cf->ctx;
|
||||
struct h2_stream_ctx *stream;
|
||||
CURLcode result = CURLE_OUT_OF_MEMORY;
|
||||
int rc;
|
||||
nghttp2_session_callbacks *cbs = NULL;
|
||||
|
||||
DEBUGASSERT(!ctx->h2);
|
||||
DEBUGASSERT(ctx->initialized);
|
||||
|
||||
rc = nghttp2_session_callbacks_new(&cbs);
|
||||
if(rc) {
|
||||
failf(data, "Could not initialize nghttp2 callbacks");
|
||||
goto out;
|
||||
}
|
||||
|
||||
nghttp2_session_callbacks_set_send_callback(cbs, send_callback);
|
||||
nghttp2_session_callbacks_set_on_frame_recv_callback(cbs, on_frame_recv);
|
||||
nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(cbs,
|
||||
cf_h2_on_invalid_frame_recv);
|
||||
#ifndef CURL_DISABLE_VERBOSE_STRINGS
|
||||
nghttp2_session_callbacks_set_on_frame_send_callback(cbs, on_frame_send);
|
||||
#endif
|
||||
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
|
||||
cbs, on_data_chunk_recv);
|
||||
nghttp2_session_callbacks_set_on_stream_close_callback(cbs, on_stream_close);
|
||||
nghttp2_session_callbacks_set_on_begin_headers_callback(
|
||||
cbs, on_begin_headers);
|
||||
nghttp2_session_callbacks_set_on_header_callback(cbs, on_header);
|
||||
#ifndef CURL_DISABLE_VERBOSE_STRINGS
|
||||
nghttp2_session_callbacks_set_error_callback(cbs, error_callback);
|
||||
#endif
|
||||
|
||||
/* The nghttp2 session is not yet setup, do it */
|
||||
rc = h2_client_new(cf, cbs);
|
||||
if(rc) {
|
||||
failf(data, "Could not initialize nghttp2");
|
||||
goto out;
|
||||
}
|
||||
ctx->max_concurrent_streams = DEFAULT_MAX_CONCURRENT_STREAMS;
|
||||
|
||||
if(ctx->via_h1_upgrade) {
|
||||
/* HTTP/1.1 Upgrade issued. H2 Settings have already been submitted
|
||||
* in the H1 request and we upgrade from there. This stream
|
||||
* is opened implicitly as #1. */
|
||||
uint8_t binsettings[H2_BINSETTINGS_LEN];
|
||||
ssize_t rclen;
|
||||
size_t binlen; /* length of the binsettings data */
|
||||
|
||||
rclen = populate_binsettings(binsettings, data);
|
||||
|
||||
if(!curlx_sztouz(rclen, &binlen) || !binlen) {
|
||||
failf(data, "nghttp2 unexpectedly failed on pack_settings_payload");
|
||||
result = CURLE_FAILED_INIT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
result = http2_data_setup(cf, data, &stream);
|
||||
if(result)
|
||||
goto out;
|
||||
DEBUGASSERT(stream);
|
||||
stream->id = 1;
|
||||
/* queue SETTINGS frame (again) */
|
||||
rc = nghttp2_session_upgrade2(ctx->h2, binsettings, binlen,
|
||||
data->state.httpreq == HTTPREQ_HEAD,
|
||||
NULL);
|
||||
if(rc) {
|
||||
failf(data, "nghttp2_session_upgrade2() failed: %s(%d)",
|
||||
nghttp2_strerror(rc), rc);
|
||||
result = CURLE_HTTP2;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = nghttp2_session_set_stream_user_data(ctx->h2, stream->id,
|
||||
data);
|
||||
if(rc) {
|
||||
infof(data, "http/2: failed to set user_data for stream %u",
|
||||
stream->id);
|
||||
DEBUGASSERT(0);
|
||||
}
|
||||
CURL_TRC_CF(data, cf, "created session via Upgrade");
|
||||
}
|
||||
else {
|
||||
nghttp2_settings_entry iv[H2_SETTINGS_IV_LEN];
|
||||
size_t ivlen;
|
||||
|
||||
ivlen = populate_settings(iv, data, ctx);
|
||||
rc = nghttp2_submit_settings(ctx->h2, NGHTTP2_FLAG_NONE,
|
||||
iv, ivlen);
|
||||
if(rc) {
|
||||
failf(data, "nghttp2_submit_settings() failed: %s(%d)",
|
||||
nghttp2_strerror(rc), rc);
|
||||
result = CURLE_HTTP2;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
rc = nghttp2_session_set_local_window_size(ctx->h2, NGHTTP2_FLAG_NONE, 0,
|
||||
HTTP2_HUGE_WINDOW_SIZE);
|
||||
if(rc) {
|
||||
failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)",
|
||||
nghttp2_strerror(rc), rc);
|
||||
result = CURLE_HTTP2;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* all set, traffic will be send on connect */
|
||||
result = CURLE_OK;
|
||||
CURL_TRC_CF(data, cf, "[0] created h2 session%s",
|
||||
ctx->via_h1_upgrade ? " (via h1 upgrade)" : "");
|
||||
|
||||
out:
|
||||
if(cbs)
|
||||
nghttp2_session_callbacks_del(cbs);
|
||||
return result;
|
||||
}
|
||||
|
||||
static CURLcode cf_h2_connect(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
bool *done)
|
||||
|
||||
31
lib/mime.c
31
lib/mime.c
@@ -53,30 +53,8 @@ struct Curl_easy;
|
||||
|
||||
static size_t mime_subparts_read(char *buffer, size_t size, size_t nitems,
|
||||
void *instream, bool *hasread);
|
||||
|
||||
/* Encoders. */
|
||||
static size_t encoder_nop_read(char *buffer, size_t size, bool ateof,
|
||||
curl_mimepart *part);
|
||||
static curl_off_t encoder_nop_size(curl_mimepart *part);
|
||||
static size_t encoder_7bit_read(char *buffer, size_t size, bool ateof,
|
||||
curl_mimepart *part);
|
||||
static size_t encoder_base64_read(char *buffer, size_t size, bool ateof,
|
||||
curl_mimepart *part);
|
||||
static curl_off_t encoder_base64_size(curl_mimepart *part);
|
||||
static size_t encoder_qp_read(char *buffer, size_t size, bool ateof,
|
||||
curl_mimepart *part);
|
||||
static curl_off_t encoder_qp_size(curl_mimepart *part);
|
||||
static curl_off_t mime_size(curl_mimepart *part);
|
||||
|
||||
static const struct mime_encoder encoders[] = {
|
||||
{ "binary", encoder_nop_read, encoder_nop_size },
|
||||
{ "8bit", encoder_nop_read, encoder_nop_size },
|
||||
{ "7bit", encoder_7bit_read, encoder_nop_size },
|
||||
{ "base64", encoder_base64_read, encoder_base64_size },
|
||||
{ "quoted-printable", encoder_qp_read, encoder_qp_size },
|
||||
{ ZERO_NULL, ZERO_NULL, ZERO_NULL }
|
||||
};
|
||||
|
||||
/* Quoted-printable character class table.
|
||||
*
|
||||
* We cannot rely on ctype functions since quoted-printable input data
|
||||
@@ -1435,6 +1413,15 @@ CURLcode curl_mime_type(curl_mimepart *part, const char *mimetype)
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static const struct mime_encoder encoders[] = {
|
||||
{ "binary", encoder_nop_read, encoder_nop_size },
|
||||
{ "8bit", encoder_nop_read, encoder_nop_size },
|
||||
{ "7bit", encoder_7bit_read, encoder_nop_size },
|
||||
{ "base64", encoder_base64_read, encoder_base64_size },
|
||||
{ "quoted-printable", encoder_qp_read, encoder_qp_size },
|
||||
{ ZERO_NULL, ZERO_NULL, ZERO_NULL }
|
||||
};
|
||||
|
||||
/* Set mime data transfer encoder. */
|
||||
CURLcode curl_mime_encoder(curl_mimepart *part, const char *encoding)
|
||||
{
|
||||
|
||||
200
lib/rtsp.c
200
lib/rtsp.c
@@ -73,31 +73,6 @@ struct RTSP {
|
||||
#define RTP_PKT_LENGTH(p) ((((unsigned int)((unsigned char)((p)[2]))) << 8) | \
|
||||
((unsigned int)((unsigned char)((p)[3]))))
|
||||
|
||||
/*
|
||||
* Parse and write out an RTSP response.
|
||||
* @param data the transfer
|
||||
* @param conn the connection
|
||||
* @param buf data read from connection
|
||||
* @param blen amount of data in buf
|
||||
* @param is_eos TRUE iff this is the last write
|
||||
* @param readmore out, TRUE iff complete buf was consumed and more data
|
||||
* is needed
|
||||
*/
|
||||
static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data,
|
||||
const char *buf,
|
||||
size_t blen,
|
||||
bool is_eos);
|
||||
static CURLcode rtsp_rtp_write_resp_hd(struct Curl_easy *data,
|
||||
const char *buf,
|
||||
size_t blen,
|
||||
bool is_eos);
|
||||
|
||||
static CURLcode rtsp_setup_connection(struct Curl_easy *data,
|
||||
struct connectdata *conn);
|
||||
static uint32_t rtsp_conncheck(struct Curl_easy *data,
|
||||
struct connectdata *check,
|
||||
uint32_t checks_to_perform);
|
||||
|
||||
/* this returns the socket to wait for in the DO and DOING state for the multi
|
||||
interface and then we are always _sending_ a request and thus we wait for
|
||||
the single socket to become writable only */
|
||||
@@ -108,11 +83,6 @@ static CURLcode rtsp_do_pollset(struct Curl_easy *data,
|
||||
return Curl_pollset_add_out(data, ps, data->conn->sock[FIRSTSOCKET]);
|
||||
}
|
||||
|
||||
static CURLcode rtp_client_write(struct Curl_easy *data, const char *ptr,
|
||||
size_t len);
|
||||
static CURLcode rtsp_parse_transport(struct Curl_easy *data,
|
||||
const char *transport);
|
||||
|
||||
#define MAX_RTP_BUFFERSIZE 1000000 /* arbitrary */
|
||||
|
||||
static void rtsp_easy_dtor(void *key, size_t klen, void *entry)
|
||||
@@ -649,6 +619,48 @@ static CURLcode rtp_write_body_junk(struct Curl_easy *data,
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static CURLcode rtp_client_write(struct Curl_easy *data, const char *ptr,
|
||||
size_t len)
|
||||
{
|
||||
size_t wrote;
|
||||
curl_write_callback writeit;
|
||||
void *user_ptr;
|
||||
|
||||
if(len == 0) {
|
||||
failf(data, "Cannot write a 0 size RTP packet.");
|
||||
return CURLE_WRITE_ERROR;
|
||||
}
|
||||
|
||||
/* If the user has configured CURLOPT_INTERLEAVEFUNCTION then use that
|
||||
function and any configured CURLOPT_INTERLEAVEDATA to write out the RTP
|
||||
data. Otherwise, use the CURLOPT_WRITEFUNCTION with the CURLOPT_WRITEDATA
|
||||
pointer to write out the RTP data. */
|
||||
if(data->set.fwrite_rtp) {
|
||||
writeit = data->set.fwrite_rtp;
|
||||
user_ptr = data->set.rtp_out;
|
||||
}
|
||||
else {
|
||||
writeit = data->set.fwrite_func;
|
||||
user_ptr = data->set.out;
|
||||
}
|
||||
|
||||
Curl_set_in_callback(data, TRUE);
|
||||
wrote = writeit((char *)CURL_UNCONST(ptr), 1, len, user_ptr);
|
||||
Curl_set_in_callback(data, FALSE);
|
||||
|
||||
if(CURL_WRITEFUNC_PAUSE == wrote) {
|
||||
failf(data, "Cannot pause RTP");
|
||||
return CURLE_WRITE_ERROR;
|
||||
}
|
||||
|
||||
if(wrote != len) {
|
||||
failf(data, "Failed writing RTP data");
|
||||
return CURLE_WRITE_ERROR;
|
||||
}
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static CURLcode rtsp_filter_rtp(struct Curl_easy *data,
|
||||
struct rtsp_conn *rtspc,
|
||||
const char *buf,
|
||||
@@ -812,6 +824,16 @@ out:
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse and write out an RTSP response.
|
||||
* @param data the transfer
|
||||
* @param conn the connection
|
||||
* @param buf data read from connection
|
||||
* @param blen amount of data in buf
|
||||
* @param is_eos TRUE iff this is the last write
|
||||
* @param readmore out, TRUE iff complete buf was consumed and more data
|
||||
* is needed
|
||||
*/
|
||||
static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data,
|
||||
const char *buf,
|
||||
size_t blen,
|
||||
@@ -908,45 +930,46 @@ static CURLcode rtsp_rtp_write_resp_hd(struct Curl_easy *data,
|
||||
return rtsp_rtp_write_resp(data, buf, blen, is_eos);
|
||||
}
|
||||
|
||||
static CURLcode rtp_client_write(struct Curl_easy *data, const char *ptr,
|
||||
size_t len)
|
||||
static CURLcode rtsp_parse_transport(struct Curl_easy *data,
|
||||
const char *transport)
|
||||
{
|
||||
size_t wrote;
|
||||
curl_write_callback writeit;
|
||||
void *user_ptr;
|
||||
|
||||
if(len == 0) {
|
||||
failf(data, "Cannot write a 0 size RTP packet.");
|
||||
return CURLE_WRITE_ERROR;
|
||||
/* If we receive multiple Transport response-headers, the interleaved
|
||||
channels of each response header is recorded and used together for
|
||||
subsequent data validity checks.*/
|
||||
/* e.g.: ' RTP/AVP/TCP;unicast;interleaved=5-6' */
|
||||
const char *start, *end;
|
||||
start = transport;
|
||||
while(start && *start) {
|
||||
curlx_str_passblanks(&start);
|
||||
end = strchr(start, ';');
|
||||
if(checkprefix("interleaved=", start)) {
|
||||
curl_off_t chan1, chan2, chan;
|
||||
const char *p = start + 12;
|
||||
if(!curlx_str_number(&p, &chan1, 255)) {
|
||||
unsigned char *rtp_channel_mask = data->state.rtp_channel_mask;
|
||||
chan2 = chan1;
|
||||
if(!curlx_str_single(&p, '-')) {
|
||||
if(curlx_str_number(&p, &chan2, 255)) {
|
||||
infof(data, "Unable to read the interleaved parameter from "
|
||||
"Transport header: [%s]", transport);
|
||||
chan2 = chan1;
|
||||
}
|
||||
}
|
||||
for(chan = chan1; chan <= chan2; chan++) {
|
||||
int idx = (int)chan / 8;
|
||||
int off = (int)chan % 8;
|
||||
rtp_channel_mask[idx] |= (unsigned char)(1 << off);
|
||||
}
|
||||
}
|
||||
else {
|
||||
infof(data, "Unable to read the interleaved parameter from "
|
||||
"Transport header: [%s]", transport);
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* skip to next parameter */
|
||||
start = (!end) ? end : (end + 1);
|
||||
}
|
||||
|
||||
/* If the user has configured CURLOPT_INTERLEAVEFUNCTION then use that
|
||||
function and any configured CURLOPT_INTERLEAVEDATA to write out the RTP
|
||||
data. Otherwise, use the CURLOPT_WRITEFUNCTION with the CURLOPT_WRITEDATA
|
||||
pointer to write out the RTP data. */
|
||||
if(data->set.fwrite_rtp) {
|
||||
writeit = data->set.fwrite_rtp;
|
||||
user_ptr = data->set.rtp_out;
|
||||
}
|
||||
else {
|
||||
writeit = data->set.fwrite_func;
|
||||
user_ptr = data->set.out;
|
||||
}
|
||||
|
||||
Curl_set_in_callback(data, TRUE);
|
||||
wrote = writeit((char *)CURL_UNCONST(ptr), 1, len, user_ptr);
|
||||
Curl_set_in_callback(data, FALSE);
|
||||
|
||||
if(CURL_WRITEFUNC_PAUSE == wrote) {
|
||||
failf(data, "Cannot pause RTP");
|
||||
return CURLE_WRITE_ERROR;
|
||||
}
|
||||
|
||||
if(wrote != len) {
|
||||
failf(data, "Failed writing RTP data");
|
||||
return CURLE_WRITE_ERROR;
|
||||
}
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
@@ -1019,49 +1042,6 @@ CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, const char *header)
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static CURLcode rtsp_parse_transport(struct Curl_easy *data,
|
||||
const char *transport)
|
||||
{
|
||||
/* If we receive multiple Transport response-headers, the interleaved
|
||||
channels of each response header is recorded and used together for
|
||||
subsequent data validity checks.*/
|
||||
/* e.g.: ' RTP/AVP/TCP;unicast;interleaved=5-6' */
|
||||
const char *start, *end;
|
||||
start = transport;
|
||||
while(start && *start) {
|
||||
curlx_str_passblanks(&start);
|
||||
end = strchr(start, ';');
|
||||
if(checkprefix("interleaved=", start)) {
|
||||
curl_off_t chan1, chan2, chan;
|
||||
const char *p = start + 12;
|
||||
if(!curlx_str_number(&p, &chan1, 255)) {
|
||||
unsigned char *rtp_channel_mask = data->state.rtp_channel_mask;
|
||||
chan2 = chan1;
|
||||
if(!curlx_str_single(&p, '-')) {
|
||||
if(curlx_str_number(&p, &chan2, 255)) {
|
||||
infof(data, "Unable to read the interleaved parameter from "
|
||||
"Transport header: [%s]", transport);
|
||||
chan2 = chan1;
|
||||
}
|
||||
}
|
||||
for(chan = chan1; chan <= chan2; chan++) {
|
||||
int idx = (int)chan / 8;
|
||||
int off = (int)chan % 8;
|
||||
rtp_channel_mask[idx] |= (unsigned char)(1 << off);
|
||||
}
|
||||
}
|
||||
else {
|
||||
infof(data, "Unable to read the interleaved parameter from "
|
||||
"Transport header: [%s]", transport);
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* skip to next parameter */
|
||||
start = (!end) ? end : (end + 1);
|
||||
}
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* RTSP handler interface.
|
||||
*/
|
||||
|
||||
160
lib/sendf.c
160
lib/sendf.c
@@ -45,42 +45,6 @@
|
||||
#include "strerror.h"
|
||||
#include "progress.h"
|
||||
|
||||
|
||||
static CURLcode do_init_writer_stack(struct Curl_easy *data);
|
||||
|
||||
/* Curl_client_write() sends data to the write callback(s)
|
||||
|
||||
The bit pattern defines to what "streams" to write to. Body and/or header.
|
||||
The defines are in sendf.h of course.
|
||||
*/
|
||||
CURLcode Curl_client_write(struct Curl_easy *data,
|
||||
int type, const char *buf, size_t blen)
|
||||
{
|
||||
CURLcode result;
|
||||
|
||||
/* it is one of those, at least */
|
||||
DEBUGASSERT(type &
|
||||
(CLIENTWRITE_BODY | CLIENTWRITE_HEADER | CLIENTWRITE_INFO));
|
||||
/* BODY is only BODY (with optional EOS) */
|
||||
DEBUGASSERT(!(type & CLIENTWRITE_BODY) ||
|
||||
((type & ~(CLIENTWRITE_BODY | CLIENTWRITE_EOS)) == 0));
|
||||
/* INFO is only INFO (with optional EOS) */
|
||||
DEBUGASSERT(!(type & CLIENTWRITE_INFO) ||
|
||||
((type & ~(CLIENTWRITE_INFO | CLIENTWRITE_EOS)) == 0));
|
||||
|
||||
if(!data->req.writer_stack) {
|
||||
result = do_init_writer_stack(data);
|
||||
if(result)
|
||||
return result;
|
||||
DEBUGASSERT(data->req.writer_stack);
|
||||
}
|
||||
|
||||
result = Curl_cwriter_write(data, data->req.writer_stack, type, buf, blen);
|
||||
CURL_TRC_WRITE(data, "client_write(type=%x, len=%zu) -> %d",
|
||||
type, blen, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void cl_reset_writer(struct Curl_easy *data)
|
||||
{
|
||||
struct Curl_cwriter *writer = data->req.writer_stack;
|
||||
@@ -206,6 +170,7 @@ struct cw_download_ctx {
|
||||
struct Curl_cwriter super;
|
||||
BIT(started_response);
|
||||
};
|
||||
|
||||
/* Download client writer in phase CURL_CW_PROTOCOL that
|
||||
* sees the "real" download body data. */
|
||||
static CURLcode cw_download_write(struct Curl_easy *data,
|
||||
@@ -351,6 +316,84 @@ static const struct Curl_cwtype cw_raw = {
|
||||
sizeof(struct Curl_cwriter)
|
||||
};
|
||||
|
||||
static CURLcode do_init_writer_stack(struct Curl_easy *data)
|
||||
{
|
||||
struct Curl_cwriter *writer;
|
||||
CURLcode result;
|
||||
|
||||
DEBUGASSERT(!data->req.writer_stack);
|
||||
result = Curl_cwriter_create(&data->req.writer_stack,
|
||||
data, &Curl_cwt_out, CURL_CW_CLIENT);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
/* This places the "pause" writer behind the "download" writer that
|
||||
* is added below. Meaning the "download" can do checks on content length
|
||||
* and other things *before* write outs are buffered for paused transfers. */
|
||||
result = Curl_cwriter_create(&writer, data, &Curl_cwt_pause,
|
||||
CURL_CW_PROTOCOL);
|
||||
if(!result) {
|
||||
result = Curl_cwriter_add(data, writer);
|
||||
if(result)
|
||||
Curl_cwriter_free(data, writer);
|
||||
}
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
result = Curl_cwriter_create(&writer, data, &cw_download, CURL_CW_PROTOCOL);
|
||||
if(!result) {
|
||||
result = Curl_cwriter_add(data, writer);
|
||||
if(result)
|
||||
Curl_cwriter_free(data, writer);
|
||||
}
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
result = Curl_cwriter_create(&writer, data, &cw_raw, CURL_CW_RAW);
|
||||
if(!result) {
|
||||
result = Curl_cwriter_add(data, writer);
|
||||
if(result)
|
||||
Curl_cwriter_free(data, writer);
|
||||
}
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Curl_client_write() sends data to the write callback(s)
|
||||
|
||||
The bit pattern defines to what "streams" to write to. Body and/or header.
|
||||
The defines are in sendf.h of course.
|
||||
*/
|
||||
CURLcode Curl_client_write(struct Curl_easy *data,
|
||||
int type, const char *buf, size_t blen)
|
||||
{
|
||||
CURLcode result;
|
||||
|
||||
/* it is one of those, at least */
|
||||
DEBUGASSERT(type &
|
||||
(CLIENTWRITE_BODY | CLIENTWRITE_HEADER | CLIENTWRITE_INFO));
|
||||
/* BODY is only BODY (with optional EOS) */
|
||||
DEBUGASSERT(!(type & CLIENTWRITE_BODY) ||
|
||||
((type & ~(CLIENTWRITE_BODY | CLIENTWRITE_EOS)) == 0));
|
||||
/* INFO is only INFO (with optional EOS) */
|
||||
DEBUGASSERT(!(type & CLIENTWRITE_INFO) ||
|
||||
((type & ~(CLIENTWRITE_INFO | CLIENTWRITE_EOS)) == 0));
|
||||
|
||||
if(!data->req.writer_stack) {
|
||||
result = do_init_writer_stack(data);
|
||||
if(result)
|
||||
return result;
|
||||
DEBUGASSERT(data->req.writer_stack);
|
||||
}
|
||||
|
||||
result = Curl_cwriter_write(data, data->req.writer_stack, type, buf, blen);
|
||||
CURL_TRC_WRITE(data, "client_write(type=%x, len=%zu) -> %d",
|
||||
type, blen, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Create an unencoding writer stage using the given handler. */
|
||||
CURLcode Curl_cwriter_create(struct Curl_cwriter **pwriter,
|
||||
struct Curl_easy *data,
|
||||
@@ -400,51 +443,6 @@ size_t Curl_cwriter_count(struct Curl_easy *data, Curl_cwriter_phase phase)
|
||||
return n;
|
||||
}
|
||||
|
||||
static CURLcode do_init_writer_stack(struct Curl_easy *data)
|
||||
{
|
||||
struct Curl_cwriter *writer;
|
||||
CURLcode result;
|
||||
|
||||
DEBUGASSERT(!data->req.writer_stack);
|
||||
result = Curl_cwriter_create(&data->req.writer_stack,
|
||||
data, &Curl_cwt_out, CURL_CW_CLIENT);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
/* This places the "pause" writer behind the "download" writer that
|
||||
* is added below. Meaning the "download" can do checks on content length
|
||||
* and other things *before* write outs are buffered for paused transfers. */
|
||||
result = Curl_cwriter_create(&writer, data, &Curl_cwt_pause,
|
||||
CURL_CW_PROTOCOL);
|
||||
if(!result) {
|
||||
result = Curl_cwriter_add(data, writer);
|
||||
if(result)
|
||||
Curl_cwriter_free(data, writer);
|
||||
}
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
result = Curl_cwriter_create(&writer, data, &cw_download, CURL_CW_PROTOCOL);
|
||||
if(!result) {
|
||||
result = Curl_cwriter_add(data, writer);
|
||||
if(result)
|
||||
Curl_cwriter_free(data, writer);
|
||||
}
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
result = Curl_cwriter_create(&writer, data, &cw_raw, CURL_CW_RAW);
|
||||
if(!result) {
|
||||
result = Curl_cwriter_add(data, writer);
|
||||
if(result)
|
||||
Curl_cwriter_free(data, writer);
|
||||
}
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
CURLcode Curl_cwriter_add(struct Curl_easy *data,
|
||||
struct Curl_cwriter *writer)
|
||||
{
|
||||
|
||||
153
lib/urlapi.c
153
lib/urlapi.c
@@ -83,9 +83,6 @@ struct Curl_URL {
|
||||
|
||||
#define DEFAULT_SCHEME "https"
|
||||
|
||||
static CURLUcode parseurl_and_replace(const char *url, CURLU *u,
|
||||
unsigned int flags);
|
||||
|
||||
static void free_urlhandle(struct Curl_URL *u)
|
||||
{
|
||||
curlx_free(u->scheme);
|
||||
@@ -222,81 +219,6 @@ size_t Curl_is_absolute_url(const char *url, char *buf, size_t buflen,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Concatenate a relative URL onto a base URL making it absolute.
|
||||
*/
|
||||
static CURLUcode redirect_url(const char *base, const char *relurl,
|
||||
CURLU *u, unsigned int flags)
|
||||
{
|
||||
struct dynbuf urlbuf;
|
||||
bool host_changed = FALSE;
|
||||
const char *useurl = relurl;
|
||||
const char *cutoff = NULL;
|
||||
size_t prelen;
|
||||
CURLUcode uc;
|
||||
|
||||
/* protsep points to the start of the hostname, after [scheme]:// */
|
||||
const char *protsep = base + strlen(u->scheme) + 3;
|
||||
DEBUGASSERT(base && relurl && u); /* all set here */
|
||||
if(!base)
|
||||
return CURLUE_MALFORMED_INPUT; /* should never happen */
|
||||
|
||||
/* handle different relative URL types */
|
||||
switch(relurl[0]) {
|
||||
case '/':
|
||||
if(relurl[1] == '/') {
|
||||
/* protocol-relative URL: //example.com/path */
|
||||
cutoff = protsep;
|
||||
useurl = &relurl[2];
|
||||
host_changed = TRUE;
|
||||
}
|
||||
else
|
||||
/* absolute /path */
|
||||
cutoff = strchr(protsep, '/');
|
||||
break;
|
||||
|
||||
case '#':
|
||||
/* fragment-only change */
|
||||
if(u->fragment)
|
||||
cutoff = strchr(protsep, '#');
|
||||
break;
|
||||
|
||||
default:
|
||||
/* path or query-only change */
|
||||
if(u->query && u->query[0])
|
||||
/* remove existing query */
|
||||
cutoff = strchr(protsep, '?');
|
||||
else if(u->fragment && u->fragment[0])
|
||||
/* Remove existing fragment */
|
||||
cutoff = strchr(protsep, '#');
|
||||
|
||||
if(relurl[0] != '?') {
|
||||
/* append a relative path after the last slash */
|
||||
cutoff = memrchr(protsep, '/',
|
||||
cutoff ? (size_t)(cutoff - protsep) : strlen(protsep));
|
||||
if(cutoff)
|
||||
cutoff++; /* truncate after last slash */
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
prelen = cutoff ? (size_t)(cutoff - base) : strlen(base);
|
||||
|
||||
/* build new URL */
|
||||
curlx_dyn_init(&urlbuf, CURL_MAX_INPUT_LENGTH);
|
||||
|
||||
if(!curlx_dyn_addn(&urlbuf, base, prelen) &&
|
||||
!urlencode_str(&urlbuf, useurl, strlen(useurl), !host_changed, FALSE)) {
|
||||
uc = parseurl_and_replace(curlx_dyn_ptr(&urlbuf), u,
|
||||
flags & ~CURLU_PATH_AS_IS);
|
||||
}
|
||||
else
|
||||
uc = CURLUE_OUT_OF_MEMORY;
|
||||
|
||||
curlx_dyn_free(&urlbuf);
|
||||
return uc;
|
||||
}
|
||||
|
||||
/* scan for byte values <= 31, 127 and sometimes space */
|
||||
CURLUcode Curl_junkscan(const char *url, size_t *urllen, bool allowspace)
|
||||
{
|
||||
@@ -1286,6 +1208,81 @@ static CURLUcode parseurl_and_replace(const char *url, CURLU *u,
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Concatenate a relative URL onto a base URL making it absolute.
|
||||
*/
|
||||
static CURLUcode redirect_url(const char *base, const char *relurl,
|
||||
CURLU *u, unsigned int flags)
|
||||
{
|
||||
struct dynbuf urlbuf;
|
||||
bool host_changed = FALSE;
|
||||
const char *useurl = relurl;
|
||||
const char *cutoff = NULL;
|
||||
size_t prelen;
|
||||
CURLUcode uc;
|
||||
|
||||
/* protsep points to the start of the hostname, after [scheme]:// */
|
||||
const char *protsep = base + strlen(u->scheme) + 3;
|
||||
DEBUGASSERT(base && relurl && u); /* all set here */
|
||||
if(!base)
|
||||
return CURLUE_MALFORMED_INPUT; /* should never happen */
|
||||
|
||||
/* handle different relative URL types */
|
||||
switch(relurl[0]) {
|
||||
case '/':
|
||||
if(relurl[1] == '/') {
|
||||
/* protocol-relative URL: //example.com/path */
|
||||
cutoff = protsep;
|
||||
useurl = &relurl[2];
|
||||
host_changed = TRUE;
|
||||
}
|
||||
else
|
||||
/* absolute /path */
|
||||
cutoff = strchr(protsep, '/');
|
||||
break;
|
||||
|
||||
case '#':
|
||||
/* fragment-only change */
|
||||
if(u->fragment)
|
||||
cutoff = strchr(protsep, '#');
|
||||
break;
|
||||
|
||||
default:
|
||||
/* path or query-only change */
|
||||
if(u->query && u->query[0])
|
||||
/* remove existing query */
|
||||
cutoff = strchr(protsep, '?');
|
||||
else if(u->fragment && u->fragment[0])
|
||||
/* Remove existing fragment */
|
||||
cutoff = strchr(protsep, '#');
|
||||
|
||||
if(relurl[0] != '?') {
|
||||
/* append a relative path after the last slash */
|
||||
cutoff = memrchr(protsep, '/',
|
||||
cutoff ? (size_t)(cutoff - protsep) : strlen(protsep));
|
||||
if(cutoff)
|
||||
cutoff++; /* truncate after last slash */
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
prelen = cutoff ? (size_t)(cutoff - base) : strlen(base);
|
||||
|
||||
/* build new URL */
|
||||
curlx_dyn_init(&urlbuf, CURL_MAX_INPUT_LENGTH);
|
||||
|
||||
if(!curlx_dyn_addn(&urlbuf, base, prelen) &&
|
||||
!urlencode_str(&urlbuf, useurl, strlen(useurl), !host_changed, FALSE)) {
|
||||
uc = parseurl_and_replace(curlx_dyn_ptr(&urlbuf), u,
|
||||
flags & ~CURLU_PATH_AS_IS);
|
||||
}
|
||||
else
|
||||
uc = CURLUE_OUT_OF_MEMORY;
|
||||
|
||||
curlx_dyn_free(&urlbuf);
|
||||
return uc;
|
||||
}
|
||||
|
||||
/*
|
||||
*/
|
||||
CURLU *curl_url(void)
|
||||
|
||||
@@ -134,10 +134,6 @@
|
||||
static bool s_win_has_alpn;
|
||||
#endif
|
||||
|
||||
static CURLcode schannel_pkp_pin_peer_pubkey(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
const char *pinnedpubkey);
|
||||
|
||||
static void InitSecBuffer(SecBuffer *buffer, unsigned long BufType,
|
||||
void *BufDataPtr, unsigned long BufByteSize)
|
||||
{
|
||||
@@ -1115,6 +1111,74 @@ static CURLcode schannel_error(struct Curl_easy *data,
|
||||
}
|
||||
}
|
||||
|
||||
static CURLcode schannel_pkp_pin_peer_pubkey(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
const char *pinnedpubkey)
|
||||
{
|
||||
struct ssl_connect_data *connssl = cf->ctx;
|
||||
struct schannel_ssl_backend_data *backend =
|
||||
(struct schannel_ssl_backend_data *)connssl->backend;
|
||||
CERT_CONTEXT *pCertContextServer = NULL;
|
||||
|
||||
/* Result is returned to caller */
|
||||
CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH;
|
||||
|
||||
DEBUGASSERT(backend);
|
||||
|
||||
/* if a path was not specified, do not pin */
|
||||
if(!pinnedpubkey)
|
||||
return CURLE_OK;
|
||||
|
||||
do {
|
||||
SECURITY_STATUS sspi_status;
|
||||
const char *x509_der;
|
||||
DWORD x509_der_len;
|
||||
struct Curl_X509certificate x509_parsed;
|
||||
struct Curl_asn1Element *pubkey;
|
||||
|
||||
sspi_status =
|
||||
Curl_pSecFn->QueryContextAttributes(&backend->ctxt->ctxt_handle,
|
||||
SECPKG_ATTR_REMOTE_CERT_CONTEXT,
|
||||
&pCertContextServer);
|
||||
|
||||
if((sspi_status != SEC_E_OK) || !pCertContextServer) {
|
||||
char buffer[STRERROR_LEN];
|
||||
failf(data, "schannel: Failed to read remote certificate context: %s",
|
||||
Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
|
||||
break; /* failed */
|
||||
}
|
||||
|
||||
if(!(((pCertContextServer->dwCertEncodingType & X509_ASN_ENCODING) != 0) &&
|
||||
(pCertContextServer->cbCertEncoded > 0)))
|
||||
break;
|
||||
|
||||
x509_der = (const char *)pCertContextServer->pbCertEncoded;
|
||||
x509_der_len = pCertContextServer->cbCertEncoded;
|
||||
memset(&x509_parsed, 0, sizeof(x509_parsed));
|
||||
if(Curl_parseX509(&x509_parsed, x509_der, x509_der + x509_der_len))
|
||||
break;
|
||||
|
||||
pubkey = &x509_parsed.subjectPublicKeyInfo;
|
||||
if(!pubkey->header || pubkey->end <= pubkey->header) {
|
||||
failf(data, "SSL: failed retrieving public key from server certificate");
|
||||
break;
|
||||
}
|
||||
|
||||
result = Curl_pin_peer_pubkey(data,
|
||||
pinnedpubkey,
|
||||
(const unsigned char *)pubkey->header,
|
||||
(size_t)(pubkey->end - pubkey->header));
|
||||
if(result) {
|
||||
failf(data, "SSL: public key does not match pinned public key");
|
||||
}
|
||||
} while(0);
|
||||
|
||||
if(pCertContextServer)
|
||||
CertFreeCertificateContext(pCertContextServer);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static CURLcode schannel_connect_step2(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
@@ -2580,74 +2644,6 @@ static CURLcode schannel_random(struct Curl_easy *data,
|
||||
return Curl_win32_random(entropy, length);
|
||||
}
|
||||
|
||||
static CURLcode schannel_pkp_pin_peer_pubkey(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
const char *pinnedpubkey)
|
||||
{
|
||||
struct ssl_connect_data *connssl = cf->ctx;
|
||||
struct schannel_ssl_backend_data *backend =
|
||||
(struct schannel_ssl_backend_data *)connssl->backend;
|
||||
CERT_CONTEXT *pCertContextServer = NULL;
|
||||
|
||||
/* Result is returned to caller */
|
||||
CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH;
|
||||
|
||||
DEBUGASSERT(backend);
|
||||
|
||||
/* if a path was not specified, do not pin */
|
||||
if(!pinnedpubkey)
|
||||
return CURLE_OK;
|
||||
|
||||
do {
|
||||
SECURITY_STATUS sspi_status;
|
||||
const char *x509_der;
|
||||
DWORD x509_der_len;
|
||||
struct Curl_X509certificate x509_parsed;
|
||||
struct Curl_asn1Element *pubkey;
|
||||
|
||||
sspi_status =
|
||||
Curl_pSecFn->QueryContextAttributes(&backend->ctxt->ctxt_handle,
|
||||
SECPKG_ATTR_REMOTE_CERT_CONTEXT,
|
||||
&pCertContextServer);
|
||||
|
||||
if((sspi_status != SEC_E_OK) || !pCertContextServer) {
|
||||
char buffer[STRERROR_LEN];
|
||||
failf(data, "schannel: Failed to read remote certificate context: %s",
|
||||
Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
|
||||
break; /* failed */
|
||||
}
|
||||
|
||||
if(!(((pCertContextServer->dwCertEncodingType & X509_ASN_ENCODING) != 0) &&
|
||||
(pCertContextServer->cbCertEncoded > 0)))
|
||||
break;
|
||||
|
||||
x509_der = (const char *)pCertContextServer->pbCertEncoded;
|
||||
x509_der_len = pCertContextServer->cbCertEncoded;
|
||||
memset(&x509_parsed, 0, sizeof(x509_parsed));
|
||||
if(Curl_parseX509(&x509_parsed, x509_der, x509_der + x509_der_len))
|
||||
break;
|
||||
|
||||
pubkey = &x509_parsed.subjectPublicKeyInfo;
|
||||
if(!pubkey->header || pubkey->end <= pubkey->header) {
|
||||
failf(data, "SSL: failed retrieving public key from server certificate");
|
||||
break;
|
||||
}
|
||||
|
||||
result = Curl_pin_peer_pubkey(data,
|
||||
pinnedpubkey,
|
||||
(const unsigned char *)pubkey->header,
|
||||
(size_t)(pubkey->end - pubkey->header));
|
||||
if(result) {
|
||||
failf(data, "SSL: public key does not match pinned public key");
|
||||
}
|
||||
} while(0);
|
||||
|
||||
if(pCertContextServer)
|
||||
CertFreeCertificateContext(pCertContextServer);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void schannel_checksum(const unsigned char *input,
|
||||
size_t inputlen,
|
||||
unsigned char *checksum,
|
||||
|
||||
@@ -46,8 +46,6 @@
|
||||
#include "../rand.h"
|
||||
|
||||
|
||||
static bool cf_ssl_peer_key_is_global(const char *peer_key);
|
||||
|
||||
/* a peer+tls-config we cache sessions for */
|
||||
struct Curl_ssl_scache_peer {
|
||||
char *ssl_peer_key; /* id for peer + relevant TLS configuration */
|
||||
@@ -69,6 +67,235 @@ struct Curl_ssl_scache_peer {
|
||||
|
||||
#define GOOD_SCACHE(x) ((x) && (x)->magic == CURL_SCACHE_MAGIC)
|
||||
|
||||
static CURLcode cf_ssl_peer_key_add_path(struct dynbuf *buf,
|
||||
const char *name,
|
||||
const char *path,
|
||||
bool *is_local)
|
||||
{
|
||||
if(path && path[0]) {
|
||||
/* We try to add absolute paths, so that the session key can stay
|
||||
* valid when used in another process with different CWD. However,
|
||||
* when a path does not exist, this does not work. Then, we add
|
||||
* the path as is. */
|
||||
#ifdef _WIN32
|
||||
char abspath[_MAX_PATH];
|
||||
if(_fullpath(abspath, path, _MAX_PATH))
|
||||
return curlx_dyn_addf(buf, ":%s-%s", name, abspath);
|
||||
*is_local = TRUE;
|
||||
#elif defined(HAVE_REALPATH)
|
||||
if(path[0] != '/') {
|
||||
char *abspath = realpath(path, NULL);
|
||||
if(abspath) {
|
||||
CURLcode r = curlx_dyn_addf(buf, ":%s-%s", name, abspath);
|
||||
/* !checksrc! disable BANNEDFUNC 1 */
|
||||
free(abspath); /* allocated by libc, free without memdebug */
|
||||
return r;
|
||||
}
|
||||
*is_local = TRUE;
|
||||
}
|
||||
#endif
|
||||
return curlx_dyn_addf(buf, ":%s-%s", name, path);
|
||||
}
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static CURLcode cf_ssl_peer_key_add_hash(struct dynbuf *buf,
|
||||
const char *name,
|
||||
struct curl_blob *blob)
|
||||
{
|
||||
CURLcode r = CURLE_OK;
|
||||
if(blob && blob->len) {
|
||||
unsigned char hash[CURL_SHA256_DIGEST_LENGTH];
|
||||
size_t i;
|
||||
|
||||
r = curlx_dyn_addf(buf, ":%s-", name);
|
||||
if(r)
|
||||
goto out;
|
||||
r = Curl_sha256it(hash, blob->data, blob->len);
|
||||
if(r)
|
||||
goto out;
|
||||
for(i = 0; i < CURL_SHA256_DIGEST_LENGTH; ++i) {
|
||||
r = curlx_dyn_addf(buf, "%02x", hash[i]);
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
out:
|
||||
return r;
|
||||
}
|
||||
|
||||
#define CURL_SSLS_LOCAL_SUFFIX ":L"
|
||||
#define CURL_SSLS_GLOBAL_SUFFIX ":G"
|
||||
|
||||
static bool cf_ssl_peer_key_is_global(const char *peer_key)
|
||||
{
|
||||
size_t len = peer_key ? strlen(peer_key) : 0;
|
||||
return (len > 2) &&
|
||||
(peer_key[len - 1] == 'G') &&
|
||||
(peer_key[len - 2] == ':');
|
||||
}
|
||||
|
||||
CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf,
|
||||
const struct ssl_peer *peer,
|
||||
const char *tls_id,
|
||||
char **ppeer_key)
|
||||
{
|
||||
struct ssl_primary_config *ssl = Curl_ssl_cf_get_primary_config(cf);
|
||||
struct dynbuf buf;
|
||||
size_t key_len;
|
||||
bool is_local = FALSE;
|
||||
CURLcode r;
|
||||
|
||||
*ppeer_key = NULL;
|
||||
curlx_dyn_init(&buf, 10 * 1024);
|
||||
|
||||
r = curlx_dyn_addf(&buf, "%s:%d", peer->hostname, peer->port);
|
||||
if(r)
|
||||
goto out;
|
||||
|
||||
switch(peer->transport) {
|
||||
case TRNSPRT_TCP:
|
||||
break;
|
||||
case TRNSPRT_UDP:
|
||||
r = curlx_dyn_add(&buf, ":UDP");
|
||||
break;
|
||||
case TRNSPRT_QUIC:
|
||||
r = curlx_dyn_add(&buf, ":QUIC");
|
||||
break;
|
||||
case TRNSPRT_UNIX:
|
||||
r = curlx_dyn_add(&buf, ":UNIX");
|
||||
break;
|
||||
default:
|
||||
r = curlx_dyn_addf(&buf, ":TRNSPRT-%d", peer->transport);
|
||||
break;
|
||||
}
|
||||
if(r)
|
||||
goto out;
|
||||
|
||||
if(!ssl->verifypeer) {
|
||||
r = curlx_dyn_add(&buf, ":NO-VRFY-PEER");
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
if(!ssl->verifyhost) {
|
||||
r = curlx_dyn_add(&buf, ":NO-VRFY-HOST");
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
if(ssl->verifystatus) {
|
||||
r = curlx_dyn_add(&buf, ":VRFY-STATUS");
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
if(!ssl->verifypeer || !ssl->verifyhost) {
|
||||
if(cf->conn->bits.conn_to_host) {
|
||||
r = curlx_dyn_addf(&buf, ":CHOST-%s", cf->conn->conn_to_host.name);
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
if(cf->conn->bits.conn_to_port) {
|
||||
r = curlx_dyn_addf(&buf, ":CPORT-%d", cf->conn->conn_to_port);
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if(ssl->version || ssl->version_max) {
|
||||
r = curlx_dyn_addf(&buf, ":TLSVER-%d-%d", ssl->version,
|
||||
(ssl->version_max >> 16));
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
if(ssl->ssl_options) {
|
||||
r = curlx_dyn_addf(&buf, ":TLSOPT-%x", ssl->ssl_options);
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
if(ssl->cipher_list) {
|
||||
r = curlx_dyn_addf(&buf, ":CIPHER-%s", ssl->cipher_list);
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
if(ssl->cipher_list13) {
|
||||
r = curlx_dyn_addf(&buf, ":CIPHER13-%s", ssl->cipher_list13);
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
if(ssl->curves) {
|
||||
r = curlx_dyn_addf(&buf, ":CURVES-%s", ssl->curves);
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
if(ssl->verifypeer) {
|
||||
r = cf_ssl_peer_key_add_path(&buf, "CA", ssl->CAfile, &is_local);
|
||||
if(r)
|
||||
goto out;
|
||||
r = cf_ssl_peer_key_add_path(&buf, "CApath", ssl->CApath, &is_local);
|
||||
if(r)
|
||||
goto out;
|
||||
r = cf_ssl_peer_key_add_path(&buf, "CRL", ssl->CRLfile, &is_local);
|
||||
if(r)
|
||||
goto out;
|
||||
r = cf_ssl_peer_key_add_path(&buf, "Issuer", ssl->issuercert, &is_local);
|
||||
if(r)
|
||||
goto out;
|
||||
if(ssl->cert_blob) {
|
||||
r = cf_ssl_peer_key_add_hash(&buf, "CertBlob", ssl->cert_blob);
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
if(ssl->ca_info_blob) {
|
||||
r = cf_ssl_peer_key_add_hash(&buf, "CAInfoBlob", ssl->ca_info_blob);
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
if(ssl->issuercert_blob) {
|
||||
r = cf_ssl_peer_key_add_hash(&buf, "IssuerBlob", ssl->issuercert_blob);
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
if(ssl->pinned_key && ssl->pinned_key[0]) {
|
||||
r = curlx_dyn_addf(&buf, ":Pinned-%s", ssl->pinned_key);
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(ssl->clientcert && ssl->clientcert[0]) {
|
||||
r = curlx_dyn_add(&buf, ":CCERT");
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
#ifdef USE_TLS_SRP
|
||||
if(ssl->username || ssl->password) {
|
||||
r = curlx_dyn_add(&buf, ":SRP-AUTH");
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
#endif
|
||||
|
||||
if(!tls_id || !tls_id[0]) {
|
||||
r = CURLE_FAILED_INIT;
|
||||
goto out;
|
||||
}
|
||||
r = curlx_dyn_addf(&buf, ":IMPL-%s", tls_id);
|
||||
if(r)
|
||||
goto out;
|
||||
|
||||
r = curlx_dyn_addf(&buf, is_local ?
|
||||
CURL_SSLS_LOCAL_SUFFIX : CURL_SSLS_GLOBAL_SUFFIX);
|
||||
if(r)
|
||||
goto out;
|
||||
|
||||
*ppeer_key = curlx_dyn_take(&buf, &key_len);
|
||||
/* we just added printable char, and dynbuf always null-terminates, no need
|
||||
* to track length */
|
||||
|
||||
out:
|
||||
curlx_dyn_free(&buf);
|
||||
return r;
|
||||
}
|
||||
|
||||
struct Curl_ssl_scache {
|
||||
unsigned int magic;
|
||||
struct Curl_ssl_scache_peer *peers;
|
||||
@@ -368,235 +595,6 @@ void Curl_ssl_scache_unlock(struct Curl_easy *data)
|
||||
Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION);
|
||||
}
|
||||
|
||||
static CURLcode cf_ssl_peer_key_add_path(struct dynbuf *buf,
|
||||
const char *name,
|
||||
const char *path,
|
||||
bool *is_local)
|
||||
{
|
||||
if(path && path[0]) {
|
||||
/* We try to add absolute paths, so that the session key can stay
|
||||
* valid when used in another process with different CWD. However,
|
||||
* when a path does not exist, this does not work. Then, we add
|
||||
* the path as is. */
|
||||
#ifdef _WIN32
|
||||
char abspath[_MAX_PATH];
|
||||
if(_fullpath(abspath, path, _MAX_PATH))
|
||||
return curlx_dyn_addf(buf, ":%s-%s", name, abspath);
|
||||
*is_local = TRUE;
|
||||
#elif defined(HAVE_REALPATH)
|
||||
if(path[0] != '/') {
|
||||
char *abspath = realpath(path, NULL);
|
||||
if(abspath) {
|
||||
CURLcode r = curlx_dyn_addf(buf, ":%s-%s", name, abspath);
|
||||
/* !checksrc! disable BANNEDFUNC 1 */
|
||||
free(abspath); /* allocated by libc, free without memdebug */
|
||||
return r;
|
||||
}
|
||||
*is_local = TRUE;
|
||||
}
|
||||
#endif
|
||||
return curlx_dyn_addf(buf, ":%s-%s", name, path);
|
||||
}
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static CURLcode cf_ssl_peer_key_add_hash(struct dynbuf *buf,
|
||||
const char *name,
|
||||
struct curl_blob *blob)
|
||||
{
|
||||
CURLcode r = CURLE_OK;
|
||||
if(blob && blob->len) {
|
||||
unsigned char hash[CURL_SHA256_DIGEST_LENGTH];
|
||||
size_t i;
|
||||
|
||||
r = curlx_dyn_addf(buf, ":%s-", name);
|
||||
if(r)
|
||||
goto out;
|
||||
r = Curl_sha256it(hash, blob->data, blob->len);
|
||||
if(r)
|
||||
goto out;
|
||||
for(i = 0; i < CURL_SHA256_DIGEST_LENGTH; ++i) {
|
||||
r = curlx_dyn_addf(buf, "%02x", hash[i]);
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
out:
|
||||
return r;
|
||||
}
|
||||
|
||||
#define CURL_SSLS_LOCAL_SUFFIX ":L"
|
||||
#define CURL_SSLS_GLOBAL_SUFFIX ":G"
|
||||
|
||||
static bool cf_ssl_peer_key_is_global(const char *peer_key)
|
||||
{
|
||||
size_t len = peer_key ? strlen(peer_key) : 0;
|
||||
return (len > 2) &&
|
||||
(peer_key[len - 1] == 'G') &&
|
||||
(peer_key[len - 2] == ':');
|
||||
}
|
||||
|
||||
CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf,
|
||||
const struct ssl_peer *peer,
|
||||
const char *tls_id,
|
||||
char **ppeer_key)
|
||||
{
|
||||
struct ssl_primary_config *ssl = Curl_ssl_cf_get_primary_config(cf);
|
||||
struct dynbuf buf;
|
||||
size_t key_len;
|
||||
bool is_local = FALSE;
|
||||
CURLcode r;
|
||||
|
||||
*ppeer_key = NULL;
|
||||
curlx_dyn_init(&buf, 10 * 1024);
|
||||
|
||||
r = curlx_dyn_addf(&buf, "%s:%d", peer->hostname, peer->port);
|
||||
if(r)
|
||||
goto out;
|
||||
|
||||
switch(peer->transport) {
|
||||
case TRNSPRT_TCP:
|
||||
break;
|
||||
case TRNSPRT_UDP:
|
||||
r = curlx_dyn_add(&buf, ":UDP");
|
||||
break;
|
||||
case TRNSPRT_QUIC:
|
||||
r = curlx_dyn_add(&buf, ":QUIC");
|
||||
break;
|
||||
case TRNSPRT_UNIX:
|
||||
r = curlx_dyn_add(&buf, ":UNIX");
|
||||
break;
|
||||
default:
|
||||
r = curlx_dyn_addf(&buf, ":TRNSPRT-%d", peer->transport);
|
||||
break;
|
||||
}
|
||||
if(r)
|
||||
goto out;
|
||||
|
||||
if(!ssl->verifypeer) {
|
||||
r = curlx_dyn_add(&buf, ":NO-VRFY-PEER");
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
if(!ssl->verifyhost) {
|
||||
r = curlx_dyn_add(&buf, ":NO-VRFY-HOST");
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
if(ssl->verifystatus) {
|
||||
r = curlx_dyn_add(&buf, ":VRFY-STATUS");
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
if(!ssl->verifypeer || !ssl->verifyhost) {
|
||||
if(cf->conn->bits.conn_to_host) {
|
||||
r = curlx_dyn_addf(&buf, ":CHOST-%s", cf->conn->conn_to_host.name);
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
if(cf->conn->bits.conn_to_port) {
|
||||
r = curlx_dyn_addf(&buf, ":CPORT-%d", cf->conn->conn_to_port);
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if(ssl->version || ssl->version_max) {
|
||||
r = curlx_dyn_addf(&buf, ":TLSVER-%d-%d", ssl->version,
|
||||
(ssl->version_max >> 16));
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
if(ssl->ssl_options) {
|
||||
r = curlx_dyn_addf(&buf, ":TLSOPT-%x", ssl->ssl_options);
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
if(ssl->cipher_list) {
|
||||
r = curlx_dyn_addf(&buf, ":CIPHER-%s", ssl->cipher_list);
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
if(ssl->cipher_list13) {
|
||||
r = curlx_dyn_addf(&buf, ":CIPHER13-%s", ssl->cipher_list13);
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
if(ssl->curves) {
|
||||
r = curlx_dyn_addf(&buf, ":CURVES-%s", ssl->curves);
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
if(ssl->verifypeer) {
|
||||
r = cf_ssl_peer_key_add_path(&buf, "CA", ssl->CAfile, &is_local);
|
||||
if(r)
|
||||
goto out;
|
||||
r = cf_ssl_peer_key_add_path(&buf, "CApath", ssl->CApath, &is_local);
|
||||
if(r)
|
||||
goto out;
|
||||
r = cf_ssl_peer_key_add_path(&buf, "CRL", ssl->CRLfile, &is_local);
|
||||
if(r)
|
||||
goto out;
|
||||
r = cf_ssl_peer_key_add_path(&buf, "Issuer", ssl->issuercert, &is_local);
|
||||
if(r)
|
||||
goto out;
|
||||
if(ssl->cert_blob) {
|
||||
r = cf_ssl_peer_key_add_hash(&buf, "CertBlob", ssl->cert_blob);
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
if(ssl->ca_info_blob) {
|
||||
r = cf_ssl_peer_key_add_hash(&buf, "CAInfoBlob", ssl->ca_info_blob);
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
if(ssl->issuercert_blob) {
|
||||
r = cf_ssl_peer_key_add_hash(&buf, "IssuerBlob", ssl->issuercert_blob);
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
if(ssl->pinned_key && ssl->pinned_key[0]) {
|
||||
r = curlx_dyn_addf(&buf, ":Pinned-%s", ssl->pinned_key);
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(ssl->clientcert && ssl->clientcert[0]) {
|
||||
r = curlx_dyn_add(&buf, ":CCERT");
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
#ifdef USE_TLS_SRP
|
||||
if(ssl->username || ssl->password) {
|
||||
r = curlx_dyn_add(&buf, ":SRP-AUTH");
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
#endif
|
||||
|
||||
if(!tls_id || !tls_id[0]) {
|
||||
r = CURLE_FAILED_INIT;
|
||||
goto out;
|
||||
}
|
||||
r = curlx_dyn_addf(&buf, ":IMPL-%s", tls_id);
|
||||
if(r)
|
||||
goto out;
|
||||
|
||||
r = curlx_dyn_addf(&buf, is_local ?
|
||||
CURL_SSLS_LOCAL_SUFFIX : CURL_SSLS_GLOBAL_SUFFIX);
|
||||
if(r)
|
||||
goto out;
|
||||
|
||||
*ppeer_key = curlx_dyn_take(&buf, &key_len);
|
||||
/* we just added printable char, and dynbuf always null-terminates, no need
|
||||
* to track length */
|
||||
|
||||
out:
|
||||
curlx_dyn_free(&buf);
|
||||
return r;
|
||||
}
|
||||
|
||||
static bool cf_ssl_scache_match_auth(struct Curl_ssl_scache_peer *peer,
|
||||
struct ssl_primary_config *conn_config)
|
||||
{
|
||||
|
||||
@@ -100,10 +100,6 @@
|
||||
#undef USE_BIO_CHAIN
|
||||
#endif
|
||||
|
||||
static CURLcode wssl_connect(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
bool *done);
|
||||
|
||||
#ifdef OPENSSL_EXTRA
|
||||
/*
|
||||
* Availability note:
|
||||
|
||||
@@ -29,10 +29,6 @@
|
||||
#include "tool_util.h"
|
||||
#include "toolx/tool_time.h"
|
||||
|
||||
static void dump(const char *timebuf, const char *idsbuf, const char *text,
|
||||
FILE *stream, const unsigned char *ptr, size_t size,
|
||||
trace tracetype, curl_infotype infotype);
|
||||
|
||||
/*
|
||||
* Return the formatted HH:MM:SS for the tv_sec given.
|
||||
*/
|
||||
@@ -69,6 +65,60 @@ static void log_line_start(FILE *log, const char *timebuf,
|
||||
fputs(s_infotype[type], log);
|
||||
}
|
||||
|
||||
static void dump(const char *timebuf, const char *idsbuf, const char *text,
|
||||
FILE *stream, const unsigned char *ptr, size_t size,
|
||||
trace tracetype, curl_infotype infotype)
|
||||
{
|
||||
size_t i;
|
||||
size_t c;
|
||||
|
||||
unsigned int width = 0x10;
|
||||
|
||||
if(tracetype == TRACE_ASCII)
|
||||
/* without the hex output, we can fit more on screen */
|
||||
width = 0x40;
|
||||
|
||||
curl_mfprintf(stream, "%s%s%s, %zu bytes (0x%zx)\n", timebuf, idsbuf,
|
||||
text, size, size);
|
||||
|
||||
for(i = 0; i < size; i += width) {
|
||||
|
||||
curl_mfprintf(stream, "%04zx: ", i);
|
||||
|
||||
if(tracetype == TRACE_BIN) {
|
||||
/* hex not disabled, show it */
|
||||
for(c = 0; c < width; c++)
|
||||
if(i + c < size)
|
||||
curl_mfprintf(stream, "%02x ", ptr[i + c]);
|
||||
else
|
||||
fputs(" ", stream);
|
||||
}
|
||||
|
||||
for(c = 0; (c < width) && (i + c < size); c++) {
|
||||
/* check for 0D0A; if found, skip past and start a new line of output */
|
||||
if((tracetype == TRACE_ASCII) &&
|
||||
(i + c + 1 < size) && (ptr[i + c] == 0x0D) &&
|
||||
(ptr[i + c + 1] == 0x0A)) {
|
||||
i += (c + 2 - width);
|
||||
break;
|
||||
}
|
||||
(void)infotype;
|
||||
curl_mfprintf(stream, "%c",
|
||||
((ptr[i + c] >= 0x20) && (ptr[i + c] < 0x7F)) ?
|
||||
ptr[i + c] : UNPRINTABLE_CHAR);
|
||||
/* check again for 0D0A, to avoid an extra \n if it is at width */
|
||||
if((tracetype == TRACE_ASCII) &&
|
||||
(i + c + 2 < size) && (ptr[i + c + 1] == 0x0D) &&
|
||||
(ptr[i + c + 2] == 0x0A)) {
|
||||
i += (c + 3 - width);
|
||||
break;
|
||||
}
|
||||
}
|
||||
fputc('\n', stream); /* newline */
|
||||
}
|
||||
fflush(stream);
|
||||
}
|
||||
|
||||
#define TRC_IDS_FORMAT_IDS_1 "[%" CURL_FORMAT_CURL_OFF_T "-x] "
|
||||
#define TRC_IDS_FORMAT_IDS_2 "[%" CURL_FORMAT_CURL_OFF_T "-%" \
|
||||
CURL_FORMAT_CURL_OFF_T "] "
|
||||
@@ -230,57 +280,3 @@ int tool_debug_cb(CURL *handle, curl_infotype type,
|
||||
global->tracetype, type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dump(const char *timebuf, const char *idsbuf, const char *text,
|
||||
FILE *stream, const unsigned char *ptr, size_t size,
|
||||
trace tracetype, curl_infotype infotype)
|
||||
{
|
||||
size_t i;
|
||||
size_t c;
|
||||
|
||||
unsigned int width = 0x10;
|
||||
|
||||
if(tracetype == TRACE_ASCII)
|
||||
/* without the hex output, we can fit more on screen */
|
||||
width = 0x40;
|
||||
|
||||
curl_mfprintf(stream, "%s%s%s, %zu bytes (0x%zx)\n", timebuf, idsbuf,
|
||||
text, size, size);
|
||||
|
||||
for(i = 0; i < size; i += width) {
|
||||
|
||||
curl_mfprintf(stream, "%04zx: ", i);
|
||||
|
||||
if(tracetype == TRACE_BIN) {
|
||||
/* hex not disabled, show it */
|
||||
for(c = 0; c < width; c++)
|
||||
if(i + c < size)
|
||||
curl_mfprintf(stream, "%02x ", ptr[i + c]);
|
||||
else
|
||||
fputs(" ", stream);
|
||||
}
|
||||
|
||||
for(c = 0; (c < width) && (i + c < size); c++) {
|
||||
/* check for 0D0A; if found, skip past and start a new line of output */
|
||||
if((tracetype == TRACE_ASCII) &&
|
||||
(i + c + 1 < size) && (ptr[i + c] == 0x0D) &&
|
||||
(ptr[i + c + 1] == 0x0A)) {
|
||||
i += (c + 2 - width);
|
||||
break;
|
||||
}
|
||||
(void)infotype;
|
||||
curl_mfprintf(stream, "%c",
|
||||
((ptr[i + c] >= 0x20) && (ptr[i + c] < 0x7F)) ?
|
||||
ptr[i + c] : UNPRINTABLE_CHAR);
|
||||
/* check again for 0D0A, to avoid an extra \n if it is at width */
|
||||
if((tracetype == TRACE_ASCII) &&
|
||||
(i + c + 2 < size) && (ptr[i + c + 1] == 0x0D) &&
|
||||
(ptr[i + c + 2] == 0x0A)) {
|
||||
i += (c + 3 - width);
|
||||
break;
|
||||
}
|
||||
}
|
||||
fputc('\n', stream); /* newline */
|
||||
}
|
||||
fflush(stream);
|
||||
}
|
||||
|
||||
@@ -27,22 +27,6 @@
|
||||
#include "tool_writeout.h"
|
||||
#include "tool_writeout_json.h"
|
||||
|
||||
static int writeTime(FILE *stream, const struct writeoutvar *wovar,
|
||||
struct per_transfer *per, CURLcode per_result,
|
||||
bool use_json);
|
||||
|
||||
static int writeString(FILE *stream, const struct writeoutvar *wovar,
|
||||
struct per_transfer *per, CURLcode per_result,
|
||||
bool use_json);
|
||||
|
||||
static int writeLong(FILE *stream, const struct writeoutvar *wovar,
|
||||
struct per_transfer *per, CURLcode per_result,
|
||||
bool use_json);
|
||||
|
||||
static int writeOffset(FILE *stream, const struct writeoutvar *wovar,
|
||||
struct per_transfer *per, CURLcode per_result,
|
||||
bool use_json);
|
||||
|
||||
struct httpmap {
|
||||
const char *str;
|
||||
int num;
|
||||
@@ -57,103 +41,6 @@ static const struct httpmap http_version[] = {
|
||||
{ NULL, 0 } /* end of list */
|
||||
};
|
||||
|
||||
/* The designated write function should be the same as the CURLINFO return type
|
||||
with exceptions special cased in the respective function. For example,
|
||||
http_version uses CURLINFO_HTTP_VERSION which returns the version as a long,
|
||||
however it is output as a string and therefore is handled in writeString.
|
||||
|
||||
Yes: "http_version": "1.1"
|
||||
No: "http_version": 1.1
|
||||
|
||||
Variable names MUST be in alphabetical order.
|
||||
*/
|
||||
static const struct writeoutvar variables[] = {
|
||||
{ "certs", VAR_CERT, CURLINFO_NONE, writeString },
|
||||
{ "conn_id", VAR_CONN_ID, CURLINFO_CONN_ID, writeOffset },
|
||||
{ "content_type", VAR_CONTENT_TYPE, CURLINFO_CONTENT_TYPE, writeString },
|
||||
{ "errormsg", VAR_ERRORMSG, CURLINFO_NONE, writeString },
|
||||
{ "exitcode", VAR_EXITCODE, CURLINFO_NONE, writeLong },
|
||||
{ "filename_effective", VAR_EFFECTIVE_FILENAME, CURLINFO_NONE, writeString },
|
||||
{ "ftp_entry_path", VAR_FTP_ENTRY_PATH, CURLINFO_FTP_ENTRY_PATH,
|
||||
writeString },
|
||||
{ "header_json", VAR_HEADER_JSON, CURLINFO_NONE, NULL },
|
||||
{ "http_code", VAR_HTTP_CODE, CURLINFO_RESPONSE_CODE, writeLong },
|
||||
{ "http_connect", VAR_HTTP_CODE_PROXY, CURLINFO_HTTP_CONNECTCODE,
|
||||
writeLong },
|
||||
{ "http_version", VAR_HTTP_VERSION, CURLINFO_HTTP_VERSION, writeString },
|
||||
{ "json", VAR_JSON, CURLINFO_NONE, NULL },
|
||||
{ "local_ip", VAR_LOCAL_IP, CURLINFO_LOCAL_IP, writeString },
|
||||
{ "local_port", VAR_LOCAL_PORT, CURLINFO_LOCAL_PORT, writeLong },
|
||||
{ "method", VAR_EFFECTIVE_METHOD, CURLINFO_EFFECTIVE_METHOD, writeString },
|
||||
{ "num_certs", VAR_NUM_CERTS, CURLINFO_NONE, writeLong },
|
||||
{ "num_connects", VAR_NUM_CONNECTS, CURLINFO_NUM_CONNECTS, writeLong },
|
||||
{ "num_headers", VAR_NUM_HEADERS, CURLINFO_NONE, writeLong },
|
||||
{ "num_redirects", VAR_REDIRECT_COUNT, CURLINFO_REDIRECT_COUNT, writeLong },
|
||||
{ "num_retries", VAR_NUM_RETRY, CURLINFO_NONE, writeLong },
|
||||
{ "onerror", VAR_ONERROR, CURLINFO_NONE, NULL },
|
||||
{ "proxy_ssl_verify_result", VAR_PROXY_SSL_VERIFY_RESULT,
|
||||
CURLINFO_PROXY_SSL_VERIFYRESULT, writeLong },
|
||||
{ "proxy_used", VAR_PROXY_USED, CURLINFO_USED_PROXY, writeLong },
|
||||
{ "redirect_url", VAR_REDIRECT_URL, CURLINFO_REDIRECT_URL, writeString },
|
||||
{ "referer", VAR_REFERER, CURLINFO_REFERER, writeString },
|
||||
{ "remote_ip", VAR_PRIMARY_IP, CURLINFO_PRIMARY_IP, writeString },
|
||||
{ "remote_port", VAR_PRIMARY_PORT, CURLINFO_PRIMARY_PORT, writeLong },
|
||||
{ "response_code", VAR_HTTP_CODE, CURLINFO_RESPONSE_CODE, writeLong },
|
||||
{ "scheme", VAR_SCHEME, CURLINFO_SCHEME, writeString },
|
||||
{ "size_download", VAR_SIZE_DOWNLOAD, CURLINFO_SIZE_DOWNLOAD_T,
|
||||
writeOffset },
|
||||
{ "size_header", VAR_HEADER_SIZE, CURLINFO_HEADER_SIZE, writeLong },
|
||||
{ "size_request", VAR_REQUEST_SIZE, CURLINFO_REQUEST_SIZE, writeLong },
|
||||
{ "size_upload", VAR_SIZE_UPLOAD, CURLINFO_SIZE_UPLOAD_T, writeOffset },
|
||||
{ "speed_download", VAR_SPEED_DOWNLOAD, CURLINFO_SPEED_DOWNLOAD_T,
|
||||
writeOffset },
|
||||
{ "speed_upload", VAR_SPEED_UPLOAD, CURLINFO_SPEED_UPLOAD_T, writeOffset },
|
||||
{ "ssl_verify_result", VAR_SSL_VERIFY_RESULT, CURLINFO_SSL_VERIFYRESULT,
|
||||
writeLong },
|
||||
{ "stderr", VAR_STDERR, CURLINFO_NONE, NULL },
|
||||
{ "stdout", VAR_STDOUT, CURLINFO_NONE, NULL },
|
||||
{ "time_appconnect", VAR_APPCONNECT_TIME, CURLINFO_APPCONNECT_TIME_T,
|
||||
writeTime },
|
||||
{ "time_connect", VAR_CONNECT_TIME, CURLINFO_CONNECT_TIME_T, writeTime },
|
||||
{ "time_namelookup", VAR_NAMELOOKUP_TIME, CURLINFO_NAMELOOKUP_TIME_T,
|
||||
writeTime },
|
||||
{ "time_posttransfer", VAR_POSTTRANSFER_TIME, CURLINFO_POSTTRANSFER_TIME_T,
|
||||
writeTime },
|
||||
{ "time_pretransfer", VAR_PRETRANSFER_TIME, CURLINFO_PRETRANSFER_TIME_T,
|
||||
writeTime },
|
||||
{ "time_queue", VAR_QUEUE_TIME, CURLINFO_QUEUE_TIME_T, writeTime },
|
||||
{ "time_redirect", VAR_REDIRECT_TIME, CURLINFO_REDIRECT_TIME_T, writeTime },
|
||||
{ "time_starttransfer", VAR_STARTTRANSFER_TIME,
|
||||
CURLINFO_STARTTRANSFER_TIME_T, writeTime },
|
||||
{ "time_total", VAR_TOTAL_TIME, CURLINFO_TOTAL_TIME_T, writeTime },
|
||||
{ "tls_earlydata", VAR_TLS_EARLYDATA_SENT, CURLINFO_EARLYDATA_SENT_T,
|
||||
writeOffset },
|
||||
{ "url", VAR_INPUT_URL, CURLINFO_NONE, writeString },
|
||||
{ "url.fragment", VAR_INPUT_URLFRAGMENT, CURLINFO_NONE, writeString },
|
||||
{ "url.host", VAR_INPUT_URLHOST, CURLINFO_NONE, writeString },
|
||||
{ "url.options", VAR_INPUT_URLOPTIONS, CURLINFO_NONE, writeString },
|
||||
{ "url.password", VAR_INPUT_URLPASSWORD, CURLINFO_NONE, writeString },
|
||||
{ "url.path", VAR_INPUT_URLPATH, CURLINFO_NONE, writeString },
|
||||
{ "url.port", VAR_INPUT_URLPORT, CURLINFO_NONE, writeString },
|
||||
{ "url.query", VAR_INPUT_URLQUERY, CURLINFO_NONE, writeString },
|
||||
{ "url.scheme", VAR_INPUT_URLSCHEME, CURLINFO_NONE, writeString },
|
||||
{ "url.user", VAR_INPUT_URLUSER, CURLINFO_NONE, writeString },
|
||||
{ "url.zoneid", VAR_INPUT_URLZONEID, CURLINFO_NONE, writeString },
|
||||
{ "url_effective", VAR_EFFECTIVE_URL, CURLINFO_EFFECTIVE_URL, writeString },
|
||||
{ "urle.fragment", VAR_INPUT_URLEFRAGMENT, CURLINFO_NONE, writeString },
|
||||
{ "urle.host", VAR_INPUT_URLEHOST, CURLINFO_NONE, writeString },
|
||||
{ "urle.options", VAR_INPUT_URLEOPTIONS, CURLINFO_NONE, writeString },
|
||||
{ "urle.password", VAR_INPUT_URLEPASSWORD, CURLINFO_NONE, writeString },
|
||||
{ "urle.path", VAR_INPUT_URLEPATH, CURLINFO_NONE, writeString },
|
||||
{ "urle.port", VAR_INPUT_URLEPORT, CURLINFO_NONE, writeString },
|
||||
{ "urle.query", VAR_INPUT_URLEQUERY, CURLINFO_NONE, writeString },
|
||||
{ "urle.scheme", VAR_INPUT_URLESCHEME, CURLINFO_NONE, writeString },
|
||||
{ "urle.user", VAR_INPUT_URLEUSER, CURLINFO_NONE, writeString },
|
||||
{ "urle.zoneid", VAR_INPUT_URLEZONEID, CURLINFO_NONE, writeString },
|
||||
{ "urlnum", VAR_URLNUM, CURLINFO_NONE, writeOffset },
|
||||
{ "xfer_id", VAR_EASY_ID, CURLINFO_XFER_ID, writeOffset }
|
||||
};
|
||||
|
||||
static int writeTime(FILE *stream, const struct writeoutvar *wovar,
|
||||
struct per_transfer *per, CURLcode per_result,
|
||||
bool use_json)
|
||||
@@ -530,13 +417,102 @@ static int writeOffset(FILE *stream, const struct writeoutvar *wovar,
|
||||
return 1; /* return 1 if anything was written */
|
||||
}
|
||||
|
||||
static int matchvar(const void *m1, const void *m2)
|
||||
{
|
||||
const struct writeoutvar *v1 = m1;
|
||||
const struct writeoutvar *v2 = m2;
|
||||
/* The designated write function should be the same as the CURLINFO return type
|
||||
with exceptions special cased in the respective function. For example,
|
||||
http_version uses CURLINFO_HTTP_VERSION which returns the version as a long,
|
||||
however it is output as a string and therefore is handled in writeString.
|
||||
|
||||
return strcmp(v1->name, v2->name);
|
||||
}
|
||||
Yes: "http_version": "1.1"
|
||||
No: "http_version": 1.1
|
||||
|
||||
Variable names MUST be in alphabetical order.
|
||||
*/
|
||||
static const struct writeoutvar variables[] = {
|
||||
{ "certs", VAR_CERT, CURLINFO_NONE, writeString },
|
||||
{ "conn_id", VAR_CONN_ID, CURLINFO_CONN_ID, writeOffset },
|
||||
{ "content_type", VAR_CONTENT_TYPE, CURLINFO_CONTENT_TYPE, writeString },
|
||||
{ "errormsg", VAR_ERRORMSG, CURLINFO_NONE, writeString },
|
||||
{ "exitcode", VAR_EXITCODE, CURLINFO_NONE, writeLong },
|
||||
{ "filename_effective", VAR_EFFECTIVE_FILENAME, CURLINFO_NONE, writeString },
|
||||
{ "ftp_entry_path", VAR_FTP_ENTRY_PATH, CURLINFO_FTP_ENTRY_PATH,
|
||||
writeString },
|
||||
{ "header_json", VAR_HEADER_JSON, CURLINFO_NONE, NULL },
|
||||
{ "http_code", VAR_HTTP_CODE, CURLINFO_RESPONSE_CODE, writeLong },
|
||||
{ "http_connect", VAR_HTTP_CODE_PROXY, CURLINFO_HTTP_CONNECTCODE,
|
||||
writeLong },
|
||||
{ "http_version", VAR_HTTP_VERSION, CURLINFO_HTTP_VERSION, writeString },
|
||||
{ "json", VAR_JSON, CURLINFO_NONE, NULL },
|
||||
{ "local_ip", VAR_LOCAL_IP, CURLINFO_LOCAL_IP, writeString },
|
||||
{ "local_port", VAR_LOCAL_PORT, CURLINFO_LOCAL_PORT, writeLong },
|
||||
{ "method", VAR_EFFECTIVE_METHOD, CURLINFO_EFFECTIVE_METHOD, writeString },
|
||||
{ "num_certs", VAR_NUM_CERTS, CURLINFO_NONE, writeLong },
|
||||
{ "num_connects", VAR_NUM_CONNECTS, CURLINFO_NUM_CONNECTS, writeLong },
|
||||
{ "num_headers", VAR_NUM_HEADERS, CURLINFO_NONE, writeLong },
|
||||
{ "num_redirects", VAR_REDIRECT_COUNT, CURLINFO_REDIRECT_COUNT, writeLong },
|
||||
{ "num_retries", VAR_NUM_RETRY, CURLINFO_NONE, writeLong },
|
||||
{ "onerror", VAR_ONERROR, CURLINFO_NONE, NULL },
|
||||
{ "proxy_ssl_verify_result", VAR_PROXY_SSL_VERIFY_RESULT,
|
||||
CURLINFO_PROXY_SSL_VERIFYRESULT, writeLong },
|
||||
{ "proxy_used", VAR_PROXY_USED, CURLINFO_USED_PROXY, writeLong },
|
||||
{ "redirect_url", VAR_REDIRECT_URL, CURLINFO_REDIRECT_URL, writeString },
|
||||
{ "referer", VAR_REFERER, CURLINFO_REFERER, writeString },
|
||||
{ "remote_ip", VAR_PRIMARY_IP, CURLINFO_PRIMARY_IP, writeString },
|
||||
{ "remote_port", VAR_PRIMARY_PORT, CURLINFO_PRIMARY_PORT, writeLong },
|
||||
{ "response_code", VAR_HTTP_CODE, CURLINFO_RESPONSE_CODE, writeLong },
|
||||
{ "scheme", VAR_SCHEME, CURLINFO_SCHEME, writeString },
|
||||
{ "size_download", VAR_SIZE_DOWNLOAD, CURLINFO_SIZE_DOWNLOAD_T,
|
||||
writeOffset },
|
||||
{ "size_header", VAR_HEADER_SIZE, CURLINFO_HEADER_SIZE, writeLong },
|
||||
{ "size_request", VAR_REQUEST_SIZE, CURLINFO_REQUEST_SIZE, writeLong },
|
||||
{ "size_upload", VAR_SIZE_UPLOAD, CURLINFO_SIZE_UPLOAD_T, writeOffset },
|
||||
{ "speed_download", VAR_SPEED_DOWNLOAD, CURLINFO_SPEED_DOWNLOAD_T,
|
||||
writeOffset },
|
||||
{ "speed_upload", VAR_SPEED_UPLOAD, CURLINFO_SPEED_UPLOAD_T, writeOffset },
|
||||
{ "ssl_verify_result", VAR_SSL_VERIFY_RESULT, CURLINFO_SSL_VERIFYRESULT,
|
||||
writeLong },
|
||||
{ "stderr", VAR_STDERR, CURLINFO_NONE, NULL },
|
||||
{ "stdout", VAR_STDOUT, CURLINFO_NONE, NULL },
|
||||
{ "time_appconnect", VAR_APPCONNECT_TIME, CURLINFO_APPCONNECT_TIME_T,
|
||||
writeTime },
|
||||
{ "time_connect", VAR_CONNECT_TIME, CURLINFO_CONNECT_TIME_T, writeTime },
|
||||
{ "time_namelookup", VAR_NAMELOOKUP_TIME, CURLINFO_NAMELOOKUP_TIME_T,
|
||||
writeTime },
|
||||
{ "time_posttransfer", VAR_POSTTRANSFER_TIME, CURLINFO_POSTTRANSFER_TIME_T,
|
||||
writeTime },
|
||||
{ "time_pretransfer", VAR_PRETRANSFER_TIME, CURLINFO_PRETRANSFER_TIME_T,
|
||||
writeTime },
|
||||
{ "time_queue", VAR_QUEUE_TIME, CURLINFO_QUEUE_TIME_T, writeTime },
|
||||
{ "time_redirect", VAR_REDIRECT_TIME, CURLINFO_REDIRECT_TIME_T, writeTime },
|
||||
{ "time_starttransfer", VAR_STARTTRANSFER_TIME,
|
||||
CURLINFO_STARTTRANSFER_TIME_T, writeTime },
|
||||
{ "time_total", VAR_TOTAL_TIME, CURLINFO_TOTAL_TIME_T, writeTime },
|
||||
{ "tls_earlydata", VAR_TLS_EARLYDATA_SENT, CURLINFO_EARLYDATA_SENT_T,
|
||||
writeOffset },
|
||||
{ "url", VAR_INPUT_URL, CURLINFO_NONE, writeString },
|
||||
{ "url.fragment", VAR_INPUT_URLFRAGMENT, CURLINFO_NONE, writeString },
|
||||
{ "url.host", VAR_INPUT_URLHOST, CURLINFO_NONE, writeString },
|
||||
{ "url.options", VAR_INPUT_URLOPTIONS, CURLINFO_NONE, writeString },
|
||||
{ "url.password", VAR_INPUT_URLPASSWORD, CURLINFO_NONE, writeString },
|
||||
{ "url.path", VAR_INPUT_URLPATH, CURLINFO_NONE, writeString },
|
||||
{ "url.port", VAR_INPUT_URLPORT, CURLINFO_NONE, writeString },
|
||||
{ "url.query", VAR_INPUT_URLQUERY, CURLINFO_NONE, writeString },
|
||||
{ "url.scheme", VAR_INPUT_URLSCHEME, CURLINFO_NONE, writeString },
|
||||
{ "url.user", VAR_INPUT_URLUSER, CURLINFO_NONE, writeString },
|
||||
{ "url.zoneid", VAR_INPUT_URLZONEID, CURLINFO_NONE, writeString },
|
||||
{ "url_effective", VAR_EFFECTIVE_URL, CURLINFO_EFFECTIVE_URL, writeString },
|
||||
{ "urle.fragment", VAR_INPUT_URLEFRAGMENT, CURLINFO_NONE, writeString },
|
||||
{ "urle.host", VAR_INPUT_URLEHOST, CURLINFO_NONE, writeString },
|
||||
{ "urle.options", VAR_INPUT_URLEOPTIONS, CURLINFO_NONE, writeString },
|
||||
{ "urle.password", VAR_INPUT_URLEPASSWORD, CURLINFO_NONE, writeString },
|
||||
{ "urle.path", VAR_INPUT_URLEPATH, CURLINFO_NONE, writeString },
|
||||
{ "urle.port", VAR_INPUT_URLEPORT, CURLINFO_NONE, writeString },
|
||||
{ "urle.query", VAR_INPUT_URLEQUERY, CURLINFO_NONE, writeString },
|
||||
{ "urle.scheme", VAR_INPUT_URLESCHEME, CURLINFO_NONE, writeString },
|
||||
{ "urle.user", VAR_INPUT_URLEUSER, CURLINFO_NONE, writeString },
|
||||
{ "urle.zoneid", VAR_INPUT_URLEZONEID, CURLINFO_NONE, writeString },
|
||||
{ "urlnum", VAR_URLNUM, CURLINFO_NONE, writeOffset },
|
||||
{ "xfer_id", VAR_EASY_ID, CURLINFO_XFER_ID, writeOffset }
|
||||
};
|
||||
|
||||
#define MAX_WRITEOUT_NAME_LENGTH 24
|
||||
|
||||
@@ -720,6 +696,14 @@ static void output_header(struct per_transfer *per,
|
||||
*pptr = ptr;
|
||||
}
|
||||
|
||||
static int matchvar(const void *m1, const void *m2)
|
||||
{
|
||||
const struct writeoutvar *v1 = m1;
|
||||
const struct writeoutvar *v2 = m2;
|
||||
|
||||
return strcmp(v1->name, v2->name);
|
||||
}
|
||||
|
||||
void ourWriteOut(struct OperationConfig *config, struct per_transfer *per,
|
||||
CURLcode per_result)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user