WEBBY - the embedded web server with many faces / objects in plain C Snapshot
|
00001 #ifndef __WEBBY_API_H__ 00002 #define __WEBBY_API_H__ 00003 00004 00005 #include <cutils/array.h> 00006 #include <cutils/dbuf.h> 00007 #include <nutils/bf.h> 00008 #include <nutils/addrutil.h> 00009 #include <hutils/http.h> 00010 00011 struct tagHTTP_FILTER; 00012 struct tagHTTP_SERVLET; 00013 00014 // ============================================================================================= 00015 00016 /** 00017 * @defgroup HTTP_FILTER 00018 * @brief HTTP_FILTER - a filter implements a stage of processing the HTTP request / response. 00019 * 00020 * A filter can modify either one of the following aspects, as they are processed. 00021 * - http request header 00022 * - http request data 00023 * - http response header 00024 * - http response data 00025 * 00026 * @{ 00027 */ 00028 00029 00030 /** 00031 * @brief structure is passed to filter function as argument. 00032 */ 00033 typedef struct tagFILTER_CONTEXT { 00034 void *connection_ctx; /** per connection data data maintained for particular filter */ 00035 struct tagHTTP_FILTER *filter; /** pointer to filter instance */ 00036 00037 size_t next_request_filter_idx; /** relative offset to next request filter */ 00038 size_t next_response_filter_idx; /** relative offset to next response filter */ 00039 00040 } FILTER_CONTEXT; 00041 00042 00043 typedef int (*HTTP_FILTER_INIT) (struct tagHTTP_FILTER *filter); 00044 00045 00046 typedef int (*HTTP_FILTER_FREE) (struct tagHTTP_FILTER *filter); 00047 00048 /** 00049 * @brief filter callback: a http request header has been parsed, now the request header is passed through the filter chain. 00050 * 00051 * @param request - the http request header 00052 * @param context - filter context, we need it to call the next filter. 00053 * 00054 */ 00055 typedef int (*HTTP_FILTER_REQUEST_HEADER_PARSED) (HTTP_REQUEST *request, FILTER_CONTEXT *context ); 00056 00057 /** 00058 * @brief filter callback: a buffer that is part of the request data has been received. 00059 * 00060 * @param request - the http request header 00061 * @param data - request data, if request data is chunked then this is after parsing of chunk (no chunk headers included) 00062 * @param data_size - size of request data 00063 * @param context - filter context, we need it to call the next filter. 00064 */ 00065 typedef int (*HTTP_FILTER_REQUEST_DATA) (HTTP_REQUEST *request, void *data, size_t data_size, FILTER_CONTEXT *context ); 00066 00067 /** 00068 * @brief filter callback: the http request has been parsed completely, and all request data has already been processed. 00069 * 00070 * @brief request - the http request header. 00071 * @param context - filter context, we need it to call the next filter. 00072 */ 00073 typedef int (*HTTP_FILTER_REQUEST_COMPLETED) (HTTP_REQUEST *request, FILTER_CONTEXT *context ); 00074 00075 /** 00076 * @brief filter callback: a response header has now passed through the filter. 00077 * 00078 * @brief response - the http response header. 00079 * @param context - filter context, we need it to call the next filter. 00080 */ 00081 typedef int (*HTTP_FILTER_RESPONSE_HEADER) (HTTP_RESPONSE *response, FILTER_CONTEXT *context ); 00082 00083 00084 typedef union { 00085 struct { 00086 void *data; 00087 size_t data_size; 00088 } no_chunk; 00089 00090 struct { 00091 BF *bf; 00092 int chunk_no; 00093 } chunk; 00094 } RDATA; 00095 00096 /** 00097 * @brief filter callback: a buffer that is part of the response data has now passed through the filter 00098 * 00099 * There are two situations - the response uses 'chunks', the response does not use chunks. 00100 * 00101 * If the response is chunked then a buffer object (BF) is used, which reserves space for chunk header before the start of the buffer. 00102 00103 * The case of chunks: is_chunk != 0 00104 * rdata.chunk.bf - the chunk buffer (zero for the last chunk) 00105 * rdata.chunk.chunk_no - the number of the chunk (starts with zero). 00106 * 00107 * The case of no chunks: 00108 * rdata.no_chunk.data - response data 00109 * rdata.no_chunk.data_size - size of response data. 00110 * 00111 * 00112 * @brief response - the http response header. 00113 * @brief is_chunk - not zero if response is chunked 00114 * @brief rdata - union that holds data for both chunked and not chunked case; 00115 * @param context - filter context, we need it to call the next filter. 00116 */ 00117 typedef int (*HTTP_FILTER_RESPONSE_DATA) (HTTP_RESPONSE *response, int is_chunk, RDATA rdata, FILTER_CONTEXT *context ); 00118 00119 /** 00120 * @brief filter callback: a http response has now passed completely through the filter, all response data has already been sent. 00121 * 00122 * @brief response - the http response header. 00123 * @param context - filter context, we need it to call the next filter. 00124 */ 00125 typedef int (*HTTP_FILTER_RESPONSE_COMPLETED) (HTTP_RESPONSE *response, FILTER_CONTEXT *context ); 00126 00127 /** 00128 * @brief filter callback: called when a connection has been closed, the filter should free the connection context data, if any of it has been allocated. 00129 * 00130 * @param context - filter context, we need it to call the next filter. 00131 */ 00132 typedef int (*HTTP_FILTER_CONNECTION_CLOSE) (FILTER_CONTEXT *context ); 00133 00134 /** 00135 * @brief http filter definition 00136 */ 00137 typedef struct tagHTTP_FILTER { 00138 00139 size_t next_request_filter_idx; /** absolute index of next request filter */ 00140 size_t next_response_filter_idx; /** absolute index of next response filter */ 00141 00142 00143 HTTP_FILTER_INIT filter_free; 00144 HTTP_FILTER_FREE filter_init; 00145 00146 HTTP_FILTER_REQUEST_HEADER_PARSED on_request_header_parsed; 00147 HTTP_FILTER_REQUEST_DATA on_request_data; 00148 HTTP_FILTER_REQUEST_COMPLETED on_request_completed; 00149 00150 HTTP_FILTER_RESPONSE_HEADER on_response_header; 00151 HTTP_FILTER_RESPONSE_DATA on_response_data; 00152 HTTP_FILTER_RESPONSE_COMPLETED on_response_completed; 00153 00154 HTTP_FILTER_CONNECTION_CLOSE on_connection_close; 00155 00156 } HTTP_FILTER; 00157 00158 00159 /** 00160 * @brief called by implementaiton of HTTP_FILTER_REQUEST_HEADER_PARSED filter callback, calls the next filter in the chain 00161 */ 00162 M_INLINE int call_next_filter_request_header_parsed( HTTP_REQUEST *request, FILTER_CONTEXT *context ) 00163 { 00164 FILTER_CONTEXT *next = context + context->next_request_filter_idx; 00165 if (next->filter == 0) { 00166 return 0; 00167 } 00168 return next->filter->on_request_header_parsed( request, next ); 00169 } 00170 00171 /** 00172 * @brief called by implementaiton of HTTP_FILTER_REQUEST_DATA filter callback, calls the next filter in the chain 00173 */ 00174 M_INLINE int call_next_filter_request_data( HTTP_REQUEST *request, void *data, size_t data_size, FILTER_CONTEXT *context ) 00175 { 00176 FILTER_CONTEXT *next = context + context->next_request_filter_idx; 00177 if (next->filter == 0) { 00178 return 0; 00179 } 00180 return next->filter->on_request_data( request, data, data_size, next ); 00181 } 00182 00183 /** 00184 * @brief called by implementaiton of HTTP_FILTER_REQUEST_COMPLETED filter callback, calls the next filter in the chain 00185 */ 00186 M_INLINE int call_next_filter_request_completed( HTTP_REQUEST *request, FILTER_CONTEXT *context ) 00187 { 00188 FILTER_CONTEXT *next = context + context->next_request_filter_idx; 00189 if (next->filter == 0) { 00190 return 0; 00191 } 00192 return next->filter->on_request_completed( request, next ); 00193 } 00194 00195 /** 00196 * @brief called by implementaiton of HTTP_FILTER_RESPONSE_HEADER filter callback, calls the next filter in the chain 00197 */ 00198 M_INLINE int call_next_filter_response_header (HTTP_RESPONSE *response, FILTER_CONTEXT *context ) 00199 { 00200 FILTER_CONTEXT *next = context - context->next_response_filter_idx; 00201 if (next->filter == 0) { 00202 return 0; 00203 } 00204 return next->filter->on_response_header( response, next ); 00205 } 00206 00207 /** 00208 * @brief called by implementaiton of HTTP_FILTER_RESPONSE_DATA filter callback, calls the next filter in the chain 00209 */ 00210 M_INLINE int call_next_filter_response_data (HTTP_RESPONSE *response, int is_chunk, RDATA rdata, FILTER_CONTEXT *context ) 00211 { 00212 FILTER_CONTEXT *next = context - context->next_response_filter_idx; 00213 if (next->filter == 0) { 00214 return 0; 00215 } 00216 return next->filter->on_response_data( response, is_chunk, rdata, next ); 00217 } 00218 00219 /** 00220 * @brief called by implementaiton of HTTP_FILTER_RESPONSE_COMPLETED filter callback, calls the next filter in the chain 00221 */ 00222 M_INLINE int call_next_filter_response_completed (HTTP_RESPONSE *response, FILTER_CONTEXT *context ) 00223 { 00224 FILTER_CONTEXT *next = context - context->next_response_filter_idx; 00225 if (next->filter == 0) { 00226 return 0; 00227 } 00228 return next->filter->on_response_completed( response, next ); 00229 } 00230 00231 00232 /** 00233 * @} 00234 */ 00235 00236 // ============================================================================================= 00237 /** 00238 * @defgroup HTTP_SERVLET 00239 * @brief a servlet is a class that processes the http request and produces the http response 00240 */ 00241 00242 /** 00243 * @defgroup HTTP_servlet_request 00244 * @ingroup HTTP_SERVLET 00245 * @brief represents the http request and includes methods to access the request header and request body. 00246 * @{ 00247 */ 00248 typedef struct tagHTTP_servlet_request 00249 { 00250 HTTP_REQUEST *request; 00251 DBUF *request_data; 00252 } HTTP_servlet_request; 00253 00254 00255 M_INLINE Http_version_type HTTP_servlet_protocol(HTTP_servlet_request *req) 00256 { 00257 return req->request->version; 00258 } 00259 00260 M_INLINE Http_method_type HTTP_servlet_method(HTTP_servlet_request *req) 00261 { 00262 return req->request->method; 00263 } 00264 00265 M_INLINE URI *HTTP_servlet_uri( HTTP_servlet_request *req ) 00266 { 00267 return &req->request->url; 00268 } 00269 00270 M_INLINE const char * HTTP_servlet_find_header( HTTP_servlet_request *req, const char *header_name ) { 00271 return HTTP_MESSAGE_find_header( &req->request->base, header_name ); 00272 } 00273 00274 M_INLINE STRINGPAIR * HTTP_servlet_first_header( HTTP_servlet_request *req, DLISTUNR_position *pos ) { 00275 return HTTP_MESSAGE_first_header( &req->request->base, pos ); 00276 } 00277 00278 M_INLINE STRINGPAIR * HTTP_servlet_next_header( HTTP_servlet_request *req, DLISTUNR_position *pos ) { 00279 return HTTP_MESSAGE_next_header( &req->request->base, pos ); 00280 } 00281 00282 M_INLINE DBUF * HTTP_servlet_data( HTTP_servlet_request * req ) { 00283 return req->request_data; 00284 } 00285 00286 /** 00287 * 00288 * @} 00289 */ 00290 00291 // ============================================================================================= 00292 00293 /** 00294 * @defgroup HTTP_servlet_response 00295 * @ingroup HTTP_SERVLET 00296 * @brief represents the http response and includes methods to set response header and send the response body. 00297 * @{ 00298 */ 00299 typedef struct tagHTTP_servlet_response { 00300 HTTP_RESPONSE response; 00301 HTTP_REQUEST *request; 00302 FILTER_CONTEXT *filter_context; 00303 size_t data_sent; 00304 int state; 00305 BF bf; 00306 void *chunk_buf; 00307 size_t chunk_buf_size; 00308 int chunk_no; 00309 } HTTP_servlet_response; 00310 00311 typedef enum { 00312 RESPONSE_CONNECTION_CLOSE, 00313 RESPONSE_CONTENT_LENGTH, 00314 RESPONSE_CHUNKED, 00315 } HTTP_servlet_response_type; 00316 00317 /** 00318 * @brief initiate an HTTP response 00319 */ 00320 int HTTP_response_start( HTTP_servlet_response *resp, int status, const char *mime_type, HTTP_servlet_response_type rtype, size_t length ); 00321 00322 /** 00323 * @brief send response data when sending RESPONSE_CONTENT_LENGTH or RESPONSE_CONNECTION_CLOSE responses 00324 * 00325 * Precondition: HTTP_response_start has been called. and response type is either RESPONSE_CONTENT_LENGTH or RESPONSE_CONNECTION_CLOSE 00326 */ 00327 int HTTP_response_send( HTTP_servlet_response *resp, void *data, size_t size); 00328 00329 /** 00330 * @brief finish sending of response 00331 * 00332 */ 00333 int HTTP_response_finish( HTTP_servlet_response *resp ); 00334 00335 /** 00336 * @brief returns buffer for sending chunks 00337 * The buffer reserves enough space before start of buffer, in order to allow addition of chunk header before the sent daa 00338 */ 00339 BF *HTTP_response_get_chunk_buffer( HTTP_servlet_response *resp, size_t chunk_size ); 00340 00341 /** 00342 * @brief send chunk from buffer 00343 * The buffer must reserve enough space for the chunk header, before start of buffer 00344 * 00345 * Precondition: HTTP_response_start has been called, and response type is RESPONSE_CHUNKED 00346 * 00347 */ 00348 int HTTP_response_write_chunk( HTTP_servlet_response *resp, BF *bf); 00349 00350 /** 00351 * 00352 * @} 00353 */ 00354 00355 00356 // ============================================================================================= 00357 00358 /** 00359 * @addtogroup HTTP_SERVLET 00360 * 00361 * @{ 00362 */ 00363 00364 struct tagHTTP_SERVLET; 00365 00366 typedef enum { 00367 /** error occured while servlet instance has handled a request */ 00368 SERVLET_REQUEST_ERROR = -1, 00369 00370 /** the request has been handled by this servlet */ 00371 SERVLET_REQUEST_HANDLED = 0, 00372 00373 /** the request has not been handled by this servlet */ 00374 SERVLET_REQUEST_IGNORED = 1, 00375 00376 00377 } SERVLET_STATUS; 00378 00379 typedef struct tagSERVLET_CONTEXT { 00380 /** pointer to servlet */ 00381 struct tagHTTP_SERVLET *servlet; 00382 00383 /** per connection data of servlet */ 00384 void *connection_ctx; 00385 00386 } SERVLET_CONTEXT; 00387 00388 /** 00389 * @brief initialise the servlet instance (optional) 00390 */ 00391 typedef int (* HTTP_SERVLET_INIT) (struct tagHTTP_SERVLET *servlet_ctx); 00392 00393 /** 00394 * @brief free the servlet instance (optional) 00395 */ 00396 typedef void * (* HTTP_SERVLET_FREE) (struct tagHTTP_SERVLET *servlet_ctx); 00397 00398 00399 00400 /** 00401 * @brief called to handle an HTTP request 00402 */ 00403 typedef SERVLET_STATUS ( *HTTP_SERVLET_ACTION) ( HTTP_servlet_request * request, HTTP_servlet_response *response, SERVLET_CONTEXT *context ); 00404 00405 /** 00406 * @brief called when socket connection is closed. 00407 */ 00408 typedef void ( *HTTP_SERVLET_FREE_CONNECTION) ( SERVLET_CONTEXT *contextx ); 00409 00410 /** 00411 * @brief HTTP_SERVLET definition 00412 */ 00413 typedef struct tagHTTP_SERVLET { 00414 HTTP_SERVLET_INIT init_servlet; 00415 HTTP_SERVLET_FREE free_servlet; 00416 HTTP_SERVLET_ACTION servlet_action; 00417 HTTP_SERVLET_FREE_CONNECTION free_connection; 00418 00419 } HTTP_SERVLET; 00420 00421 00422 M_INLINE void HTTP_SERVLET_init( HTTP_SERVLET *servlet, HTTP_SERVLET_INIT init_servlet, HTTP_SERVLET_FREE free_servlet, HTTP_SERVLET_ACTION servlet_action, HTTP_SERVLET_FREE_CONNECTION free_connection ) 00423 { 00424 servlet->init_servlet = init_servlet; 00425 servlet->free_servlet = free_servlet; 00426 servlet->servlet_action = servlet_action; 00427 servlet->free_connection = free_connection; 00428 } 00429 00430 /** 00431 * @} 00432 */ 00433 00434 00435 // ============================================================================================= 00436 00437 /** 00438 * @defgroup WEBBYCFG 00439 * @brief embedded web server configuration 00440 * 00441 * @{ 00442 */ 00443 00444 typedef struct tagWEBBY_CONFIG { 00445 00446 /** listening address */ 00447 SOCKADDR listening_address; 00448 00449 /** listen(2) backlog parameter */ 00450 int listen_backlog; 00451 00452 /** socket buffer size, in bytes, for each client connection (both receive and send buffer sizes) */ 00453 int socket_buffer_size; 00454 00455 /** maximum number of connections supported at the same time */ 00456 int max_connections; 00457 00458 /** read / write timeout in seconds. */ 00459 int io_timeout; 00460 00461 /** idle timeout in seconds - if connection that inactive for more than this value between requests then connection will be close */ 00462 int idle_timeout; 00463 00464 /** pages per thread */ 00465 int stack_pages_per_thread; 00466 00467 } WEBBY_CONFIG; 00468 00469 int WEBBY_CONFIG_load( WEBBY_CONFIG *cfg, const char *file); 00470 00471 /** 00472 * @} 00473 */ 00474 00475 00476 // ============================================================================================= 00477 00478 struct tagSERVLET_RUNNER_FILTER; 00479 00480 /** 00481 * @defgroup WEBBY 00482 * @brief embedded web server facade class. 00483 * 00484 * @{ 00485 */ 00486 typedef struct tagWEBBY { 00487 /** implementation object used to send / receive data on connection */ 00488 void *impl; 00489 00490 /** precompouted filter context layout - copied for each new connection */ 00491 FILTER_CONTEXT *filter_ctx_layout; 00492 00493 /** size of filter context layoyt */ 00494 size_t filter_ctx_layout_size; 00495 00496 /** all filters, all of them */ 00497 ARRAY filters; 00498 00499 /** the filter that runs servlets */ 00500 struct tagSERVLET_RUNNER_FILTER * servlet_runner_filter; 00501 00502 struct tagDATA_SINK_FILTER * sink_filter; 00503 00504 /** configuration */ 00505 WEBBY_CONFIG *cfg; 00506 00507 } WEBBY; 00508 00509 00510 WEBBY *WEBBY_init( WEBBY_CONFIG * ); 00511 00512 int WEBBY_add_vhost( WEBBY *server, const char *host, int port_num, size_t * vhost_idx ); 00513 00514 int WEBBY_add_filter( WEBBY *server, size_t vhost_idx, HTTP_FILTER *filter ); 00515 00516 int WEBBY_add_servlet( WEBBY *server, HTTP_SERVLET *servlet ); 00517 00518 int WEBBY_run( WEBBY *server ); 00519 00520 int WEBBY_shutdown( WEBBY *server ); 00521 00522 00523 #define HTTP_PARSER_BUFFER_SIZE 4096 00524 00525 typedef struct tagWEBBY_CONNECTION { 00526 void *in_buf_data; 00527 BF in_buf; 00528 HTTP_REQUEST_PARSER request_parser; 00529 HTTP_REQUEST request; 00530 00531 FILTER_CONTEXT *filter_data; 00532 size_t num_filters; 00533 00534 } WEBBY_CONNECTION; 00535 00536 WEBBY_CONNECTION *WEBBY_new_connection( WEBBY *server, void *implconndata ); 00537 int WEBBY_connection_data_received( WEBBY_CONNECTION * connection ); 00538 void WEBBY_connection_close( WEBBY_CONNECTION * connection ); 00539 00540 /** 00541 * @} 00542 */ 00543 00544 #endif 00545 00546