lib: add Curl_xfer_write_resp_hd

Add method in protocol handlers to allow writing of a single,
0-terminated header line. Avoids parsing and copying these lines.

Closes #13165
This commit is contained in:
Stefan Eissing 2024-03-21 12:15:59 +01:00 коммит произвёл Daniel Stenberg
Родитель c296abd42d
Коммит 8dd81bd5db
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 5CC908FDB71E12C2
30 изменённых файлов: 472 добавлений и 403 удалений

Просмотреть файл

@ -171,7 +171,7 @@ static int hyper_each_header(void *userdata,
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;

Просмотреть файл

@ -80,6 +80,7 @@ const struct Curl_handler Curl_handler_rtmp = {
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 */
@ -103,6 +104,7 @@ const struct Curl_handler Curl_handler_rtmpt = {
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 */
@ -126,6 +128,7 @@ const struct Curl_handler Curl_handler_rtmpe = {
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 */
@ -149,6 +152,7 @@ const struct Curl_handler Curl_handler_rtmpte = {
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 */
@ -172,6 +176,7 @@ const struct Curl_handler Curl_handler_rtmps = {
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 */
@ -195,6 +200,7 @@ const struct Curl_handler Curl_handler_rtmpts = {
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 */

Просмотреть файл

@ -90,6 +90,7 @@ const struct Curl_handler Curl_handler_dict = {
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 */

Просмотреть файл

@ -115,6 +115,7 @@ const struct Curl_handler Curl_handler_file = {
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 */

Просмотреть файл

@ -177,6 +177,7 @@ const struct Curl_handler Curl_handler_ftp = {
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 */
@ -208,6 +209,7 @@ const struct Curl_handler Curl_handler_ftps = {
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 */

Просмотреть файл

@ -76,6 +76,7 @@ const struct Curl_handler Curl_handler_gopher = {
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 */
@ -100,6 +101,7 @@ const struct Curl_handler Curl_handler_gophers = {
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 */

Просмотреть файл

@ -100,7 +100,7 @@
* 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);
@ -123,6 +123,7 @@ const struct Curl_handler Curl_handler_http = {
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 */
@ -151,6 +152,7 @@ const struct Curl_handler Curl_handler_https = {
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 */
@ -243,8 +245,6 @@ char *Curl_copy_header_value(const char *header)
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');
@ -565,7 +565,7 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data)
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;
@ -1006,21 +1006,18 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy,
}
/**
* 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.
@ -2836,9 +2833,10 @@ checkprotoprefix(struct Curl_easy *data, struct connectdata *conn,
/*
* 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;
@ -2848,7 +2846,7 @@ CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn,
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")
@ -3306,12 +3304,11 @@ CURLcode Curl_http_size(struct Curl_easy *data)
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");
@ -3320,11 +3317,11 @@ static CURLcode verify_header(struct Curl_easy *data)
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");
@ -3369,7 +3366,6 @@ static CURLcode http_on_response(struct Curl_easy *data,
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;
@ -3388,96 +3384,92 @@ static CURLcode http_on_response(struct Curl_easy *data,
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;
}
}
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);
}
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");
}
return result;
}
if(!k->header) {
result = Curl_http_size(data);
if(result)
return result;
/* 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);
}
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.
@ -3511,31 +3503,25 @@ static CURLcode http_on_response(struct Curl_easy *data,
}
#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;
@ -3606,65 +3592,244 @@ static CURLcode http_on_response(struct Curl_easy *data,
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;
Curl_debug(data, CURLINFO_HEADER_IN, (char *)hd, hdlen);
/* 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;
}
writetype = CLIENTWRITE_HEADER |
((k->httpcode/100 == 1) ? CLIENTWRITE_1XX : 0);
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);
result = Curl_client_write(data, writetype, hd, hdlen);
if(result)
return result;
*pconsumed += blen;
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 += 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. */
@ -3700,14 +3865,13 @@ static CURLcode http_rw_headers(struct Curl_easy *data,
}
/* 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'.
@ -3735,195 +3899,21 @@ static CURLcode http_rw_headers(struct Curl_easy *data,
}
}
/* 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;
*pconsumed += consumed;
}
/*
* 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;
}
}
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
@ -3935,6 +3925,22 @@ out:
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.
@ -3950,11 +3956,8 @@ CURLcode Curl_http_write_resp_hds(struct Curl_easy *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

Просмотреть файл

@ -102,8 +102,8 @@ CURLcode Curl_http_target(struct Curl_easy *data, struct connectdata *conn,
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,
@ -134,6 +134,9 @@ int Curl_http_getsock_do(struct Curl_easy *data, struct connectdata *conn,
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,

Просмотреть файл

@ -127,6 +127,7 @@ struct cf_h2_ctx {
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;
@ -153,6 +154,7 @@ static void cf_h2_ctx_clear(struct cf_h2_ctx *ctx)
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;
}
@ -408,6 +410,7 @@ static CURLcode cf_h2_ctx_init(struct Curl_cfilter *cf,
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);
@ -945,14 +948,6 @@ fail:
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)
@ -1008,7 +1003,7 @@ static CURLcode on_stream_frame(struct Curl_cfilter *cf,
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;
@ -1359,6 +1354,7 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
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;
@ -1468,14 +1464,15 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
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 */
@ -1490,16 +1487,17 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
/* 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 */

Просмотреть файл

@ -131,6 +131,7 @@ const struct Curl_handler Curl_handler_imap = {
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 */
@ -160,6 +161,7 @@ const struct Curl_handler Curl_handler_imaps = {
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 */

Просмотреть файл

@ -178,6 +178,7 @@ const struct Curl_handler Curl_handler_ldap = {
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 */
@ -206,6 +207,7 @@ const struct Curl_handler Curl_handler_ldaps = {
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 */

Просмотреть файл

@ -89,6 +89,7 @@ const struct Curl_handler Curl_handler_mqtt = {
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 */

Просмотреть файл

@ -131,6 +131,7 @@ const struct Curl_handler Curl_handler_ldap = {
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 */
@ -159,6 +160,7 @@ const struct Curl_handler Curl_handler_ldaps = {
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 */

Просмотреть файл

@ -126,6 +126,7 @@ const struct Curl_handler Curl_handler_pop3 = {
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 */
@ -155,6 +156,7 @@ const struct Curl_handler Curl_handler_pop3s = {
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 */
@ -1450,7 +1452,7 @@ static CURLcode pop3_parse_custom_request(struct Curl_easy *data)
* 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;

Просмотреть файл

@ -92,6 +92,7 @@ extern const struct Curl_handler Curl_handler_pop3s;
/* 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 */

Просмотреть файл

@ -266,7 +266,7 @@ static CURLcode req_set_upload_done(struct Curl_easy *data)
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");

Просмотреть файл

@ -93,7 +93,7 @@ static int rtsp_getsock_do(struct Curl_easy *data, struct connectdata *conn,
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);
/*
@ -114,6 +114,7 @@ const struct Curl_handler Curl_handler_rtsp = {
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 */
@ -913,12 +914,12 @@ CURLcode rtp_client_write(struct Curl_easy *data, const char *ptr, size_t len)
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);
@ -933,8 +934,7 @@ CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, char *header)
}
}
else if(checkprefix("Session:", header)) {
char *start;
char *end;
const char *start, *end;
size_t idlen;
/* Find the first non-space letter */
@ -989,14 +989,13 @@ CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, char *header)
}
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) )
@ -1005,7 +1004,7 @@ CURLcode rtsp_parse_transport(struct Curl_easy *data, char *transport)
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;

Просмотреть файл

@ -31,7 +31,7 @@
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 */

Просмотреть файл

@ -273,6 +273,7 @@ const struct Curl_handler Curl_handler_smb = {
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 */
@ -300,6 +301,7 @@ const struct Curl_handler Curl_handler_smbs = {
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 */

Просмотреть файл

@ -132,6 +132,7 @@ const struct Curl_handler Curl_handler_smtp = {
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 */
@ -161,6 +162,7 @@ const struct Curl_handler Curl_handler_smtps = {
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 */

Просмотреть файл

@ -187,6 +187,7 @@ const struct Curl_handler Curl_handler_telnet = {
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 */

Просмотреть файл

@ -182,6 +182,7 @@ const struct Curl_handler Curl_handler_tftp = {
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 */

Просмотреть файл

@ -1156,7 +1156,7 @@ void Curl_xfer_setup(
}
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;
@ -1195,6 +1195,18 @@ CURLcode Curl_xfer_write_resp(struct Curl_easy *data,
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;

Просмотреть файл

@ -62,12 +62,20 @@ bool Curl_meets_timecondition(struct Curl_easy *data, time_t timeofdoc);
* @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 */

Просмотреть файл

@ -701,12 +701,18 @@ struct Curl_handler {
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. */

Просмотреть файл

@ -130,6 +130,7 @@ struct cf_ngtcp2_ctx {
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;
@ -765,12 +766,6 @@ static int cb_h3_stream_close(nghttp3_conn *conn, int64_t sid,
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)
@ -835,7 +830,7 @@ static int cb_h3_end_headers(nghttp3_conn *conn, int64_t sid,
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;
}
@ -855,6 +850,7 @@ static int cb_h3_recv_header(nghttp3_conn *conn, int64_t sid,
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);
@ -872,17 +868,23 @@ static int cb_h3_recv_header(nghttp3_conn *conn, int64_t sid,
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;
}
@ -892,19 +894,19 @@ static int cb_h3_recv_header(nghttp3_conn *conn, int64_t sid,
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;
}
@ -1857,6 +1859,7 @@ static void cf_ngtcp2_ctx_clear(struct cf_ngtcp2_ctx *ctx)
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));
@ -1994,6 +1997,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf,
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)

Просмотреть файл

@ -162,6 +162,7 @@ const struct Curl_handler Curl_handler_scp = {
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 */
@ -189,6 +190,7 @@ const struct Curl_handler Curl_handler_sftp = {
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 */

Просмотреть файл

@ -139,6 +139,7 @@ const struct Curl_handler Curl_handler_scp = {
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 */
@ -168,6 +169,7 @@ const struct Curl_handler Curl_handler_sftp = {
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 */

Просмотреть файл

@ -94,6 +94,7 @@ const struct Curl_handler Curl_handler_scp = {
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 */
@ -123,6 +124,7 @@ const struct Curl_handler Curl_handler_sftp = {
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 */

Просмотреть файл

@ -1199,6 +1199,7 @@ const struct Curl_handler Curl_handler_ws = {
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 */
@ -1224,6 +1225,7 @@ const struct Curl_handler Curl_handler_wss = {
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 */