diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/http.c | 46 | ||||
-rw-r--r-- | lib/http.h | 3 | ||||
-rw-r--r-- | lib/setopt.c | 10 | ||||
-rw-r--r-- | lib/transfer.c | 168 | ||||
-rw-r--r-- | lib/urldata.h | 18 |
5 files changed, 224 insertions, 21 deletions
diff --git a/lib/http.c b/lib/http.c index 0a3e46243..07665743c 100644 --- a/lib/http.c +++ b/lib/http.c @@ -1681,6 +1681,52 @@ enum proxy_use { HEADER_CONNECT /* sending CONNECT to a proxy */ }; +/* used to compile the provided trailers into one buffer + will return an error code if one of the headers is + not formatted correctly */ +CURLcode Curl_http_compile_trailers(struct curl_slist *trailers, + Curl_send_buffer *buffer, + struct Curl_easy *handle) +{ + char *ptr = NULL; + CURLcode result = CURLE_OK; + const char *endofline_native = NULL; + const char *endofline_network = NULL; + + /* TODO: Maybe split Curl_add_custom_headers to make it reusable here */ + + if( +#ifdef CURL_DO_LINEEND_CONV + (handle->set.prefer_ascii) || +#endif + (handle->set.crlf)) { + /* \n will become \r\n later on */ + endofline_native = "\n"; + endofline_network = "\x0a"; + } + else { + endofline_native = "\r\n"; + endofline_network = "\x0d\x0a"; + } + + while(trailers) { + /* only add correctly formatted trailers */ + ptr = strchr(trailers->data, ':'); + if(ptr && *(ptr + 1) == ' ') { + result = Curl_add_bufferf(&buffer, "%s%s", trailers->data, + endofline_native); + if(result) + return result; + } + else + infof(handle, "Malformatted trailing header ! Skipping trailer."); + trailers = trailers->next; + } + result = Curl_add_buffer(&buffer, endofline_network, + strlen(endofline_network)); + return result; +} + CURLcode Curl_add_custom_headers(struct connectdata *conn, bool is_connect, Curl_send_buffer *req_buffer) diff --git a/lib/http.h b/lib/http.h index 21fa701ab..7fa0471ad 100644 --- a/lib/http.h +++ b/lib/http.h @@ -74,6 +74,9 @@ CURLcode Curl_add_timecondition(struct Curl_easy *data, CURLcode Curl_add_custom_headers(struct connectdata *conn, bool is_connect, Curl_send_buffer *req_buffer); +CURLcode Curl_http_compile_trailers(struct curl_slist *trailers, + Curl_send_buffer *buffer, + struct Curl_easy *handle); /* protocol-specific functions set up to be called by the main engine */ CURLcode Curl_http(struct connectdata *conn, bool *done); diff --git a/lib/setopt.c b/lib/setopt.c index 01a890b39..27046768c 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -2636,6 +2636,16 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, return CURLE_BAD_FUNCTION_ARGUMENT; data->set.upkeep_interval_ms = arg; break; + case CURLOPT_TRAILERFUNCTION: +#ifndef CURL_DISABLE_HTTP + data->set.trailer_callback = va_arg(param, curl_trailer_callback); +#endif + break; + case CURLOPT_TRAILERDATA: +#ifndef CURL_DISABLE_HTTP + data->set.trailer_data = va_arg(param, void *); +#endif + break; default: /* unknown tag and its companion, just ignore: */ result = CURLE_UNKNOWN_OPTION; diff --git a/lib/transfer.c b/lib/transfer.c index 6390821bb..7483cf1f8 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -117,6 +117,35 @@ CURLcode Curl_get_upload_buffer(struct Curl_easy *data) return CURLE_OK; } +#ifndef CURL_DISABLE_HTTP +/* + * This function will be called to loop through the trailers buffer + * until no more data is available for sending. + */ +static size_t Curl_trailers_read(char *buffer, size_t size, size_t nitems, + void *raw) +{ + struct Curl_easy *data = (struct Curl_easy *)raw; + Curl_send_buffer *trailers_buf = data->state.trailers_buf; + size_t bytes_left = trailers_buf->size_used-data->state.trailers_bytes_sent; + size_t to_copy = (size*nitems < bytes_left) ? size*nitems : bytes_left; + if(to_copy) { + memcpy(buffer, + &trailers_buf->buffer[data->state.trailers_bytes_sent], + to_copy); + data->state.trailers_bytes_sent += to_copy; + } + return to_copy; +} + +static size_t Curl_trailers_left(void *raw) +{ + struct Curl_easy *data = (struct Curl_easy *)raw; + Curl_send_buffer *trailers_buf = data->state.trailers_buf; + return trailers_buf->size_used - data->state.trailers_bytes_sent; +} +#endif + /* * This function will call the read callback to fill our buffer with data * to upload. @@ -127,6 +156,17 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, size_t bytes, struct Curl_easy *data = conn->data; size_t buffersize = bytes; size_t nread; + +#ifndef CURL_DISABLE_HTTP + struct curl_slist *trailers = NULL; + CURLcode c; + int trailers_ret_code; +#endif + + curl_read_callback readfunc = NULL; + void *extra_data = NULL; + bool added_crlf = FALSE; + #ifdef CURL_DOES_CONVERSIONS bool sending_http_headers = FALSE; @@ -140,15 +180,71 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, size_t bytes, } #endif - if(data->req.upload_chunky) { +#ifndef CURL_DISABLE_HTTP + if(data->state.trailers_state == TRAILERS_INITIALIZED) { + /* at this point we already verified that the callback exists + so we compile and store the trailers buffer, then proceed */ + infof(data, + "Moving trailers state machine from initialized to sending.\n"); + data->state.trailers_state = TRAILERS_SENDING; + data->state.trailers_buf = Curl_add_buffer_init(); + if(!data->state.trailers_buf) { + failf(data, "Unable to allocate trailing headers buffer !"); + return CURLE_OUT_OF_MEMORY; + } + data->state.trailers_bytes_sent = 0; + Curl_set_in_callback(data, true); + trailers_ret_code = data->set.trailer_callback(&trailers, + data->set.trailer_data); + Curl_set_in_callback(data, false); + if(trailers_ret_code == CURL_TRAILERFUNC_OK) { + c = Curl_http_compile_trailers(trailers, data->state.trailers_buf, data); + } + else { + failf(data, "operation aborted by trailing headers callback"); + *nreadp = 0; + c = CURLE_ABORTED_BY_CALLBACK; + } + if(c != CURLE_OK) { + Curl_add_buffer_free(&data->state.trailers_buf); + curl_slist_free_all(trailers); + return c; + } + infof(data, "Successfully compiled trailers.\r\n"); + curl_slist_free_all(trailers); + } +#endif + + /* if we are transmitting trailing data, we don't need to write + a chunk size so we skip this */ + if(data->req.upload_chunky && + data->state.trailers_state == TRAILERS_NONE) { /* if chunked Transfer-Encoding */ buffersize -= (8 + 2 + 2); /* 32bit hex + CRLF + CRLF */ data->req.upload_fromhere += (8 + 2); /* 32bit hex + CRLF */ } +#ifndef CURL_DISABLE_HTTP + if(data->state.trailers_state == TRAILERS_SENDING) { + /* if we're here then that means that we already sent the last empty chunk + but we didn't send a final CR LF, so we sent 0 CR LF. We then start + pulling trailing data until we ²have no more at which point we + simply return to the previous point in the state machine as if + nothing happened. + */ + readfunc = Curl_trailers_read; + extra_data = (void *)data; + } + else +#endif + { + readfunc = data->state.fread_func; + extra_data = data->state.in; + } + Curl_set_in_callback(data, true); - nread = data->state.fread_func(data->req.upload_fromhere, 1, - buffersize, data->state.in); + nread = readfunc(data->req.upload_fromhere, 1, + buffersize, extra_data); Curl_set_in_callback(data, false); if(nread == CURL_READFUNC_ABORT) { @@ -203,7 +299,7 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, size_t bytes, char hexbuffer[11]; const char *endofline_native; const char *endofline_network; - int hexlen; + int hexlen = 0; if( #ifdef CURL_DO_LINEEND_CONV @@ -218,20 +314,36 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, size_t bytes, endofline_native = "\r\n"; endofline_network = "\x0d\x0a"; } - hexlen = msnprintf(hexbuffer, sizeof(hexbuffer), - "%x%s", nread, endofline_native); - /* move buffer pointer */ - data->req.upload_fromhere -= hexlen; - nread += hexlen; + /* if we're not handling trailing data, proceed as usual */ + if(data->state.trailers_state != TRAILERS_SENDING) { + hexlen = msnprintf(hexbuffer, sizeof(hexbuffer), + "%x%s", nread, endofline_native); - /* copy the prefix to the buffer, leaving out the NUL */ - memcpy(data->req.upload_fromhere, hexbuffer, hexlen); + /* move buffer pointer */ + data->req.upload_fromhere -= hexlen; + nread += hexlen; - /* always append ASCII CRLF to the data */ - memcpy(data->req.upload_fromhere + nread, - endofline_network, - strlen(endofline_network)); + /* copy the prefix to the buffer, leaving out the NUL */ + memcpy(data->req.upload_fromhere, hexbuffer, hexlen); + + /* always append ASCII CRLF to the data unless + we have a valid trailer callback */ +#ifndef CURL_DISABLE_HTTP + if((nread-hexlen) == 0 && + data->set.trailer_callback != NULL && + data->state.trailers_state == TRAILERS_NONE) { + data->state.trailers_state = TRAILERS_INITIALIZED; + } + else +#endif + { + memcpy(data->req.upload_fromhere + nread, + endofline_network, + strlen(endofline_network)); + added_crlf = TRUE; + } + } #ifdef CURL_DOES_CONVERSIONS { @@ -251,13 +363,29 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, size_t bytes, } #endif /* CURL_DOES_CONVERSIONS */ - if((nread - hexlen) == 0) { - /* mark this as done once this chunk is transferred */ +#ifndef CURL_DISABLE_HTTP + if(data->state.trailers_state == TRAILERS_SENDING && + !Curl_trailers_left(data)) { + Curl_add_buffer_free(&data->state.trailers_buf); + data->state.trailers_state = TRAILERS_DONE; + data->set.trailer_data = NULL; + data->set.trailer_callback = NULL; + /* mark the transfer as done */ data->req.upload_done = TRUE; - infof(data, "Signaling end of chunked upload via terminating chunk.\n"); + infof(data, "Signaling end of chunked upload after trailers.\n"); } + else +#endif + if((nread - hexlen) == 0 && + data->state.trailers_state != TRAILERS_INITIALIZED) { + /* mark this as done once this chunk is transferred */ + data->req.upload_done = TRUE; + infof(data, + "Signaling end of chunked upload via terminating chunk.\n"); + } - nread += strlen(endofline_native); /* for the added end of line */ + if(added_crlf) + nread += strlen(endofline_network); /* for the added end of line */ } #ifdef CURL_DOES_CONVERSIONS else if((data->set.prefer_ascii) && (!sending_http_headers)) { @@ -925,7 +1053,6 @@ static CURLcode readwrite_upload(struct Curl_easy *data, *didwhat |= KEEP_SEND; do { - /* only read more data if there's no upload data already present in the upload buffer */ if(0 == k->upload_present) { @@ -950,7 +1077,6 @@ static CURLcode readwrite_upload(struct Curl_easy *data, k->keepon &= ~KEEP_SEND; /* disable writing */ k->start100 = Curl_now(); /* timeout count starts now */ *didwhat &= ~KEEP_SEND; /* we didn't write anything actually */ - /* set a timeout for the multi interface */ Curl_expire(data, data->set.expect_100_timeout, EXPIRE_100_TIMEOUT); break; diff --git a/lib/urldata.h b/lib/urldata.h index 448437d2a..8fb2d2894 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -1216,6 +1216,15 @@ typedef enum { EXPIRE_LAST /* not an actual timer, used as a marker only */ } expire_id; + +typedef enum { + TRAILERS_NONE, + TRAILERS_INITIALIZED, + TRAILERS_SENDING, + TRAILERS_DONE +} trailers_state; + + /* * One instance for each timeout an easy handle can set. */ @@ -1362,6 +1371,13 @@ struct UrlState { #endif CURLU *uh; /* URL handle for the current parsed URL */ struct urlpieces up; +#ifndef CURL_DISABLE_HTTP + size_t trailers_bytes_sent; + Curl_send_buffer *trailers_buf; /* a buffer containing the compiled trailing + headers */ +#endif + trailers_state trailers_state; /* whether we are sending trailers + and what stage are we at */ }; @@ -1730,6 +1746,8 @@ struct UserDefined { multidone_func fmultidone; struct Curl_easy *dohfor; /* this is a DoH request for that transfer */ CURLU *uh; /* URL handle for the current parsed URL */ + void *trailer_data; /* pointer to pass to trailer data callback */ + curl_trailer_callback trailer_callback; /* trailing data callback */ }; struct Names { |