call*cgi, python: Try to emulate standard CGI behavior closer.
authorFredrik Tolf <fredrik@dolda2000.com>
Sun, 26 Jun 2011 05:16:08 +0000 (07:16 +0200)
committerFredrik Tolf <fredrik@dolda2000.com>
Sun, 26 Jun 2011 05:16:08 +0000 (07:16 +0200)
The proper way to handle the problem in question seems very unclear,
however; see the comment in callcgi.c for further details.

python/ashd-wsgi
src/callcgi.c
src/callfcgi.c
src/callscgi.c

index e43dbc0..894211d 100755 (executable)
@@ -92,10 +92,6 @@ def dowsgi(req):
     env["SERVER_PROTOCOL"] = req.ver
     env["REQUEST_METHOD"] = req.method
     env["REQUEST_URI"] = req.url
-    try:
-        env["PATH_INFO"] = unquoteurl(req.rest)
-    except:
-        env["PATH_INFO"] = req.rest
     name = req.url
     p = name.find('?')
     if p >= 0:
@@ -104,8 +100,19 @@ def dowsgi(req):
     else:
         env["QUERY_STRING"] = ""
     if name[-len(req.rest):] == req.rest:
+        # This is the same hack used in call*cgi.
         name = name[:-len(req.rest)]
+    try:
+        pi = unquoteurl(req.rest)
+    except:
+        pi = req.rest
+    if name == '/':
+        # This seems to be normal CGI behavior, but see callcgi.c for
+        # details.
+        pi = "/" + pi
+        name = ""
     env["SCRIPT_NAME"] = name
+    env["PATH_INFO"] = pi
     if "Host" in req: env["SERVER_NAME"] = req["Host"]
     if "X-Ash-Server-Port" in req: env["SERVER_PORT"] = req["X-Ash-Server-Port"]
     if "X-Ash-Protocol" in req and req["X-Ash-Protocol"] == "https": env["HTTPS"] = "on"
index 14430aa..3add3ed 100644 (file)
@@ -89,7 +89,7 @@ static pid_t forkchild(int inpath, char *prog, char *file, char *method, char *u
     char *qp, **env, *name;
     int inp[2], outp[2];
     pid_t pid;
-    char *unqr;
+    char *pi;
 
     pipe(inp);
     pipe(outp);
@@ -111,8 +111,6 @@ static pid_t forkchild(int inpath, char *prog, char *file, char *method, char *u
        if(getenv("HTTP_VERSION"))
            putenv(sprintf2("SERVER_PROTOCOL=%s", getenv("HTTP_VERSION")));
        putenv(sprintf2("REQUEST_METHOD=%s", method));
-       unqr = unquoteurl(rest);
-       putenv(sprintf2("PATH_INFO=%s", unqr?unqr:rest));
        name = url;
        /* XXX: This is an ugly hack (I think), but though I can think
         * of several alternatives, none seem to be better. */
@@ -120,6 +118,29 @@ static pid_t forkchild(int inpath, char *prog, char *file, char *method, char *u
           !strcmp(rest, url + strlen(url) - strlen(rest))) {
            name = sprintf2("%.*s", (int)(strlen(url) - strlen(rest)), url);
        }
+       if((pi = unquoteurl(rest)) == NULL)
+           pi = rest;
+       if(!strcmp(name, "/")) {
+           /*
+            * Normal CGI behavior appears to be to always let
+            * PATH_INFO begin with a slash and never let SCRIPT_NAME
+            * end with one. That conflicts, however, with some
+            * behaviors, such as "mounting" CGI applications on a
+            * directory element of the URI space -- a handler
+            * responding to "/foo/" would not be able to tell that it
+            * is not called "/foo", which makes a large difference,
+            * not least in relation to URI reconstruction and
+            * redirections. A common practical case is CGI-handled
+            * index files in directories. Therefore, this only
+            * handles the nonconditional case of the root directory
+            * and leaves other decisions to the previous handler
+            * handing over the request to callcgi. It is unclear if
+            * there is a better way to handle the problem.
+            */
+           name[0] = 0;
+           pi = sprintf2("/%s", pi);
+       }
+       putenv(sprintf2("PATH_INFO=%s", pi));
        putenv(sprintf2("SCRIPT_NAME=%s", name));
        putenv(sprintf2("QUERY_STRING=%s", qp?qp:""));
        if(getenv("REQ_HOST"))
index 9af5929..99d21ca 100644 (file)
@@ -441,19 +441,13 @@ static char *absolutify(char *file)
 static void mkcgienv(struct hthead *req, struct charbuf *dst)
 {
     int i;
-    char *url, *unq, *qp, *h, *p;
+    char *url, *pi, *tmp, *qp, *h, *p;
     
     bufaddenv(dst, "SERVER_SOFTWARE", "ashd/%s", VERSION);
     bufaddenv(dst, "GATEWAY_INTERFACE", "CGI/1.1");
     bufaddenv(dst, "SERVER_PROTOCOL", "%s", req->ver);
     bufaddenv(dst, "REQUEST_METHOD", "%s", req->method);
     bufaddenv(dst, "REQUEST_URI", "%s", req->url);
-    if((unq = unquoteurl(req->rest)) != NULL) {
-       bufaddenv(dst, "PATH_INFO", unq);
-       free(unq);
-    } else {
-       bufaddenv(dst, "PATH_INFO", req->rest);
-    }
     url = sstrdup(req->url);
     if((qp = strchr(url, '?')) != NULL)
        *(qp++) = 0;
@@ -461,11 +455,21 @@ static void mkcgienv(struct hthead *req, struct charbuf *dst)
      * several alternatives, none seem to be better. */
     if(*req->rest && (strlen(url) >= strlen(req->rest)) &&
        !strcmp(req->rest, url + strlen(url) - strlen(req->rest))) {
-       bufaddenv(dst, "SCRIPT_NAME", "%.*s", (int)(strlen(url) - strlen(req->rest)), url);
-    } else {
-       bufaddenv(dst, "SCRIPT_NAME", "%s", url);
-    }
+       url[strlen(url) - strlen(req->rest)] = 0;
+    }
+    if((pi = unquoteurl(req->rest)) == NULL)
+       pi = sstrdup(req->rest);
+    if(!strcmp(url, "/")) {
+       /* This seems to be normal CGI behavior, but see callcgi.c for
+        * details. */
+       url[0] = 0;
+       pi = sprintf2("/%s", tmp = pi);
+       free(tmp);
+    }
+    bufaddenv(dst, "PATH_INFO", pi);
+    bufaddenv(dst, "SCRIPT_NAME", url);
     bufaddenv(dst, "QUERY_STRING", "%s", qp?qp:"");
+    free(pi);
     free(url);
     if((h = getheader(req, "Host")) != NULL)
        bufaddenv(dst, "SERVER_NAME", "%s", h);
index 062baa1..299e09f 100644 (file)
@@ -403,7 +403,7 @@ static char *absolutify(char *file)
 static void mkcgienv(struct hthead *req, struct charbuf *dst)
 {
     int i;
-    char *url, *unq, *qp, *h, *p;
+    char *url, *pi, *tmp, *qp, *h, *p;
     
     bufaddenv(dst, "SERVER_SOFTWARE", "ashd/%s", VERSION);
     bufaddenv(dst, "GATEWAY_INTERFACE", "CGI/1.1");
@@ -411,12 +411,6 @@ static void mkcgienv(struct hthead *req, struct charbuf *dst)
     bufaddenv(dst, "SERVER_PROTOCOL", "%s", req->ver);
     bufaddenv(dst, "REQUEST_METHOD", "%s", req->method);
     bufaddenv(dst, "REQUEST_URI", "%s", req->url);
-    if((unq = unquoteurl(req->rest)) != NULL) {
-       bufaddenv(dst, "PATH_INFO", unq);
-       free(unq);
-    } else {
-       bufaddenv(dst, "PATH_INFO", req->rest);
-    }
     url = sstrdup(req->url);
     if((qp = strchr(url, '?')) != NULL)
        *(qp++) = 0;
@@ -424,11 +418,21 @@ static void mkcgienv(struct hthead *req, struct charbuf *dst)
      * several alternatives, none seem to be better. */
     if(*req->rest && (strlen(url) >= strlen(req->rest)) &&
        !strcmp(req->rest, url + strlen(url) - strlen(req->rest))) {
-       bufaddenv(dst, "SCRIPT_NAME", "%.*s", (int)(strlen(url) - strlen(req->rest)), url);
-    } else {
-       bufaddenv(dst, "SCRIPT_NAME", "%s", url);
+       url[strlen(url) - strlen(req->rest)] = 0;
+    }
+    if((pi = unquoteurl(req->rest)) == NULL)
+       pi = sstrdup(req->rest);
+    if(!strcmp(url, "/")) {
+       /* This seems to be normal CGI behavior, but see callcgi.c for
+        * details. */
+       url[0] = 0;
+       pi = sprintf2("/%s", tmp = pi);
+       free(tmp);
     }
+    bufaddenv(dst, "PATH_INFO", pi);
+    bufaddenv(dst, "SCRIPT_NAME", url);
     bufaddenv(dst, "QUERY_STRING", "%s", qp?qp:"");
+    free(pi);
     free(url);
     if((h = getheader(req, "Host")) != NULL)
        bufaddenv(dst, "SERVER_NAME", "%s", h);