uhttpd: make Lua handler more CGI like and fork child away
[project/luci.git] / contrib / package / uhttpd / src / uhttpd-file.c
index fb70b33..2a06f85 100644 (file)
@@ -1,9 +1,27 @@
-#define _XOPEN_SOURCE 500      /* strptime() ... */
-#define _BSD_SOURCE                    /* scandir() ... */
+/*
+ * uhttpd - Tiny single-threaded httpd - Static file handler
+ *
+ *   Copyright (C) 2010 Jo-Philipp Wich <xm@subsignal.org>
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#define _XOPEN_SOURCE 500      /* strptime() */
+#define _BSD_SOURCE                    /* scandir(), timegm() */
 
 #include "uhttpd.h"
-#include "uhttpd-file.h"
 #include "uhttpd-utils.h"
+#include "uhttpd-file.h"
 
 #include "uhttpd-mimetypes.h"
 
@@ -53,7 +71,7 @@ static time_t uh_file_date2unix(const char *date)
        memset(&t, 0, sizeof(t));
 
        if( strptime(date, "%a, %d %b %Y %H:%M:%S %Z", &t) != NULL )
-               return mktime(&t);
+               return timegm(&t);
 
        return 0;
 }
@@ -61,9 +79,9 @@ static time_t uh_file_date2unix(const char *date)
 static char * uh_file_unix2date(time_t ts)
 {
        static char str[128];
-       struct tm *t = localtime(&ts);
+       struct tm *t = gmtime(&ts);
 
-       strftime(str, sizeof(str), "%a, %d %b %Y %H:%M:%S %Z", t);
+       strftime(str, sizeof(str), "%a, %d %b %Y %H:%M:%S GMT", t);
 
        return str;
 }
@@ -83,6 +101,8 @@ static char * uh_file_header_lookup(struct http_request *req, const char *name)
 
 static void uh_file_response_ok_hdrs(struct client *cl, struct http_request *req, struct stat *s)
 {
+       uh_http_sendf(cl, NULL, "Connection: close\r\n");
+
        if( s )
        {
                uh_http_sendf(cl, NULL, "ETag: %s\r\n", uh_file_mktag(s));
@@ -106,8 +126,9 @@ static void uh_file_response_304(struct client *cl, struct http_request *req, st
 
 static void uh_file_response_412(struct client *cl, struct http_request *req)
 {
-       uh_http_sendf(cl, NULL, "HTTP/%.1f 412 Precondition Failed\r\n",
-               req->version);
+       uh_http_sendf(cl, NULL,
+               "HTTP/%.1f 412 Precondition Failed\r\n"
+               "Connection: close\r\n", req->version);
 }
 
 static int uh_file_if_match(struct client *cl, struct http_request *req, struct stat *s)
@@ -230,7 +251,7 @@ static int uh_file_scandir_filter_dir(const struct dirent *e)
        return strcmp(e->d_name, ".") ? 1 : 0;
 }
 
-static void uh_file_dirlist(struct client *cl, struct http_request *req, struct uh_path_info *pi)
+static void uh_file_dirlist(struct client *cl, struct http_request *req, struct path_info *pi)
 {
        int i, count;
        char filename[PATH_MAX];
@@ -296,87 +317,79 @@ static void uh_file_dirlist(struct client *cl, struct http_request *req, struct
 }
 
 
-void uh_file_request(struct client *cl, struct http_request *req)
+void uh_file_request(struct client *cl, struct http_request *req, struct path_info *pi)
 {
        int fd, rlen;
        char buf[UH_LIMIT_MSGHEAD];
-       struct uh_path_info *pi;
 
-       /* obtain path information */
-       if( (pi = uh_path_lookup(cl, req->url)) != NULL )
+       /* we have a file */
+       if( (pi->stat.st_mode & S_IFREG) && ((fd = open(pi->phys, O_RDONLY)) > 0) )
        {
-               /* we have a file */
-               if( (pi->stat.st_mode & S_IFREG) &&
-                   ((fd = open(pi->phys, O_RDONLY)) > 0)
+               /* test preconditions */
+               if(
+                       uh_file_if_modified_since(cl, req, &pi->stat)   &&
+                       uh_file_if_match(cl, req, &pi->stat)            &&
+                       uh_file_if_range(cl, req, &pi->stat)            &&
+                       uh_file_if_unmodified_since(cl, req, &pi->stat) &&
+                       uh_file_if_none_match(cl, req, &pi->stat)
                ) {
-                       /* test preconditions */
-                       if(
-                               uh_file_if_modified_since(cl, req, &pi->stat)   &&
-                               uh_file_if_match(cl, req, &pi->stat)            &&
-                               uh_file_if_range(cl, req, &pi->stat)            &&
-                               uh_file_if_unmodified_since(cl, req, &pi->stat) &&
-                               uh_file_if_none_match(cl, req, &pi->stat)
-                       ) {
-                               /* write status */
-                               uh_file_response_200(cl, req, &pi->stat);
-
-                               uh_http_sendf(cl, NULL, "Content-Type: %s\r\n", uh_file_mime_lookup(pi->name));
-                               uh_http_sendf(cl, NULL, "Content-Length: %i\r\n", pi->stat.st_size);
-
-                               /* if request was HTTP 1.1 we'll respond chunked */
-                               if( req->version > 1.0 )
-                                       uh_http_send(cl, NULL, "Transfer-Encoding: chunked\r\n", -1);
-
-                               /* close header */
-                               uh_http_send(cl, NULL, "\r\n", -1);
+                       /* write status */
+                       uh_file_response_200(cl, req, &pi->stat);
+
+                       uh_http_sendf(cl, NULL, "Content-Type: %s\r\n", uh_file_mime_lookup(pi->name));
+                       uh_http_sendf(cl, NULL, "Content-Length: %i\r\n", pi->stat.st_size);
+
+                       /* if request was HTTP 1.1 we'll respond chunked */
+                       if( (req->version > 1.0) && (req->method != UH_HTTP_MSG_HEAD) )
+                               uh_http_send(cl, NULL, "Transfer-Encoding: chunked\r\n", -1);
+
+                       /* close header */
+                       uh_http_send(cl, NULL, "\r\n", -1);
 
+                       /* send body */
+                       if( req->method != UH_HTTP_MSG_HEAD )
+                       {
                                /* pump file data */
                                while( (rlen = read(fd, buf, sizeof(buf))) > 0 )
                                {
-                                       uh_http_send(cl, req, buf, rlen);
+                                       if( uh_http_send(cl, req, buf, rlen) < 0 )
+                                               break;
                                }
 
                                /* send trailer in chunked mode */
                                uh_http_send(cl, req, "", 0);
                        }
-
-                       /* one of the preconditions failed, terminate opened header and exit */
-                       else
-                       {
-                               uh_http_send(cl, NULL, "\r\n", -1);
-                       }
-
-                       close(fd);
                }
 
-               /* directory */
-               else if( pi->stat.st_mode & S_IFDIR )
+               /* one of the preconditions failed, terminate opened header and exit */
+               else
                {
-                       /* write status */
-                       uh_file_response_200(cl, req, NULL);
+                       uh_http_send(cl, NULL, "\r\n", -1);
+               }
 
-                       if( req->version > 1.0 )
-                               uh_http_send(cl, NULL, "Transfer-Encoding: chunked\r\n", -1);
+               close(fd);
+       }
 
-                       uh_http_send(cl, NULL, "Content-Type: text/html\r\n\r\n", -1);
+       /* directory */
+       else if( pi->stat.st_mode & S_IFDIR )
+       {
+               /* write status */
+               uh_file_response_200(cl, req, NULL);
 
-                       /* content */
-                       uh_file_dirlist(cl, req, pi);
-               }
+               if( req->version > 1.0 )
+                       uh_http_send(cl, NULL, "Transfer-Encoding: chunked\r\n", -1);
 
-               /* 403 */
-               else
-               {
-                       uh_http_sendhf(cl, 403, "Forbidden",
-                               "Access to this resource is forbidden");
-               }
+               uh_http_send(cl, NULL, "Content-Type: text/html\r\n\r\n", -1);
+
+               /* content */
+               uh_file_dirlist(cl, req, pi);
        }
 
-       /* 404 */
+       /* 403 */
        else
        {
-               uh_http_sendhf(cl, 404, "Not Found",
-                       "No such file or directory");
+               uh_http_sendhf(cl, 403, "Forbidden",
+                       "Access to this resource is forbidden");
        }
 }