cgi env
authorAndy Green <andy@warmcat.com>
Sun, 20 Mar 2016 03:55:25 +0000 (11:55 +0800)
committerAndy Green <andy@warmcat.com>
Mon, 21 Mar 2016 07:17:33 +0000 (15:17 +0800)
Improve cgi support so it's capable of running cgit

13 files changed:
.travis.yml
CMakeLists.txt
changelog
lib/client-handshake.c
lib/client.c
lib/handshake.c
lib/libwebsockets.c
lib/libwebsockets.h
lib/private-libwebsockets.h
lib/service.c
lws_config_private.h.in
test-server/test-server-http.c
test-server/test-server.h

index e504f2e..b61a765 100644 (file)
@@ -13,6 +13,8 @@ env:
     - LWS_METHOD=http2 CMAKE_ARGS="-DLWS_WITH_HTTP2=ON"
     - LWS_METHOD=nossl CMAKE_ARGS="-DLWS_WITH_SSL=OFF"
     - LWS_METHOD=nodaemon CMAKE_ARGS="-DLWS_WITHOUT_DAEMONIZE=ON"
+    - LWS_METHOD=cgi CMAKE_ARGS="-DLWS_WITH_CGI=ON"
+
 os:
   - linux
   - osx
index 490abd6..7ea0944 100644 (file)
@@ -344,6 +344,7 @@ CHECK_FUNCTION_EXISTS(realloc LWS_HAVE_REALLOC)
 CHECK_FUNCTION_EXISTS(socket LWS_HAVE_SOCKET)
 CHECK_FUNCTION_EXISTS(strerror LWS_HAVE_STRERROR)
 CHECK_FUNCTION_EXISTS(vfork LWS_HAVE_VFORK)
+CHECK_FUNCTION_EXISTS(execvpe LWS_HAVE_EXECVPE)
 CHECK_FUNCTION_EXISTS(getifaddrs LWS_HAVE_GETIFADDRS)
 CHECK_FUNCTION_EXISTS(snprintf LWS_HAVE_SNPRINTF)
 CHECK_FUNCTION_EXISTS(_snprintf LWS_HAVE__SNPRINTF)
index bc580d6..101f57a 100644 (file)
--- a/changelog
+++ b/changelog
@@ -111,7 +111,8 @@ lws_adopt_socket_readbuf(struct lws_context *context, lws_sockfd_type accept_fd,
 a simple api.
 
 LWS_VISIBLE LWS_EXTERN int
-lws_cgi(struct lws *wsi, char * const *exec_array, int timeout_secs);
+lws_cgi(struct lws *wsi, char * const *exec_array,  int script_uri_path_len,
+        int timeout_secs);
 
 LWS_VISIBLE LWS_EXTERN int
 lws_cgi_kill(struct lws *wsi);
@@ -131,6 +132,9 @@ $ wget http://localhost:7681/cgitest --post-file=hello.txt -O- --quiet
 lwstest script
 read="hello"
 
+The test script returns text/html table showing /proc/meminfo.  But the cgi
+support is complete enough to run cgit cgi.
+
 4) There is a helper api for forming logging timestamps
 
 LWS_VISIBLE int
index b3419a1..f4018b1 100644 (file)
@@ -338,7 +338,7 @@ lws_client_reset(struct lws *wsi, int ssl, const char *address, int port, const
        return lws_client_connect_2(wsi);
 }
 
-
+#ifdef LWS_WITH_HTTP_PROXY
 static hubbub_error
 html_parser_cb(const hubbub_token *token, void *pw)
 {
@@ -446,7 +446,7 @@ html_parser_cb(const hubbub_token *token, void *pw)
 
        return HUBBUB_OK;
 }
-
+#endif
 /**
  * lws_client_connect_via_info() - Connect to another websocket server
  * @i:pointer to lws_client_connect_info struct
@@ -560,11 +560,12 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
                wsi->sibling_list = i->parent_wsi->child_list;
                i->parent_wsi->child_list = wsi;
        }
-
+#ifdef LWS_WITH_HTTP_PROXY
        if (i->uri_replace_to)
                wsi->rw = lws_rewrite_create(wsi, html_parser_cb,
                                             i->uri_replace_from,
                                             i->uri_replace_to);
+#endif
 
        return wsi;
 
index 77eb7f0..b8262f8 100644 (file)
@@ -501,7 +501,7 @@ strtolower(char *s)
  *       transaction if possible
  */
 
-LWS_VISIBLE int LWS_WARN_UNUSED_RESULT
+int LWS_WARN_UNUSED_RESULT
 lws_http_transaction_completed_client(struct lws *wsi)
 {
        lwsl_debug("%s: wsi %p\n", __func__, wsi);
@@ -622,7 +622,7 @@ lws_client_interpret_server_handshake(struct lws *wsi)
                        goto bail2;
                }
 
-#ifndef LWS_NO_CLIENT
+#ifdef LWS_WITH_HTTP_PROXY
        wsi->perform_rewrite = 0;
        if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE)) {
                if (!strncmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE),
index 6148ba5..d2dd4c7 100644 (file)
@@ -171,7 +171,7 @@ http_postbody:
                                        wsi, LWS_CALLBACK_CGI_STDIN_DATA,
                                        wsi->user_space,
                                        (void *)&args, 0);
-                               if (n < 0)
+                               if ((int)n < 0)
                                        goto bail;
                        } else {
 #endif
index 7942ac1..5e08b99 100644 (file)
@@ -391,12 +391,12 @@ just_kill_connection:
 
        lwsl_info("%s: real just_kill_connection: %p (sockfd %d)\n", __func__,
                  wsi, wsi->sock);
-
+#ifdef LWS_WITH_HTTP_PROXY
        if (wsi->rw) {
                lws_rewrite_destroy(wsi->rw);
                wsi->rw = NULL;
        }
-
+#endif
        /*
         * we won't be servicing or receiving anything further from this guy
         * delete socket from the internal poll list if still present
@@ -1484,6 +1484,35 @@ lws_socket_bind(struct lws_context *context, int sockfd, int port,
 }
 
 LWS_VISIBLE LWS_EXTERN int
+lws_urlencode(const char *in, int inlen, char *out, int outlen)
+{
+       const char *hex = "0123456789ABCDEF";
+       char *start = out, *end = out + outlen;
+
+       while (inlen-- && out > end - 4) {
+               if ((*in >= 'A' && *in <= 'Z') ||
+                   (*in >= 'a' && *in <= 'z') ||
+                   (*in >= '0' && *in <= '9') ||
+                   *in == '-' ||
+                   *in == '_' ||
+                   *in == '.' ||
+                   *in == '~')
+                       *out++ = *in++;
+               else {
+                       *out++ = '%';
+                       *out++ = hex[(*in) >> 4];
+                       *out++ = hex[(*in++) & 15];
+               }
+       }
+       *out = '\0';
+
+       if (out >= end - 4)
+               return -1;
+
+       return out - start;
+}
+
+LWS_VISIBLE LWS_EXTERN int
 lws_is_cgi(struct lws *wsi) {
 #ifdef LWS_WITH_CGI
        return !!wsi->cgi;
@@ -1546,12 +1575,14 @@ lws_create_basic_wsi(struct lws_context *context, int tsi)
  */
 
 LWS_VISIBLE LWS_EXTERN int
-lws_cgi(struct lws *wsi, char * const *exec_array, int timeout_secs)
+lws_cgi(struct lws *wsi, char * const *exec_array, int script_uri_path_len,
+       int timeout_secs)
 {
        struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
-       char *env_array[30], cgi_path[400];
+       char *env_array[30], cgi_path[400], e[1024], *p = e,
+            *end = p + sizeof(e) - 1, tok[256];
        struct lws_cgi *cgi;
-       int n;
+       int n, m, i;
 
        /*
         * give the master wsi a cgi struct
@@ -1612,8 +1643,11 @@ lws_cgi(struct lws *wsi, char * const *exec_array, int timeout_secs)
        /* prepare his CGI env */
 
        n = 0;
+
+       if (lws_is_ssl(wsi))
+               env_array[n++] = "HTTPS=ON";
        if (wsi->u.hdr.ah) {
-               snprintf(cgi_path, sizeof(cgi_path) - 1, "PATH_INFO=%s",
+               snprintf(cgi_path, sizeof(cgi_path) - 1, "REQUEST_URI=%s",
                         lws_hdr_simple_ptr(wsi, WSI_TOKEN_GET_URI));
                cgi_path[sizeof(cgi_path) - 1] = '\0';
                env_array[n++] = cgi_path;
@@ -1621,18 +1655,66 @@ lws_cgi(struct lws *wsi, char * const *exec_array, int timeout_secs)
                        env_array[n++] = "REQUEST_METHOD=POST";
                else
                        env_array[n++] = "REQUEST_METHOD=GET";
+
+               env_array[n++] = p;
+               p += snprintf(p, end - p, "QUERY_STRING=");
+               /* dump the individual URI Arg parameters */
+               m = 0;
+               while (1) {
+                       i = lws_hdr_copy_fragment(wsi, tok, sizeof(tok),
+                                            WSI_TOKEN_HTTP_URI_ARGS, m);
+                       if (i < 0)
+                               break;
+                       i = lws_urlencode(tok, i, p, end - p);
+                       p += i;
+                       *p++ = '&';
+                       m++;
+               }
+               if (m)
+                       p--;
+               *p++ = '\0';
+
+               env_array[n++] = p;
+               p += snprintf(p, end - p, "PATH_INFO=%s",
+                             lws_hdr_simple_ptr(wsi, WSI_TOKEN_GET_URI) +
+                             script_uri_path_len);
+               p++;
        }
-       if (lws_is_ssl(wsi))
-               env_array[n++] = "HTTPS=ON";
+       if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_REFERER)) {
+               env_array[n++] = p;
+               p += snprintf(p, end - p, "HTTP_REFERER=%s",
+                             lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_REFERER));
+               p++;
+       }
+       if (lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) {
+               env_array[n++] = p;
+               p += snprintf(p, end - p, "HTTP_HOST=%s",
+                             lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST));
+               p++;
+       }
+       if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_USER_AGENT)) {
+               env_array[n++] = p;
+               p += snprintf(p, end - p, "USER_AGENT=%s",
+                             lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_USER_AGENT));
+               p++;
+       }
+       env_array[n++] = p;
+       p += snprintf(p, end - p, "SCRIPT_PATH=%s", exec_array[2]) + 1;
+
        env_array[n++] = "SERVER_SOFTWARE=libwebsockets";
-       env_array[n++] = "PATH=/bin:/usr/bin:/usrlocal/bin";
+       env_array[n++] = "PATH=/bin:/usr/bin:/usr/local/bin:/var/www/cgi-bin";
        env_array[n] = NULL;
 
+#if 0
+       for (m = 0; m < n; m++)
+               lwsl_err("    %s\n", env_array[m]);
+#endif
+
        /* we are ready with the redirection pipes... run the thing */
-#ifdef LWS_HAVE_VFORK
-       cgi->pid = vfork();
-#else
+#if !defined(LWS_HAVE_VFORK) || !defined(LWS_HAVE_EXECVPE)
        cgi->pid = fork();
+#else
+       cgi->pid = vfork();
 #endif
        if (cgi->pid < 0) {
                lwsl_err("fork failed, errno %d", errno);
@@ -1658,7 +1740,17 @@ lws_cgi(struct lws *wsi, char * const *exec_array, int timeout_secs)
                close(cgi->pipe_fds[n][!(n == 0)]);
        }
 
+#if !defined(LWS_HAVE_VFORK) || !defined(LWS_HAVE_EXECVPE)
+       for (m = 0; m < n; m++) {
+               p = strchr(env_array[m], '=');
+               *p++ = '\0';
+               setenv(env_array[m], p, 1);
+       }
+       execvp(exec_array[0], &exec_array[0]);
+#else
        execvpe(exec_array[0], &exec_array[0], &env_array[0]);
+#endif
+
        exit(1);
 
 bail3:
@@ -1711,7 +1803,7 @@ lws_cgi_write_split_stdout_headers(struct lws *wsi)
                                n = 0;
                }
                if (n) {
-                       lwsl_err("-- %c\n", c);
+                       //lwsl_err("-- %c\n", c);
                        switch (wsi->hdr_state) {
                        case LCHS_HEADER:
                                *p++ = c;
@@ -1799,7 +1891,7 @@ lws_cgi_kill(struct lws *wsi)
        if (!wsi->cgi)
                return 0;
 
-       lwsl_notice("%s: wsi %p\n", __func__, wsi);
+//     lwsl_notice("%s: wsi %p\n", __func__, wsi);
 
        assert(wsi->cgi);
 
index 72b8865..91fadbc 100644 (file)
@@ -1890,7 +1890,8 @@ struct lws_cgi_args {
 };
 
 LWS_VISIBLE LWS_EXTERN int
-lws_cgi(struct lws *wsi, char * const *exec_array, int timeout_secs);
+lws_cgi(struct lws *wsi, char * const *exec_array,  int script_uri_path_len,
+       int timeout_secs);
 
 LWS_VISIBLE LWS_EXTERN int
 lws_cgi_write_split_stdout_headers(struct lws *wsi);
index b70a31b..f3d6f5c 100644 (file)
@@ -1124,7 +1124,7 @@ struct lws {
        BIO *client_bio;
        struct lws *pending_read_list_prev, *pending_read_list_next;
 #endif
-#ifndef LWS_NO_CLIENT
+#ifdef LWS_WITH_HTTP_PROXY
        struct lws_rewrite *rw;
 #endif
 #ifdef LWS_LATENCY
@@ -1154,6 +1154,8 @@ struct lws {
        unsigned int do_ws:1; /* whether we are doing http or ws flow */
        unsigned int chunked:1; /* if the clientside connection is chunked */
        unsigned int client_rx_avail:1;
+#endif
+#ifdef LWS_WITH_HTTP_PROXY
        unsigned int perform_rewrite:1;
 #endif
 #ifndef LWS_NO_EXTENSIONS
@@ -1530,9 +1532,6 @@ static LWS_INLINE int hstrcmp(hubbub_string *s, const char *p, int len)
        return strncmp((const char *)s->ptr, p, len);
 }
 typedef hubbub_error (*hubbub_callback_t)(const hubbub_token *token, void *pw);
-LWS_EXTERN int lws_client_socket_service(struct lws_context *context,
-                                        struct lws *wsi,
-                                        struct lws_pollfd *pollfd);
 LWS_EXTERN struct lws_rewrite *
 lws_rewrite_create(struct lws *wsi, hubbub_callback_t cb, const char *from, const char *to);
 LWS_EXTERN void
@@ -1542,6 +1541,11 @@ lws_rewrite_parse(struct lws_rewrite *r, const unsigned char *in, int in_len);
 #endif
 
 #ifndef LWS_NO_CLIENT
+LWS_EXTERN int lws_client_socket_service(struct lws_context *context,
+                                        struct lws *wsi,
+                                        struct lws_pollfd *pollfd);
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_http_transaction_completed_client(struct lws *wsi);
 #ifdef LWS_OPENSSL_SUPPORT
 LWS_EXTERN int
 lws_context_init_client_ssl(struct lws_context_creation_info *info,
index 368043d..8ed2669 100644 (file)
@@ -473,10 +473,8 @@ lws_service_flag_pending(struct lws_context *context, int tsi)
        return forced;
 }
 
+#ifndef LWS_NO_CLIENT
 
-/*
- *
- */
 LWS_VISIBLE int
 lws_http_client_read(struct lws *wsi, char **buf, int *len)
 {
@@ -550,7 +548,7 @@ spin_chunks:
                return 0;
 
        if (wsi->u.http.content_remain &&
-           wsi->u.http.content_remain < *len)
+           (int)wsi->u.http.content_remain < *len)
                n = wsi->u.http.content_remain;
        else
                n = *len;
@@ -559,11 +557,12 @@ spin_chunks:
            wsi->chunk_remaining < n)
                n = wsi->chunk_remaining;
 
+#ifdef LWS_WITH_HTTP_PROXY
        /* hubbub */
        if (wsi->perform_rewrite)
                lws_rewrite_parse(wsi->rw, (unsigned char *)*buf, n);
        else
-
+#endif
                if (user_callback_handle_rxflow(wsi->protocol->callback,
                                wsi, LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ,
                                wsi->user_space, *buf, n))
@@ -595,11 +594,12 @@ completed:
                        wsi->user_space, NULL, 0))
                return -1;
 
-       if (lws_http_transaction_completed(wsi))
+       if (lws_http_transaction_completed_client(wsi))
                return -1;
 
        return 0;
 }
+#endif
 
 /**
  * lws_service_fd() - Service polled socket with something waiting
@@ -878,6 +878,7 @@ read:
                }
 
 drain:
+#ifndef LWS_NO_CLIENT
                if (wsi->mode == LWSCM_HTTP_CLIENT_ACCEPTED) {
 
                        /*
@@ -894,9 +895,8 @@ drain:
                                        wsi, LWS_CALLBACK_RECEIVE_CLIENT_HTTP,
                                        wsi->user_space, NULL, 0))
                                goto close_and_handled;
-
-
                }
+#endif
                /*
                 * give any active extensions a chance to munge the buffer
                 * before parse.  We pass in a pointer to an lws_tokens struct
index 06da558..2079e90 100644 (file)
@@ -96,6 +96,9 @@
 /* Define to 1 if `vfork' works. */
 #cmakedefine LWS_HAVE_WORKING_VFORK
 
+/* Define to 1 if execvpe() exists */
+#cmakedefine LWS_HAVE_EXECVPE
+
 /* Define to 1 if you have the <zlib.h> header file. */
 #cmakedefine LWS_HAVE_ZLIB_H
 
index 1535e43..a142b70 100644 (file)
@@ -104,6 +104,9 @@ const char * get_mimetype(const char *file)
        if (!strcmp(&file[n - 5], ".html"))
                return "text/html";
 
+       if (!strcmp(&file[n - 4], ".css"))
+               return "text/css";
+
        return NULL;
 }
 
@@ -117,7 +120,7 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
                  void *in, size_t len)
 {
        struct per_session_data__http *pss =
-                       (struct per_session_data__http *)user, *pss1;
+                       (struct per_session_data__http *)user;
        unsigned char buffer[4096 + LWS_PRE];
        unsigned long amount, file_len, sent;
        char leaf_path[1024];
@@ -126,7 +129,10 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
        unsigned char *end;
        struct timeval tv;
        unsigned char *p;
+#ifndef LWS_NO_CLIENT
+       struct per_session_data__http *pss1;
        struct lws *wsi1;
+#endif
        char buf[256];
        char b64[64];
        int n, m;
@@ -178,7 +184,7 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
                        i.port = 80;
                        i.ssl_connection = 0;
                        if (p[10])
-                               i.path = in + 10;
+                               i.path = (char *)in + 10;
                        else
                                i.path = rootpath;
                        i.host = "git.libwebsockets.org";
@@ -198,23 +204,26 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
                }
 #endif
 
+#if 1
                /* this example server has no concept of directories */
                if (strchr((const char *)in + 1, '/')) {
                        lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL);
                        goto try_to_reuse;
                }
+#endif
 
 #ifdef LWS_WITH_CGI
-               if (!strcmp(in, "/cgitest")) {
+               if (!strncmp(in, "/cgitest", 8)) {
                        static char *cmd[] = {
                                "/bin/sh",
                                "-c",
                                INSTALL_DATADIR"/libwebsockets-test-server/lws-cgi-test.sh",
+//                             "/var/www/cgi-bin/cgit",
                                NULL
                        };
 
                        lwsl_notice("%s: cgitest\n", __func__);
-                       n = lws_cgi(wsi, cmd, 5);
+                       n = lws_cgi(wsi, cmd, 8, 5);
                        if (n) {
                                lwsl_err("%s: cgi failed\n");
                                return -1;
@@ -320,11 +329,16 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
                }
 
                /* if not, send a file the easy way */
-               strcpy(buf, resource_path);
+               if (!strncmp(in, "/cgit-data/", 11)) {
+                       in = (char *)in + 11;
+                       strcpy(buf, "/usr/share/cgit");
+               } else
+                       strcpy(buf, resource_path);
+
                if (strcmp(in, "/")) {
                        if (*((const char *)in) != '/')
                                strcat(buf, "/");
-                       strncat(buf, in, sizeof(buf) - strlen(resource_path));
+                       strncat(buf, in, sizeof(buf) - strlen(buf) - 1);
                } else /* default file to serve */
                        strcat(buf, "/test.html");
                buf[sizeof(buf) - 1] = '\0';
@@ -510,7 +524,7 @@ bail:
                /* if we returned non-zero from here, we kill the connection */
                break;
 
-#ifndef LWS_WITH_CLIENT
+#ifndef LWS_NO_CLIENT
        case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: {
                char ctype[64], ctlen = 0;
                lwsl_err("LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP\n");
@@ -618,16 +632,16 @@ bail:
                break;
 
        case LWS_CALLBACK_CGI_TERMINATED:
-               lwsl_notice("LWS_CALLBACK_CGI_TERMINATED\n");
+               //lwsl_notice("LWS_CALLBACK_CGI_TERMINATED\n");
                /* because we sent on openended http, close the connection */
                return -1;
 
        case LWS_CALLBACK_CGI_STDIN_DATA:  /* POST body for stdin */
-               lwsl_notice("LWS_CALLBACK_CGI_STDIN_DATA\n");
+               //lwsl_notice("LWS_CALLBACK_CGI_STDIN_DATA\n");
                pss->args = *((struct lws_cgi_args *)in);
                n = write(lws_get_socket_fd(pss->args.stdwsi[LWS_STDIN]),
                          pss->args.data, pss->args.len);
-               lwsl_notice("LWS_CALLBACK_CGI_STDIN_DATA: write says %d", n);
+               //lwsl_notice("LWS_CALLBACK_CGI_STDIN_DATA: write says %d", n);
                if (n < pss->args.len)
                        lwsl_notice("LWS_CALLBACK_CGI_STDIN_DATA: sent %d only %d went",
                                        n, pss->args.len);
index a546904..c468a69 100644 (file)
@@ -69,6 +69,8 @@ struct per_session_data__http {
        lws_filefd_type fd;
 #ifdef LWS_WITH_CGI
        struct lws_cgi_args args;
+#endif
+#if defined(LWS_WITH_CGI) || !defined(LWS_NO_CLIENT)
        int reason_bf;
 #endif
        unsigned int client_finished:1;