http: fix "Expect: 100-continue" client side

Instead of sending data always at the beginning of the request wait until the
server will respond with "HTTP/1.1 100 Continue".
Before this patch server do send "HTTP/1.1 100 Continue" but client always send
post data even without waiting server response.

P.S. this patch also touches some not 100% related tab-align issues.

Covered-by: http/data_length_constraints
Covered-by: http/read_on_write_error
This commit is contained in:
Azat Khuzhin 2016-03-11 13:08:28 +03:00
parent 5c2b4c19f1
commit 0b46b39e95
2 changed files with 48 additions and 22 deletions

View File

@ -190,6 +190,7 @@ enum message_read_status evhttp_parse_firstline_(struct evhttp_request *, struct
enum message_read_status evhttp_parse_headers_(struct evhttp_request *, struct evbuffer*);
void evhttp_start_read_(struct evhttp_connection *);
void evhttp_start_write_(struct evhttp_connection *);
/* response sending HTML the data in the buffer */
void evhttp_response_code_(struct evhttp_request *, int, const char *);

69
http.c
View File

@ -580,6 +580,23 @@ evhttp_make_header_response(struct evhttp_connection *evcon,
}
}
enum expect { NO, CONTINUE, OTHER };
static enum expect evhttp_have_expect(struct evhttp_request *req, int input)
{
const char *expect;
struct evkeyvalq *h = input ? req->input_headers : req->output_headers;
if (!req->kind == EVHTTP_REQUEST || !REQ_VERSION_ATLEAST(req, 1, 1))
return NO;
expect = evhttp_find_header(h, "Expect");
if (!expect)
return NO;
return !evutil_ascii_strcasecmp(expect, "100-continue") ? CONTINUE : OTHER;
}
/** Generate all headers appropriate for sending the http request in req (or
* the response, if we're sending a response), and write them to evcon's
* bufferevent. Also writes all data from req->output_buffer */
@ -605,14 +622,12 @@ evhttp_make_header(struct evhttp_connection *evcon, struct evhttp_request *req)
}
evbuffer_add(output, "\r\n", 2);
if (evbuffer_get_length(req->output_buffer) > 0) {
if (evhttp_have_expect(req, 0) != CONTINUE &&
evbuffer_get_length(req->output_buffer)) {
/*
* For a request, we add the POST data, for a reply, this
* is the regular data.
*/
/* XXX We might want to support waiting (a limited amount of
time) for a continue status line from the server before
sending POST/PUT message bodies. */
evbuffer_add_buffer(output, req->output_buffer);
}
}
@ -1173,12 +1188,15 @@ evhttp_write_connectioncb(struct evhttp_connection *evcon, void *arg)
{
/* This is after writing the request to the server */
struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
struct evbuffer *output = bufferevent_get_output(evcon->bufev);
EVUTIL_ASSERT(req != NULL);
EVUTIL_ASSERT(evcon->state == EVCON_WRITING);
/* We need to wait until we've written all of our output data before we can continue */
if (evbuffer_get_length(bufferevent_get_output(evcon->bufev)) > 0) { return; }
/* We need to wait until we've written all of our output data before we can
* continue */
if (evbuffer_get_length(output) > 0)
return;
/* We are done writing our header and are now expecting the response */
req->kind = EVHTTP_RESPONSE;
@ -2165,8 +2183,7 @@ evhttp_get_body(struct evhttp_connection *evcon, struct evhttp_request *req)
req->ntoread = -1;
} else {
if (evhttp_get_body_length(req) == -1) {
evhttp_connection_fail_(evcon,
EVREQ_HTTP_INVALID_HEADER);
evhttp_connection_fail_(evcon, EVREQ_HTTP_INVALID_HEADER);
return;
}
if (req->kind == EVHTTP_REQUEST && req->ntoread < 1) {
@ -2178,12 +2195,8 @@ evhttp_get_body(struct evhttp_connection *evcon, struct evhttp_request *req)
}
/* Should we send a 100 Continue status line? */
if (req->kind == EVHTTP_REQUEST && REQ_VERSION_ATLEAST(req, 1, 1)) {
const char *expect;
expect = evhttp_find_header(req->input_headers, "Expect");
if (expect) {
if (!evutil_ascii_strcasecmp(expect, "100-continue")) {
switch (evhttp_have_expect(req, 1)) {
case CONTINUE:
/* XXX It would be nice to do some sanity
checking here. Does the resource exist?
Should the resource accept post requests? If
@ -2192,19 +2205,19 @@ evhttp_get_body(struct evhttp_connection *evcon, struct evhttp_request *req)
send their message body. */
if (req->ntoread > 0) {
/* ntoread is ev_int64_t, max_body_size is ev_uint64_t */
if ((req->evcon->max_body_size <= EV_INT64_MAX) && (ev_uint64_t)req->ntoread > req->evcon->max_body_size) {
if ((req->evcon->max_body_size <= EV_INT64_MAX) &&
(ev_uint64_t)req->ntoread > req->evcon->max_body_size) {
evhttp_lingering_fail(evcon, req);
return;
}
}
if (!evbuffer_get_length(bufferevent_get_input(evcon->bufev)))
evhttp_send_continue(evcon, req);
} else {
evhttp_send_error(req, HTTP_EXPECTATIONFAILED,
NULL);
return;
}
}
break;
case OTHER:
evhttp_send_error(req, HTTP_EXPECTATIONFAILED, NULL);
return;
case NO: break;
}
evhttp_read_body(evcon, req);
@ -2272,7 +2285,9 @@ evhttp_read_header(struct evhttp_connection *evcon,
case EVHTTP_RESPONSE:
/* Start over if we got a 100 Continue response. */
if (req->response_code == 100) {
evhttp_start_read_(evcon);
struct evbuffer *output = bufferevent_get_output(evcon->bufev);
evbuffer_add_buffer(output, req->output_buffer);
evhttp_start_write_(evcon);
return;
}
if (!evhttp_response_needs_body(req)) {
@ -2685,6 +2700,16 @@ evhttp_start_read_(struct evhttp_connection *evcon)
}
}
void
evhttp_start_write_(struct evhttp_connection *evcon)
{
bufferevent_disable(evcon->bufev, EV_WRITE);
bufferevent_enable(evcon->bufev, EV_READ);
evcon->state = EVCON_WRITING;
evhttp_write_buffer(evcon, evhttp_write_connectioncb, NULL);
}
static void
evhttp_send_done(struct evhttp_connection *evcon, void *arg)
{