You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by tr...@apache.org on 2013/08/13 14:16:40 UTC

svn commit: r1513454 - in /httpd/httpd/trunk: CHANGES NWGNUmakefile build/nw_export.inc include/ap_mmn.h include/util_fcgi.h libhttpd.dsp server/Makefile.in server/util_fcgi.c

Author: trawick
Date: Tue Aug 13 12:16:39 2013
New Revision: 1513454

URL: http://svn.apache.org/r1513454
Log:
Add util_fcgi.h and associated definitions and support
routines for FastCGI, based largely on mod_proxy_fcgi.

Added:
    httpd/httpd/trunk/include/util_fcgi.h
    httpd/httpd/trunk/server/util_fcgi.c
Modified:
    httpd/httpd/trunk/CHANGES
    httpd/httpd/trunk/NWGNUmakefile
    httpd/httpd/trunk/build/nw_export.inc
    httpd/httpd/trunk/include/ap_mmn.h
    httpd/httpd/trunk/libhttpd.dsp
    httpd/httpd/trunk/server/Makefile.in

Modified: httpd/httpd/trunk/CHANGES
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/CHANGES?rev=1513454&r1=1513453&r2=1513454&view=diff
==============================================================================
--- httpd/httpd/trunk/CHANGES [utf-8] (original)
+++ httpd/httpd/trunk/CHANGES [utf-8] Tue Aug 13 12:16:39 2013
@@ -1,6 +1,10 @@
                                                          -*- coding: utf-8 -*-
 Changes with Apache 2.5.0
 
+  *) core: Add util_fcgi.h and associated definitions and support
+     routines for FastCGI, based largely on mod_proxy_fcgi.
+     [Jeff Trawick]
+
   *) core: Add ap_log_data(), ap_log_rdata(), etc. for logging buffers.
      [Jeff Trawick]
 

Modified: httpd/httpd/trunk/NWGNUmakefile
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/NWGNUmakefile?rev=1513454&r1=1513453&r2=1513454&view=diff
==============================================================================
--- httpd/httpd/trunk/NWGNUmakefile (original)
+++ httpd/httpd/trunk/NWGNUmakefile Tue Aug 13 12:16:39 2013
@@ -276,6 +276,7 @@ FILES_nlm_objs = \
 	$(OBJDIR)/util_expr_eval.o \
 	$(OBJDIR)/util_expr_parse.o \
 	$(OBJDIR)/util_expr_scan.o \
+	$(OBJDIR)/util_fcgi.o \
 	$(OBJDIR)/util_filter.o \
 	$(OBJDIR)/util_md5.o \
 	$(OBJDIR)/util_mutex.o \

Modified: httpd/httpd/trunk/build/nw_export.inc
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/build/nw_export.inc?rev=1513454&r1=1513453&r2=1513454&view=diff
==============================================================================
--- httpd/httpd/trunk/build/nw_export.inc (original)
+++ httpd/httpd/trunk/build/nw_export.inc Tue Aug 13 12:16:39 2013
@@ -62,6 +62,7 @@
 #include "util_charset.h"
 #include "util_cookies.h"
 #include "util_ebcdic.h"
+#include "util_fcgi.h"
 #include "util_filter.h"
 /*#include "util_ldap.h"*/
 #include "util_md5.h"

Modified: httpd/httpd/trunk/include/ap_mmn.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/include/ap_mmn.h?rev=1513454&r1=1513453&r2=1513454&view=diff
==============================================================================
--- httpd/httpd/trunk/include/ap_mmn.h (original)
+++ httpd/httpd/trunk/include/ap_mmn.h Tue Aug 13 12:16:39 2013
@@ -437,6 +437,7 @@
  * 20130702.0 (2.5.0-dev)  Remove pre_htaccess hook, add open_htaccess hook.
  * 20130702.1 (2.5.0-dev)  Restore AUTH_HANDLED to mod_auth.h
  * 20130702.2 (2.5.0-dev)  Add ap_log_data(), ap_log_rdata(), etc.
+ * 20130702.3 (2.5.0-dev)  Add util_fcgi.h, FastCGI protocol support
  */
 
 #define MODULE_MAGIC_COOKIE 0x41503235UL /* "AP25" */
@@ -444,7 +445,7 @@
 #ifndef MODULE_MAGIC_NUMBER_MAJOR
 #define MODULE_MAGIC_NUMBER_MAJOR 20130702
 #endif
-#define MODULE_MAGIC_NUMBER_MINOR 2                  /* 0...n */
+#define MODULE_MAGIC_NUMBER_MINOR 3                  /* 0...n */
 
 /**
  * Determine if the server's current MODULE_MAGIC_NUMBER is at least a

Added: httpd/httpd/trunk/include/util_fcgi.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/include/util_fcgi.h?rev=1513454&view=auto
==============================================================================
--- httpd/httpd/trunk/include/util_fcgi.h (added)
+++ httpd/httpd/trunk/include/util_fcgi.h Tue Aug 13 12:16:39 2013
@@ -0,0 +1,280 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+/**
+ * @file  util_fcgi.h
+ * @brief FastCGI protocol defitions and support routines
+ *
+ * @defgroup APACHE_CORE_FASTCGI FastCGI Tools
+ * @ingroup  APACHE_CORE
+ * @{
+ */
+
+#ifndef APACHE_UTIL_FCGI_H
+#define APACHE_UTIL_FCGI_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "httpd.h"
+
+/**
+ * @brief A structure that represents the fixed header fields
+ * at the beginning of a "FastCGI record" (i.e., the data prior
+ * to content data and padding).
+ */
+typedef struct {
+    /** See values for version, below */
+    unsigned char version;
+    /** See values for type, below */
+    unsigned char type;
+    /** request id, in two parts */
+    unsigned char requestIdB1;
+    unsigned char requestIdB0;
+    /** content length, in two parts */
+    unsigned char contentLengthB1;
+    unsigned char contentLengthB0;
+    /** padding length */
+    unsigned char paddingLength;
+    /** 8-bit reserved field */
+    unsigned char reserved;
+} ap_fcgi_header;
+
+/*
+ * Number of bytes in the header portion of a FastCGI record
+ * (i.e., ap_fcgi_header structure).  Future versions of the
+ * protocol may increase the size.
+ */
+#define AP_FCGI_HEADER_LEN  8
+
+/*
+ * Maximum number of bytes in the content portion of a FastCGI record.
+ */
+#define AP_FCGI_MAX_CONTENT_LEN 65535
+
+/**
+ * Possible values for the version field of ap_fcgi_header
+ */
+#define AP_FCGI_VERSION_1 1
+
+/**
+ * Possible values for the type field of ap_fcgi_header
+ */
+#define AP_FCGI_BEGIN_REQUEST       1
+#define AP_FCGI_ABORT_REQUEST       2
+#define AP_FCGI_END_REQUEST         3
+#define AP_FCGI_PARAMS              4
+#define AP_FCGI_STDIN               5
+#define AP_FCGI_STDOUT              6
+#define AP_FCGI_STDERR              7
+#define AP_FCGI_DATA                8
+#define AP_FCGI_GET_VALUES          9
+#define AP_FCGI_GET_VALUES_RESULT  10
+#define AP_FCGI_UNKNOWN_TYPE       11
+#define AP_FCGI_MAXTYPE (AP_FCGI_UNKNOWN_TYPE)
+
+/**
+ * Offsets of the various fields of ap_fcgi_header
+ */
+#define AP_FCGI_HDR_VERSION_OFFSET         0
+#define AP_FCGI_HDR_TYPE_OFFSET            1
+#define AP_FCGI_HDR_REQUEST_ID_B1_OFFSET   2
+#define AP_FCGI_HDR_REQUEST_ID_B0_OFFSET   3
+#define AP_FCGI_HDR_CONTENT_LEN_B1_OFFSET  4
+#define AP_FCGI_HDR_CONTENT_LEN_B0_OFFSET  5
+#define AP_FCGI_HDR_PADDING_LEN_OFFSET     6
+#define AP_FCGI_HDR_RESERVED_OFFSET        7
+
+/**
+ * @brief This represents the content data of the FastCGI record when
+ * the type is AP_FCGI_BEGIN_REQUEST.
+ */
+typedef struct {
+    /**
+     * role, in two parts
+     * See values for role, below
+     */
+    unsigned char roleB1;
+    unsigned char roleB0;
+    /**
+     * flags
+     * See values for flags bits, below
+     */
+    unsigned char flags;
+    /** reserved */
+    unsigned char reserved[5];
+} ap_fcgi_begin_request_body;
+
+/*
+ * Values for role component of ap_fcgi_begin_request_body
+ */
+#define AP_FCGI_RESPONDER  1
+#define AP_FCGI_AUTHORIZER 2
+#define AP_FCGI_FILTER     3
+
+/*
+ * Values for flags bits of ap_fcgi_begin_request_body
+ */
+#define AP_FCGI_KEEP_CONN  1  /* otherwise the application closes */
+
+/**
+ * Offsets of the various fields of ap_fcgi_begin_request_body
+ */
+#define AP_FCGI_BRB_ROLEB1_OFFSET       0
+#define AP_FCGI_BRB_ROLEB0_OFFSET       1
+#define AP_FCGI_BRB_FLAGS_OFFSET        2
+#define AP_FCGI_BRB_RESERVED0_OFFSET    3
+#define AP_FCGI_BRB_RESERVED1_OFFSET    4
+#define AP_FCGI_BRB_RESERVED2_OFFSET    5
+#define AP_FCGI_BRB_RESERVED3_OFFSET    6
+#define AP_FCGI_BRB_RESERVED4_OFFSET    7
+
+/**
+ * Pack ap_fcgi_header
+ * @param h The header to read from
+ * @param a The array to write to, of size AP_FCGI_HEADER_LEN
+ */
+AP_DECLARE(void) ap_fcgi_header_to_array(ap_fcgi_header *h,
+                                         unsigned char a[]);
+
+/**
+ * Unpack header of FastCGI record into ap_fcgi_header
+ * @param h The header to write to
+ * @param a The array to read from, of size AP_FCGI_HEADER_LEN
+ */
+AP_DECLARE(void) ap_fcgi_header_from_array(ap_fcgi_header *h,
+                                           unsigned char a[]);
+
+/**
+ * Unpack header of FastCGI record into individual fields
+ * @param version The version, on output
+ * @param type The type, on output
+ * @param request_id The request id, on output
+ * @param content_len The content length, on output
+ * @param padding_len The amount of padding following the content, on output
+ * @param a The array to read from, of size AP_FCGI_HEADER_LEN
+ */
+AP_DECLARE(void) ap_fcgi_header_fields_from_array(unsigned char *version,
+                                                  unsigned char *type,
+                                                  apr_uint16_t *request_id,
+                                                  apr_uint16_t *content_len,
+                                                  unsigned char *padding_len,
+                                                  unsigned char a[]);
+
+/**
+ * Pack ap_fcgi_begin_request_body
+ * @param h The begin-request body to read from
+ * @param a The array to write to, of size AP_FCGI_HEADER_LEN
+ */
+AP_DECLARE(void) ap_fcgi_begin_request_body_to_array(ap_fcgi_begin_request_body *h,
+                                                     unsigned char a[]);
+
+/**
+ * Fill in a FastCGI request header with the required field values.
+ * @param header The header to fill in
+ * @param type The type of record
+ * @param request_id The request id
+ * @param content_len The amount of content which follows the header
+ * @param padding_len The amount of padding which follows the content
+ *
+ * The header array must be at least AP_FCGI_HEADER_LEN bytes long.
+ */
+AP_DECLARE(void) ap_fcgi_fill_in_header(ap_fcgi_header *header,
+                                        unsigned char type,
+                                        apr_uint16_t request_id,
+                                        apr_uint16_t content_len,
+                                        unsigned char padding_len);
+
+/**
+ * Fill in a FastCGI begin request body with the required field values.
+ * @param brb The begin-request-body to fill in
+ * @param role AP_FCGI_RESPONDER or other roles
+ * @param flags 0 or a combination of flags like AP_FCGI_KEEP_CONN
+ */
+AP_DECLARE(void) ap_fcgi_fill_in_request_body(ap_fcgi_begin_request_body *brb,
+                                              int role,
+                                              unsigned char flags);
+
+/**
+ * Compute the buffer size needed to encode the next portion of
+ * the provided environment table.
+ * @param env The environment table
+ * @param maxlen The maximum buffer size allowable, capped at 
+ * AP_FCGI_MAX_CONTENT_LEN.
+ * @param starting_elem On input, the next element of the table array
+ * to process in this FastCGI record.  On output, the next element to
+ * process on the *next* FastCGI record.
+ * @return Size of buffer needed to encode the next part, or 0
+ * if no more can be encoded.  When 0 is returned: If starting_elem
+ * has reached the end of the table array, all has been encoded;
+ * otherwise, the next envvar can't be encoded within the specified
+ * limit.
+ * @note If an envvar can't be encoded within the specified limit,
+ * the caller can log a warning and increment starting_elem and try 
+ * again or increase the limit or fail, as appropriate for the module.
+ */
+AP_DECLARE(apr_size_t) ap_fcgi_encoded_env_len(apr_table_t *env,
+                                               apr_size_t maxlen,
+                                               int *starting_elem);
+
+/**
+ * Encode the next portion of the provided environment table using
+ * a buffer previously allocated.
+ * @param r The request, for logging
+ * @param env The environment table
+ * @param buffer A buffer to contain the encoded environment table
+ * @param buflen The length of the buffer, previously computed by
+ * ap_fcgi_encoded_env_len().
+ * @param starting_elem On input, the next element of the table array
+ * to process in this FastCGI record.  On output, the next element to
+ * process on the *next* FastCGI record.
+ * @return APR_SUCCESS if a section could be encoded or APR_ENOSPC
+ * otherwise.
+ * @note The output starting_elem from ap_fcgi_encoded_env_len
+ * shouldn't be used as input to ap_fcgi_encode_env when building the
+ * same FastCGI record.
+ */
+AP_DECLARE(apr_status_t) ap_fcgi_encode_env(request_rec *r,
+                                            apr_table_t *env,
+                                            void *buffer,
+                                            apr_size_t buflen,
+                                            int *starting_elem);
+
+/**
+ * String forms for the value of the FCGI_ROLE envvar
+ */
+#define AP_FCGI_RESPONDER_STR   "RESPONDER"
+#define AP_FCGI_AUTHORIZER_STR  "AUTHORIZER"
+#define AP_FCGI_FILTER_STR      "FILTER"
+
+/**
+ * FastCGI implementations that implement the AUTHORIZER role
+ * for Apache httpd and allow the application to participate in
+ * any of the Apache httpd AAA phases typically set the variable
+ * FCGI_APACHE_ROLE to one of these strings to indicate the
+ * specific AAA phase.
+ */
+#define AP_FCGI_APACHE_ROLE_AUTHENTICATOR_STR  "AUTHENTICATOR"
+#define AP_FCGI_APACHE_ROLE_AUTHORIZER_STR     "AUTHORIZER"
+#define AP_FCGI_APACHE_ROLE_ACCESS_CHECKER_STR "ACCESS_CHECKER"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* !APACHE_UTIL_FCGI_H */
+/** @} */

Modified: httpd/httpd/trunk/libhttpd.dsp
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/libhttpd.dsp?rev=1513454&r1=1513453&r2=1513454&view=diff
==============================================================================
--- httpd/httpd/trunk/libhttpd.dsp (original)
+++ httpd/httpd/trunk/libhttpd.dsp Tue Aug 13 12:16:39 2013
@@ -569,6 +569,14 @@ SOURCE=.\server\util_expr_parse.c
 # End Source File
 # Begin Source File
 
+SOURCE=.\server\util_fcgi.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\include\util_fcgi.h
+# End Source File
+# Begin Source File
+
 SOURCE=.\server\util_filter.c
 # End Source File
 # Begin Source File

Modified: httpd/httpd/trunk/server/Makefile.in
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/server/Makefile.in?rev=1513454&r1=1513453&r2=1513454&view=diff
==============================================================================
--- httpd/httpd/trunk/server/Makefile.in (original)
+++ httpd/httpd/trunk/server/Makefile.in Tue Aug 13 12:16:39 2013
@@ -7,7 +7,7 @@ SUBDIRS = mpm
 
 LTLIBRARY_NAME    = libmain.la
 LTLIBRARY_SOURCES = \
-	config.c log.c main.c vhost.c util.c \
+	config.c log.c main.c vhost.c util.c util_fcgi.c \
 	util_script.c util_md5.c util_cfgtree.c util_ebcdic.c util_time.c \
 	connection.c listen.c util_mutex.c mpm_common.c mpm_unix.c \
 	util_charset.c util_cookies.c util_debug.c util_xml.c \

Added: httpd/httpd/trunk/server/util_fcgi.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/server/util_fcgi.c?rev=1513454&view=auto
==============================================================================
--- httpd/httpd/trunk/server/util_fcgi.c (added)
+++ httpd/httpd/trunk/server/util_fcgi.c Tue Aug 13 12:16:39 2013
@@ -0,0 +1,287 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+#include "httpd.h"
+#include "http_core.h"
+#include "http_log.h"
+#include "util_fcgi.h"
+
+/* we know core's module_index is 0 */
+#undef APLOG_MODULE_INDEX
+#define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX
+
+AP_DECLARE(void) ap_fcgi_header_to_array(ap_fcgi_header *h,
+                                         unsigned char a[])
+{
+    a[AP_FCGI_HDR_VERSION_OFFSET]        = h->version;
+    a[AP_FCGI_HDR_TYPE_OFFSET]           = h->type;
+    a[AP_FCGI_HDR_REQUEST_ID_B1_OFFSET]  = h->requestIdB1;
+    a[AP_FCGI_HDR_REQUEST_ID_B0_OFFSET]  = h->requestIdB0;
+    a[AP_FCGI_HDR_CONTENT_LEN_B1_OFFSET] = h->contentLengthB1;
+    a[AP_FCGI_HDR_CONTENT_LEN_B0_OFFSET] = h->contentLengthB0;
+    a[AP_FCGI_HDR_PADDING_LEN_OFFSET]    = h->paddingLength;
+    a[AP_FCGI_HDR_RESERVED_OFFSET]       = h->reserved;
+}
+
+AP_DECLARE(void) ap_fcgi_header_from_array(ap_fcgi_header *h,
+                                           unsigned char a[])
+{
+    h->version         = a[AP_FCGI_HDR_VERSION_OFFSET];
+    h->type            = a[AP_FCGI_HDR_TYPE_OFFSET];
+    h->requestIdB1     = a[AP_FCGI_HDR_REQUEST_ID_B1_OFFSET];
+    h->requestIdB0     = a[AP_FCGI_HDR_REQUEST_ID_B0_OFFSET];
+    h->contentLengthB1 = a[AP_FCGI_HDR_CONTENT_LEN_B1_OFFSET];
+    h->contentLengthB0 = a[AP_FCGI_HDR_CONTENT_LEN_B0_OFFSET];
+    h->paddingLength   = a[AP_FCGI_HDR_PADDING_LEN_OFFSET];
+    h->reserved        = a[AP_FCGI_HDR_RESERVED_OFFSET];
+}
+
+AP_DECLARE(void) ap_fcgi_header_fields_from_array(unsigned char *version,
+                                                  unsigned char *type,
+                                                  apr_uint16_t *request_id,
+                                                  apr_uint16_t *content_len,
+                                                  unsigned char *padding_len,
+                                                  unsigned char a[])
+{
+    *version         = a[AP_FCGI_HDR_VERSION_OFFSET];
+    *type            = a[AP_FCGI_HDR_TYPE_OFFSET];
+    *request_id      = (a[AP_FCGI_HDR_REQUEST_ID_B1_OFFSET] << 8)
+                     +  a[AP_FCGI_HDR_REQUEST_ID_B0_OFFSET];
+    *content_len     = (a[AP_FCGI_HDR_CONTENT_LEN_B1_OFFSET] << 8)
+                     +  a[AP_FCGI_HDR_CONTENT_LEN_B0_OFFSET];
+    *padding_len     = a[AP_FCGI_HDR_PADDING_LEN_OFFSET];
+}
+
+AP_DECLARE(void) ap_fcgi_begin_request_body_to_array(ap_fcgi_begin_request_body *h,
+                                                     unsigned char a[])
+{
+    a[AP_FCGI_BRB_ROLEB1_OFFSET]    = h->roleB1;
+    a[AP_FCGI_BRB_ROLEB0_OFFSET]    = h->roleB0;
+    a[AP_FCGI_BRB_FLAGS_OFFSET]     = h->flags;
+    a[AP_FCGI_BRB_RESERVED0_OFFSET] = h->reserved[0];
+    a[AP_FCGI_BRB_RESERVED1_OFFSET] = h->reserved[1];
+    a[AP_FCGI_BRB_RESERVED2_OFFSET] = h->reserved[2];
+    a[AP_FCGI_BRB_RESERVED3_OFFSET] = h->reserved[3];
+    a[AP_FCGI_BRB_RESERVED4_OFFSET] = h->reserved[4];
+}
+
+AP_DECLARE(void) ap_fcgi_fill_in_header(ap_fcgi_header *header,
+                                        unsigned char type,
+                                        apr_uint16_t request_id,
+                                        apr_uint16_t content_len,
+                                        unsigned char padding_len)
+{
+    header->version = AP_FCGI_VERSION_1;
+
+    header->type = type;
+
+    header->requestIdB1 = ((request_id >> 8) & 0xff);
+    header->requestIdB0 = ((request_id) & 0xff);
+
+    header->contentLengthB1 = ((content_len >> 8) & 0xff);
+    header->contentLengthB0 = ((content_len) & 0xff);
+
+    header->paddingLength = padding_len;
+
+    header->reserved = 0;
+}
+
+AP_DECLARE(void) ap_fcgi_fill_in_request_body(ap_fcgi_begin_request_body *brb,
+                                              int role,
+                                              unsigned char flags)
+{
+    brb->roleB1 = ((role >> 8) & 0xff);
+    brb->roleB0 = (role & 0xff);
+    brb->flags = flags;
+    brb->reserved[0] = 0;
+    brb->reserved[1] = 0;
+    brb->reserved[2] = 0;
+    brb->reserved[3] = 0;
+    brb->reserved[4] = 0;
+}
+
+AP_DECLARE(apr_size_t) ap_fcgi_encoded_env_len(apr_table_t *env,
+                                               apr_size_t maxlen,
+                                               int *starting_elem)
+{
+    const apr_array_header_t *envarr;
+    const apr_table_entry_t *elts;
+    apr_size_t envlen, actualenvlen;
+    int i;
+
+    if (maxlen > AP_FCGI_MAX_CONTENT_LEN) {
+        maxlen = AP_FCGI_MAX_CONTENT_LEN;
+    }
+
+    envarr = apr_table_elts(env);
+    elts = (const apr_table_entry_t *) envarr->elts;
+
+    /* envlen - speculative, may overflow the limit
+     * actualenvlen - len required without overflowing
+     */
+    envlen = actualenvlen = 0;
+    for (i = *starting_elem; i < envarr->nelts; ) {
+        apr_size_t keylen, vallen;
+
+        if (!elts[i].key) {
+            (*starting_elem)++;
+            i++;
+            continue;
+        }
+
+        keylen = strlen(elts[i].key);
+
+        if (keylen >> 7 == 0) {
+            envlen += 1;
+        }
+        else {
+            envlen += 4;
+        }
+
+        envlen += keylen;
+
+        vallen = strlen(elts[i].val);
+
+        if (vallen >> 7 == 0) {
+            envlen += 1;
+        }
+        else {
+            envlen += 4;
+        }
+
+        envlen += vallen;
+
+        if (envlen > maxlen) {
+            break;
+        }
+
+        actualenvlen = envlen;
+        (*starting_elem)++;
+        i++;
+    }
+
+    return actualenvlen;
+}
+
+AP_DECLARE(apr_status_t) ap_fcgi_encode_env(request_rec *r,
+                                            apr_table_t *env,
+                                            void *buffer,
+                                            apr_size_t buflen,
+                                            int *starting_elem)
+{
+    apr_status_t rv = APR_SUCCESS;
+    const apr_array_header_t *envarr;
+    const apr_table_entry_t *elts;
+    char *itr;
+    int i;
+
+    envarr = apr_table_elts(env);
+    elts = (const apr_table_entry_t *) envarr->elts;
+
+    itr = buffer;
+
+    for (i = *starting_elem; i < envarr->nelts; ) {
+        apr_size_t keylen, vallen;
+
+        if (!elts[i].key) {
+            (*starting_elem)++;
+            i++;
+            continue;
+        }
+
+        keylen = strlen(elts[i].key);
+
+        if (keylen >> 7 == 0) {
+            if (buflen < 1) {
+                rv = APR_ENOSPC; /* overflow */
+                break;
+            }
+            itr[0] = keylen & 0xff;
+            itr += 1;
+            buflen -= 1;
+        }
+        else {
+            if (buflen < 4) {
+                rv = APR_ENOSPC; /* overflow */
+                break;
+            }
+            itr[0] = ((keylen >> 24) & 0xff) | 0x80;
+            itr[1] = ((keylen >> 16) & 0xff);
+            itr[2] = ((keylen >> 8) & 0xff);
+            itr[3] = ((keylen) & 0xff);
+            itr += 4;
+            buflen -= 4;
+        }
+
+        vallen = strlen(elts[i].val);
+
+        if (vallen >> 7 == 0) {
+            if (buflen < 1) {
+                rv = APR_ENOSPC; /* overflow */
+                break;
+            }
+            itr[0] = vallen & 0xff;
+            itr += 1;
+            buflen -= 1;
+        }
+        else {
+            if (buflen < 4) {
+                rv = APR_ENOSPC; /* overflow */
+                break;
+            }
+            itr[0] = ((vallen >> 24) & 0xff) | 0x80;
+            itr[1] = ((vallen >> 16) & 0xff);
+            itr[2] = ((vallen >> 8) & 0xff);
+            itr[3] = ((vallen) & 0xff);
+            itr += 4;
+            buflen -= 4;
+        }
+
+        if (buflen < keylen) {
+            rv = APR_ENOSPC; /* overflow */
+            break;
+        }
+        memcpy(itr, elts[i].key, keylen);
+        itr += keylen;
+        buflen -= keylen;
+
+        if (buflen < vallen) {
+            rv = APR_ENOSPC; /* overflow */
+            break;
+        }
+        memcpy(itr, elts[i].val, vallen);
+        itr += vallen;
+
+        if (buflen == vallen) {
+            (*starting_elem)++;
+            i++;
+            break; /* filled up predicted space, as expected */
+        }
+
+        buflen -= vallen;
+
+        (*starting_elem)++;
+        i++;
+    }
+
+    if (rv != APR_SUCCESS) {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+                      APLOGNO() "ap_fcgi_encode_env: out of space "
+                      "encoding environment");
+    }
+
+    return rv;
+}