]> granicus.if.org Git - php/commitdiff
PHP filters and Apache 2 aren't quite a perfect match yet, so we have
authorAaron Bannert <aaron@php.net>
Thu, 11 Apr 2002 19:27:27 +0000 (19:27 +0000)
committerAaron Bannert <aaron@php.net>
Thu, 11 Apr 2002 19:27:27 +0000 (19:27 +0000)
to do some trickery with the server_context to make sure it is always
valid within the current thread.

This patch makes sure the server_context is created in apache's
post_read_request hook phase, and then registeres a cleanup that
will NULL out the server context when the request goes out of scope.
Then, inside the output filters, if the server_context is null we
throw an error. Finally, instead of saving the output filter in
the server_context, now we store the entire request_rec pointer
in there.

POST bodies appear to be working now, although they are very inefficient.
The input filter is still just realloc()ing for whatever data comes
down the input pipe, and then sending this to PHP. This means that
we are doing some really nasty memory management on big POST bodies.
For now this it allows for unlimited input bodies, which means that
a big POST could potentially DoS a box by making it run out of memory.
We might want to put a limit on here just in case, at least until
we figure out how to consume input data more efficiently into php.

sapi/apache2filter/php_apache.h
sapi/apache2filter/sapi_apache2.c

index e758a860e4c06766622b8130f2cca185af009686..cc60ad22931a6830337d1a594b64b0c6570bce8e 100644 (file)
@@ -21,7 +21,7 @@
 
 typedef struct php_struct {
        int state;
-       ap_filter_t *f;
+       request_rec *r;
        /* Length of post_data buffer */
        int post_len;
        /* Index for reading from buffer */
index f0d3b6d4cf98ac58e02cc66f1df68d5ec3365618..6b44c7ec1d57bbf48217b06345045bf5570d71a2 100644 (file)
@@ -49,14 +49,16 @@ php_apache_sapi_ub_write(const char *str, uint str_length TSRMLS_DC)
        apr_bucket *b;
        apr_bucket_brigade *bb;
        apr_bucket_alloc_t *ba;
+       ap_filter_t *f; /* output filters */
        php_struct *ctx;
 
        ctx = SG(server_context);
+       f = ctx->r->output_filters;
 
        if (str_length == 0) return 0;
        
-       ba = ctx->f->c->bucket_alloc;
-       bb = apr_brigade_create(ctx->f->r->pool, ba);
+       ba = f->c->bucket_alloc;
+       bb = apr_brigade_create(ctx->r->pool, ba);
 
        b = apr_bucket_transient_create(str, str_length, ba);
        APR_BRIGADE_INSERT_TAIL(bb, b);
@@ -68,7 +70,7 @@ php_apache_sapi_ub_write(const char *str, uint str_length TSRMLS_DC)
        b = apr_bucket_flush_create(ba);
        APR_BRIGADE_INSERT_TAIL(bb, b);
 
-       if (ap_pass_brigade(ctx->f->next, bb) != APR_SUCCESS) {
+       if (ap_pass_brigade(f->next, bb) != APR_SUCCESS) {
                php_handle_aborted_connection();
        }
        
@@ -78,9 +80,13 @@ php_apache_sapi_ub_write(const char *str, uint str_length TSRMLS_DC)
 static int
 php_apache_sapi_header_handler(sapi_header_struct *sapi_header, sapi_headers_struct *sapi_headers TSRMLS_DC)
 {
-       php_struct *ctx = SG(server_context);
+       php_struct *ctx;
+       ap_filter_t *f;
        char *val;
 
+       ctx = SG(server_context);
+       f = ctx->r->output_filters;
+
        val = strchr(sapi_header->header, ':');
 
        if (!val) return 0;
@@ -92,11 +98,11 @@ php_apache_sapi_header_handler(sapi_header_struct *sapi_header, sapi_headers_str
        } while (*val == ' ');
 
        if (!strcasecmp(sapi_header->header, "content-type"))
-               ctx->f->r->content_type = apr_pstrdup(ctx->f->r->pool, val);
+               ctx->r->content_type = apr_pstrdup(ctx->r->pool, val);
        else if (sapi_header->replace)
-               apr_table_set(ctx->f->r->headers_out, sapi_header->header, val);
+               apr_table_set(ctx->r->headers_out, sapi_header->header, val);
        else
-               apr_table_add(ctx->f->r->headers_out, sapi_header->header, val);
+               apr_table_add(ctx->r->headers_out, sapi_header->header, val);
        
        sapi_free_header(sapi_header);
 
@@ -108,7 +114,7 @@ php_apache_sapi_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
 {
        php_struct *ctx = SG(server_context);
 
-       ctx->f->r->status = SG(sapi_headers).http_response_code;
+       ctx->r->status = SG(sapi_headers).http_response_code;
 
        return SAPI_HEADER_SENT_SUCCESSFULLY;
 }
@@ -140,7 +146,7 @@ php_apache_sapi_read_cookies(TSRMLS_D)
        php_struct *ctx = SG(server_context);
        const char *http_cookie;
 
-       http_cookie = apr_table_get(ctx->f->r->headers_in, "cookie");
+       http_cookie = apr_table_get(ctx->r->headers_in, "cookie");
 
        /* The SAPI interface should use 'const char *' */
        return (char *) http_cookie;
@@ -150,7 +156,7 @@ static void
 php_apache_sapi_register_variables(zval *track_vars_array TSRMLS_DC)
 {
        php_struct *ctx = SG(server_context);
-       const apr_array_header_t *arr = apr_table_elts(ctx->f->r->subprocess_env);
+       const apr_array_header_t *arr = apr_table_elts(ctx->r->subprocess_env);
        char *key, *val;
        
        APR_ARRAY_FOREACH_OPEN(arr, key, val)
@@ -158,30 +164,37 @@ php_apache_sapi_register_variables(zval *track_vars_array TSRMLS_DC)
                php_register_variable(key, val, track_vars_array TSRMLS_CC);
        APR_ARRAY_FOREACH_CLOSE()
                
-       php_register_variable("PHP_SELF", ctx->f->r->uri, track_vars_array TSRMLS_CC);
+       php_register_variable("PHP_SELF", ctx->r->uri, track_vars_array TSRMLS_CC);
 }
 
 static void
 php_apache_sapi_flush(void *server_context)
 {
-       php_struct *ctx = server_context;
+       php_struct *ctx;
        apr_bucket_brigade *bb;
        apr_bucket_alloc_t *ba;
        apr_bucket *b;
+       ap_filter_t *f; /* output filters */
+
+       ctx = server_context;
 
+       /* If we haven't registered a server_context yet,
+        * then don't bother flushing. */
        if (!server_context)
                return;
     
+       f = ctx->r->output_filters;
+
        /* Send a flush bucket down the filter chain. The current default
         * handler seems to act on the first flush bucket, but ignores
         * all further flush buckets.
         */
        
-       ba = ctx->f->r->connection->bucket_alloc;
-       bb = apr_brigade_create(ctx->f->r->pool, ba);
+       ba = ctx->r->connection->bucket_alloc;
+       bb = apr_brigade_create(ctx->r->pool, ba);
        b = apr_bucket_flush_create(ba);
        APR_BRIGADE_INSERT_TAIL(bb, b);
-       if (ap_pass_brigade(ctx->f->next, bb) != APR_SUCCESS) {
+       if (ap_pass_brigade(f->next, bb) != APR_SUCCESS) {
                php_handle_aborted_connection();
        }
 }
@@ -198,7 +211,7 @@ static void php_apache_sapi_log_message(char *msg)
         * line.  Not sure if this is correct, but it mirrors what happens
         * with Apache 1.3 -- rbb
         */
-       ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO | APLOG_STARTUP, 0, ctx->f->r->server, "%s", msg);
+       ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO | APLOG_STARTUP, 0, ctx->r->server, "%s", msg);
 }
 
 static sapi_module_struct apache2_sapi_module = {
@@ -252,9 +265,11 @@ static int php_input_filter(ap_filter_t *f, apr_bucket_brigade *bb,
                return ap_get_brigade(f->next, bb, mode, block, readbytes);
        }
 
-       if (SG(server_context) == NULL) {
-               /* Initialize filter context */
-               SG(server_context) = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx));
+       ctx = SG(server_context);
+       if (ctx == NULL) {
+               ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, f->r,
+                                        "php failed to get server context");
+               return HTTP_INTERNAL_SERVER_ERROR;
        }
 
        if ((rv = ap_get_brigade(f->next, bb, mode, block, readbytes)) != APR_SUCCESS) {
@@ -328,10 +343,11 @@ static int php_output_filter(ap_filter_t *f, apr_bucket_brigade *bb)
        ap_add_common_vars(f->r);
        ap_add_cgi_vars(f->r);
 
-       if (SG(server_context) == NULL) {
-               /* Initialize filter context */
-               SG(server_context) = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx));
-               ctx->f = f;
+       ctx = SG(server_context);
+       if (ctx == NULL) {
+               ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, f->r,
+                                        "php failed to get server context");
+        return HTTP_INTERNAL_SERVER_ERROR;
        }
 
        if (ctx->request_processed) {
@@ -454,10 +470,40 @@ static void php_insert_filter(request_rec *r)
        }
 }
 
+static apr_status_t php_server_context_cleanup(void *data_)
+{
+       void **data = data_;
+       *data = NULL;
+       return APR_SUCCESS;
+}
+
+static int php_post_read_request(request_rec *r)
+{
+       php_struct *ctx;
+       TSRMLS_FETCH();
+
+       /* Initialize filter context */
+       SG(server_context) = ctx = apr_pcalloc(r->pool, sizeof(*ctx));
+
+       /* register a cleanup so we clear out the SG(server_context)
+        * after each request. Note: We pass in the pointer to the
+        * server_context in case this is handled by a different thread. */
+       apr_pool_cleanup_register(r->pool, (void *)&SG(server_context),
+                                                         php_server_context_cleanup,
+                                                         apr_pool_cleanup_null);
+
+       /* Save the entire request, so we can get the input or output
+        * filters if we need them. */
+       ctx->r = r;
+
+       return OK;
+}
+
 static void php_register_hook(apr_pool_t *p)
 {
        ap_hook_post_config(php_apache_server_startup, NULL, NULL, APR_HOOK_MIDDLE);
        ap_hook_insert_filter(php_insert_filter, NULL, NULL, APR_HOOK_MIDDLE);
+    ap_hook_post_read_request(php_post_read_request, NULL, NULL, APR_HOOK_MIDDLE);
        ap_register_output_filter("PHP", php_output_filter, AP_FTYPE_RESOURCE);
        ap_register_input_filter("PHP", php_input_filter, AP_FTYPE_RESOURCE);
 }