diff options
Diffstat (limited to 'Utilities/cmcurl/lib/sendf.c')
-rw-r--r-- | Utilities/cmcurl/lib/sendf.c | 205 |
1 files changed, 192 insertions, 13 deletions
diff --git a/Utilities/cmcurl/lib/sendf.c b/Utilities/cmcurl/lib/sendf.c index d79ad08..0482c5d 100644 --- a/Utilities/cmcurl/lib/sendf.c +++ b/Utilities/cmcurl/lib/sendf.c @@ -40,6 +40,7 @@ #include "sendf.h" #include "cfilters.h" #include "connect.h" +#include "content_encoding.h" #include "vtls/vtls.h" #include "vssh/ssh.h" #include "easyif.h" @@ -213,6 +214,7 @@ CURLcode Curl_write(struct Curl_easy *data, static CURLcode pausewrite(struct Curl_easy *data, int type, /* what type of data */ + bool paused_body, const char *ptr, size_t len) { @@ -228,7 +230,8 @@ static CURLcode pausewrite(struct Curl_easy *data, if(s->tempcount) { for(i = 0; i< s->tempcount; i++) { - if(s->tempwrite[i].type == type) { + if(s->tempwrite[i].type == type && + !!s->tempwrite[i].paused_body == !!paused_body) { /* data for this type exists */ newtype = FALSE; break; @@ -246,6 +249,7 @@ static CURLcode pausewrite(struct Curl_easy *data, /* store this information in the state struct for later use */ Curl_dyn_init(&s->tempwrite[i].b, DYN_PAUSE_BUFFER); s->tempwrite[i].type = type; + s->tempwrite[i].paused_body = paused_body; s->tempcount++; } @@ -265,6 +269,7 @@ static CURLcode pausewrite(struct Curl_easy *data, */ static CURLcode chop_write(struct Curl_easy *data, int type, + bool skip_body_write, char *optr, size_t olen) { @@ -281,10 +286,12 @@ static CURLcode chop_write(struct Curl_easy *data, /* If reading is paused, append this data to the already held data for this type. */ if(data->req.keepon & KEEP_RECV_PAUSE) - return pausewrite(data, type, ptr, len); + return pausewrite(data, type, !skip_body_write, ptr, len); /* Determine the callback(s) to use. */ - if(type & CLIENTWRITE_BODY) { + if(!skip_body_write && + ((type & CLIENTWRITE_BODY) || + ((type & CLIENTWRITE_HEADER) && data->set.include_header))) { #ifdef USE_WEBSOCKETS if(conn->handler->protocol & (CURLPROTO_WS|CURLPROTO_WSS)) { writebody = Curl_ws_writecb; @@ -294,7 +301,7 @@ static CURLcode chop_write(struct Curl_easy *data, #endif writebody = data->set.fwrite_func; } - if((type & CLIENTWRITE_HEADER) && + if((type & (CLIENTWRITE_HEADER|CLIENTWRITE_INFO)) && (data->set.fwrite_header || data->set.writeheader)) { /* * Write headers to the same callback or to the especially setup @@ -322,7 +329,7 @@ static CURLcode chop_write(struct Curl_easy *data, failf(data, "Write callback asked for PAUSE when not supported"); return CURLE_WRITE_ERROR; } - return pausewrite(data, type, ptr, len); + return pausewrite(data, type, TRUE, ptr, len); } if(wrote != chunklen) { failf(data, "Failure writing output to destination"); @@ -357,13 +364,7 @@ static CURLcode chop_write(struct Curl_easy *data, Curl_set_in_callback(data, false); if(CURL_WRITEFUNC_PAUSE == wrote) - /* here we pass in the HEADER bit only since if this was body as well - then it was passed already and clearly that didn't trigger the - pause, so this is saved for later with the HEADER bit only */ - return pausewrite(data, CLIENTWRITE_HEADER | - (type & (CLIENTWRITE_STATUS|CLIENTWRITE_CONNECT| - CLIENTWRITE_1XX|CLIENTWRITE_TRAILER)), - optr, olen); + return pausewrite(data, type, FALSE, optr, olen); if(wrote != olen) { failf(data, "Failed writing header"); return CURLE_WRITE_ERROR; @@ -397,9 +398,187 @@ CURLcode Curl_client_write(struct Curl_easy *data, len = convert_lineends(data, ptr, len); } #endif - return chop_write(data, type, ptr, len); + /* it is one of those, at least */ + DEBUGASSERT(type & (CLIENTWRITE_BODY|CLIENTWRITE_HEADER|CLIENTWRITE_INFO)); + /* BODY is only BODY */ + DEBUGASSERT(!(type & CLIENTWRITE_BODY) || (type == CLIENTWRITE_BODY)); + /* INFO is only INFO */ + DEBUGASSERT(!(type & CLIENTWRITE_INFO) || (type == CLIENTWRITE_INFO)); + + if(type == CLIENTWRITE_BODY) { + if(data->req.ignorebody) + return CURLE_OK; + + if(data->req.writer_stack && !data->set.http_ce_skip) + return Curl_unencode_write(data, data->req.writer_stack, ptr, len); + } + return chop_write(data, type, FALSE, ptr, len); +} + +CURLcode Curl_client_unpause(struct Curl_easy *data) +{ + CURLcode result = CURLE_OK; + + if(data->state.tempcount) { + /* there are buffers for sending that can be delivered as the receive + pausing is lifted! */ + unsigned int i; + unsigned int count = data->state.tempcount; + struct tempbuf writebuf[3]; /* there can only be three */ + + /* copy the structs to allow for immediate re-pausing */ + for(i = 0; i < data->state.tempcount; i++) { + writebuf[i] = data->state.tempwrite[i]; + Curl_dyn_init(&data->state.tempwrite[i].b, DYN_PAUSE_BUFFER); + } + data->state.tempcount = 0; + + for(i = 0; i < count; i++) { + /* even if one function returns error, this loops through and frees + all buffers */ + if(!result) + result = chop_write(data, writebuf[i].type, + !writebuf[i].paused_body, + Curl_dyn_ptr(&writebuf[i].b), + Curl_dyn_len(&writebuf[i].b)); + Curl_dyn_free(&writebuf[i].b); + } + } + return result; } +void Curl_client_cleanup(struct Curl_easy *data) +{ + struct contenc_writer *writer = data->req.writer_stack; + size_t i; + + while(writer) { + data->req.writer_stack = writer->downstream; + writer->handler->close_writer(data, writer); + free(writer); + writer = data->req.writer_stack; + } + + for(i = 0; i < data->state.tempcount; i++) { + Curl_dyn_free(&data->state.tempwrite[i].b); + } + data->state.tempcount = 0; + +} + +/* Real client writer: no downstream. */ +static CURLcode client_cew_init(struct Curl_easy *data, + struct contenc_writer *writer) +{ + (void) data; + (void)writer; + return CURLE_OK; +} + +static CURLcode client_cew_write(struct Curl_easy *data, + struct contenc_writer *writer, + const char *buf, size_t nbytes) +{ + (void)writer; + if(!nbytes || data->req.ignorebody) + return CURLE_OK; + return chop_write(data, CLIENTWRITE_BODY, FALSE, (char *)buf, nbytes); +} + +static void client_cew_close(struct Curl_easy *data, + struct contenc_writer *writer) +{ + (void) data; + (void) writer; +} + +static const struct content_encoding client_cew = { + NULL, + NULL, + client_cew_init, + client_cew_write, + client_cew_close, + sizeof(struct contenc_writer) +}; + +/* Create an unencoding writer stage using the given handler. */ +CURLcode Curl_client_create_writer(struct contenc_writer **pwriter, + struct Curl_easy *data, + const struct content_encoding *ce_handler, + int order) +{ + struct contenc_writer *writer; + CURLcode result = CURLE_OUT_OF_MEMORY; + + DEBUGASSERT(ce_handler->writersize >= sizeof(struct contenc_writer)); + writer = (struct contenc_writer *) calloc(1, ce_handler->writersize); + if(!writer) + goto out; + + writer->handler = ce_handler; + writer->order = order; + result = ce_handler->init_writer(data, writer); + +out: + *pwriter = result? NULL : writer; + if(result) + free(writer); + return result; +} + +void Curl_client_free_writer(struct Curl_easy *data, + struct contenc_writer *writer) +{ + if(writer) { + writer->handler->close_writer(data, writer); + free(writer); + } +} + +/* allow no more than 5 "chained" compression steps */ +#define MAX_ENCODE_STACK 5 + + +static CURLcode init_writer_stack(struct Curl_easy *data) +{ + DEBUGASSERT(!data->req.writer_stack); + return Curl_client_create_writer(&data->req.writer_stack, + data, &client_cew, 0); +} + +CURLcode Curl_client_add_writer(struct Curl_easy *data, + struct contenc_writer *writer) +{ + CURLcode result; + + if(!data->req.writer_stack) { + result = init_writer_stack(data); + if(result) + return result; + } + + if(data->req.writer_stack_depth++ >= MAX_ENCODE_STACK) { + failf(data, "Reject response due to more than %u content encodings", + MAX_ENCODE_STACK); + return CURLE_BAD_CONTENT_ENCODING; + } + + /* Stack the unencoding stage. */ + if(writer->order >= data->req.writer_stack->order) { + writer->downstream = data->req.writer_stack; + data->req.writer_stack = writer; + } + else { + struct contenc_writer *w = data->req.writer_stack; + while(w->downstream && writer->order < w->downstream->order) + w = w->downstream; + writer->downstream = w->downstream; + w->downstream = writer; + } + return CURLE_OK; +} + + /* * Internal read-from-socket function. This is meant to deal with plain * sockets, SSL sockets and kerberos sockets. |