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