I wasy playing around with the FastCGI stuff tonight, and I
implemented the next step in the request process, sending the
environment over to the backend fcgi process.  This also involved
refactoring some of the existing code a bit, removing unused
variables, etc, but nothing too extraordinary.  Some basic testing
with a Ruby fastcgi script shows the environment getting parsed by the
script just fine.

Log message follows, patch is attached.

-garrett

Add support for sending the environment to the backend fastcgi process
to mod_proxy_fcgi.

* modules/proxy/mod_proxy_fcgi.c
  (proxy_fcgi_canon): Remove unused variables, wrap a long line.
  (send_begin_request): Helper function to send the FCGI_BEGIN_REQUEST
   message.
  (send_environment): Helper function to send the environment.
  (fcgi_do_request): Use send_begin_request and send_environment.
Index: modules/proxy/mod_proxy_fcgi.c
===================================================================
--- modules/proxy/mod_proxy_fcgi.c	(revision 358962)
+++ modules/proxy/mod_proxy_fcgi.c	(working copy)
@@ -26,7 +26,7 @@
  */
 static int proxy_fcgi_canon(request_rec *r, char *url)
 {
-    char *host, *path, *search, sport[7];
+    char *host, sport[7];
     const char *err;
     const char* scheme;
     apr_port_t port = 8000;
@@ -61,7 +61,8 @@
             host = apr_pstrcat(r->pool, "[", host, "]", NULL);
         }
         
-        r->filename = apr_pstrcat(r->pool, "proxy:", scheme, host, sport, "/", NULL);
+        r->filename = apr_pstrcat(r->pool, "proxy:", scheme, host, sport, "/",
+                                  NULL);
     }
     else if (strncmp(url, "local://", 8) == 0) {
         url += 6;
@@ -124,49 +125,196 @@
     unsigned char reserved[5];
 } fcgi_begin_request_body;
 
-/*
- * process the request and write the response.
- */
-static int fcgi_do_request(apr_pool_t *p, request_rec *r,
-                            proxy_conn_rec *conn,
-                            conn_rec *origin,
-                            proxy_dir_conf *conf,
-                            apr_uri_t *uri,
-                            char *url, char *server_portstr)
+
+static apr_status_t
+send_begin_request (proxy_conn_rec *conn, int request_id)
 {
+    struct iovec vec[2];
+    fcgi_header header;
+    fcgi_begin_request_body brb;
+    apr_size_t len;
+
+    header.version = FCGI_VERSION;
+    header.type = FCGI_BEGIN_REQUEST;
+    header.requestIdB1 = ((request_id >> 8) & 0xff);
+    header.requestIdB0 = ((request_id) & 0xff); 
+    header.contentLengthB1 = ((sizeof(brb) >> 8) & 0xff);
+    header.contentLengthB0 = ((sizeof(brb)) & 0xff); 
+    header.paddingLength = 0;
+
+    brb.roleB1 = ((FCGI_RESPONDER >> 8) & 0xff);
+    brb.roleB0 = ((FCGI_RESPONDER) & 0xff); 
+    brb.flags = FCGI_KEEP_CONN;
+
+    vec[0].iov_base = &header;
+    vec[0].iov_len = sizeof(header);
+    vec[1].iov_base = &brb;
+    vec[1].iov_len = sizeof(brb);
+
+    return apr_socket_sendv(conn->sock, vec, 2, &len);
+}
+
+static apr_status_t
+send_environment (proxy_conn_rec *conn, request_rec *r, int request_id)
+{
+    const apr_array_header_t *envarr;
+    const apr_table_entry_t *elts;
+    struct iovec vec[2];
+    fcgi_header header;
+    apr_size_t bodylen;
+    char *body, *itr;
     apr_status_t rv;
+    apr_size_t len;
+    int i;
 
-    {
-        struct iovec vec[2];
-        fcgi_header header;
-        fcgi_begin_request_body brb;
-    
-        /* Step 1: Send FCGI_BEGIN_REQUEST */
-        header.version = FCGI_VERSION;
-        header.type = FCGI_BEGIN_REQUEST;
-        header.requestIdB1 = ((1 >> 8) & 0xff);
-        header.requestIdB0 = ((1) & 0xff); 
-        header.contentLengthB1 = ((sizeof(brb) >> 8) & 0xff);
-        header.contentLengthB0 = ((sizeof(brb)) & 0xff); 
-        header.paddingLength = 0;
-    
-        brb.roleB1 = ((FCGI_RESPONDER >> 8) & 0xff);
-        brb.roleB0 = ((FCGI_RESPONDER) & 0xff); 
-        brb.flags = FCGI_KEEP_CONN;
+    header.version = FCGI_VERSION;
+    header.type = FCGI_PARAMS;
+    header.requestIdB1 = ((request_id >> 8) & 0xff);
+    header.requestIdB0 = ((request_id) & 0xff); 
 
-        vec[0].iov_base = &header;
-        vec[0].iov_len = sizeof(header);
-        vec[1].iov_base = &brb;
-        vec[1].iov_len = sizeof(brb);
+    ap_add_common_vars(r);
+    ap_add_cgi_vars(r);
 
-        rv = apr_socket_sendv(conn->sock, vec, 2, NULL);
-        if (rv != APR_SUCCESS) {
-            ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, r->server,
-                         "proxy: FCGI: Failed Writing Request to %s:", server_portstr);
+    /* XXX are there any FastCGI specific env vars we need to send? */
+
+    bodylen = 0;
+
+    /* XXX mod_cgi/mod_cgid use ap_create_environment here, which fills in
+     *     the TZ value specially.  We could use that, but it would mean
+     *     parsing the key/value pairs back OUT of the allocated env array,
+     *     not to mention allocating a totally useless array in the first
+     *     place, which would suck. */
+
+    envarr = apr_table_elts(r->subprocess_env);
+
+    elts = (const apr_table_entry_t *) envarr->elts;
+
+    for (i = 0; i < envarr->nelts; ++i) {
+        apr_size_t keylen, vallen;
+
+        if (! elts[i].key)
+          continue;
+
+        keylen = strlen(elts[i].key);
+
+        if (keylen >> 7 == 0)
+            bodylen += 1;
+        else
+            bodylen += 4;
+
+        bodylen += keylen;
+
+        vallen = strlen(elts[i].val);
+
+        if (vallen >> 7 == 0)
+            bodylen += 1;
+        else
+            bodylen += 4;
+
+        bodylen += vallen;
+    }
+
+    body = apr_pcalloc(r->pool, bodylen);
+
+    itr = body;
+
+    for (i = 0; i < envarr->nelts; ++i) {
+        apr_size_t keylen, vallen;
+       
+        if (! elts[i].key)
+          continue;
+
+        keylen = strlen(elts[i].key);
+
+        if (keylen >> 7 == 0) {
+            itr[0] = keylen & 0xff;
+            itr += 1;
         }
+        else {
+            itr[0] = ((keylen >> 24) & 0xff) | 0x80;
+            itr[1] = ((keylen >> 16) & 0xff);
+            itr[2] = ((keylen >> 8) & 0xff);
+            itr[3] = ((keylen) & 0xff);
+            itr += 4;
+        }
+
+        vallen = strlen(elts[i].val);
+
+        if (vallen >> 7 == 0) {
+            itr[0] = vallen & 0xff;
+            itr += 1;
+        }
+        else {
+            itr[0] = ((vallen >> 24) & 0xff) | 0x80;
+            itr[1] = ((vallen >> 16) & 0xff);
+            itr[2] = ((vallen >> 8) & 0xff);
+            itr[3] = ((vallen) & 0xff);
+            itr += 4;
+        }
+
+        memcpy(itr, elts[i].key, keylen);
+        itr += keylen;
+
+        memcpy(itr, elts[i].val, vallen);
+        itr += vallen;
     }
+
+    /* First we send the actual env... */
+    header.contentLengthB1 = ((bodylen >> 8) & 0xff);
+    header.contentLengthB0 = ((bodylen) & 0xff); 
+    header.paddingLength = 0;
+
+    vec[0].iov_base = &header;
+    vec[0].iov_len = sizeof(header);
+    vec[1].iov_base = body;
+    vec[1].iov_len = bodylen;
+
+    rv = apr_socket_sendv(conn->sock, vec, 2, &len);
+    if (rv)
+        return rv;
+
+    /* Then, an empty record to signify the end of the stream. */
+    header.contentLengthB1 = 0;
+    header.contentLengthB0 = 0;
+    header.paddingLength = 0;
+
+    vec[0].iov_base = &header;
+    vec[0].iov_len = sizeof(header);
+
+    return apr_socket_sendv(conn->sock, vec, 1, &len);
+}
+
+/*
+ * process the request and write the response.
+ */
+static int fcgi_do_request(apr_pool_t *p, request_rec *r,
+                           proxy_conn_rec *conn,
+                           conn_rec *origin,
+                           proxy_dir_conf *conf,
+                           apr_uri_t *uri,
+                           char *url, char *server_portstr)
+{
+    int request_id = 1; /* XXX where do request ids come from? */
+    apr_status_t rv;
+   
+    /* Step 1: Send FCGI_BEGIN_REQUEST */
+    rv = send_begin_request(conn, request_id);
+    if (rv != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, r->server,
+                     "proxy: FCGI: Failed Writing Request to %s:",
+                     server_portstr);
+        return HTTP_SERVICE_UNAVAILABLE;
+    }
     
     /* Step 2: Send Enviroment via FCGI_PARAMS */
+    rv = send_environment(conn, r, request_id);
+    if (rv != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, r->server,
+                     "proxy: FCGI: Failed writing Environment to %s:",
+                     server_portstr);
+        return HTTP_SERVICE_UNAVAILABLE;
+    }
+
     /* Step 3: Send Request Body via FCGI_STDIN */
     /* Step 4: Read for CGI_STDOUT|CGI_STDERR */
     /* Step 5: Parse reply headers. */

Reply via email to