Author: rhuijben
Date: Tue Nov 17 09:15:05 2015
New Revision: 1714735
URL: http://svn.apache.org/viewvc?rev=1714735&view=rev
Log:
Add some verification to the protocol design used for http2 handling by
adding a fcgi protocol implementation next to it. This should eventually
allow using the serf httpd engine as a fcgi backend via mod_proxy_fcgi.
This patch adds the initial bucket support.
* buckets/fcgi_buckets.c
New file.
* protocols/fcgi_buckets.h
New file.
Added:
serf/trunk/buckets/fcgi_buckets.c (with props)
serf/trunk/protocols/fcgi_buckets.h (with props)
Added: serf/trunk/buckets/fcgi_buckets.c
URL:
http://svn.apache.org/viewvc/serf/trunk/buckets/fcgi_buckets.c?rev=1714735&view=auto
==============================================================================
--- serf/trunk/buckets/fcgi_buckets.c (added)
+++ serf/trunk/buckets/fcgi_buckets.c Tue Nov 17 09:15:05 2015
@@ -0,0 +1,329 @@
+/* ====================================================================
+ * 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 <stdlib.h>
+
+#include <apr_pools.h>
+
+#include "serf.h"
+#include "serf_bucket_util.h"
+#include "serf_private.h"
+
+#include "protocols/fcgi_buckets.h"
+
+#define FCGI_RECORD_SIZE 8
+
+typedef struct fcgi_unframe_ctx_t
+{
+ serf_bucket_t *stream;
+
+ serf_bucket_end_of_frame_t end_of_frame;
+ void *end_of_frame_baton;
+
+ apr_size_t record_remaining;
+ apr_size_t payload_remaining;
+ apr_size_t pad_remaining;
+
+ apr_uint16_t frame_type;
+ apr_uint16_t streamid;
+
+ unsigned char buffer[FCGI_RECORD_SIZE];
+} fcgi_unframe_ctx_t;
+
+serf_bucket_t * serf__bucket_fcgi_unframe_create(serf_bucket_t *stream,
+ serf_bucket_alloc_t
*allocator)
+{
+ fcgi_unframe_ctx_t *ctx;
+
+ ctx = serf_bucket_mem_calloc(allocator, sizeof(*ctx));
+ ctx->stream = stream;
+ ctx->record_remaining = FCGI_RECORD_SIZE;
+
+ return serf_bucket_create(&serf_bucket_type__fcgi_unframe, allocator, ctx);
+}
+
+void serf__bucket_fcgi_unframe_set_eof(serf_bucket_t *bucket,
+ serf_bucket_end_of_frame_t end_of_frame,
+ void *end_of_frame_baton)
+{
+ fcgi_unframe_ctx_t *ctx = bucket->data;
+
+ ctx->end_of_frame = end_of_frame;
+ ctx->end_of_frame_baton = end_of_frame_baton;
+}
+
+apr_status_t serf__bucket_fcgi_unframe_read_info(serf_bucket_t *bucket,
+ apr_uint16_t *stream_id,
+ apr_uint16_t *frame_type)
+{
+ fcgi_unframe_ctx_t *ctx = bucket->data;
+ const char *data;
+ apr_size_t len;
+ apr_status_t status;
+
+ if (ctx->record_remaining == 0)
+ {
+ if (stream_id)
+ *stream_id = ctx->streamid;
+ if (frame_type)
+ *frame_type = ctx->frame_type;
+
+ return APR_SUCCESS;
+ }
+
+ status = serf_bucket_read(ctx->stream, ctx->record_remaining, &data, &len);
+ if (!SERF_BUCKET_READ_ERROR(status))
+ {
+ const unsigned char *header;
+
+ if (len < FCGI_RECORD_SIZE)
+ {
+ memcpy(ctx->buffer + FCGI_RECORD_SIZE - ctx->record_remaining,
+ data, len);
+
+ ctx->record_remaining -= len;
+ header = ctx->buffer;
+ }
+ else
+ {
+ header = (const void *)data;
+ ctx->record_remaining = 0;
+ }
+
+ if (ctx->record_remaining == 0)
+ {
+ /* We combine version and frametype in a single value */
+ ctx->frame_type = (header[0] << 8) | header[1];
+ ctx->streamid = (header[2] << 8) | header[3];
+ ctx->payload_remaining = (header[4] << 8) | header[5];
+ /* header[6] is reserved */
+ ctx->pad_remaining = header[7];
+
+ /* Fill output arguments if necessary */
+ if (stream_id)
+ *stream_id = ctx->streamid;
+ if (frame_type)
+ *frame_type = ctx->frame_type;
+
+ status = (ctx->payload_remaining == 0) ? APR_EOF
+ : APR_SUCCESS;
+
+ /* If we hava a zero-length frame we have to call the eof callback
+ now, as the read operations will just shortcut to APR_EOF */
+ if (ctx->payload_remaining == 0 && ctx->end_of_frame)
+ {
+ apr_status_t cb_status;
+
+ cb_status = (*ctx->end_of_frame)(ctx->end_of_frame_baton,
+ bucket);
+
+ ctx->end_of_frame = NULL;
+
+ if (SERF_BUCKET_READ_ERROR(cb_status))
+ status = cb_status;
+ }
+ }
+ else if (APR_STATUS_IS_EOF(status))
+ {
+ /* Reading frame failed because we couldn't read the header. Report
+ a read failure instead of semi-success */
+ status = SERF_ERROR_TRUNCATED_STREAM;
+ }
+ else if (!status)
+ status = APR_EAGAIN;
+
+ }
+ return status;
+}
+
+static apr_status_t serf_fcgi_unframe_read(serf_bucket_t *bucket,
+ apr_size_t requested,
+ const char **data,
+ apr_size_t *len)
+{
+ fcgi_unframe_ctx_t *ctx = bucket->data;
+ apr_status_t status;
+
+ status = serf__bucket_fcgi_unframe_read_info(bucket, NULL, NULL);
+ if (status)
+ {
+ *len = 0;
+ return status;
+ }
+
+ if (requested > ctx->payload_remaining)
+ requested = ctx->payload_remaining;
+
+ if (requested == ctx->payload_remaining && ctx->pad_remaining)
+ requested += ctx->pad_remaining;
+
+ status = serf_bucket_read(ctx->stream, requested, data, len);
+ if (!SERF_BUCKET_READ_ERROR(status)) {
+ if (*len >= ctx->payload_remaining) {
+ ctx->pad_remaining -= (*len - ctx->payload_remaining);
+ *len = ctx->payload_remaining;
+ ctx->payload_remaining = 0;
+ }
+ else {
+ ctx->payload_remaining -= *len;
+ }
+
+ if (!ctx->payload_remaining && !ctx->pad_remaining) {
+ if (ctx->end_of_frame) {
+ status = (*ctx->end_of_frame)(ctx->end_of_frame_baton,
+ bucket);
+ ctx->end_of_frame = NULL;
+ }
+
+ if (!SERF_BUCKET_READ_ERROR(status))
+ status = APR_EOF;
+ }
+ else if (APR_STATUS_IS_EOF(status))
+ status = SERF_ERROR_TRUNCATED_STREAM;
+ }
+
+ return status;
+}
+
+static apr_status_t serf_fcgi_unframe_peek(serf_bucket_t *bucket,
+ const char **data,
+ apr_size_t *len)
+{
+ fcgi_unframe_ctx_t *ctx = bucket->data;
+ apr_status_t status;
+
+ status = serf__bucket_fcgi_unframe_read_info(bucket, NULL, NULL);
+
+ if (status)
+ {
+ *len = 0;
+ return status;
+ }
+
+ status = serf_bucket_peek(ctx->stream, data, len);
+ if (!SERF_BUCKET_READ_ERROR(status))
+ {
+ if (*len > ctx->payload_remaining)
+ *len = ctx->payload_remaining;
+ }
+
+ return status;
+}
+
+static apr_uint64_t serf_fcgi_unframe_get_remaining(serf_bucket_t *bucket)
+{
+ fcgi_unframe_ctx_t *ctx = bucket->data;
+
+ if (ctx->record_remaining)
+ return SERF_LENGTH_UNKNOWN;
+ else
+ return ctx->payload_remaining;
+}
+
+static apr_status_t serf_fcgi_unframe_set_config(serf_bucket_t *bucket,
+ serf_config_t *config)
+{
+ fcgi_unframe_ctx_t *ctx = bucket->data;
+
+ return serf_bucket_set_config(ctx->stream, config);
+}
+
+extern const serf_bucket_type_t serf_bucket_type__fcgi_unframe =
+{
+ "FCGI-UNFRAME",
+ serf_fcgi_unframe_read,
+ serf_default_readline,
+ serf_default_read_iovec,
+ serf_default_read_for_sendfile,
+ serf_buckets_are_v2,
+ serf_fcgi_unframe_peek,
+ serf_default_destroy_and_data,
+ serf_default_read_bucket,
+ serf_fcgi_unframe_get_remaining,
+ serf_fcgi_unframe_set_config
+};
+
+
+/* ==================================================================== */
+
+serf_bucket_t *
+serf__bucket_fcgi_frame_create(serf_bucket_t *stream,
+ apr_uint16_t stream_id,
+ apr_uint16_t frame_type,
+ serf_bucket_alloc_t *alloc)
+{
+ return NULL;
+}
+
+
+static apr_status_t serf_fcgi_frame_read(serf_bucket_t *bucket,
+ apr_size_t requested,
+ const char **data,
+ apr_size_t *len)
+{
+ return APR_ENOTIMPL;
+}
+
+static apr_status_t serf_fcgi_frame_read_iovec(serf_bucket_t *bucket,
+ apr_size_t requested,
+ int vecs_size,
+ struct iovec *vecs,
+ int *vecs_used)
+{
+ return APR_ENOTIMPL;
+}
+
+static apr_status_t serf_fcgi_frame_peek(serf_bucket_t *bucket,
+ const char **data,
+ apr_size_t *len)
+{
+ return APR_ENOTIMPL;
+}
+
+static void serf_fcgi_frame_destroy(serf_bucket_t *bucket)
+{
+ serf_default_destroy_and_data(bucket);
+}
+
+static apr_uint64_t serf_fcgi_frame_get_remaining(serf_bucket_t *bucket)
+{
+ return APR_ENOTIMPL;
+}
+
+static apr_status_t serf_fcgi_frame_set_config(serf_bucket_t *bucket,
+ serf_config_t *config)
+{
+ return APR_ENOTIMPL;
+}
+
+extern const serf_bucket_type_t serf_bucket_type__fcgi_frame =
+{
+ "FCGI-FRAME",
+ serf_fcgi_frame_read,
+ serf_default_readline,
+ serf_fcgi_frame_read_iovec,
+ serf_default_read_for_sendfile,
+ serf_buckets_are_v2,
+ serf_fcgi_frame_peek,
+ serf_fcgi_frame_destroy,
+ serf_default_read_bucket,
+ serf_fcgi_frame_get_remaining,
+ serf_fcgi_frame_set_config
+};
Propchange: serf/trunk/buckets/fcgi_buckets.c
------------------------------------------------------------------------------
svn:eol-style = native
Added: serf/trunk/protocols/fcgi_buckets.h
URL:
http://svn.apache.org/viewvc/serf/trunk/protocols/fcgi_buckets.h?rev=1714735&view=auto
==============================================================================
--- serf/trunk/protocols/fcgi_buckets.h (added)
+++ serf/trunk/protocols/fcgi_buckets.h Tue Nov 17 09:15:05 2015
@@ -0,0 +1,84 @@
+/* ====================================================================
+* 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.
+* ====================================================================
+*/
+
+#ifndef SERF_PROTOCOL_FCGI_BUCKETS_H
+#define SERF_PROTOCOL_FCGI_BUCKETS_H
+
+#include "serf_bucket_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern const serf_bucket_type_t serf_bucket_type__fcgi_unframe;
+#define SERF__BUCKET_IS_FCGI_UNFRAME(b) SERF_BUCKET_CHECK((b), _fcgi_unframe)
+
+typedef apr_status_t (*serf_bucket_end_of_frame_t)(
+ void *baton,
+ serf_bucket_t *unframe_bucket);
+
+/* Creates a bucket that reads a single fcgi frame from stream. Note that
+ unlike many other buckets destroying the unframe bucket doesn't destroy the
+ underlying stream.
+
+ The frame header information can be obtained by calling
+ serf__bucket_fcgi_unframe_read_info().
+
+ After the header has been read the remaining payload size can be retrieved
+ using serf_bucket_get_remaining()
+ */
+serf_bucket_t * serf__bucket_fcgi_unframe_create(serf_bucket_t *stream,
+ serf_bucket_alloc_t
*allocator);
+
+/* Sets the end of frame handler on the frame, which will be called as soon as
+ the whole frame has been read from the contained stream */
+void serf__bucket_fcgi_unframe_set_eof(serf_bucket_t *bucket,
+ serf_bucket_end_of_frame_t end_of_frame,
+ void *end_of_frame_baton);
+
+
+/* Obtains the frame header state, reading from the bucket if necessary.
+ If the header was read successfully (or was already read before calling)
+ the *stream_id, * frame_type and *flags values (when not pointing to NULL)
+ will be set to the requested values.
+
+ returns APR_SUCCESS when the header was already read before calling this,
+ function. Otherwise it will return the result of reading. */
+apr_status_t serf__bucket_fcgi_unframe_read_info(serf_bucket_t *bucket,
+ apr_uint16_t *stream_id,
+ apr_uint16_t *frame_type);
+
+/* ==================================================================== */
+extern const serf_bucket_type_t serf_bucket_type__fcgi_frame;
+
+#define SERF__BUCKET_IS_FCGI_FRAME(b) SERF_BUCKET_CHECK((b), _fcgi_frame)
+
+serf_bucket_t *
+serf__bucket_fcgi_frame_create(serf_bucket_t *stream,
+ apr_uint16_t stream_id,
+ apr_uint16_t frame_type,
+ serf_bucket_alloc_t *alloc);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !SERF_PROTOCOL_FCGI_BUCKETS_H */
+
Propchange: serf/trunk/protocols/fcgi_buckets.h
------------------------------------------------------------------------------
svn:eol-style = native