build: drop more forward function declarations
No binary (build failed)
—
(log yok)
| @@ -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) | |
| @@ -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; | |
| @@ -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) | |
| @@ -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) | |
| { | |
| @@ -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 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; |
| + | /* 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; | |
| } | |
| @@ -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. | |
| */ | |
| @@ -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) | |
| { | |
| @@ -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) | |
| { | |