Most by moving functions around. Also delete unused ones.
Reducing their number from 83 to 33.
Remaining ones due to:
- circular dependencies.
- H3 code, that I did not attempt to update and likely the above applies.
- static declarations with attributes (`CURL_PRINTF`, `WARN_UNUSED_RESULT`).
- OS400 code.
Closes #20321
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) &&
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)
#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)
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)
}
#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,
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);
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)
{
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;
}
}
+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) {
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)
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)
{
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;
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)
{
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;
#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)
{
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))
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)
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;
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.
*/
(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
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)
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
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)
{
#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 */
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)
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,
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,
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;
}
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.
*/
#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;
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,
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,
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)
{
#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);
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)
{
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)
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)
{
}
}
+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)
{
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,
#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 */
#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;
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)
{
#undef USE_BIO_CHAIN
#endif
-static CURLcode wssl_connect(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- bool *done);
-
#ifdef OPENSSL_EXTRA
/*
* Availability note:
#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.
*/
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 "] "
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);
-}
#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;
{ 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)
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
*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)
{