if(!data->state.hconnect || !data->set.suppress_connect_headers) {
writetype = CLIENTWRITE_HEADER;
- if(data->set.include_header)
- writetype |= CLIENTWRITE_BODY;
if(data->state.hconnect)
writetype |= CLIENTWRITE_CONNECT;
if(data->req.httpcode/100 == 1)
if(!data->state.hconnect || !data->set.suppress_connect_headers) {
writetype = CLIENTWRITE_HEADER|CLIENTWRITE_STATUS;
- if(data->set.include_header)
- writetype |= CLIENTWRITE_BODY;
result = Curl_client_write(data, writetype,
Curl_dyn_ptr(&data->state.headerb), len);
if(result)
if(!data->set.suppress_connect_headers) {
/* send the header to the callback */
int writetype = CLIENTWRITE_HEADER | CLIENTWRITE_CONNECT |
- (data->set.include_header ? CLIENTWRITE_BODY : 0) |
(ts->headerlines == 1 ? CLIENTWRITE_STATUS : 0);
result = Curl_client_write(data, writetype, linep, perline);
if(!(newstate & KEEP_RECV_PAUSE)) {
Curl_conn_ev_data_pause(data, FALSE);
-
- 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 = Curl_client_write(data, writebuf[i].type,
- Curl_dyn_ptr(&writebuf[i].b),
- Curl_dyn_len(&writebuf[i].b));
- Curl_dyn_free(&writebuf[i].b);
- }
-
- if(result)
- return result;
- }
+ result = Curl_client_unpause(data);
+ if(result)
+ return result;
}
#ifdef USE_HYPER
return TRUE;
}
+static CURLcode client_write_header(struct Curl_easy *data,
+ char *buf, size_t blen)
+{
+ /* Some replies from an FTP server are written to the client
+ * as CLIENTWRITE_HEADER, formatted as if they came from a
+ * HTTP conversation.
+ * In all protocols, CLIENTWRITE_HEADER data is only passed to
+ * the body write callback when data->set.include_header is set
+ * via CURLOPT_HEADER.
+ * For historic reasons, FTP never played this game and expects
+ * all its HEADERs to do that always. Set that flag during the
+ * call to Curl_client_write() so it does the right thing.
+ *
+ * Notice that we cannot enable this flag for FTP in general,
+ * as an FTP transfer might involve a HTTP proxy connection and
+ * headers from CONNECT should not automatically be part of the
+ * output. */
+ CURLcode result;
+ int save = data->set.include_header;
+ data->set.include_header = TRUE;
+ result = Curl_client_write(data, CLIENTWRITE_HEADER, buf, blen);
+ data->set.include_header = save? TRUE:FALSE;
+ return result;
+}
+
static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data,
int ftpcode)
{
tm->tm_hour,
tm->tm_min,
tm->tm_sec);
- result = Curl_client_write(data, CLIENTWRITE_BOTH, headerbuf,
- headerbuflen);
+ result = client_write_header(data, headerbuf, headerbuflen);
if(result)
return result;
} /* end of a ridiculous amount of conditionals */
char clbuf[128];
int clbuflen = msnprintf(clbuf, sizeof(clbuf),
"Content-Length: %" CURL_FORMAT_CURL_OFF_T "\r\n", filesize);
- result = Curl_client_write(data, CLIENTWRITE_BOTH, clbuf, clbuflen);
+ result = client_write_header(data, clbuf, clbuflen);
if(result)
return result;
}
#ifdef CURL_FTP_HTTPSTYLE_HEAD
if(ftpcode == 350) {
char buffer[24]= { "Accept-ranges: bytes\r\n" };
- result = Curl_client_write(data, CLIENTWRITE_BOTH, buffer,
- strlen(buffer));
+ result = client_write_header(data, buffer, strlen(buffer));
if(result)
return result;
}
/* now, only output this if the header AND body are requested:
*/
writetype = CLIENTWRITE_HEADER |
- (data->set.include_header ? CLIENTWRITE_BODY : 0) |
((k->httpcode/100 == 1) ? CLIENTWRITE_1XX : 0);
headerlen = Curl_dyn_len(&data->state.headerb);
/*
* End of header-checks. Write them to the client.
*/
- if(data->set.include_header)
- writetype |= CLIENTWRITE_BODY;
if(k->httpcode/100 == 1)
writetype |= CLIENTWRITE_1XX;
* for "headers". The response lines can be seen as a kind of
* headers.
*/
- result = Curl_client_write(data, CLIENTWRITE_HEADER,
+ result = Curl_client_write(data, CLIENTWRITE_INFO,
pp->linestart_resp, perline);
if(result)
return result;
static CURLcode pausewrite(struct Curl_easy *data,
int type, /* what type of data */
+ bool paused_body,
const char *ptr,
size_t len)
{
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;
/* 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++;
}
*/
static CURLcode chop_write(struct Curl_easy *data,
int type,
+ bool skip_body_write,
char *optr,
size_t olen)
{
/* 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;
#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
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");
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;
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));
+ 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;
}
/*
#include "curl_trc.h"
-
-#define CLIENTWRITE_BODY (1<<0)
-#define CLIENTWRITE_HEADER (1<<1)
-#define CLIENTWRITE_STATUS (1<<2) /* the first "header" is the status line */
-#define CLIENTWRITE_CONNECT (1<<3) /* a CONNECT response */
-#define CLIENTWRITE_1XX (1<<4) /* a 1xx response */
-#define CLIENTWRITE_TRAILER (1<<5) /* a trailer header */
-#define CLIENTWRITE_BOTH (CLIENTWRITE_BODY|CLIENTWRITE_HEADER)
+/**
+ * Type of data that is being written to the client (application)
+ * - data written can be eiter BODY or META data
+ * - META data is either INFO or HEADER
+ * - INFO is meta information, e.g. not BODY, that cannot be interpreted
+ * as headers of a response. Example FTP/IMAP pingpong answers.
+ * - HEADER can have additional bits set (more than one)
+ * - STATUS special "header", e.g. response status line in HTTP
+ * - CONNECT header was received during proxying the connection
+ * - 1XX header is part of an intermediate response, e.g. HTTP 1xx code
+ * - TRAILER header is trailing response data, e.g. HTTP trailers
+ * BODY, INFO and HEADER should not be mixed, as this would lead to
+ * confusion on how to interpret/format/convert the data.
+ */
+#define CLIENTWRITE_BODY (1<<0) /* non-meta information, BODY */
+#define CLIENTWRITE_INFO (1<<1) /* meta information, not a HEADER */
+#define CLIENTWRITE_HEADER (1<<2) /* meta information, HEADER */
+#define CLIENTWRITE_STATUS (1<<3) /* a special status HEADER */
+#define CLIENTWRITE_CONNECT (1<<4) /* a CONNECT related HEADER */
+#define CLIENTWRITE_1XX (1<<5) /* a 1xx response related HEADER */
+#define CLIENTWRITE_TRAILER (1<<6) /* a trailer HEADER */
CURLcode Curl_client_write(struct Curl_easy *data, int type, char *ptr,
size_t len) WARN_UNUSED_RESULT;
+CURLcode Curl_client_unpause(struct Curl_easy *data);
+
/* internal read-function, does plain socket, SSL and krb4 */
CURLcode Curl_read(struct Curl_easy *data, curl_socket_t sockfd,
char *buf, size_t buffersize,
struct dynbuf b;
int type; /* type of the 'tempwrite' buffer as a bitmask that is used with
Curl_client_write() */
+ BIT(paused_body); /* if PAUSE happend before/during BODY write */
};
/* Timers */