cgi header processing
authorAndy Green <andy.green@linaro.org>
Tue, 8 Mar 2016 23:41:59 +0000 (07:41 +0800)
committerAndy Green <andy@warmcat.com>
Sat, 19 Mar 2016 09:21:45 +0000 (17:21 +0800)
Signed-off-by: Andy Green <andy.green@linaro.org>
lib/libwebsockets.c
lib/libwebsockets.h
lib/private-libwebsockets.h
lib/service.c
test-server/lws-cgi-test.sh
test-server/test-server-http.c

index 178a24f..04c2ef5 100644 (file)
@@ -1585,13 +1585,13 @@ lws_cgi(struct lws *wsi, char * const *exec_array, int timeout_secs)
        lws_change_pollfd(cgi->stdwsi[LWS_STDERR], LWS_POLLOUT, LWS_POLLIN);
 
        lwsl_debug("%s: fds in %d, out %d, err %d\n", __func__,
-                       cgi->stdwsi[LWS_STDIN]->sock,
-                       cgi->stdwsi[LWS_STDOUT]->sock,
-                       cgi->stdwsi[LWS_STDERR]->sock);
+                  cgi->stdwsi[LWS_STDIN]->sock, cgi->stdwsi[LWS_STDOUT]->sock,
+                  cgi->stdwsi[LWS_STDERR]->sock);
 
        lws_set_timeout(wsi, PENDING_TIMEOUT_CGI, timeout_secs);
 
-
+       /* the cgi stdout is always sending us http1.x header data first */
+       wsi->hdr_state = LCHS_HEADER;
 
        /* add us to the pt list of active cgis */
        cgi->cgi_list = pt->cgi_list;
@@ -1674,6 +1674,103 @@ bail1:
 
        return -1;
 }
+/**
+ * lws_cgi_write_split_headers: write cgi output accounting for header part
+ *
+ * @wsi: connection to own the process
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_cgi_write_split_stdout_headers(struct lws *wsi)
+{
+       int n, m;
+       char buf[LWS_PRE + 1024], *start = &buf[LWS_PRE], *p = start,
+            *end = &buf[sizeof(buf) - 1 - LWS_PRE], c;
+
+       while (wsi->hdr_state != LHCS_PAYLOAD) {
+               /* we have to separate header / finalize and
+                * payload chunks, since they need to be
+                * handled separately
+                */
+               n = read(lws_get_socket_fd(wsi->cgi->stdwsi[LWS_STDOUT]), &c, 1);
+               if (n < 0) {
+                       if (errno != EAGAIN)
+                               return -1;
+                       else
+                               n = 0;
+               }
+               if (n) {
+                       lwsl_err("-- %c\n", c);
+                       switch (wsi->hdr_state) {
+                       case LCHS_HEADER:
+                               *p++ = c;
+                               if (c == '\x0d') {
+                                       wsi->hdr_state = LCHS_LF1;
+                                       break;
+                               }
+                               break;
+                       case LCHS_LF1:
+                               *p++ = c;
+                               if (c == '\x0a') {
+                                       wsi->hdr_state = LCHS_CR2;
+                                       break;
+                               }
+                               /* we got \r[^\n]... it's unreasonable */
+                               return -1;
+                       case LCHS_CR2:
+                               if (c == '\x0d') {
+                                       /* drop the \x0d */
+                                       wsi->hdr_state = LCHS_LF2;
+                                       break;
+                               }
+                               *p++ = c;
+                               break;
+                       case LCHS_LF2:
+                               if (c == '\x0a') {
+                                       wsi->hdr_state = LHCS_PAYLOAD;
+                                       /* drop the \0xa ... finalize will add it if needed */
+                                       lws_finalize_http_header(wsi,
+                                                       (unsigned char **)&p,
+                                                       (unsigned char *)end);
+                                       break;
+                               }
+                               /* we got \r\n\r[^\n]... it's unreasonable */
+                               return -1;
+                       case LHCS_PAYLOAD:
+                               break;
+                       }
+               }
+
+               /* ran out of input, ended the headers, or filled up the headers buf */
+               if (!n || wsi->hdr_state == LHCS_PAYLOAD || (p + 4) == end) {
+lwsl_err("a\n");
+                       m = lws_write(wsi, (unsigned char *)start,
+                                     p - start, LWS_WRITE_HTTP_HEADERS);
+                       if (m < 0)
+                               return -1;
+lwsl_err("b\n");
+                       /* writeability becomes uncertain now we wrote
+                        * something, we must return to the event loop
+                        */
+
+                       return 0;
+               }
+       }
+       lwsl_err("%s: stdout\n", __func__);
+       n = read(lws_get_socket_fd(wsi->cgi->stdwsi[LWS_STDOUT]),
+                start, sizeof(buf) - LWS_PRE);
+
+       if (n < 0 && errno != EAGAIN)
+               return -1;
+       if (n > 0) {
+               m = lws_write(wsi, (unsigned char *)start, n,
+                             LWS_WRITE_HTTP);
+               //lwsl_notice("write %d\n", m);
+               if (m < 0)
+                       return -1;
+       }
+
+       return 0;
+}
 
 /**
  * lws_cgi_kill: terminate cgi process associated with wsi
index 63442d2..f4b20c6 100644 (file)
@@ -1866,10 +1866,20 @@ enum lws_enum_stdinouterr {
        LWS_STDERR = 2,
 };
 
+enum lws_cgi_hdr_state {
+       LCHS_HEADER,
+       LCHS_CR1,
+       LCHS_LF1,
+       LCHS_CR2,
+       LCHS_LF2,
+       LHCS_PAYLOAD,
+};
+
 struct lws_cgi_args {
        struct lws **stdwsi; /* get fd with lws_get_socket_fd() */
        enum lws_enum_stdinouterr ch;
        unsigned char *data; /* for messages with payload */
+       enum lws_cgi_hdr_state hdr_state;
        int len;
 };
 
@@ -1877,6 +1887,9 @@ LWS_VISIBLE LWS_EXTERN int
 lws_cgi(struct lws *wsi, char * const *exec_array, int timeout_secs);
 
 LWS_VISIBLE LWS_EXTERN int
+lws_cgi_write_split_stdout_headers(struct lws *wsi);
+
+LWS_VISIBLE LWS_EXTERN int
 lws_cgi_kill(struct lws *wsi);
 #endif
 
index 70dfeaf..5f118ad 100644 (file)
@@ -1054,6 +1054,7 @@ struct lws_cgi {
 
        unsigned int being_closed:1;
 };
+
 #endif
 
 struct lws {
@@ -1158,6 +1159,7 @@ struct lws {
        char tsi; /* thread service index we belong to */
 #ifdef LWS_WITH_CGI
        char cgi_channel; /* which of stdin/out/err */
+       char hdr_state;
 #endif
 };
 
index 7565895..ee872fe 100644 (file)
@@ -874,6 +874,7 @@ handle_pending:
 
                        args.ch = wsi->cgi_channel;
                        args.stdwsi = &wsi->parent->cgi->stdwsi[0];
+                       args.hdr_state = wsi->hdr_state;
 
                        if (user_callback_handle_rxflow(
                                        wsi->parent->protocol->callback,
index 2f5df11..914673f 100755 (executable)
@@ -1,18 +1,31 @@
 #!/bin/sh
 
-echo "lwstest script stdout"
+echo -e -n "Content-type: text/html\x0d\x0a"
+echo -e -n "\x0d\x0a"
+
+echo "<html><body>"
+echo "<h1>lwstest script stdout</h1>"
 >&2 echo "lwstest script stderr"
 
-echo "REQUEST_METHOD=$REQUEST_METHOD"
+echo "<h2>REQUEST_METHOD=$REQUEST_METHOD</h2>"
 
 if [ "$REQUEST_METHOD" = "POST" ] ; then
        read line
        echo "read=\"$line\""
 else
-       cat /proc/meminfo
+       echo "<table>"
+       echo "<tr><td colspan=\"2\" style=\"font-size:120%;text-align:center\">/proc/meminfo</td></tr>"
+       cat /proc/meminfo | while read line ; do
+               A=`echo "$line" | cut -d: -f1`
+               B=`echo "$line" | tr -s ' ' | cut -d' ' -f2-`
+               echo -e "<tr><td style=\"background-color:#f0e8c0\">$A</td>"
+               echo -e "<td style=\"text-align:right\">$B</td></tr>"
+       done
+       echo "</table>"
 fi
 
-echo "done"
+echo "<br/>done"
+echo "</body></html>"
 
 exit 0
 
index 4de62e8..594c8df 100644 (file)
@@ -213,19 +213,17 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
 
                        if (lws_add_http_header_status(wsi, 200, &p, end))
                                return 1;
-                       if (lws_add_http_header_by_token(wsi,
-                                       WSI_TOKEN_HTTP_CONTENT_TYPE,
-                                       (unsigned char *)"text/plain",
-                                       10, &p, end))
-                               return 1;
                        if (lws_add_http_header_by_token(wsi, WSI_TOKEN_CONNECTION,
                                        (unsigned char *)"close", 5, &p, end))
                                return 1;
-                       if (lws_finalize_http_header(wsi, &p, end))
-                               return 1;
                        n = lws_write(wsi, buffer + LWS_PRE,
                                      p - (buffer + LWS_PRE),
                                      LWS_WRITE_HTTP_HEADERS);
+
+                       /* the cgi starts by outputting headers, we can't
+                        *  finalize the headers until we see the end of that
+                        */
+
                        break;
                }
 #endif
@@ -399,20 +397,10 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
                        goto try_to_reuse;
 #ifdef LWS_WITH_CGI
                if (pss->reason_bf) {
-                       lwsl_debug("%s: stdout\n", __func__);
-                       n = read(lws_get_socket_fd(pss->args.stdwsi[LWS_STDOUT]),
-                                       buf + LWS_PRE, sizeof(buf) - LWS_PRE);
-                       //lwsl_notice("read %d (errno %d)\n", n, errno);
-                       if (n < 0 && errno != EAGAIN)
-                               return -1;
-                       if (n > 0) {
-                               m = lws_write(wsi, (unsigned char *)buf + LWS_PRE, n,
-                                             LWS_WRITE_HTTP);
-                               //lwsl_notice("write %d\n", m);
-                               if (m < 0)
-                                       goto bail;
-                               pss->reason_bf = 0;
-                       }
+                       if (lws_cgi_write_split_stdout_headers(wsi) < 0)
+                               goto bail;
+
+                       pss->reason_bf = 0;
                        break;
                }
 #endif