]> granicus.if.org Git - libevent/commitdiff
http: fix "Expect: 100-continue" client side
authorAzat Khuzhin <a3at.mail@gmail.com>
Fri, 11 Mar 2016 10:08:28 +0000 (13:08 +0300)
committerAzat Khuzhin <a3at.mail@gmail.com>
Fri, 11 Mar 2016 17:58:46 +0000 (20:58 +0300)
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
http-internal.h
http.c

index 31002e0d2afca8ced15e581f3d0196ab94eacd72..9647347c3eb085e481dce382b8e0560e9d7ebace 100644 (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 *);
diff --git a/http.c b/http.c
index f7241020d58ec1d6b0b20e19993c8215df47a75f..21d080b82569d8a04885af2339bd175e03ab18c7 100644 (file)
--- a/http.c
+++ b/http.c
@@ -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)
 {