len = Curl_dyn_len(&data->state.headerb);
headp = Curl_dyn_ptr(&data->state.headerb);
- result = Curl_http_header(data, data->conn, headp, len);
+ result = Curl_http_header(data, headp, len);
if(result) {
data->state.hresult = result;
return HYPER_ITER_BREAK;
ZERO_NULL, /* perform_getsock */
rtmp_disconnect, /* disconnect */
ZERO_NULL, /* write_resp */
+ ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_RTMP, /* defport */
ZERO_NULL, /* perform_getsock */
rtmp_disconnect, /* disconnect */
ZERO_NULL, /* write_resp */
+ ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_RTMPT, /* defport */
ZERO_NULL, /* perform_getsock */
rtmp_disconnect, /* disconnect */
ZERO_NULL, /* write_resp */
+ ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_RTMP, /* defport */
ZERO_NULL, /* perform_getsock */
rtmp_disconnect, /* disconnect */
ZERO_NULL, /* write_resp */
+ ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_RTMPT, /* defport */
ZERO_NULL, /* perform_getsock */
rtmp_disconnect, /* disconnect */
ZERO_NULL, /* write_resp */
+ ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_RTMPS, /* defport */
ZERO_NULL, /* perform_getsock */
rtmp_disconnect, /* disconnect */
ZERO_NULL, /* write_resp */
+ ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_RTMPS, /* defport */
ZERO_NULL, /* perform_getsock */
ZERO_NULL, /* disconnect */
ZERO_NULL, /* write_resp */
+ ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_DICT, /* defport */
ZERO_NULL, /* perform_getsock */
file_disconnect, /* disconnect */
ZERO_NULL, /* write_resp */
+ ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
0, /* defport */
ZERO_NULL, /* perform_getsock */
ftp_disconnect, /* disconnect */
ZERO_NULL, /* write_resp */
+ ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_FTP, /* defport */
ZERO_NULL, /* perform_getsock */
ftp_disconnect, /* disconnect */
ZERO_NULL, /* write_resp */
+ ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_FTPS, /* defport */
ZERO_NULL, /* perform_getsock */
ZERO_NULL, /* disconnect */
ZERO_NULL, /* write_resp */
+ ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_GOPHER, /* defport */
ZERO_NULL, /* perform_getsock */
ZERO_NULL, /* disconnect */
ZERO_NULL, /* write_resp */
+ ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_GOPHER, /* defport */
* Forward declarations.
*/
-static bool http_should_fail(struct Curl_easy *data);
+static bool http_should_fail(struct Curl_easy *data, int httpcode);
static bool http_exp100_is_waiting(struct Curl_easy *data);
static CURLcode http_exp100_add_reader(struct Curl_easy *data);
static void http_exp100_send_anyway(struct Curl_easy *data);
ZERO_NULL, /* perform_getsock */
ZERO_NULL, /* disconnect */
Curl_http_write_resp, /* write_resp */
+ Curl_http_write_resp_hd, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_HTTP, /* defport */
ZERO_NULL, /* perform_getsock */
ZERO_NULL, /* disconnect */
Curl_http_write_resp, /* write_resp */
+ Curl_http_write_resp_hd, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_HTTPS, /* defport */
while(*start && ISSPACE(*start))
start++;
- /* data is in the host encoding so
- use '\r' and '\n' instead of 0x0d and 0x0a */
end = strchr(start, '\r');
if(!end)
end = strchr(start, '\n');
data->state.authhost.done = TRUE;
}
}
- if(http_should_fail(data)) {
+ if(http_should_fail(data, data->req.httpcode)) {
failf(data, "The requested URL returned error: %d",
data->req.httpcode);
result = CURLE_HTTP_RETURNED_ERROR;
}
/**
- * http_should_fail() determines whether an HTTP response has gotten us
+ * http_should_fail() determines whether an HTTP response code has gotten us
* into an error state or not.
*
* @retval FALSE communications should continue
*
* @retval TRUE communications should not continue
*/
-static bool http_should_fail(struct Curl_easy *data)
+static bool http_should_fail(struct Curl_easy *data, int httpcode)
{
- int httpcode;
DEBUGASSERT(data);
DEBUGASSERT(data->conn);
- httpcode = data->req.httpcode;
-
/*
** If we haven't been asked to fail on error,
** don't fail.
/*
* Curl_http_header() parses a single response header.
*/
-CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn,
- char *hd, size_t hdlen)
+CURLcode Curl_http_header(struct Curl_easy *data,
+ const char *hd, size_t hdlen)
{
+ struct connectdata *conn = data->conn;
CURLcode result;
struct SingleRequest *k = &data->req;
const char *v;
case 'A':
#ifndef CURL_DISABLE_ALTSVC
v = (data->asi &&
- ((conn->handler->flags & PROTOPT_SSL) ||
+ ((data->conn->handler->flags & PROTOPT_SSL) ||
#ifdef CURLDEBUG
/* allow debug builds to circumvent the HTTPS restriction */
getenv("CURL_ALTSVC_HTTP")
return CURLE_OK;
}
-static CURLcode verify_header(struct Curl_easy *data)
+static CURLcode verify_header(struct Curl_easy *data,
+ const char *hd, size_t hdlen)
{
struct SingleRequest *k = &data->req;
- const char *header = Curl_dyn_ptr(&data->state.headerb);
- size_t hlen = Curl_dyn_len(&data->state.headerb);
- char *ptr = memchr(header, 0x00, hlen);
+ char *ptr = memchr(hd, 0x00, hdlen);
if(ptr) {
/* this is bad, bail out */
failf(data, "Nul byte in header");
if(k->headerline < 2)
/* the first "header" is the status-line and it has no colon */
return CURLE_OK;
- if(((header[0] == ' ') || (header[0] == '\t')) && k->headerline > 2)
+ if(((hd[0] == ' ') || (hd[0] == '\t')) && k->headerline > 2)
/* line folding, can't happen on line 2 */
;
else {
- ptr = memchr(header, ':', hlen);
+ ptr = memchr(hd, ':', hdlen);
if(!ptr) {
/* this is bad, bail out */
failf(data, "Header without colon");
struct connectdata *conn = data->conn;
CURLcode result = CURLE_OK;
struct SingleRequest *k = &data->req;
- bool switch_to_h2 = FALSE;
(void)buf; /* not used without HTTP2 enabled */
*pconsumed = 0;
return CURLE_UNSUPPORTED_PROTOCOL;
}
else if(k->httpcode < 200) {
- /* "A user agent MAY ignore unexpected 1xx status responses." */
+ /* "A user agent MAY ignore unexpected 1xx status responses."
+ * By default, we expect to get more responses after this one. */
+ k->header = TRUE;
+ k->headerline = 0; /* restart the header line counter */
+
switch(k->httpcode) {
case 100:
/*
* We have made an HTTP PUT or POST and this is 1.1-lingo
* that tells us that the server is OK with this and ready
* to receive the data.
- * However, we'll get more headers now so we must get
- * back into the header-parsing state!
*/
- k->header = TRUE;
- k->headerline = 0; /* restart the header line counter */
-
- /* if we did wait for this do enable write now! */
Curl_http_exp100_got100(data);
break;
case 101:
- if(conn->httpversion == 11) {
- /* Switching Protocols only allowed from HTTP/1.1 */
- if(k->upgr101 == UPGR101_H2) {
- /* Switching to HTTP/2 */
- infof(data, "Received 101, Switching to HTTP/2");
- k->upgr101 = UPGR101_RECEIVED;
-
- /* we'll get more headers (HTTP/2 response) */
- k->header = TRUE;
- k->headerline = 0; /* restart the header line counter */
- switch_to_h2 = TRUE;
- }
-#ifdef USE_WEBSOCKETS
- else if(k->upgr101 == UPGR101_WS) {
- /* verify the response */
- result = Curl_ws_accept(data, buf, blen);
- if(result)
- return result;
- k->header = FALSE; /* no more header to parse! */
- *pconsumed += blen; /* ws accept handled the data */
- blen = 0;
- if(data->set.connect_only)
- k->keepon &= ~KEEP_RECV; /* read no more content */
- }
-#endif
- else {
- /* Not switching to another protocol */
- k->header = FALSE; /* no more header to parse! */
- }
- }
- else {
+ /* Switching Protocols only allowed from HTTP/1.1 */
+ if(conn->httpversion != 11) {
/* invalid for other HTTP versions */
failf(data, "unexpected 101 response code");
return CURLE_WEIRD_SERVER_REPLY;
}
+ if(k->upgr101 == UPGR101_H2) {
+ /* Switching to HTTP/2, where we will get more responses */
+ infof(data, "Received 101, Switching to HTTP/2");
+ k->upgr101 = UPGR101_RECEIVED;
+ /* We expect more response from HTTP/2 later */
+ k->header = TRUE;
+ k->headerline = 0; /* restart the header line counter */
+ /* Any remaining `buf` bytes are already HTTP/2 and passed to
+ * be processed. */
+ result = Curl_http2_upgrade(data, conn, FIRSTSOCKET, buf, blen);
+ if(result)
+ return result;
+ *pconsumed += blen;
+ }
+#ifdef USE_WEBSOCKETS
+ else if(k->upgr101 == UPGR101_WS) {
+ /* verify the response. Any passed `buf` bytes are already in
+ * WebSockets format and taken in by the protocol handler. */
+ result = Curl_ws_accept(data, buf, blen);
+ if(result)
+ return result;
+ *pconsumed += blen; /* ws accept handled the data */
+ k->header = FALSE; /* we will not get more responses */
+ if(data->set.connect_only)
+ k->keepon &= ~KEEP_RECV; /* read no more content */
+ }
+#endif
+ else {
+ /* We silently accept this as the final response.
+ * TODO: this looks, uhm, wrong. What are we switching to if we
+ * did not ask for an Upgrade? Maybe the application provided an
+ * `Upgrade: xxx` header? */
+ k->header = FALSE;
+ }
break;
default:
- /* the status code 1xx indicates a provisional response, so
- we'll get another set of headers */
- k->header = TRUE;
- k->headerline = 0; /* restart the header line counter */
+ /* The server may send us other 1xx responses, like informative
+ * 103. This have no influence on request processing and we expect
+ * to receive a final response eventually. */
break;
}
+ return result;
}
- else {
- /* k->httpcode >= 200, final response */
- k->header = FALSE;
- if(k->upgr101 == UPGR101_H2) {
- /* A requested upgrade was denied, poke the multi handle to possibly
- allow a pending pipewait to continue */
- Curl_multi_connchanged(data->multi);
- }
+ /* k->httpcode >= 200, final response */
+ k->header = FALSE;
- if((k->size == -1) && !k->chunk && !conn->bits.close &&
- (conn->httpversion == 11) &&
- !(conn->handler->protocol & CURLPROTO_RTSP) &&
- data->state.httpreq != HTTPREQ_HEAD) {
- /* On HTTP 1.1, when connection is not to get closed, but no
- Content-Length nor Transfer-Encoding chunked have been
- received, according to RFC2616 section 4.4 point 5, we
- assume that the server will close the connection to
- signal the end of the document. */
- infof(data, "no chunk, no close, no size. Assume close to "
- "signal end");
- streamclose(conn, "HTTP: No end-of-message indicator");
- }
+ if(k->upgr101 == UPGR101_H2) {
+ /* A requested upgrade was denied, poke the multi handle to possibly
+ allow a pending pipewait to continue */
+ Curl_multi_connchanged(data->multi);
}
- if(!k->header) {
- result = Curl_http_size(data);
- if(result)
- return result;
+ if((k->size == -1) && !k->chunk && !conn->bits.close &&
+ (conn->httpversion == 11) &&
+ !(conn->handler->protocol & CURLPROTO_RTSP) &&
+ data->state.httpreq != HTTPREQ_HEAD) {
+ /* On HTTP 1.1, when connection is not to get closed, but no
+ Content-Length nor Transfer-Encoding chunked have been
+ received, according to RFC2616 section 4.4 point 5, we
+ assume that the server will close the connection to
+ signal the end of the document. */
+ infof(data, "no chunk, no close, no size. Assume close to "
+ "signal end");
+ streamclose(conn, "HTTP: No end-of-message indicator");
}
/* At this point we have some idea about the fate of the connection.
}
#endif
- /*
- * When all the headers have been parsed, see if we should give
- * up and return an error.
- */
- if(http_should_fail(data)) {
- failf(data, "The requested URL returned error: %d",
- k->httpcode);
- return CURLE_HTTP_RETURNED_ERROR;
- }
-
#ifdef USE_WEBSOCKETS
- /* All non-101 HTTP status codes are bad when wanting to upgrade to
- websockets */
+ /* All >=200 HTTP status codes are errors when wanting websockets */
if(data->req.upgr101 == UPGR101_WS) {
failf(data, "Refused WebSockets upgrade: %d", k->httpcode);
return CURLE_HTTP_RETURNED_ERROR;
}
#endif
+ /* Check if this response means the transfer errored. */
+ if(http_should_fail(data, data->req.httpcode)) {
+ failf(data, "The requested URL returned error: %d",
+ k->httpcode);
+ return CURLE_HTTP_RETURNED_ERROR;
+ }
/* Curl_http_auth_act() checks what authentication methods
* that are available and decides which one (if any) to
* use. It will set 'newurl' if an auth method was picked. */
result = Curl_http_auth_act(data);
-
if(result)
return result;
infof(data, "Keep sending data to get tossed away");
k->keepon |= KEEP_SEND;
}
+
}
- if(!k->header) {
- /*
- * really end-of-headers.
- *
- * If we requested a "no body", this is a good time to get
- * out and return home.
+ /* This is the last response that we will got for the current request.
+ * Check on the body size and determine if the response is complete.
+ */
+ result = Curl_http_size(data);
+ if(result)
+ return result;
+
+ /* If we requested a "no body", this is a good time to get
+ * out and return home.
+ */
+ if(data->req.no_body)
+ k->download_done = TRUE;
+
+ /* If max download size is *zero* (nothing) we already have
+ nothing and can safely return ok now! But for HTTP/2, we'd
+ like to call http2_handle_stream_close to properly close a
+ stream. In order to do this, we keep reading until we
+ close the stream. */
+ if(0 == k->maxdownload
+ && !Curl_conn_is_http2(data, conn, FIRSTSOCKET)
+ && !Curl_conn_is_http3(data, conn, FIRSTSOCKET))
+ k->download_done = TRUE;
+
+ /* final response without error, prepare to receive the body */
+ return Curl_http_firstwrite(data);
+}
+
+static CURLcode http_rw_hd(struct Curl_easy *data,
+ const char *hd, size_t hdlen,
+ const char *buf_remain, size_t blen,
+ size_t *pconsumed)
+{
+ CURLcode result = CURLE_OK;
+ struct SingleRequest *k = &data->req;
+ int writetype;
+
+ *pconsumed = 0;
+ if((0x0a == *hd) || (0x0d == *hd)) {
+ /* Empty header line means end of headers! */
+ size_t consumed;
+
+ /* now, only output this if the header AND body are requested:
*/
- if(data->req.no_body)
- k->download_done = TRUE;
-
- /* If max download size is *zero* (nothing) we already have
- nothing and can safely return ok now! But for HTTP/2, we'd
- like to call http2_handle_stream_close to properly close a
- stream. In order to do this, we keep reading until we
- close the stream. */
- if(0 == k->maxdownload
- && !Curl_conn_is_http2(data, conn, FIRSTSOCKET)
- && !Curl_conn_is_http3(data, conn, FIRSTSOCKET))
- k->download_done = TRUE;
- }
-
- if(switch_to_h2) {
- /* Having handled the headers, we can do the HTTP/2 switch.
- * Any remaining `buf` bytes are already HTTP/2 and passed to
- * be processed. */
- result = Curl_http2_upgrade(data, conn, FIRSTSOCKET, buf, blen);
+ Curl_debug(data, CURLINFO_HEADER_IN, (char *)hd, hdlen);
+
+ writetype = CLIENTWRITE_HEADER |
+ ((k->httpcode/100 == 1) ? CLIENTWRITE_1XX : 0);
+
+ result = Curl_client_write(data, writetype, hd, hdlen);
+ if(result)
+ return result;
+
+ result = Curl_bump_headersize(data, hdlen, FALSE);
+ if(result)
+ return result;
+
+ data->req.deductheadercount =
+ (100 <= k->httpcode && 199 >= k->httpcode)?data->req.headerbytecount:0;
+
+ /* analyze the response to find out what to do. */
+ /* Caveat: we clear anything in the header brigade, because a
+ * response might switch HTTP version which may call use recursively.
+ * Not nice, but that is currently the way of things. */
+ Curl_dyn_reset(&data->state.headerb);
+ result = http_on_response(data, buf_remain, blen, &consumed);
if(result)
return result;
- *pconsumed += blen;
+ *pconsumed += consumed;
+ return CURLE_OK;
}
+ /*
+ * Checks for special headers coming up.
+ */
+
+ writetype = CLIENTWRITE_HEADER;
+ if(!k->headerline++) {
+ /* This is the first header, it MUST be the error code line
+ or else we consider this to be the body right away! */
+ bool fine_statusline = FALSE;
+
+ k->httpversion = 0; /* Don't know yet */
+ if(data->conn->handler->protocol & PROTO_FAMILY_HTTP) {
+ /*
+ * https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.2
+ *
+ * The response code is always a three-digit number in HTTP as the spec
+ * says. We allow any three-digit number here, but we cannot make
+ * guarantees on future behaviors since it isn't within the protocol.
+ */
+ const char *p = hd;
+
+ while(*p && ISBLANK(*p))
+ p++;
+ if(!strncmp(p, "HTTP/", 5)) {
+ p += 5;
+ switch(*p) {
+ case '1':
+ p++;
+ if((p[0] == '.') && (p[1] == '0' || p[1] == '1')) {
+ if(ISBLANK(p[2])) {
+ k->httpversion = 10 + (p[1] - '0');
+ p += 3;
+ if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) {
+ k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 +
+ (p[2] - '0');
+ p += 3;
+ if(ISSPACE(*p))
+ fine_statusline = TRUE;
+ }
+ }
+ }
+ if(!fine_statusline) {
+ failf(data, "Unsupported HTTP/1 subversion in response");
+ return CURLE_UNSUPPORTED_PROTOCOL;
+ }
+ break;
+ case '2':
+ case '3':
+ if(!ISBLANK(p[1]))
+ break;
+ k->httpversion = (*p - '0') * 10;
+ p += 2;
+ if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) {
+ k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 +
+ (p[2] - '0');
+ p += 3;
+ if(!ISSPACE(*p))
+ break;
+ fine_statusline = TRUE;
+ }
+ break;
+ default: /* unsupported */
+ failf(data, "Unsupported HTTP version in response");
+ return CURLE_UNSUPPORTED_PROTOCOL;
+ }
+ }
+
+ if(!fine_statusline) {
+ /* If user has set option HTTP200ALIASES,
+ compare header line against list of aliases
+ */
+ statusline check = checkhttpprefix(data, hd, hdlen);
+ if(check == STATUS_DONE) {
+ fine_statusline = TRUE;
+ k->httpcode = 200;
+ k->httpversion = 10;
+ }
+ }
+ }
+ else if(data->conn->handler->protocol & CURLPROTO_RTSP) {
+ const char *p = hd;
+ while(*p && ISBLANK(*p))
+ p++;
+ if(!strncmp(p, "RTSP/", 5)) {
+ p += 5;
+ if(ISDIGIT(*p)) {
+ p++;
+ if((p[0] == '.') && ISDIGIT(p[1])) {
+ if(ISBLANK(p[2])) {
+ p += 3;
+ if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) {
+ k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 +
+ (p[2] - '0');
+ p += 3;
+ if(ISSPACE(*p)) {
+ fine_statusline = TRUE;
+ k->httpversion = 11; /* RTSP acts like HTTP 1.1 */
+ }
+ }
+ }
+ }
+ }
+ if(!fine_statusline)
+ return CURLE_WEIRD_SERVER_REPLY;
+ }
+ }
+
+ if(fine_statusline) {
+ result = Curl_http_statusline(data, data->conn);
+ if(result)
+ return result;
+ writetype |= CLIENTWRITE_STATUS;
+ }
+ else {
+ k->header = FALSE; /* this is not a header line */
+ return CURLE_WEIRD_SERVER_REPLY;
+ }
+ }
+
+ result = verify_header(data, hd, hdlen);
+ if(result)
+ return result;
+
+ result = Curl_http_header(data, hd, hdlen);
+ if(result)
+ return result;
+
+ /*
+ * Taken in one (more) header. Write it to the client.
+ */
+ Curl_debug(data, CURLINFO_HEADER_IN, (char *)hd, hdlen);
+
+ if(k->httpcode/100 == 1)
+ writetype |= CLIENTWRITE_1XX;
+ result = Curl_client_write(data, writetype, hd, hdlen);
+ if(result)
+ return result;
+
+ result = Curl_bump_headersize(data, hdlen, FALSE);
+ if(result)
+ return result;
+
return CURLE_OK;
}
+
/*
* Read any HTTP header lines from the server and pass them to the client app.
*/
-static CURLcode http_rw_headers(struct Curl_easy *data,
- const char *buf, size_t blen,
- size_t *pconsumed)
+static CURLcode http_parse_headers(struct Curl_easy *data,
+ const char *buf, size_t blen,
+ size_t *pconsumed)
{
struct connectdata *conn = data->conn;
CURLcode result = CURLE_OK;
struct SingleRequest *k = &data->req;
- char *hd;
- size_t hdlen;
char *end_ptr;
bool leftover_body = FALSE;
/* header line within buffer loop */
*pconsumed = 0;
- do {
- size_t line_length;
- int writetype;
-
- /* data is in network encoding so use 0x0a instead of '\n' */
- end_ptr = memchr(buf, 0x0a, blen);
+ while(blen && k->header) {
+ size_t consumed;
+ end_ptr = memchr(buf, '\n', blen);
if(!end_ptr) {
/* Not a complete header line within buffer, append the data to
the end of the headerbuff. */
}
/* decrease the size of the remaining (supposed) header line */
- line_length = (end_ptr - buf) + 1;
- result = Curl_dyn_addn(&data->state.headerb, buf, line_length);
+ consumed = (end_ptr - buf) + 1;
+ result = Curl_dyn_addn(&data->state.headerb, buf, consumed);
if(result)
return result;
-
- blen -= line_length;
- buf += line_length;
- *pconsumed += line_length;
+ blen -= consumed;
+ buf += consumed;
+ *pconsumed += consumed;
/****
* We now have a FULL header line in 'headerb'.
}
}
- /* headers are in network encoding so use 0x0a and 0x0d instead of '\n'
- and '\r' */
- hd = Curl_dyn_ptr(&data->state.headerb);
- hdlen = Curl_dyn_len(&data->state.headerb);
- if((0x0a == *hd) || (0x0d == *hd)) {
- /* Empty header line means end of headers! */
- size_t consumed;
-
- /* now, only output this if the header AND body are requested:
- */
- Curl_debug(data, CURLINFO_HEADER_IN, hd, hdlen);
-
- writetype = CLIENTWRITE_HEADER |
- ((k->httpcode/100 == 1) ? CLIENTWRITE_1XX : 0);
-
- result = Curl_client_write(data, writetype, hd, hdlen);
- if(result)
- return result;
-
- result = Curl_bump_headersize(data, hdlen, FALSE);
- if(result)
- return result;
- /* We are done with this line. We reset because response
- * processing might switch to HTTP/2 and that might call us
- * directly again. */
- Curl_dyn_reset(&data->state.headerb);
-
- data->req.deductheadercount =
- (100 <= k->httpcode && 199 >= k->httpcode)?data->req.headerbytecount:0;
-
- /* analyze the response to find out what to do */
- result = http_on_response(data, buf, blen, &consumed);
- if(result)
- return result;
- *pconsumed += consumed;
+ result = http_rw_hd(data, Curl_dyn_ptr(&data->state.headerb),
+ Curl_dyn_len(&data->state.headerb),
+ buf, blen, &consumed);
+ /* We are done with this line. We reset because response
+ * processing might switch to HTTP/2 and that might call us
+ * directly again. */
+ Curl_dyn_reset(&data->state.headerb);
+ if(consumed) {
blen -= consumed;
buf += consumed;
-
- if(!k->header || !blen)
- goto out; /* exit header line loop */
-
- continue;
- }
-
- /*
- * Checks for special headers coming up.
- */
-
- writetype = CLIENTWRITE_HEADER;
- if(!k->headerline++) {
- /* This is the first header, it MUST be the error code line
- or else we consider this to be the body right away! */
- bool fine_statusline = FALSE;
-
- k->httpversion = 0; /* Don't know yet */
- if(conn->handler->protocol & PROTO_FAMILY_HTTP) {
- /*
- * https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.2
- *
- * The response code is always a three-digit number in HTTP as the spec
- * says. We allow any three-digit number here, but we cannot make
- * guarantees on future behaviors since it isn't within the protocol.
- */
- char *p = hd;
-
- while(*p && ISBLANK(*p))
- p++;
- if(!strncmp(p, "HTTP/", 5)) {
- p += 5;
- switch(*p) {
- case '1':
- p++;
- if((p[0] == '.') && (p[1] == '0' || p[1] == '1')) {
- if(ISBLANK(p[2])) {
- k->httpversion = 10 + (p[1] - '0');
- p += 3;
- if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) {
- k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 +
- (p[2] - '0');
- p += 3;
- if(ISSPACE(*p))
- fine_statusline = TRUE;
- }
- }
- }
- if(!fine_statusline) {
- failf(data, "Unsupported HTTP/1 subversion in response");
- return CURLE_UNSUPPORTED_PROTOCOL;
- }
- break;
- case '2':
- case '3':
- if(!ISBLANK(p[1]))
- break;
- k->httpversion = (*p - '0') * 10;
- p += 2;
- if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) {
- k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 +
- (p[2] - '0');
- p += 3;
- if(!ISSPACE(*p))
- break;
- fine_statusline = TRUE;
- }
- break;
- default: /* unsupported */
- failf(data, "Unsupported HTTP version in response");
- return CURLE_UNSUPPORTED_PROTOCOL;
- }
- }
-
- if(!fine_statusline) {
- /* If user has set option HTTP200ALIASES,
- compare header line against list of aliases
- */
- statusline check = checkhttpprefix(data, hd, hdlen);
- if(check == STATUS_DONE) {
- fine_statusline = TRUE;
- k->httpcode = 200;
- k->httpversion = 10;
- }
- }
- }
- else if(conn->handler->protocol & CURLPROTO_RTSP) {
- char *p = hd;
- while(*p && ISBLANK(*p))
- p++;
- if(!strncmp(p, "RTSP/", 5)) {
- p += 5;
- if(ISDIGIT(*p)) {
- p++;
- if((p[0] == '.') && ISDIGIT(p[1])) {
- if(ISBLANK(p[2])) {
- p += 3;
- if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) {
- k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 +
- (p[2] - '0');
- p += 3;
- if(ISSPACE(*p)) {
- fine_statusline = TRUE;
- k->httpversion = 11; /* RTSP acts like HTTP 1.1 */
- }
- }
- }
- }
- }
- if(!fine_statusline)
- return CURLE_WEIRD_SERVER_REPLY;
- }
- }
-
- if(fine_statusline) {
- result = Curl_http_statusline(data, conn);
- if(result)
- return result;
- writetype |= CLIENTWRITE_STATUS;
- }
- else {
- k->header = FALSE; /* this is not a header line */
- break;
- }
+ *pconsumed += consumed;
}
-
- result = verify_header(data);
- if(result)
- return result;
-
- result = Curl_http_header(data, conn, hd, hdlen);
- if(result)
- return result;
-
- /*
- * Taken in one (more) header. Write it to the client.
- */
- Curl_debug(data, CURLINFO_HEADER_IN, hd, hdlen);
-
- if(k->httpcode/100 == 1)
- writetype |= CLIENTWRITE_1XX;
- result = Curl_client_write(data, writetype, hd, hdlen);
- if(result)
- return result;
-
- result = Curl_bump_headersize(data, hdlen, FALSE);
if(result)
return result;
-
- Curl_dyn_reset(&data->state.headerb);
}
- while(blen);
/* We might have reached the end of the header part here, but
there might be a non-header part left in the end of the read
return CURLE_OK;
}
+CURLcode Curl_http_write_resp_hd(struct Curl_easy *data,
+ const char *hd, size_t hdlen,
+ bool is_eos)
+{
+ CURLcode result;
+ size_t consumed;
+ char tmp = 0;
+
+ result = http_rw_hd(data, hd, hdlen, &tmp, 0, &consumed);
+ if(!result && is_eos) {
+ result = Curl_client_write(data, (CLIENTWRITE_BODY|CLIENTWRITE_EOS),
+ &tmp, 0);
+ }
+ return result;
+}
+
/*
* HTTP protocol `write_resp` implementation. Will parse headers
* when not done yet and otherwise return without consuming data.
else {
CURLcode result;
- result = http_rw_headers(data, buf, blen, pconsumed);
+ result = http_parse_headers(data, buf, blen, pconsumed);
if(!result && !data->req.header) {
- /* we have successfully finished parsing the HEADERs */
- result = Curl_http_firstwrite(data);
-
if(!data->req.no_body && Curl_dyn_len(&data->state.headerb)) {
/* leftover from parsing something that turned out not
* to be a header, only happens if we allow for
struct dynbuf *req);
CURLcode Curl_http_statusline(struct Curl_easy *data,
struct connectdata *conn);
-CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn,
- char *headp, size_t hdlen);
+CURLcode Curl_http_header(struct Curl_easy *data,
+ const char *hd, size_t hdlen);
CURLcode Curl_transferencode(struct Curl_easy *data);
CURLcode Curl_http_req_set_reader(struct Curl_easy *data,
Curl_HttpReq httpreq,
CURLcode Curl_http_write_resp(struct Curl_easy *data,
const char *buf, size_t blen,
bool is_eos);
+CURLcode Curl_http_write_resp_hd(struct Curl_easy *data,
+ const char *hd, size_t hdlen,
+ bool is_eos);
/* These functions are in http.c */
CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy,
struct bufq inbufq; /* network input */
struct bufq outbufq; /* network output */
struct bufc_pool stream_bufcp; /* spares for stream buffers */
+ struct dynbuf scratch; /* scratch buffer for temp use */
size_t drain_total; /* sum of all stream's UrlState drain */
uint32_t max_concurrent_streams;
Curl_bufq_free(&ctx->inbufq);
Curl_bufq_free(&ctx->outbufq);
Curl_bufcp_free(&ctx->stream_bufcp);
+ Curl_dyn_free(&ctx->scratch);
memset(ctx, 0, sizeof(*ctx));
ctx->call_data = save;
}
Curl_bufcp_init(&ctx->stream_bufcp, H2_CHUNK_SIZE, H2_STREAM_POOL_SPARES);
Curl_bufq_initp(&ctx->inbufq, &ctx->stream_bufcp, H2_NW_RECV_CHUNKS, 0);
Curl_bufq_initp(&ctx->outbufq, &ctx->stream_bufcp, H2_NW_SEND_CHUNKS, 0);
+ Curl_dyn_init(&ctx->scratch, CURL_MAX_HTTP_HEADER);
ctx->last_stream_id = 2147483647;
rc = nghttp2_session_callbacks_new(&cbs);
return rv;
}
-static CURLcode recvbuf_write_hds(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- const char *buf, size_t blen)
-{
- (void)cf;
- return Curl_xfer_write_resp(data, (char *)buf, blen, FALSE);
-}
-
static CURLcode on_stream_frame(struct Curl_cfilter *cf,
struct Curl_easy *data,
const nghttp2_frame *frame)
stream->status_code = -1;
}
- result = recvbuf_write_hds(cf, data, STRCONST("\r\n"));
+ result = Curl_xfer_write_resp_hd(data, STRCONST("\r\n"), stream->closed);
if(result)
return result;
void *userp)
{
struct Curl_cfilter *cf = userp;
+ struct cf_h2_ctx *ctx = cf->ctx;
struct h2_stream_ctx *stream;
struct Curl_easy *data_s;
int32_t stream_id = frame->hd.stream_id;
result = Curl_headers_push(data_s, buffer, CURLH_PSEUDO);
if(result)
return NGHTTP2_ERR_CALLBACK_FAILURE;
- result = recvbuf_write_hds(cf, data_s, STRCONST("HTTP/2 "));
- if(result)
- return NGHTTP2_ERR_CALLBACK_FAILURE;
- result = recvbuf_write_hds(cf, data_s, (const char *)value, valuelen);
- if(result)
- return NGHTTP2_ERR_CALLBACK_FAILURE;
- /* the space character after the status code is mandatory */
- result = recvbuf_write_hds(cf, data_s, STRCONST(" \r\n"));
+ Curl_dyn_reset(&ctx->scratch);
+ result = Curl_dyn_addn(&ctx->scratch, STRCONST("HTTP/2 "));
+ if(!result)
+ result = Curl_dyn_addn(&ctx->scratch, value, valuelen);
+ if(!result)
+ result = Curl_dyn_addn(&ctx->scratch, STRCONST(" \r\n"));
+ if(!result)
+ result = Curl_xfer_write_resp_hd(data_s, Curl_dyn_ptr(&ctx->scratch),
+ Curl_dyn_len(&ctx->scratch), FALSE);
if(result)
return NGHTTP2_ERR_CALLBACK_FAILURE;
/* if we receive data for another handle, wake that up */
/* nghttp2 guarantees that namelen > 0, and :status was already
received, and this is not pseudo-header field . */
/* convert to an HTTP1-style header */
- result = recvbuf_write_hds(cf, data_s, (const char *)name, namelen);
- if(result)
- return NGHTTP2_ERR_CALLBACK_FAILURE;
- result = recvbuf_write_hds(cf, data_s, STRCONST(": "));
- if(result)
- return NGHTTP2_ERR_CALLBACK_FAILURE;
- result = recvbuf_write_hds(cf, data_s, (const char *)value, valuelen);
- if(result)
- return NGHTTP2_ERR_CALLBACK_FAILURE;
- result = recvbuf_write_hds(cf, data_s, STRCONST("\r\n"));
+ Curl_dyn_reset(&ctx->scratch);
+ result = Curl_dyn_addn(&ctx->scratch, (const char *)name, namelen);
+ if(!result)
+ result = Curl_dyn_addn(&ctx->scratch, STRCONST(": "));
+ if(!result)
+ result = Curl_dyn_addn(&ctx->scratch, (const char *)value, valuelen);
+ if(!result)
+ result = Curl_dyn_addn(&ctx->scratch, STRCONST("\r\n"));
+ if(!result)
+ result = Curl_xfer_write_resp_hd(data_s, Curl_dyn_ptr(&ctx->scratch),
+ Curl_dyn_len(&ctx->scratch), FALSE);
if(result)
return NGHTTP2_ERR_CALLBACK_FAILURE;
/* if we receive data for another handle, wake that up */
ZERO_NULL, /* perform_getsock */
imap_disconnect, /* disconnect */
ZERO_NULL, /* write_resp */
+ ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_IMAP, /* defport */
ZERO_NULL, /* perform_getsock */
imap_disconnect, /* disconnect */
ZERO_NULL, /* write_resp */
+ ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_IMAPS, /* defport */
ZERO_NULL, /* perform_getsock */
ZERO_NULL, /* disconnect */
ZERO_NULL, /* write_resp */
+ ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_LDAP, /* defport */
ZERO_NULL, /* perform_getsock */
ZERO_NULL, /* disconnect */
ZERO_NULL, /* write_resp */
+ ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_LDAPS, /* defport */
ZERO_NULL, /* perform_getsock */
ZERO_NULL, /* disconnect */
ZERO_NULL, /* write_resp */
+ ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_MQTT, /* defport */
ZERO_NULL, /* perform_getsock */
oldap_disconnect, /* disconnect */
ZERO_NULL, /* write_resp */
+ ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_LDAP, /* defport */
ZERO_NULL, /* perform_getsock */
oldap_disconnect, /* disconnect */
ZERO_NULL, /* write_resp */
+ ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_LDAPS, /* defport */
ZERO_NULL, /* perform_getsock */
pop3_disconnect, /* disconnect */
ZERO_NULL, /* write_resp */
+ ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_POP3, /* defport */
ZERO_NULL, /* perform_getsock */
pop3_disconnect, /* disconnect */
ZERO_NULL, /* write_resp */
+ ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_POP3S, /* defport */
* This function scans the body after the end-of-body and writes everything
* until the end is found.
*/
-CURLcode Curl_pop3_write(struct Curl_easy *data, char *str, size_t nread)
+CURLcode Curl_pop3_write(struct Curl_easy *data, const char *str, size_t nread)
{
/* This code could be made into a special function in the handler struct */
CURLcode result = CURLE_OK;
/* This function scans the body after the end-of-body and writes everything
* until the end is found */
-CURLcode Curl_pop3_write(struct Curl_easy *data, char *str, size_t nread);
+CURLcode Curl_pop3_write(struct Curl_easy *data,
+ const char *str, size_t nread);
#endif /* HEADER_CURL_POP3_H */
else if(data->req.writebytecount)
infof(data, "upload completely sent off: %" CURL_FORMAT_CURL_OFF_T
" bytes", data->req.writebytecount);
- else
+ else if(!data->req.download_done)
infof(data, Curl_creader_total_length(data)?
"We are completely uploaded and fine" :
"Request completely sent off");
static
CURLcode rtp_client_write(struct Curl_easy *data, const char *ptr, size_t len);
static
-CURLcode rtsp_parse_transport(struct Curl_easy *data, char *transport);
+CURLcode rtsp_parse_transport(struct Curl_easy *data, const char *transport);
/*
ZERO_NULL, /* perform_getsock */
rtsp_disconnect, /* disconnect */
rtsp_rtp_write_resp, /* write_resp */
+ ZERO_NULL, /* write_resp_hd */
rtsp_conncheck, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_RTSP, /* defport */
return CURLE_OK;
}
-CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, char *header)
+CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, const char *header)
{
if(checkprefix("CSeq:", header)) {
long CSeq = 0;
char *endp;
- char *p = &header[5];
+ const char *p = &header[5];
while(ISBLANK(*p))
p++;
CSeq = strtol(p, &endp, 10);
}
}
else if(checkprefix("Session:", header)) {
- char *start;
- char *end;
+ const char *start, *end;
size_t idlen;
/* Find the first non-space letter */
}
static
-CURLcode rtsp_parse_transport(struct Curl_easy *data, char *transport)
+CURLcode rtsp_parse_transport(struct Curl_easy *data, const char *transport)
{
/* If we receive multiple Transport response-headers, the linterleaved
channels of each response header is recorded and used together for
subsequent data validity checks.*/
/* e.g.: ' RTP/AVP/TCP;unicast;interleaved=5-6' */
- char *start;
- char *end;
+ const char *start, *end;
start = transport;
while(start && *start) {
while(*start && ISBLANK(*start) )
if(checkprefix("interleaved=", start)) {
long chan1, chan2, chan;
char *endp;
- char *p = start + 12;
+ const char *p = start + 12;
chan1 = strtol(p, &endp, 10);
if(p != endp && chan1 >= 0 && chan1 <= 255) {
unsigned char *rtp_channel_mask = data->state.rtp_channel_mask;
extern const struct Curl_handler Curl_handler_rtsp;
-CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, char *header);
+CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, const char *header);
#else
/* disabled */
ZERO_NULL, /* perform_getsock */
smb_disconnect, /* disconnect */
ZERO_NULL, /* write_resp */
+ ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_SMB, /* defport */
ZERO_NULL, /* perform_getsock */
smb_disconnect, /* disconnect */
ZERO_NULL, /* write_resp */
+ ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_SMBS, /* defport */
ZERO_NULL, /* perform_getsock */
smtp_disconnect, /* disconnect */
ZERO_NULL, /* write_resp */
+ ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_SMTP, /* defport */
ZERO_NULL, /* perform_getsock */
smtp_disconnect, /* disconnect */
ZERO_NULL, /* write_resp */
+ ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_SMTPS, /* defport */
ZERO_NULL, /* perform_getsock */
ZERO_NULL, /* disconnect */
ZERO_NULL, /* write_resp */
+ ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_TELNET, /* defport */
ZERO_NULL, /* perform_getsock */
tftp_disconnect, /* disconnect */
ZERO_NULL, /* write_resp */
+ ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_TFTP, /* defport */
}
CURLcode Curl_xfer_write_resp(struct Curl_easy *data,
- char *buf, size_t blen,
+ const char *buf, size_t blen,
bool is_eos)
{
CURLcode result = CURLE_OK;
return result;
}
+CURLcode Curl_xfer_write_resp_hd(struct Curl_easy *data,
+ const char *hd0, size_t hdlen, bool is_eos)
+{
+ if(data->conn->handler->write_resp_hd) {
+ /* protocol handlers offering this function take full responsibility
+ * for writing all received download data to the client. */
+ return data->conn->handler->write_resp_hd(data, hd0, hdlen, is_eos);
+ }
+ /* No special handling by protocol handler, write as response bytes */
+ return Curl_xfer_write_resp(data, hd0, hdlen, is_eos);
+}
+
CURLcode Curl_xfer_write_done(struct Curl_easy *data, bool premature)
{
(void)premature;
* @param blen the amount of bytes in `buf`
* @param is_eos TRUE iff the connection indicates this to be the last
* bytes of the response
- * @param done on returnm, TRUE iff the response is complete
*/
CURLcode Curl_xfer_write_resp(struct Curl_easy *data,
- char *buf, size_t blen,
+ const char *buf, size_t blen,
bool is_eos);
+/**
+ * Write a single "header" line from a server response.
+ * @param hd0 the 0-terminated, single header line
+ * @param hdlen the length of the header line
+ * @param is_eos TRUE iff this is the end of the response
+ */
+CURLcode Curl_xfer_write_resp_hd(struct Curl_easy *data,
+ const char *hd0, size_t hdlen, bool is_eos);
+
/* This sets up a forthcoming transfer */
void Curl_xfer_setup(struct Curl_easy *data,
int sockindex, /* socket index to read from or -1 */
CURLcode (*disconnect)(struct Curl_easy *, struct connectdata *,
bool dead_connection);
- /* If used, this function gets called from transfer.c:readwrite_data() to
+ /* If used, this function gets called from transfer.c to
allow the protocol to do extra handling in writing response to
the client. */
CURLcode (*write_resp)(struct Curl_easy *data, const char *buf, size_t blen,
bool is_eos);
+ /* If used, this function gets called from transfer.c to
+ allow the protocol to do extra handling in writing a single response
+ header line to the client. */
+ CURLcode (*write_resp_hd)(struct Curl_easy *data,
+ const char *hd, size_t hdlen, bool is_eos);
+
/* This function can perform various checks on the connection. See
CONNCHECK_* for more information about the checks that can be performed,
and CONNRESULT_* for the results that can be returned. */
struct curltime handshake_at; /* time connect handshake finished */
struct curltime reconnect_at; /* time the next attempt should start */
struct bufc_pool stream_bufcp; /* chunk pool for streams */
+ struct dynbuf scratch; /* temp buffer for header construction */
size_t max_stream_window; /* max flow window for one stream */
uint64_t max_idle_ms; /* max idle time for QUIC connection */
int qlogfd;
return 0;
}
-static CURLcode write_resp_hds(struct Curl_easy *data,
- const char *buf, size_t blen)
-{
- return Curl_xfer_write_resp(data, (char *)buf, blen, FALSE);
-}
-
static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id,
const uint8_t *buf, size_t blen,
void *user_data, void *stream_user_data)
if(!stream)
return 0;
/* add a CRLF only if we've received some headers */
- result = write_resp_hds(data, "\r\n", 2);
+ result = Curl_xfer_write_resp_hd(data, STRCONST("\r\n"), stream->closed);
if(result) {
return -1;
}
void *user_data, void *stream_user_data)
{
struct Curl_cfilter *cf = user_data;
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
curl_int64_t stream_id = (curl_int64_t)sid;
nghttp3_vec h3name = nghttp3_rcbuf_get_buf(name);
nghttp3_vec h3val = nghttp3_rcbuf_get_buf(value);
return 0;
if(token == NGHTTP3_QPACK_TOKEN__STATUS) {
- char line[14]; /* status line is always 13 characters long */
- size_t ncopy;
result = Curl_http_decode_status(&stream->status_code,
(const char *)h3val.base, h3val.len);
if(result)
return -1;
- ncopy = msnprintf(line, sizeof(line), "HTTP/3 %03d \r\n",
- stream->status_code);
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] status: %s", stream_id, line);
- result = write_resp_hds(data, line, ncopy);
+ Curl_dyn_reset(&ctx->scratch);
+ result = Curl_dyn_addn(&ctx->scratch, STRCONST("HTTP/3 "));
+ if(!result)
+ result = Curl_dyn_addn(&ctx->scratch,
+ (const char *)h3val.base, h3val.len);
+ if(!result)
+ result = Curl_dyn_addn(&ctx->scratch, STRCONST(" \r\n"));
+ if(!result)
+ result = Curl_xfer_write_resp_hd(data, Curl_dyn_ptr(&ctx->scratch),
+ Curl_dyn_len(&ctx->scratch), FALSE);
+ CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] status: %s",
+ stream_id, Curl_dyn_ptr(&ctx->scratch));
if(result) {
return -1;
}
CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] header: %.*s: %.*s",
stream_id, (int)h3name.len, h3name.base,
(int)h3val.len, h3val.base);
- result = write_resp_hds(data, (const char *)h3name.base, h3name.len);
- if(result) {
- return -1;
- }
- result = write_resp_hds(data, ": ", 2);
- if(result) {
- return -1;
- }
- result = write_resp_hds(data, (const char *)h3val.base, h3val.len);
- if(result) {
- return -1;
- }
- result = write_resp_hds(data, "\r\n", 2);
+ Curl_dyn_reset(&ctx->scratch);
+ result = Curl_dyn_addn(&ctx->scratch,
+ (const char *)h3name.base, h3name.len);
+ if(!result)
+ result = Curl_dyn_addn(&ctx->scratch, STRCONST(": "));
+ if(!result)
+ result = Curl_dyn_addn(&ctx->scratch,
+ (const char *)h3val.base, h3val.len);
+ if(!result)
+ result = Curl_dyn_addn(&ctx->scratch, STRCONST("\r\n"));
+ if(!result)
+ result = Curl_xfer_write_resp_hd(data, Curl_dyn_ptr(&ctx->scratch),
+ Curl_dyn_len(&ctx->scratch), FALSE);
if(result) {
return -1;
}
if(ctx->qconn)
ngtcp2_conn_del(ctx->qconn);
Curl_bufcp_free(&ctx->stream_bufcp);
+ Curl_dyn_free(&ctx->scratch);
Curl_ssl_peer_cleanup(&ctx->peer);
memset(ctx, 0, sizeof(*ctx));
ctx->max_idle_ms = CURL_QUIC_MAX_IDLE_MS;
Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE,
H3_STREAM_POOL_SPARES);
+ Curl_dyn_init(&ctx->scratch, CURL_MAX_HTTP_HEADER);
result = Curl_ssl_peer_init(&ctx->peer, cf, TRNSPRT_QUIC);
if(result)
myssh_getsock, /* perform_getsock */
scp_disconnect, /* disconnect */
ZERO_NULL, /* write_resp */
+ ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_SSH, /* defport */
myssh_getsock, /* perform_getsock */
sftp_disconnect, /* disconnect */
ZERO_NULL, /* write_resp */
+ ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_SSH, /* defport */
ssh_getsock, /* perform_getsock */
scp_disconnect, /* disconnect */
ZERO_NULL, /* write_resp */
+ ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ssh_attach, /* attach */
PORT_SSH, /* defport */
ssh_getsock, /* perform_getsock */
sftp_disconnect, /* disconnect */
ZERO_NULL, /* write_resp */
+ ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ssh_attach, /* attach */
PORT_SSH, /* defport */
wssh_getsock, /* perform_getsock */
wscp_disconnect, /* disconnect */
ZERO_NULL, /* write_resp */
+ ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_SSH, /* defport */
wssh_getsock, /* perform_getsock */
wsftp_disconnect, /* disconnect */
ZERO_NULL, /* write_resp */
+ ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_SSH, /* defport */
ZERO_NULL, /* perform_getsock */
ws_disconnect, /* disconnect */
Curl_http_write_resp, /* write_resp */
+ Curl_http_write_resp_hd, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_HTTP, /* defport */
ZERO_NULL, /* perform_getsock */
ws_disconnect, /* disconnect */
Curl_http_write_resp, /* write_resp */
+ Curl_http_write_resp_hd, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_HTTPS, /* defport */