The HTTP/1.1 spec allows for clients to send an 'Expect: 100-continue'
header for large request payloads (8.2.3 Use of the 100 (Continue) Status -
Hypertext Transfer Protocol -- HTTP/1.1 - RFC2616).

This will pause the request after the headers have been sent and allow the
server to reject it based on those headers.

If those headers did not indicate a bad request (e.g. a too large
Content-Length), then it responds with a 100 Continue status line, allowing
the client to proceed.
At this point the client sends the request body.

The attached patch enables a basic support for the above in Pound.

This would probably require further modification to support multiple
scenario and error cases. However, after review and any updates if
required, it would be great if this gets integrated in future pound
releases.

Sincerely,
Piyush
diff -rupN Pound-2.6/config.c Pound-2.6-expect-100/config.c
--- Pound-2.6/config.c	2011-12-28 19:27:45.000000000 +0530
+++ Pound-2.6-expect-100/config.c	2013-11-29 16:17:50.307823907 +0530
@@ -73,7 +73,7 @@ static CODE facilitynames[] = {
 
 static regex_t  Empty, Comment, User, Group, RootJail, Daemon, LogFacility, LogLevel, Alive, SSLEngine, Control;
 static regex_t  ListenHTTP, ListenHTTPS, End, Address, Port, Cert, xHTTP, Client, CheckURL;
-static regex_t  Err414, Err500, Err501, Err503, MaxRequest, HeadRemove, RewriteLocation, RewriteDestination;
+static regex_t  Err414, Err417, Err500, Err501, Err503, MaxRequest, HeadRemove, RewriteLocation, RewriteDestination;
 static regex_t  Service, ServiceName, URL, HeadRequire, HeadDeny, BackEnd, Emergency, Priority, HAport, HAportAddr;
 static regex_t  Redirect, RedirectN, TimeOut, Session, Type, TTL, ID, DynScale;
 static regex_t  ClientCert, AddHeader, Ciphers, CAlist, VerifyList, CRLlist, NoHTTPS11;
@@ -657,6 +657,7 @@ parse_HTTP(void)
     res->to = clnt_to;
     res->rewr_loc = 1;
     res->err414 = "Request URI is too long";
+    res->err417 = "Expectation Failed";
     res->err500 = "An internal server error occurred. Please try again later.";
     res->err501 = "This method may not be used.";
     res->err503 = "The service is not available. Please try again later.";
@@ -709,6 +710,9 @@ parse_HTTP(void)
         } else if(!regexec(&Err414, lin, 4, matches, 0)) {
             lin[matches[1].rm_eo] = '\0';
             res->err414 = file2str(lin + matches[1].rm_so);
+        } else if(!regexec(&Err417, lin, 4, matches, 0)) {
+            lin[matches[1].rm_eo] = '\0';
+            res->err417 = file2str(lin + matches[1].rm_so);
         } else if(!regexec(&Err500, lin, 4, matches, 0)) {
             lin[matches[1].rm_eo] = '\0';
             res->err500 = file2str(lin + matches[1].rm_so);
@@ -841,6 +845,7 @@ parse_HTTPS(void)
     res->to = clnt_to;
     res->rewr_loc = 1;
     res->err414 = "Request URI is too long";
+    res->err417 = "Expectation Failed";
     res->err500 = "An internal server error occurred. Please try again later.";
     res->err501 = "This method may not be used.";
     res->err503 = "The service is not available. Please try again later.";
@@ -888,6 +893,9 @@ parse_HTTPS(void)
         } else if(!regexec(&Err414, lin, 4, matches, 0)) {
             lin[matches[1].rm_eo] = '\0';
             res->err414 = file2str(lin + matches[1].rm_so);
+        } else if(!regexec(&Err417, lin, 4, matches, 0)) {
+            lin[matches[1].rm_eo] = '\0';
+            res->err417 = file2str(lin + matches[1].rm_so);
         } else if(!regexec(&Err500, lin, 4, matches, 0)) {
             lin[matches[1].rm_eo] = '\0';
             res->err500 = file2str(lin + matches[1].rm_so);
@@ -1278,6 +1286,7 @@ config_parse(const int argc, char **cons
     || regcomp(&Client, "^[ \t]*Client[ \t]+([1-9][0-9]*)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
     || regcomp(&CheckURL, "^[ \t]*CheckURL[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
     || regcomp(&Err414, "^[ \t]*Err414[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
+    || regcomp(&Err417, "^[ \t]*Err417[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
     || regcomp(&Err500, "^[ \t]*Err500[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
     || regcomp(&Err501, "^[ \t]*Err501[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
     || regcomp(&Err503, "^[ \t]*Err503[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
@@ -1436,6 +1445,7 @@ config_parse(const int argc, char **cons
     regfree(&Client);
     regfree(&CheckURL);
     regfree(&Err414);
+    regfree(&Err417);
     regfree(&Err500);
     regfree(&Err501);
     regfree(&Err503);
diff -rupN Pound-2.6/http.c Pound-2.6-expect-100/http.c
--- Pound-2.6/http.c	2011-12-28 19:27:45.000000000 +0530
+++ Pound-2.6-expect-100/http.c	2013-11-29 16:21:23.047812548 +0530
@@ -31,7 +31,8 @@
 static char *h500 = "500 Internal Server Error",
             *h501 = "501 Not Implemented",
             *h503 = "503 Service Unavailable",
-            *h414 = "414 Request URI too long";
+            *h414 = "414 Request URI too long",
+            *h417 = "417 Expectation Failed";
 
 static char *err_response = "HTTP/1.0 %s\r\nContent-Type: text/html\r\nContent-Length: %d\r\nExpires: now\r\nPragma: no-cache\r\nCache-control: no-cache,no-store\r\n\r\n%s";
 
@@ -667,6 +668,20 @@ do_http(thr_arg *arg)
                 if(!strcasecmp("close", buf))
                     conn_closed = 1;
                 break;
+            case HEADER_EXPECT:
+                // Error case where the header content for 'Expect' is not '100-continue'
+                if(strcasecmp("100-continue", buf)) {
+                    logmsg(LOG_WARNING, "(%lx) e417 headers: invalid expect header", pthread_self());
+                    err_reply(cl, h417, lstn->err417);
+                    free_headers(headers);
+                    clean_all();
+                    return;
+                }
+                headers_ok[n] = 0;
+                //make sure data gets pushed upstream
+                BIO_write(cl, "HTTP/1.1 100 Continue\r\n\r\n", 25);
+                BIO_flush(cl);
+                break;
             case HEADER_TRANSFER_ENCODING:
                 if(cont >= L0)
                     headers_ok[n] = 0;
diff -rupN Pound-2.6/pound.h Pound-2.6-expect-100/pound.h
--- Pound-2.6/pound.h	2011-12-28 19:27:45.000000000 +0530
+++ Pound-2.6-expect-100/pound.h	2013-11-29 16:21:51.131811048 +0530
@@ -395,6 +395,7 @@ typedef struct _listener {
     int                 has_pat;        /* was a URL pattern defined? */
     regex_t             url_pat;        /* pattern to match the request URL against */
     char                *err414,        /* error messages */
+                        *err417,
                         *err500,
                         *err501,
                         *err503;
@@ -432,6 +433,7 @@ typedef struct _thr_arg {
 #define HEADER_USER_AGENT           8
 #define HEADER_URI                  9
 #define HEADER_DESTINATION          10
+#define HEADER_EXPECT               11
 
 /* control request stuff */
 typedef enum    {
diff -rupN Pound-2.6/svc.c Pound-2.6-expect-100/svc.c
--- Pound-2.6/svc.c	2011-12-28 19:27:45.000000000 +0530
+++ Pound-2.6-expect-100/svc.c	2013-11-29 16:10:16.699848126 +0530
@@ -390,6 +390,7 @@ check_header(const char *header, char *c
         { "Referer",            7,  HEADER_REFERER },
         { "User-agent",         10, HEADER_USER_AGENT },
         { "Destination",        11, HEADER_DESTINATION },
+        { "Expect",             6,  HEADER_EXPECT },
         { "",                   0,  HEADER_OTHER },
     };
     int i;

Reply via email to