Author: rhuijben Date: Tue Nov 17 09:18:11 2015 New Revision: 1714736 URL: http://svn.apache.org/viewvc?rev=1714736&view=rev Log: Following up on r1714735, add a basic protocol handler for fcgi on the server side.
* incoming.c (serf_incoming_set_framing_type): Initialize fcgi if requested. * protocols/fcgi_protocol.c New file. * protocols/fcgi_protocol.h New file. * protocols/fcgi_stream.c New file. * serf.h (serf_connection_framing_type_t): Extend enum. * serf_private.h (serf__fcgi_protocol_init, serf__fcgi_protocol_init_server): New function. * test/serf_httpd.c (client_accept): Allow listening for specific protocols. E.g. --listen 127.0.0.1:99 --listen fcgi,127.0.0.1:4000 /my/web/root Added: serf/trunk/protocols/fcgi_protocol.c (with props) serf/trunk/protocols/fcgi_protocol.h (with props) serf/trunk/protocols/fcgi_stream.c (with props) Modified: serf/trunk/incoming.c serf/trunk/serf.h serf/trunk/serf_private.h serf/trunk/test/serf_httpd.c Modified: serf/trunk/incoming.c URL: http://svn.apache.org/viewvc/serf/trunk/incoming.c?rev=1714736&r1=1714735&r2=1714736&view=diff ============================================================================== --- serf/trunk/incoming.c (original) +++ serf/trunk/incoming.c Tue Nov 17 09:18:11 2015 @@ -580,6 +580,8 @@ void serf_incoming_set_framing_type( case SERF_CONNECTION_FRAMING_TYPE_HTTP2: serf__http2_protocol_init_server(client); break; + case SERF_CONNECTION_FRAMING_TYPE_FCGI: + serf__fcgi_protocol_init_server(client); default: break; } Added: serf/trunk/protocols/fcgi_protocol.c URL: http://svn.apache.org/viewvc/serf/trunk/protocols/fcgi_protocol.c?rev=1714736&view=auto ============================================================================== --- serf/trunk/protocols/fcgi_protocol.c (added) +++ serf/trunk/protocols/fcgi_protocol.c Tue Nov 17 09:18:11 2015 @@ -0,0 +1,400 @@ +/* ==================================================================== +* 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 <apr_pools.h> +#include <apr_poll.h> +#include <apr_version.h> +#include <apr_portable.h> +#include <apr_strings.h> + +#include "serf.h" +#include "serf_bucket_util.h" + +#include "serf_private.h" +#include "protocols/fcgi_buckets.h" +#include "protocols/fcgi_protocol.h" + +typedef struct serf_fcgi_protocol_t +{ + serf_context_t *ctx; + serf_connection_t *conn; + serf_incoming_t *client; + + apr_pool_t *pool; + serf_bucket_alloc_t *allocator; + bool *dirty_pollset; + serf_config_t *config; + + apr_int16_t *req_events; + + serf_bucket_t *stream; + serf_bucket_t *ostream; + + serf_fcgi_processor_t processor; + void *processor_baton; + + serf_bucket_t *read_frame; + bool in_frame; + +} serf_fcgi_protocol_t; + +static apr_status_t fcgi_cleanup(void *baton) +{ + serf_fcgi_protocol_t *fcgi = baton; + + fcgi = fcgi; + + return APR_SUCCESS; +} + +/* Implements the serf_bucket_end_of_frame_t callback */ +static apr_status_t +fcgi_end_of_frame(void *baton, + serf_bucket_t *frame) +{ + serf_fcgi_protocol_t *fcgi = baton; + + SERF_FCGI_assert(fcgi->read_frame == frame); + fcgi->read_frame = NULL; + fcgi->in_frame = FALSE; + fcgi->processor = NULL; + fcgi->processor_baton = NULL; + + return APR_SUCCESS; +} + +/* Implements serf_fcgi_processor_t */ +static apr_status_t +fcgi_bucket_processor(void *baton, + serf_fcgi_protocol_t *h2, + serf_bucket_t *frame_bucket) +{ + struct iovec vecs[IOV_MAX]; + int vecs_used; + serf_bucket_t *payload = baton; + apr_status_t status; + + status = serf_bucket_read_iovec(payload, SERF_READ_ALL_AVAIL, IOV_MAX, + vecs, &vecs_used); + + if (APR_STATUS_IS_EOF(status)) + { + SERF_FCGI_assert(!h2->in_frame && !h2->read_frame); + serf_bucket_destroy(payload); + } + + return status; +} + + + +static apr_status_t fcgi_process(serf_fcgi_protocol_t *fcgi) +{ + while (true) + { + apr_status_t status; + serf_bucket_t *body; + + if (fcgi->processor) + { + status = fcgi->processor(fcgi->processor_baton, fcgi, + fcgi->read_frame); + + if (SERF_BUCKET_READ_ERROR(status)) + return status; + else if (APR_STATUS_IS_EOF(status)) + { + /* ### frame ended */ + SERF_FCGI_assert(fcgi->read_frame == NULL); + fcgi->processor = NULL; + fcgi->processor_baton = NULL; + } + else if (fcgi->in_frame) + { + if (status) + return status; + else + continue; + } + } + else + { + SERF_FCGI_assert(!fcgi->in_frame); + } + + body = fcgi->read_frame; + + if (!body) + { + SERF_FCGI_assert(!fcgi->in_frame); + + body = serf__bucket_fcgi_unframe_create(fcgi->stream, + fcgi->allocator); + + serf__bucket_fcgi_unframe_set_eof(body, + fcgi_end_of_frame, fcgi); + + serf_bucket_set_config(body, fcgi->config); + fcgi->read_frame = body; + } + + if (!fcgi->in_frame) + { + apr_uint16_t sid; + apr_uint16_t frametype; + apr_size_t remaining; + serf_fcgi_processor_t process_handler = NULL; + void *process_baton = NULL; + serf_bucket_t *process_bucket = NULL; + serf_fcgi_stream_t *stream; + apr_uint32_t reset_reason; + + status = serf__bucket_fcgi_unframe_read_info(body, &sid, + &frametype); + + if (APR_STATUS_IS_EOF(status)) + { + /* Entire frame is already read (just header) */ + SERF_FCGI_assert(fcgi->read_frame == NULL); + SERF_FCGI_assert(!fcgi->in_frame); + } + else if (status) + { + SERF_FCGI_assert(fcgi->read_frame != NULL); + SERF_FCGI_assert(!fcgi->in_frame); + return status; + } + else + { + fcgi->in_frame = TRUE; + SERF_FCGI_assert(fcgi->read_frame != NULL); + } + + serf__log(LOGLVL_INFO, SERF_LOGFCGI, fcgi->config, + "Reading 0x%x frame, stream=0x%x\n", + frametype, sid); + + /* If status is EOF then the frame doesn't have/declare a body */ + switch (frametype) + { + case FCGI_FRAMETYPE(FCGI_V1, FCGI_BEGIN_REQUEST): + process_bucket = body; + break; + case FCGI_FRAMETYPE(FCGI_V1, FCGI_ABORT_REQUEST): + process_bucket = body; + break; + case FCGI_FRAMETYPE(FCGI_V1, FCGI_END_REQUEST): + process_bucket = body; + break; + case FCGI_FRAMETYPE(FCGI_V1, FCGI_PARAMS): + process_bucket = body; + break; + case FCGI_FRAMETYPE(FCGI_V1, FCGI_STDIN): + process_bucket = body; + break; + case FCGI_FRAMETYPE(FCGI_V1, FCGI_STDOUT): + process_bucket = body; + break; + case FCGI_FRAMETYPE(FCGI_V1, FCGI_STDERR): + process_bucket = body; + break; + case FCGI_FRAMETYPE(FCGI_V1, FCGI_DATA): + process_bucket = body; + break; + case FCGI_FRAMETYPE(FCGI_V1, FCGI_GET_VALUES): + process_bucket = body; + break; + case FCGI_FRAMETYPE(FCGI_V1, FCGI_GET_VALUES_RESULT): + process_bucket = body; + break; + case FCGI_FRAMETYPE(FCGI_V1, FCGI_UNKNOWN_TYPE): + process_bucket = body; + break; + default: + process_bucket = body; + }; + + if (body) + serf_bucket_set_config(body, fcgi->config); + + SERF_FCGI_assert(fcgi->processor == NULL); + + if (process_handler) + { + fcgi->processor = process_handler; + fcgi->processor_baton = process_baton; + } + else + { + SERF_FCGI_assert(process_bucket != NULL); + fcgi->processor = fcgi_bucket_processor; + fcgi->processor_baton = process_bucket; + } + } + } /* while(TRUE) */ +} + +static apr_status_t fcgi_read(serf_fcgi_protocol_t *fcgi) +{ + apr_status_t status = fcgi_process(fcgi); + + if (!status || SERF_BUCKET_READ_ERROR(status)) + return status; + + return APR_SUCCESS; +} + +static apr_status_t fcgi_write(serf_fcgi_protocol_t *fcgi) +{ + return APR_ENOTIMPL; +} + +static apr_status_t fcgi_teardown(serf_fcgi_protocol_t *fcgi) +{ + return APR_ENOTIMPL; +} + + +/* --------------- connection support --------------- */ +static apr_status_t fcgi_outgoing_read(serf_connection_t *conn) +{ + serf_fcgi_protocol_t *fcgi = conn->protocol_baton; + + if (!fcgi->stream) + fcgi->stream = conn->stream; + + return fcgi_read(fcgi); +} + +static apr_status_t fcgi_outgoing_write(serf_connection_t *conn) +{ + serf_fcgi_protocol_t *fcgi = conn->protocol_baton; + + return fcgi_read(fcgi); +} + +static apr_status_t fcgi_outgoing_hangup(serf_connection_t *conn) +{ + serf_fcgi_protocol_t *fcgi = conn->protocol_baton; + + return fcgi_read(fcgi); +} + +static apr_status_t fcgi_outgoing_teardown(serf_connection_t *conn) +{ + serf_fcgi_protocol_t *fcgi = conn->protocol_baton; + + return fcgi_read(fcgi); +} + +void serf__fcgi_protocol_init(serf_connection_t *conn) +{ + serf_fcgi_protocol_t *fcgi; + apr_pool_t *protocol_pool; + + apr_pool_create(&protocol_pool, conn->pool); + + fcgi = apr_pcalloc(protocol_pool, sizeof(*fcgi)); + fcgi->pool = protocol_pool; + fcgi->conn = conn; + fcgi->ctx = conn->ctx; + fcgi->dirty_pollset = &conn->dirty_conn; + fcgi->req_events = &conn->reqevents; + fcgi->stream = conn->stream; + fcgi->ostream = conn->ostream_tail; + fcgi->allocator = conn->allocator; + fcgi->config = conn->config; + + apr_pool_cleanup_register(protocol_pool, fcgi, fcgi_cleanup, + apr_pool_cleanup_null); + + conn->perform_read = fcgi_outgoing_read; + conn->perform_write = fcgi_outgoing_write; + conn->perform_hangup = fcgi_outgoing_hangup; + conn->perform_teardown = fcgi_outgoing_teardown; + conn->protocol_baton = fcgi; + + /* Disable HTTP/1.1 guessing that affects writability */ + conn->probable_keepalive_limit = 0; + conn->max_outstanding_requests = 0; +} + +/* --------------- connection support --------------- */ +static apr_status_t fcgi_server_read(serf_incoming_t *client) +{ + serf_fcgi_protocol_t *fcgi = client->protocol_baton; + + if (! fcgi->stream) { + fcgi->stream = client->stream; + fcgi->ostream = client->ostream_tail; + } + + return fcgi_read(fcgi); +} + +static apr_status_t fcgi_server_write(serf_incoming_t *client) +{ + serf_fcgi_protocol_t *fcgi = client->protocol_baton; + + return fcgi_read(fcgi); +} + +static apr_status_t fcgi_server_hangup(serf_incoming_t *client) +{ + serf_fcgi_protocol_t *fcgi = client->protocol_baton; + + return fcgi_read(fcgi); +} + +static apr_status_t fcgi_server_teardown(serf_incoming_t *client) +{ + serf_fcgi_protocol_t *fcgi = client->protocol_baton; + + return fcgi_read(fcgi); +} + +void serf__fcgi_protocol_init_server(serf_incoming_t *client) +{ + serf_fcgi_protocol_t *fcgi; + apr_pool_t *protocol_pool; + + apr_pool_create(&protocol_pool, client->pool); + + fcgi = apr_pcalloc(protocol_pool, sizeof(*fcgi)); + fcgi->pool = protocol_pool; + fcgi->client = client; + fcgi->ctx = client->ctx; + fcgi->dirty_pollset = &client->dirty_conn; + fcgi->req_events = &client->reqevents; + fcgi->stream = client->stream; + fcgi->ostream = client->ostream_tail; + fcgi->allocator = client->allocator; + fcgi->config = client->config; + + apr_pool_cleanup_register(protocol_pool, fcgi, fcgi_cleanup, + apr_pool_cleanup_null); + + client->perform_read = fcgi_server_read; + client->perform_write = fcgi_server_write; + client->perform_hangup = fcgi_server_hangup; + client->perform_teardown = fcgi_server_teardown; + client->protocol_baton = fcgi; +} + Propchange: serf/trunk/protocols/fcgi_protocol.c ------------------------------------------------------------------------------ svn:eol-style = native Added: serf/trunk/protocols/fcgi_protocol.h URL: http://svn.apache.org/viewvc/serf/trunk/protocols/fcgi_protocol.h?rev=1714736&view=auto ============================================================================== --- serf/trunk/protocols/fcgi_protocol.h (added) +++ serf/trunk/protocols/fcgi_protocol.h Tue Nov 17 09:18:11 2015 @@ -0,0 +1,88 @@ +/* ==================================================================== +* 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_PROTOCOL_H +#define SERF_PROTOCOL_FCGI_PROTOCOL_H + +#include "serf_bucket_types.h" + +#ifdef _DEBUG +#include <assert.h> +#define SERF_FCGI_assert(x) assert(x) +#else +#define SERF_FCGI_assert(x) ((void)0) +#endif + +#define SERF_LOGFCGI \ + SERF_LOGCOMP_PROTOCOL, (__FILE__ ":" APR_STRINGIFY(__LINE__)) + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct serf_fcgi_protocol_t serf_fcgi_protocol_t; +typedef struct serf_fcgi_stream_data_t serf_fcgi_stream_data_t; + +#define FCGI_FRAMETYPE(version, type) \ + ( ( (apr_uint16_t)(unsigned char)(version) << 8) \ + | ( (apr_uint16_t)(unsigned char)(type))) + +#define FCGI_V1 0x1 + +#define FCGI_BEGIN_REQUEST 1 +#define FCGI_ABORT_REQUEST 2 +#define FCGI_END_REQUEST 3 +#define FCGI_PARAMS 4 +#define FCGI_STDIN 5 +#define FCGI_STDOUT 6 +#define FCGI_STDERR 7 +#define FCGI_DATA 8 +#define FCGI_GET_VALUES 9 +#define FCGI_GET_VALUES_RESULT 10 +#define FCGI_UNKNOWN_TYPE 11 +#define FCGI_MAXTYPE (FCGI_UNKNOWN_TYPE) + + +typedef struct serf_fcgi_stream_t +{ + struct serf_fcgi_protocol_t *h2; + serf_bucket_alloc_t *alloc; + + /* Opaque implementation details */ + serf_fcgi_stream_data_t *data; + + /* Linked list of currently existing streams */ + struct serf_fcgi_stream_t *next; + struct serf_fcgi_stream_t *prev; +} serf_fcgi_stream_t; + +typedef apr_status_t(*serf_fcgi_processor_t)(void *baton, + serf_fcgi_protocol_t *fcgi, + serf_bucket_t *body); + + + +#ifdef __cplusplus +} +#endif + +#endif /* !SERF_PROTOCOL_FCGI_PROTOCOL_H */ + + Propchange: serf/trunk/protocols/fcgi_protocol.h ------------------------------------------------------------------------------ svn:eol-style = native Added: serf/trunk/protocols/fcgi_stream.c URL: http://svn.apache.org/viewvc/serf/trunk/protocols/fcgi_stream.c?rev=1714736&view=auto ============================================================================== --- serf/trunk/protocols/fcgi_stream.c (added) +++ serf/trunk/protocols/fcgi_stream.c Tue Nov 17 09:18:11 2015 @@ -0,0 +1,57 @@ +/* ==================================================================== +* 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 <apr_strings.h> + +#include "serf.h" +#include "serf_bucket_util.h" +#include "serf_private.h" + +#include "protocols/fcgi_buckets.h" +#include "protocols/fcgi_protocol.h" + +struct serf_fcgi_stream_data_t +{ + int dummy; +}; + +serf_fcgi_stream_t * +serf_fcgi__stream_create(serf_fcgi_protocol_t *h2, + apr_int32_t streamid, + apr_uint32_t lr_window, + apr_uint32_t rl_window, + serf_bucket_alloc_t *alloc) +{ + serf_fcgi_stream_t *stream = serf_bucket_mem_alloc(alloc, + sizeof(*stream)); + + stream->h2 = h2; + stream->alloc = alloc; + + stream->next = stream->prev = NULL; + + /* Delay creating this? */ + stream->data = serf_bucket_mem_alloc(alloc, sizeof(*stream->data)); + + return stream; +} Propchange: serf/trunk/protocols/fcgi_stream.c ------------------------------------------------------------------------------ svn:eol-style = native Modified: serf/trunk/serf.h URL: http://svn.apache.org/viewvc/serf/trunk/serf.h?rev=1714736&r1=1714735&r2=1714736&view=diff ============================================================================== --- serf/trunk/serf.h (original) +++ serf/trunk/serf.h Tue Nov 17 09:18:11 2015 @@ -611,7 +611,8 @@ void serf_connection_set_async_responses typedef enum serf_connection_framing_type_t { SERF_CONNECTION_FRAMING_TYPE_NONE = 0, SERF_CONNECTION_FRAMING_TYPE_HTTP1, - SERF_CONNECTION_FRAMING_TYPE_HTTP2 + SERF_CONNECTION_FRAMING_TYPE_HTTP2, + SERF_CONNECTION_FRAMING_TYPE_FCGI } serf_connection_framing_type_t; /** Modified: serf/trunk/serf_private.h URL: http://svn.apache.org/viewvc/serf/trunk/serf_private.h?rev=1714736&r1=1714735&r2=1714736&view=diff ============================================================================== --- serf/trunk/serf_private.h (original) +++ serf/trunk/serf_private.h Tue Nov 17 09:18:11 2015 @@ -685,6 +685,10 @@ serf_bucket_t *serf__bucket_log_wrapper_ void serf__http2_protocol_init(serf_connection_t *conn); void serf__http2_protocol_init_server(serf_incoming_t *client); +/* From fcgi_protocol.c: Initializes http2 state on connection */ +void serf__fcgi_protocol_init(serf_connection_t *conn); +void serf__fcgi_protocol_init_server(serf_incoming_t *client); + typedef struct serf_hpack_table_t serf_hpack_table_t; /* From http2_hpack_buckets.c */ Modified: serf/trunk/test/serf_httpd.c URL: http://svn.apache.org/viewvc/serf/trunk/test/serf_httpd.c?rev=1714736&r1=1714735&r2=1714736&view=diff ============================================================================== --- serf/trunk/test/serf_httpd.c (original) +++ serf/trunk/test/serf_httpd.c Tue Nov 17 09:18:11 2015 @@ -220,21 +220,39 @@ static apr_status_t client_accept(serf_c apr_socket_t *insock, apr_pool_t *pool) { - serf_incoming_t *incoming; + serf_incoming_t *client; listener_ctx_t *lctx = accept_baton; apr_pool_t *cctx_pool; client_ctx_t *cctx; + apr_status_t status; apr_pool_create(&cctx_pool, pool); cctx = apr_pcalloc(cctx_pool, sizeof(*cctx)); cctx->pool = cctx_pool; cctx->listener = lctx; - return serf_incoming_create2(&incoming, ctx, insock, - client_setup, cctx, - client_closed, cctx, - client_request_acceptor, cctx, - pool); + status = serf_incoming_create2(&client, ctx, insock, + client_setup, cctx, + client_closed, cctx, + client_request_acceptor, cctx, + pool); + if (status) + return status; + + if (lctx->proto && !strcmp(lctx->proto, "fcgi")) { + serf_incoming_set_framing_type(client, + SERF_CONNECTION_FRAMING_TYPE_FCGI); + } + else if (lctx->proto && !strcmp(lctx->proto, "h2c")) { + serf_incoming_set_framing_type(client, + SERF_CONNECTION_FRAMING_TYPE_HTTP2); + } + else if (lctx->proto && !strcmp(lctx->proto, "http1")) { + serf_incoming_set_framing_type(client, + SERF_CONNECTION_FRAMING_TYPE_HTTP1); + } + + return status; }