diff options
Diffstat (limited to 'src/core/transport/http/server/apache2/apache2_stream.c')
-rw-r--r-- | src/core/transport/http/server/apache2/apache2_stream.c | 292 |
1 files changed, 292 insertions, 0 deletions
diff --git a/src/core/transport/http/server/apache2/apache2_stream.c b/src/core/transport/http/server/apache2/apache2_stream.c new file mode 100644 index 0000000..5e189a7 --- /dev/null +++ b/src/core/transport/http/server/apache2/apache2_stream.c @@ -0,0 +1,292 @@ +/* + * 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 <string.h> +#include <stdlib.h> +#include "apache2_stream.h" +#include <http_protocol.h> + +typedef struct apache2_stream_impl +{ + axutil_stream_t stream; + axutil_stream_type_t stream_type; + request_rec *request; +} apache2_stream_impl_t; + +#define AXIS2_INTF_TO_IMPL(stream) ((apache2_stream_impl_t *)(stream)) + +axutil_stream_type_t AXIS2_CALL +apache2_stream_get_type( + axutil_stream_t * stream, + const axutil_env_t * env); + +int AXIS2_CALL apache2_stream_write( + axutil_stream_t * stream, + const axutil_env_t * env, + const void *buffer, + size_t count); + +int AXIS2_CALL apache2_stream_read( + axutil_stream_t * stream, + const axutil_env_t * env, + void *buffer, + size_t count); + +static int AXIS2_CALL +apache2_stream_skip( + axutil_stream_t * stream, + const axutil_env_t * env, + int count); + +int AXIS2_CALL apache2_stream_get_char( + axutil_stream_t * stream, + const axutil_env_t * env); + +static apr_size_t +apache2_ap_get_client_block( + request_rec *r, + char* buffer, + apr_size_t bufsiz); + +AXIS2_EXTERN axutil_stream_t *AXIS2_CALL +axutil_stream_create_apache2( + const axutil_env_t * env, + request_rec * request) +{ + apache2_stream_impl_t *stream_impl = NULL; + AXIS2_ENV_CHECK(env, NULL); + AXIS2_PARAM_CHECK(env->error, request, NULL); + + stream_impl = (apache2_stream_impl_t *)AXIS2_MALLOC(env->allocator, + sizeof(apache2_stream_impl_t)); + + if(!stream_impl) + { + AXIS2_ERROR_SET(env->error, AXIS2_ERROR_NO_MEMORY, AXIS2_FAILURE); + return NULL; + } + + memset(&(stream_impl->stream), 0, sizeof(axutil_stream_t)); + + stream_impl->request = request; + stream_impl->stream_type = AXIS2_STREAM_MANAGED; + + axutil_stream_set_read(&(stream_impl->stream), env, apache2_stream_read); + axutil_stream_set_write(&(stream_impl->stream), env, apache2_stream_write); + axutil_stream_set_skip(&(stream_impl->stream), env, apache2_stream_skip); + + return &(stream_impl->stream); +} + +int AXIS2_CALL +apache2_stream_read( + axutil_stream_t * stream, + const axutil_env_t * env, + void *buffer, + size_t count) +{ + apache2_stream_impl_t *stream_impl = NULL; + size_t read = 0; + size_t len = 0; + + AXIS2_ENV_CHECK(env, AXIS2_CRITICAL_FAILURE); + + stream_impl = AXIS2_INTF_TO_IMPL(stream); + + while(count - len > 0) + { + read = apache2_ap_get_client_block(stream_impl->request, (char *) buffer + len, + count - len); + if(read > 0) + { + len += read; + } + else + { + break; + } + } + + return (int)len; + /* We are sure that the difference lies within the int range */ +} + +int AXIS2_CALL +apache2_stream_write( + axutil_stream_t * stream, + const axutil_env_t * env, + const void *buf, + size_t count) +{ + apache2_stream_impl_t *stream_impl = NULL; + axis2_char_t *buffer = NULL; + AXIS2_ENV_CHECK(env, AXIS2_CRITICAL_FAILURE); + AXIS2_PARAM_CHECK(env->error, buf, AXIS2_FAILURE); + stream_impl = AXIS2_INTF_TO_IMPL(stream); + buffer = (axis2_char_t *)buf; + if(count <= 0) + { + return (int)count; + /* We are sure that the difference lies within the int range */ + } + /* assume that buffer is not null terminated */ + return ap_rwrite(buffer, (int)count, stream_impl->request); + /* We are sure that the difference lies within the int range */ +} + +static int AXIS2_CALL +apache2_stream_skip( + axutil_stream_t * stream, + const axutil_env_t * env, + int count) +{ + apache2_stream_impl_t *stream_impl = NULL; + axis2_char_t *tmp_buffer = NULL; + apr_size_t len = -1; + AXIS2_ENV_CHECK(env, AXIS2_CRITICAL_FAILURE); + stream_impl = AXIS2_INTF_TO_IMPL(stream); + + tmp_buffer = AXIS2_MALLOC(env->allocator, count * sizeof(axis2_char_t)); + if(tmp_buffer == NULL) + { + AXIS2_ERROR_SET(env->error, AXIS2_ERROR_NO_MEMORY, AXIS2_FAILURE); + return -1; + } + len = apache2_ap_get_client_block(stream_impl->request, tmp_buffer, count); + AXIS2_FREE(env->allocator, tmp_buffer); + return (int)len; + +} + +int AXIS2_CALL +apache2_stream_get_char( + axutil_stream_t * stream, + const axutil_env_t * env) +{ + int ret = -1; + AXIS2_ENV_CHECK(env, AXIS2_CRITICAL_FAILURE); + + return ret; +} + +axutil_stream_type_t AXIS2_CALL +apache2_stream_get_type( + axutil_stream_t * stream, + const axutil_env_t * env) +{ + AXIS2_ENV_CHECK(env, AXIS2_CRITICAL_FAILURE); + return AXIS2_INTF_TO_IMPL(stream)->stream_type; +} + +/* + * This is a re-write of get_client_block found in http_filters.c in httpd + * which does not work when dealing with compressed payloads (or any other input + * filters that could potentially return 0 bytes of filtered data and not be at + * the end of the stream). + * get_client_block is called in a loop to get the request message body. + * This is quite simple if the client includes a content-length + * (the normal case), but gets messy if the body is chunked. Note that + * r->remaining is used to maintain state across calls and that + * r->read_length is the total number of bytes given to the caller + * across all invocations. It is messy because we have to be careful not + * to read past the data provided by the client, since these reads block. + * Returns 0 on End-of-body, -1 on error or premature chunk end. + * + */ +static apr_size_t +apache2_ap_get_client_block( + request_rec *r, + char *buffer, + apr_size_t bufsiz) +{ + apr_status_t rv; + apr_bucket_brigade *bb; + int loop = 1; + apr_size_t origBufSize = bufsiz; + + if (r->remaining < 0 || (!r->read_chunked && r->remaining == 0)) { + return 0; + } + + bb = apr_brigade_create(r->pool, r->connection->bucket_alloc); + if (bb == NULL) { + r->connection->keepalive = AP_CONN_CLOSE; + return -1; + } + + /* we need to loop until the input filters (if any) give us data */ + while (loop) { + rv = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES, + APR_BLOCK_READ, bufsiz); + + /* We lose the failure code here. This is why ap_get_client_block should + * not be used. + */ + if (rv != APR_SUCCESS) { + /* if we actually fail here, we want to just return and + * stop trying to read data from the client. + */ + r->connection->keepalive = AP_CONN_CLOSE; + apr_brigade_destroy(bb); + return -1; + } + + /* If this fails, it means that a filter is written incorrectly and that + * it needs to learn how to properly handle APR_BLOCK_READ requests by + * returning data when requested. + */ + AP_DEBUG_ASSERT(!APR_BRIGADE_EMPTY(bb)); + + /* Check to see if EOS in the brigade. + * + * If so, we have to leave a nugget for the *next* ap_get_client_block + * call to return 0. + */ + if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) { + if (r->read_chunked) { + r->remaining = -1; + } else { + r->remaining = 0; + } + } + + rv = apr_brigade_flatten(bb, buffer, &bufsiz); + if (rv != APR_SUCCESS) { + apr_brigade_destroy(bb); + return -1; + } + + /* XXX yank me? */ + r->read_length += bufsiz; + + /* it is possible that the entire bucket brigade is exhausted, but no data + * has been produced by the input filter (mod_deflate, for example).... + * in this scenario, we really need to keep looping + */ + if (bufsiz != 0 || r->remaining <= 0) { + loop = 0; + apr_brigade_destroy(bb); + } else { + if (bufsiz == 0) { + bufsiz = origBufSize; + } + } + + } + + return (long)bufsiz; +} |