WEBBY - the embedded web server with many faces / objects in plain C Snapshot
webby.h
Go to the documentation of this file.
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