| #ifndef http1_codec_H |
| #define http1_codec_H 1 |
| /* |
| * 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 <qpid/dispatch/buffer.h> |
| #include <qpid/dispatch/message.h> |
| |
| #include <inttypes.h> |
| #include <stdbool.h> |
| |
| |
| // HTTP/1.x Encoder/Decoder Library |
| // |
| // This library provides an API for encoding and decoding HTTP/1.x messages. |
| // |
| // The decoder takes qd_buffer_t chains containing HTTP/1.x data read from the |
| // TCP connection and issues callbacks as various parts (headers, body, status) |
| // of the HTTP/1.x message are parsed. |
| // |
| // The encoder allows the application to construct an HTTP/1.x message. An API |
| // is provided for building the message and callbacks are invoked when the |
| // encoder has full qd_buffer_t or body_data to send out the TCP connection. |
| // |
| // This library provides two classes: |
| // |
| // * h1_codec_connection_t - a context for a single TCP connection over which |
| // HTTP/1.x messages are exchanged. |
| // |
| // * h1_codec_request_state_t - a context which tracks the state of a single |
| // HTTP/1.x Request <-> Response message exchange. Multiple |
| // h1_codec_request_state_t can be associated with an h1_codec_connection_t due to |
| // request pipelining. |
| // |
| |
| |
| #define HTTP1_VERSION_1_1 "HTTP/1.1" |
| #define HTTP1_VERSION_1_0 "HTTP/1.0" |
| |
| typedef struct h1_codec_connection_t h1_codec_connection_t; |
| typedef struct h1_codec_request_state_t h1_codec_request_state_t; |
| |
| |
| typedef enum { |
| HTTP1_CONN_CLIENT, // connection initiated by client |
| HTTP1_CONN_SERVER, // connection to server |
| } h1_codec_connection_type_t; |
| |
| |
| typedef enum { |
| HTTP1_STATUS_BAD_REQ = 400, |
| HTTP1_STATUS_SERVER_ERR = 500, |
| HTTP1_STATUS_BAD_VERSION = 505, |
| HTTP1_STATUS_SERVICE_UNAVAILABLE = 503, |
| } h1_codec_status_code_t; |
| |
| |
| typedef struct h1_codec_config_t { |
| |
| h1_codec_connection_type_t type; |
| |
| // Callbacks to send data out the raw connection. These callbacks are |
| // triggered by the message creation API (h1_codec_tx_*) Note well: these |
| // callbacks are called in the order in which the data must be written out |
| // the raw connection! |
| |
| // tx_buffers() |
| // Send a list of buffers containing encoded HTTP message data. The caller |
| // assumes ownership of the buffer list and must release the buffers when |
| // done. len is set to the total octets of data in the list. |
| // |
| void (*tx_buffers)(h1_codec_request_state_t *hrs, qd_buffer_list_t *data, unsigned int len); |
| |
| // tx_stream_data() |
| // Called with stream_data containing encoded HTTP message data. Only |
| // called if the outgoing HTTP message has a body. The caller assumes |
| // ownership of the stream_data and must release it when done. |
| // |
| void (*tx_stream_data)(h1_codec_request_state_t *hrs, qd_message_stream_data_t *stream_data); |
| |
| // |
| // RX message callbacks |
| // |
| // These callbacks should return 0 on success or non-zero on error. A |
| // non-zero return code is used as the return code from |
| // h1_codec_connection_rx_data() |
| // |
| |
| // HTTP request received - new h1_codec_request_state_t created (hrs). This |
| // hrs must be supplied in the h1_codec_tx_response() method when sending the |
| // response. |
| int (*rx_request)(h1_codec_request_state_t *hrs, |
| const char *method, |
| const char *target, |
| uint32_t version_major, |
| uint32_t version_minor); |
| |
| // HTTP response received - the h1_codec_request_state_t comes from the return |
| // value of the h1_codec_tx_request() method used to create the corresponding |
| // request. Note well that if status_code is Informational (1xx) then this |
| // response is NOT the last response for the current request (See RFC7231, |
| // 6.2 Informational 1xx). The request_done callback will be called after |
| // the LAST response has been received. |
| // |
| int (*rx_response)(h1_codec_request_state_t *hrs, |
| int status_code, |
| const char *reason_phrase, |
| uint32_t version_major, |
| uint32_t version_minor); |
| |
| int (*rx_header)(h1_codec_request_state_t *hrs, const char *key, const char *value); |
| int (*rx_headers_done)(h1_codec_request_state_t *hrs, bool has_body); |
| |
| int (*rx_body)(h1_codec_request_state_t *hrs, qd_buffer_list_t *body, size_t len, bool more); |
| |
| // Invoked after a received HTTP message has been completely parsed. |
| // |
| void (*rx_done)(h1_codec_request_state_t *hrs); |
| |
| // Invoked when the final response message has been decoded (server |
| // connection) or encoded (client connection), or the request has been cancelled. |
| // hrs is freed on return from this callback and must not be referenced further. |
| void (*request_complete)(h1_codec_request_state_t *hrs, |
| bool cancelled); |
| |
| } h1_codec_config_t; |
| |
| |
| // create a new connection and assign it a context |
| // |
| h1_codec_connection_t *h1_codec_connection(h1_codec_config_t *config, void *context); |
| void *h1_codec_connection_get_context(h1_codec_connection_t *conn); |
| |
| // Release the codec. This can only be done after all outstanding requests |
| // have been completed or cancelled. |
| // |
| void h1_codec_connection_free(h1_codec_connection_t *conn); |
| |
| // Push inbound network data into the http1 library. All rx_*() callbacks occur |
| // during this call. The return value is zero on success. If a non-zero value |
| // is returned the codec state is unknown - the application must cancel all |
| // outstanding requests and destroy the conn by calling |
| // h1_codec_connection_free(). |
| // |
| int h1_codec_connection_rx_data(h1_codec_connection_t *conn, qd_buffer_list_t *data, size_t len); |
| |
| // Notify the codec that the endpoint closed the connection. For server-facing |
| // connections it is safe to resume calling h1_codec_connection_rx_data() for |
| // the h1_codec_connection once the connection to the server is reestablished. |
| // Client-facing connections cannot be resumed after the connection has been |
| // closed. In the client case the application must cancel all outstanding |
| // requests and then call h1_codec_connection_free() instead. |
| // |
| void h1_codec_connection_rx_closed(h1_codec_connection_t *conn); |
| |
| void h1_codec_request_state_set_context(h1_codec_request_state_t *hrs, void *context); |
| void *h1_codec_request_state_get_context(const h1_codec_request_state_t *hrs); |
| h1_codec_connection_t *h1_codec_request_state_get_connection(const h1_codec_request_state_t *hrs); |
| |
| // Cancel the request. The h1_codec_request_state_t is freed during this call. |
| // The request_complete callback will be invoked during this call with |
| // cancelled=True. |
| // |
| void h1_codec_request_state_cancel(h1_codec_request_state_t *hrs); |
| |
| const char *h1_codec_request_state_method(const h1_codec_request_state_t *hrs); |
| const uint32_t h1_codec_request_state_response_code(const h1_codec_request_state_t *hrs); |
| |
| // true when codec has encoded/decoded a complete request message |
| bool h1_codec_request_complete(const h1_codec_request_state_t *hrs); |
| |
| // true when codec has encoded/decoded a complete response message |
| bool h1_codec_response_complete(const h1_codec_request_state_t *hrs); |
| |
| // query the amount of octets read (in) and written (out) for a request |
| void h1_codec_request_state_counters(const h1_codec_request_state_t *hrs, |
| uint64_t *in_octets, |
| uint64_t *out_octets); |
| |
| // Utility for iterating over a list of HTTP tokens. |
| // |
| // start - begin search |
| // len - (output) length of token if non-null returned |
| // next - (output) address past token - for start of next search |
| // Returns a pointer to the first byte of the token, or 0 if no token found |
| // |
| const char *h1_codec_token_list_next(const char *start, size_t *len, const char **next); |
| |
| |
| // |
| // API for sending HTTP/1.x messages |
| // |
| // The tx_msg_headers and tx_msg_body callbacks can occur during any of these |
| // calls. |
| // |
| |
| |
| // initiate a request - this creates a new request state context |
| // |
| h1_codec_request_state_t *h1_codec_tx_request(h1_codec_connection_t *conn, const char *method, const char *target, |
| uint32_t version_major, uint32_t version_minor); |
| |
| // Respond to a received request - the request state context should be the one |
| // supplied during the corresponding rx_request callback. It is required that |
| // the caller issues responses in the same order as requests arrive. |
| // |
| int h1_codec_tx_response(h1_codec_request_state_t *hrs, int status_code, const char *reason_phrase, |
| uint32_t version_major, uint32_t version_minor); |
| |
| // add header to outgoing message |
| // |
| int h1_codec_tx_add_header(h1_codec_request_state_t *hrs, const char *key, const char *value); |
| |
| // Stream outgoing body data. Ownership of stream_data is passed to the caller. |
| // |
| int h1_codec_tx_body(h1_codec_request_state_t *hrs, qd_message_stream_data_t *stream_data); |
| |
| // Write body as string |
| // |
| int h1_codec_tx_body_str(h1_codec_request_state_t *hrs, char *data); |
| |
| // outgoing message construction complete. The request_complete() callback MAY |
| // occur during this call. |
| // |
| // need_close: set to true if the outgoing message is an HTTP response that |
| // does not provide an explict body length. If true it is up to the caller to |
| // close the underlying socket connection after all outgoing data for this |
| // request has been sent. |
| // |
| int h1_codec_tx_done(h1_codec_request_state_t *hrs, bool *need_close); |
| |
| // begin multipart content; this will generate a boundary marker and set the content type header |
| // |
| int h1_codec_tx_begin_multipart(h1_codec_request_state_t *hrs); |
| |
| // begin a new multipart section |
| // |
| int h1_codec_tx_begin_multipart_section(h1_codec_request_state_t *hrs); |
| |
| // mark the end of multipart data |
| // |
| int h1_codec_tx_end_multipart(h1_codec_request_state_t *hrs); |
| |
| uint64_t h1_codec_tx_multipart_section_boundary_length(); |
| uint64_t h1_codec_tx_multipart_end_boundary_length(); |
| |
| |
| #endif // http1_codec_H |