Main Page | Modules | Data Structures | Directories | File List | Data Fields | Globals

request.c

00001 /*
00002  * Copyright (c) 2005, 2006 by KoanLogic s.r.l. <http://www.koanlogic.com>
00003  * All rights reserved.
00004  *
00005  * This file is part of KLone, and as such it is subject to the license stated
00006  * in the LICENSE file which you have received as part of this distribution.
00007  *
00008  * $Id: request.c,v 1.48 2007/10/26 11:21:51 tho Exp $
00009  */
00010 
00011 #include "klone_conf.h"
00012 #include <stdlib.h>
00013 #include <string.h>
00014 #include <ctype.h>
00015 #include <sys/types.h>
00016 #include <sys/stat.h>
00017 #include <u/libu.h>
00018 #include <klone/request.h>
00019 #include <klone/utils.h>
00020 #include <klone/io.h>
00021 #include <klone/ioprv.h>
00022 #include <klone/http.h>
00023 #include <klone/addr.h>
00024 #include <klone/vars.h>
00025 #include <klone/timer.h>
00026 
00027 
00028 struct request_s
00029 {
00030     http_t *http;               /* http server handle                       */
00031     header_t *header;           /* input header                             */
00032     io_t *io;                   /* input io stream                          */
00033     int method;                 /* get,post,etc.                            */
00034     char *cli_rq;               /* verbatim client request line             */
00035     char *uri;                  /* verbatim uri asked by the client         */
00036     char *protocol;             /* proto/ver                                */
00037     char *path_info;            /* extra info at the end of the path        */
00038     char *query;                /* query string (data after '?')            */
00039     char *filename;             /* path of the req resource                 */
00040     char *resolved_path_info;   /* resolved path_info                       */
00041     char *resolved_filename;    /* unaliased filename                       */
00042     vars_t *args;               /* get/post args                            */
00043     vars_t *cookies;            /* cookies                                  */
00044     vars_t *uploads;            /* uploaded file list                       */
00045     char *content_type;         /* type/subtype                             */
00046     char *content_encoding;     /* 7bit/8bit/base64/qp, etc                 */
00047         size_t content_length;      /* content-length http header field         */
00048     time_t if_modified_since;   /* time_t IMS header                        */
00049     addr_t local_addr, peer_addr; /* local and perr address                 */
00050     int cgi;                    /* if running in cgi mode                   */
00051     size_t idle_timeout;        /* max # of secs to wait for the request    */
00052     size_t post_timeout;        /* max # of secs for reading POSTed data    */
00053     size_t post_maxsize;        /* max # of POSTed bytes to accepts         */
00054 };
00055 
00056 typedef struct upload_info_s    /* uploaded file info struct         */
00057 {
00058     char mime_type[MIME_TYPE_BUFSZ];
00059     char filename[U_FILENAME_MAX];
00060     size_t size;
00061 } upload_info_t;
00062 
00063 enum { 
00064     REQUEST_DEFAULT_IDLE_TIMEOUT = 10,         /* 10 secs */
00065     REQUEST_DEFAULT_POST_TIMEOUT = 600,        /* 10 mins */
00066     REQUEST_DEFAULT_POST_MAXSIZE = 5*1024000,  /* 5 MB    */
00067 };
00068 
00069 
00070 #define REQUEST_SET_STRING_FIELD(lval, rval)        \
00071     do {                                            \
00072         U_FREE(lval);                               \
00073         if(rval)                                    \
00074         {                                           \
00075             lval = u_strdup(rval);                  \
00076             dbg_err_if(lval == NULL);               \
00077         }                                           \
00078     } while(0)
00079 
00080 
00081 int request_is_encoding_accepted(request_t *rq, const char *encoding)
00082 {
00083     char *pp, *tok, *src, *buf = NULL;
00084     const char *accept_encoding;
00085     int rc = 0;
00086 
00087     dbg_err_if (rq == NULL);
00088     dbg_err_if (encoding == NULL);
00089     
00090     accept_encoding = header_get_field_value(rq->header, "Accept-Encoding");
00091     if(accept_encoding)
00092     {
00093         /* get a copy to work on */
00094         buf = u_strdup(accept_encoding);
00095         dbg_err_if(buf == NULL);
00096 
00097         /* foreach encoding pair... */
00098         for(src = buf; (tok = strtok_r(src, " ,", &pp)) != NULL; src = NULL)
00099         {
00100             if(strcasecmp(tok, encoding) == 0)
00101             {
00102                 rc++; /* found */
00103                 break;
00104             }
00105         }
00106 
00107         U_FREE(buf);
00108     }
00109 
00110     return rc;
00111 err:
00112     U_FREE(buf);
00113     return 0;
00114 }
00115 
00131 io_t *request_io(request_t *rq)
00132 {
00133     dbg_return_if (rq == NULL, NULL);
00134 
00135     return rq->io;
00136 }
00137 
00149 vars_t *request_get_cookies(request_t *rq)
00150 {
00151     dbg_return_if (rq == NULL, NULL);
00152 
00153     return rq->cookies;
00154 }
00155 
00167 const char *request_get_cookie(request_t *rq, const char *name)
00168 {
00169     var_t *v;
00170 
00171     dbg_return_if (rq == NULL, NULL);
00172     dbg_return_if (name == NULL, NULL);
00173 
00174     v = vars_get(rq->cookies, name);
00175 
00176     return v ? var_get_value(v): NULL;
00177 }
00178 
00189 vars_t *request_get_args(request_t *rq)
00190 {
00191     dbg_return_if (rq == NULL, NULL);
00192 
00193     return rq->args;
00194 }
00195 
00209 const char *request_get_arg(request_t *rq, const char *name)
00210 {
00211     var_t *v;
00212 
00213     dbg_return_if (rq == NULL, NULL);
00214     dbg_return_if (name == NULL, NULL);
00215 
00216     v = vars_get(rq->args, name);
00217 
00218     return v ? var_get_value(v): NULL;
00219 }
00220 
00221 int request_set_field(request_t *rq, const char *name, const char *value)
00222 {
00223     dbg_return_if (rq == NULL, ~0);
00224     dbg_return_if (name == NULL, ~0);
00225     dbg_return_if (value == NULL, ~0);
00226 
00227     return header_set_field(rq->header, name, value);
00228 }
00229 
00240 const char *request_get_uri(request_t *rq)
00241 {
00242     dbg_return_if (rq == NULL, NULL);
00243 
00244     return rq->uri;
00245 }
00246 
00257 const char *request_get_filename(request_t *rq)
00258 {
00259     dbg_return_if (rq == NULL, NULL);
00260 
00261     return rq->filename;
00262 }
00263 
00264 /*
00265  * \ingroup request
00266  * \brief   Set the filename field of a request
00267  *
00268  * Set the filename field of request \p rq to \p filename.
00269  *
00270  * \param rq        request object
00271  * \param filename  filename string
00272  *
00273  * \return \c 0 if successful, non-zero on error
00274  */
00275 int request_set_filename(request_t *rq, const char *filename)
00276 {
00277     dbg_err_if (rq == NULL);
00278     dbg_err_if (filename == NULL);
00279     
00280     REQUEST_SET_STRING_FIELD(rq->filename, filename);
00281 
00282     return 0;
00283 err:
00284     return ~0;
00285 }
00286 
00297 const char *request_get_query_string(request_t *rq)
00298 {
00299     dbg_return_if (rq == NULL, NULL);
00300 
00301     return rq->query;
00302 }
00303 
00314 const char *request_get_path_info(request_t *rq)
00315 {
00316     dbg_return_if (rq == NULL, NULL);
00317 
00318     return rq->path_info;
00319 }
00320 
00321 /* parse and set if-modified-since value */
00322 static int request_parse_ims(request_t *rq)
00323 {
00324     const char *ims;
00325 
00326     dbg_err_if (rq == NULL);
00327     
00328     rq->if_modified_since = 0;
00329 
00330     ims = header_get_field_value(rq->header, "If-Modified-Since");
00331     if(ims)
00332         dbg_err_if(u_httpdate_to_tt(ims, &rq->if_modified_since));
00333 
00334 err: /* ignore if it's not formatted properly */
00335     return 0;
00336 }
00337 
00348 time_t request_get_if_modified_since(request_t *rq)
00349 {
00350     dbg_return_if (rq == NULL, (time_t) -1);
00351 
00352     return rq->if_modified_since;
00353 }
00354 
00355 /*
00356  * \ingroup request
00357  * \brief   Set the resolved filename field of a request
00358  *
00359  * Set the resolved filename field of request \p rq to \p resolved_fn
00360  *
00361  * \param rq          request object
00362  * \param resolved_fn resolved filename
00363  *
00364  * \return \c 0 if successful, non-zero on error
00365  */
00366 int request_set_resolved_filename(request_t *rq, const char *resolved_fn)
00367 {
00368     dbg_err_if (rq == NULL);
00369     dbg_err_if (resolved_fn == NULL);
00370 
00371     REQUEST_SET_STRING_FIELD(rq->resolved_filename, resolved_fn);
00372 
00373     return 0;
00374 err:
00375     return ~0;
00376 }
00377 
00388 http_t* request_get_http(request_t *rq)
00389 {
00390     dbg_return_if (rq == NULL, NULL);
00391 
00392     return rq->http;
00393 }
00394 
00395 /*
00396  * \ingroup request
00397  * \brief   Bind request I/O to a given I/O. 
00398  *  
00399  * Bind the I/O of request \p rq to \p in.
00400  *
00401  * \param rq    request object
00402  * \param in    input I/O object
00403  *
00404  * \return \c 0 if successful, non-zero on error
00405  */
00406 int request_bind(request_t *rq, io_t *in)
00407 {
00408     dbg_return_if (rq == NULL, ~0);
00409     dbg_return_if (in == NULL, ~0);
00410 
00411     rq->io = in;
00412 
00413     return 0;
00414 }
00415 
00416 /*
00417  * \ingroup request
00418  * \brief   Set the query string of a request
00419  *
00420  * Parse \p query string and build the \p rq->args list.
00421  *
00422  * \param rq     request object
00423  * \param query  query string 
00424  *
00425  * \return \c 0 if successful, non-zero on error
00426  */
00427 int request_set_query_string(request_t *rq, const char *query)
00428 {
00429     dbg_err_if (rq == NULL);
00430     dbg_err_if (query == NULL);
00431     
00432     REQUEST_SET_STRING_FIELD(rq->query, query);
00433 
00434     return 0;
00435 err:
00436     return ~0;
00437 }
00438 
00439 void request_clear_uri(request_t *rq)
00440 {
00441     U_FREE(rq->uri);
00442     U_FREE(rq->protocol);
00443     U_FREE(rq->path_info);
00444     U_FREE(rq->query);
00445     U_FREE(rq->filename);
00446     U_FREE(rq->resolved_path_info);
00447     U_FREE(rq->resolved_filename);
00448     U_FREE(rq->content_type);
00449     U_FREE(rq->content_encoding);
00450 }
00451 
00452 /*
00453  * \ingroup request
00454  * \brief   Set the path info field of a request
00455  *
00456  * Set the path info field of request \p rq to \p path_info.
00457  *
00458  * \param rq         request object
00459  * \param path_info  path info
00460  *
00461  * \return \c 0 if successful, non-zero on error
00462  */
00463 int request_set_path_info(request_t *rq, const char *path_info)
00464 {
00465     dbg_err_if (rq == NULL);
00466     dbg_err_if (path_info == NULL);
00467 
00468     REQUEST_SET_STRING_FIELD(rq->path_info, path_info);
00469 
00470     return 0;
00471 err:
00472     return ~0;
00473 }
00474 
00475 /*
00476  * \ingroup request
00477  * \brief   Set the resolved path info field of a request
00478  *
00479  * Set the resolved path info field of request \p rq to \p resolved_pi.
00480  *
00481  * \param rq           request object
00482  * \param resolved_pi  resolved path info
00483  *
00484  * \return \c 0 if successful, non-zero on error
00485  */
00486 int request_set_resolved_path_info(request_t *rq, const char *resolved_pi)
00487 {
00488     dbg_err_if (rq == NULL);
00489     dbg_err_if (resolved_pi == NULL);
00490 
00491     REQUEST_SET_STRING_FIELD(rq->resolved_path_info, resolved_pi);
00492 
00493     return 0;
00494 err:
00495     return ~0;
00496 }
00497 
00498 /*
00499  * \ingroup request
00500  * \brief   Set the URI field of a request
00501  *
00502  * Set the URI field of request \p rq to \p uri given 
00503  *
00504  * \param rq           request object
00505  * \param uri          URI string
00506  * \param is_valid_uri URI validation function 
00507  * \param arg          argument to is_valid_uri
00508  *
00509  * \return \c 0 if successful, non-zero on error
00510  */
00511 int request_set_uri(request_t *rq, const char *uri,
00512         int (*is_valid_uri)(void*, const char *, size_t),
00513         void* arg)
00514 {
00515     enum { REQUEST_URI_MAX_LENGTH = 4095 };
00516     char *p, *fn, *pi, *cp = NULL;
00517     size_t uri_len = strlen(uri);
00518 
00519     dbg_err_if (rq == NULL);
00520     dbg_err_if (uri == NULL);
00521     /* is_valid_uri may be NULL */
00522     /* arg may be NULL */
00523  
00524     request_clear_uri(rq);
00525 
00526     /* this is just to avoid recursive infinite redirect loops for pages that 
00527        appends something to the URI and redirects to the same page */
00528     warn_err_ifm(uri_len > REQUEST_URI_MAX_LENGTH, "Request URI too long");
00529 
00530     REQUEST_SET_STRING_FIELD(rq->uri, uri);
00531 
00532     /* save (undecoded) query string i.e. everything after '?' */
00533     if((p = strchr(uri, '?')) != NULL)
00534         dbg_err_if(request_set_query_string(rq, ++p));
00535 
00536     cp = (char*)u_malloc(uri_len + 1);
00537     dbg_err_if(cp == NULL);
00538 
00539     /* copy decoded url */
00540     dbg_err_if(u_urlncpy(cp, rq->uri, uri_len, URLCPY_DECODE) <= 0);
00541 
00542     if((p = strchr(cp, '?')) != NULL)
00543         *p++ = 0; /* remove query string from the uri copy */
00544 
00545     /* normalize the URI (remove /../, /./, ecc) */
00546     dbg_err_if(u_uri_normalize(cp));
00547 
00548     /* set filename is case there's not path_info and/or file does not exists */
00549     dbg_err_if(request_set_filename(rq, cp));
00550 
00551     /* look for path_info */
00552     fn = cp;                    /* filename     */
00553     pi = fn + strlen(fn);       /* path_info    */
00554     for(;;)
00555     {
00556         if(is_valid_uri == NULL || is_valid_uri(arg, fn, pi - fn))
00557         {
00558             dbg_err_if(request_set_filename(rq, fn));
00559             rq->filename[pi-fn] = 0; /* trunc */
00560             if(strlen(pi))
00561                 dbg_err_if(request_set_path_info(rq, pi));
00562             break;
00563         } else {
00564             if((p = u_strnrchr(fn, '/', pi - fn)) == NULL)
00565                 break; /* file pointed by this uri does not exists */
00566             pi = p; /* try again */
00567         }
00568     }
00569 
00570     U_FREE(cp);
00571 
00572     return 0;
00573 err:
00574     U_FREE(cp);
00575     return ~0;
00576 }
00577 
00578 static int request_set_proto(request_t *rq, const char *proto)
00579 {
00580     dbg_err_if (rq == NULL);
00581     dbg_err_if (proto == NULL);
00582 
00583     /* be sure that the requested protocol is http */
00584     if(strncasecmp(proto, "http", 4))
00585         return ~0; /* unknown or unsupported protocol */
00586 
00587     REQUEST_SET_STRING_FIELD(rq->protocol, proto);
00588 
00589     return 0;
00590 err:
00591     return ~0;
00592 }
00593 
00594 /*
00595  * \ingroup request
00596  * \brief   Save client request
00597  *
00598  * Save client request line
00599  *
00600  * \param rq     request object
00601  * \param ln     the request line
00602  *
00603  * \return \c 0 if successful, non-zero on error
00604  */
00605 int request_set_client_request(request_t *rq, const char *ln)
00606 {
00607     char *p;
00608     dbg_err_if(rq == NULL);
00609     dbg_err_if(ln == NULL);
00610 
00611     rq->cli_rq = u_strdup(ln);
00612     dbg_err_if(rq->cli_rq == NULL);
00613 
00614     /* cut the trailing newline if any */
00615     for(p = rq->cli_rq; *p && (*p != '\r' && *p != '\n'); ++p)
00616         continue;
00617     *p = 0;
00618 
00619     return 0;
00620 err:
00621     return ~0;
00622 }
00623 
00634 const char *request_get_client_request(request_t *rq)
00635 {
00636     return rq->cli_rq;
00637 }
00638 
00639 /*
00640  * Set the \p method of request \p rq.  Refer to http.h for possible methods.
00641  *
00642  * \param rq     request object
00643  * \param method the HTTP method 
00644  *
00645  * \return \c 0 if successful, non-zero on error
00646  */
00647 int request_set_method(request_t *rq, const char *method)
00648 {
00649     dbg_return_if (rq == NULL, ~0);
00650     dbg_return_if (method == NULL, ~0);
00651 
00652     if(!strcasecmp(method, "get"))
00653         rq->method = HM_GET;
00654     else if(!strcasecmp(method, "head"))
00655         rq->method = HM_HEAD;
00656     else if(!strcasecmp(method, "post"))
00657         rq->method = HM_POST;
00658     else {
00659         /* put, delete, * */
00660         rq->method = HM_UNKNOWN;
00661         return ~0; /* unknown or unsupported method */
00662     }
00663     
00664     return 0;
00665 }
00666 
00667 static int request_set_content_length(request_t *rq)
00668 {
00669     const char *clen;
00670     size_t len;
00671 
00672     dbg_err_if (rq == NULL);
00673 
00674     clen = header_get_field_value(rq->header, "Content-Length");
00675     dbg_err_if(clen == NULL || (len = atoi(clen)) < 0);
00676 
00677     rq->content_length = len;
00678 
00679     return 0;
00680 err:
00681     return ~0;
00682 }
00683 
00684 static int request_parse_cookie(request_t *rq, field_t *field)
00685 {
00686     enum { BUFSZ = 4096 }; /* cookie size limit */
00687     char *pp, *tok, *src, buf[BUFSZ];
00688 
00689     dbg_err_if (rq == NULL);
00690     dbg_err_if (field == NULL);
00691     
00692     dbg_err_if(field_get_value(field) == NULL);
00693 
00694     /* save a copy to tokenize it */
00695     strncpy(buf, field_get_value(field), BUFSZ);
00696 
00697     /* foreach name=value pair... */
00698     for(src = buf; (tok = strtok_r(src, " ;", &pp)) != NULL; src = NULL)
00699         dbg_if(vars_add_urlvar(rq->cookies, tok, NULL));
00700 
00701     return 0;
00702 err:
00703     return ~0;
00704 }
00705 
00706 static int request_parse_cookies(request_t *rq)
00707 {
00708     field_t *f;
00709     size_t i, count;
00710 
00711     dbg_err_if (rq == NULL);
00712     
00713     count = header_field_count(rq->header);
00714     for(i = 0; i < count; ++i)
00715     {
00716         f = header_get_fieldn(rq->header, i);
00717         dbg_err_if(f == NULL); /* shouldn't happen */
00718         if(strcasecmp(field_get_name(f), "cookie") == 0)
00719             dbg_err_if(request_parse_cookie(rq, f));
00720     }
00721 
00722     return 0;
00723 err:
00724     return ~0;
00725 }
00726 
00727 static int request_parse_query_args(request_t *rq)
00728 {
00729     char *pp, *tok, *src, *query = NULL;
00730 
00731     dbg_err_if (rq == NULL);
00732 
00733     if(!rq->query)
00734         return 0; /* no args */
00735 
00736     /* dup to tokenize it */
00737     query = u_strdup(rq->query);
00738     dbg_err_if(query == NULL);
00739 
00740     /* foreach name=value pair... */
00741     for(src = query; (tok = strtok_r(src, "&", &pp)) != NULL; src = NULL)
00742     {
00743         /* create a new var_t obj and push it into the args vars-list */
00744         dbg_if(vars_add_urlvar(rq->args, tok, NULL));
00745     }
00746 
00747     U_FREE(query);
00748 
00749     return 0;
00750 err:
00751     U_FREE(query);
00752     return ~0;
00753 }
00754 
00755 /* set is-cgi flag */
00756 void request_set_cgi(request_t *rq, int cgi)
00757 {
00758     rq->cgi = cgi;
00759     return;
00760 }
00761 
00773 ssize_t request_get_content_length(request_t *rq)
00774 {
00775     dbg_return_if (rq == NULL, -1);
00776 
00777     return (ssize_t) rq->content_length;
00778 }
00779 
00780 static int match_content_type(header_t *h, const char *mime_type)
00781 {
00782     const char *ct;
00783 
00784     dbg_return_if (h == NULL, 0);
00785     dbg_return_if (mime_type == NULL, 0);
00786 
00787     ct = header_get_field_value(h, "Content-Type");
00788     if(ct == NULL || strncasecmp(ct, mime_type, strlen(mime_type)))
00789         return 0;
00790 
00791     return 1;
00792 }
00793 
00794 static int request_is_multipart_formdata(request_t *rq)
00795 {
00796     return match_content_type(rq->header, "multipart/form-data");
00797 }
00798 
00799 static int request_parse_urlencoded_data(request_t *rq)
00800 {
00801     ssize_t qsz, len;
00802 
00803     dbg_err_if (rq == NULL);
00804 
00805     len = rq->content_length; /* shortcut */
00806 
00807     qsz = (rq->query ? strlen(rq->query) : 0);
00808 
00809     /* alloc or enlarge the query string buffer */
00810     rq->query = u_realloc(rq->query, len + qsz + 2);
00811     dbg_err_if(rq->query == NULL);
00812 
00813     /* dbg("rq->query %x  size %u", rq->query, len+qsz+2); */
00814 
00815     rq->query[qsz] = 0; /* must be zero-term for strcat to work */
00816     if(qsz)
00817     {   /* append a '&' */
00818         strcat(rq->query, "&");
00819         ++qsz;
00820     }
00821 
00822     /* append to current query string */
00823     dbg_err_if(io_read(rq->io, rq->query + qsz, len) != len);
00824 
00825     /* zero terminate it */
00826     rq->query[qsz + len] = 0;
00827 
00828     /* parse rq->query and build the args var_t* array */
00829     dbg_err_if(request_parse_query_args(rq));
00830 
00831     return 0;
00832 err:
00833     return ~0;
00834 }
00835 
00836 /* return the value of the param named 'param_name' of the field 'field_name'
00837    and save it to 'buffer' */
00838 static int request_get_fieldparam(request_t *rq, const char *field_name, 
00839     const char *param_name, char *buf, size_t size)
00840 {
00841     const char *param_value, *field_value, *p;
00842     size_t pv_len;
00843 
00844     dbg_err_if (rq == NULL);
00845     dbg_err_if (field_name == NULL);
00846     dbg_err_if (param_name == NULL);
00847     dbg_err_if (buf == NULL);
00848     dbg_err_if (size == 0);
00849 
00850     field_value = header_get_field_value(rq->header, field_name);
00851     dbg_err_if(field_value == NULL);
00852 
00853     /* look for param name=value pair */
00854     param_value = u_stristr(field_value, param_name);
00855     dbg_err_if(param_value == NULL);
00856 
00857     /* skip param name */
00858     param_value += strlen(param_name);
00859 
00860     /* first char must be an equal sign */
00861     dbg_err_if(*param_value++ != '=');
00862 
00863     /* a param value ends on the first ';', space or at the end of string */
00864     for(p = param_value; ;++p)
00865         if(*p == '\0' || *p == ';' || isspace(*p))
00866             break; /* end of param value */
00867 
00868     /* param value len */
00869     pv_len = p - param_value;
00870 
00871     /* boundary check */
00872     dbg_err_if(pv_len > size - 1); 
00873 
00874     /* copy out the param value */
00875     strncpy(buf, param_value, pv_len);
00876     buf[MIN(pv_len, size - 1)] = 0;
00877 
00878     return 0;
00879 err:
00880     return ~0;
00881 }
00882 
00883 static int is_multipart_mixed(header_t *h)
00884 {
00885     return match_content_type(h, "multipart/mixed");
00886 }
00887 
00888 static int is_encoded(header_t *h)
00889 {
00890     const char *cte;
00891 
00892     dbg_return_if (h == NULL, 0);
00893 
00894     if((cte = header_get_field_value(h, "Content-Transfer-Encoding")) == NULL)
00895         return 0; /* not encoded */
00896 
00897     if(strcasecmp(cte, "binary") == 0)
00898         return 0; /* not encoded */
00899 
00900     return 1; /* encoded */
00901 }
00902 
00903 static inline int is_nl(char c)
00904 {
00905     return (c == '\n' || c == '\r' ? c : 0);
00906 }
00907 
00908 static inline int is_quote(char c)
00909 {
00910     return (c == '"' || c == '\'' ? c : 0);
00911 }
00912 
00913 static int parse_content_disposition(header_t *h, char *name, char *filename,
00914     size_t prmsz)
00915 {
00916     enum { BUFSZ = 512 };
00917     char *pp, *tok, *src, buf[BUFSZ];
00918     size_t n_len, fn_len;
00919     const char *cd;
00920     int q;
00921 
00922     dbg_err_if (h == NULL);
00923     dbg_err_if (name == NULL);
00924     dbg_err_if (filename == NULL);
00925     dbg_err_if (prmsz == 0);
00926     
00927     cd = header_get_field_value(h, "Content-Disposition");
00928     dbg_err_if(cd == NULL);
00929 
00930     dbg_err_if(strlen(cd) >= BUFSZ);
00931 
00932     /* must start with form-data */
00933     dbg_err_if(strncmp(cd, "form-data", strlen("form-data")));
00934 
00935     name[0] = filename[0] = 0;
00936 
00937     /* save a copy to tokenize it */
00938     strncpy(buf, cd, BUFSZ);
00939 
00940     /* shortcut */
00941     n_len = strlen("name=");
00942     fn_len = strlen("filename=");
00943 
00944     /* foreach name=value pair... */
00945     for(src = buf; (tok = strtok_r(src, " ;", &pp)) != NULL; src = NULL)
00946     {
00947         if(strncmp(tok, "form-data", strlen("form-data")) == 0)
00948             continue;   /* skip */
00949         else if(strncmp(tok, "name=", n_len) == 0) {
00950             /* skip the name part */
00951             tok += n_len;
00952 
00953             /* remove single or double quotes */
00954             if((q = is_quote(tok[0])) != 0)
00955                 ++tok;
00956             if(strlen(tok) && tok[strlen(tok) - 1] == q)
00957                 tok[strlen(tok) - 1] = 0;
00958 
00959             strncpy(name, tok, prmsz);
00960         } else if(strncmp(tok, "filename=", fn_len) == 0) {
00961             /* skip the filename part */
00962             tok += fn_len;
00963 
00964             /* remove single or double quotes */
00965             if((q = is_quote(tok[0])) != 0)
00966                 ++tok;
00967             if(strlen(tok) && tok[strlen(tok) - 1] == q)
00968                 tok[strlen(tok) - 1] = 0;
00969 
00970             strncpy(filename, tok, prmsz);
00971         } 
00972         /* else ignore unknown fields */
00973     }
00974             
00975     return 0;
00976 err:
00977     return ~0;
00978 }
00979 
00980 /* 
00981  * Read from io until obuf is full or until stop_at string is found.
00982  *
00983  * Boyer-Moore algorithm is used for efficiency. 
00984  *
00985  * Returns the number of bytes written to obuf 
00986  */
00987 static ssize_t read_until(io_t *io, const char *stop_at, char *obuf, 
00988     size_t size, int *found)
00989 {
00990     /* use this macro before accessing obuf[idx] elem. the macro will load from
00991        the given io enough bytes to access the required byte. if the buffer
00992        is too small (i.e. less then idx bytes long) the function will return */
00993     #define SETUP_BUF_ACCESS_AT(idx)                                        \
00994         if(idx >= wtot) {                                                   \
00995             if(idx >= size)                                                 \
00996                 return wtot; /* the output buffer is full */                \
00997                                                                             \
00998             /* we need to fetch some more bytes to access obuf[i] */        \
00999             dbg_err_if((rc = io_read(io, wbuf, idx + 1 - wtot)) < 0);       \
01000             if(rc == 0 || rc < idx + 1 - wtot)                              \
01001                 return wtot + rc; /* eof or short count */                  \
01002             wbuf += rc;                                                     \
01003             wtot += rc;                                                     \
01004         }
01005 
01006     int sa_len = strlen(stop_at);
01007     int i, t, shift[256], rc;
01008     unsigned char c;
01009     size_t wtot = 0;
01010     char *wbuf = obuf;
01011 
01012     dbg_err_if (io == NULL);
01013     dbg_err_if (stop_at == NULL);
01014     dbg_err_if (obuf == NULL);
01015     /* size may be 0 */
01016     dbg_err_if (found == NULL);
01017 
01018     for(i = 0; i < 256; ++i)  
01019         shift[i] = sa_len;
01020 
01021     for(i = 0; i < sa_len; ++i)
01022         shift[ stop_at[i] ] = sa_len - i - 1;
01023 
01024     *found = 0;
01025 
01026     for(i = t = sa_len-1; t >= 0; --i, --t)
01027     {
01028         SETUP_BUF_ACCESS_AT(i);
01029 
01030         while((c = obuf[i]) != stop_at[t]) 
01031         {
01032             i += MAX(sa_len - t, shift[c]);
01033 
01034             SETUP_BUF_ACCESS_AT(i);
01035 
01036             t = sa_len - 1;
01037         }
01038     }
01039 
01040     *found = 1;
01041 
01042     /* found; obuf[i] is where the matching string is */
01043     return wtot;
01044 err:
01045     return -1;
01046 }
01047 
01048 
01067 vars_t *request_get_uploads(request_t *rq)
01068 {
01069     return rq->uploads;
01070 }
01071 
01072 /*
01073  * name:         form "name" <input> tag attribute value
01074  * filename:     name of the uploaded file provided by the client
01075  * tmp_filename: name on the temp file when the uploaded data has been saved
01076  *               on the local disk
01077  * mime_type:    uploaded MIME type as stated by the browser (may be NULL)
01078  */
01079 static int request_add_uploaded_file(request_t *rq, const char *name, 
01080     const char *filename, const char *tmp_filename, const char *mime_type)
01081 {
01082     struct stat st;
01083     var_t *v = NULL;
01084     upload_info_t *info = NULL;
01085 
01086     dbg_err_if (rq == NULL);
01087     dbg_err_if (name == NULL);
01088     /* filename may be NULL */
01089     dbg_err_if (tmp_filename == NULL);
01090     /* mime_type may be NULL */
01091 
01092     dbg_err_sif (stat(tmp_filename, &st) < 0);
01093 
01094     /* create a new var obj */
01095     dbg_err_if(var_create(name, tmp_filename, &v));
01096 
01097     /* alloc an info struct to attach to the var_t obj */
01098     dbg_err_if((info = u_zalloc(sizeof(upload_info_t))) == NULL);
01099 
01100     /* set info data */
01101     info->size = st.st_size;
01102     if(mime_type)
01103         snprintf(info->mime_type, MIME_TYPE_BUFSZ, "%s", mime_type);
01104     else
01105         info->mime_type[0] = 0;
01106 
01107     if(filename)
01108         snprintf(info->filename, U_FILENAME_MAX, "%s", filename);
01109 
01110     /* attach info to v */
01111     var_set_opaque(v, info);
01112     info = NULL;
01113 
01114     /* push into the cookie list */
01115     dbg_err_if(vars_add(rq->uploads, v));
01116 
01117     return 0;
01118 err:
01119     if(info)
01120         U_FREE(info);
01121     if(v)
01122         var_free(v);
01123     return ~0;
01124 }
01125 
01126 static int request_get_uploaded_filev(request_t *rq, var_t *v,
01127     char local_filename[U_FILENAME_MAX], char client_filename[U_FILENAME_MAX],
01128     char mime_type[MIME_TYPE_BUFSZ], size_t *file_size)
01129 {           
01130     upload_info_t *info;
01131     const char *tmp_fqn;
01132 
01133     dbg_err_if (rq == NULL);
01134     dbg_err_if (v == NULL);
01135     dbg_err_if (local_filename == NULL);
01136     dbg_err_if (client_filename == NULL);
01137     dbg_err_if (mime_type == NULL);
01138     dbg_err_if (file_size == NULL);
01139 
01140     info = var_get_opaque(v);
01141     dbg_err_if(info == NULL);
01142 
01143     tmp_fqn = var_get_value(v);
01144     dbg_err_if(tmp_fqn == NULL);
01145 
01146     /* copy out return values */
01147     strncpy(local_filename, tmp_fqn, U_FILENAME_MAX);
01148     strncpy(mime_type, info->mime_type, MIME_TYPE_BUFSZ);
01149     strncpy(client_filename, info->filename, U_FILENAME_MAX);
01150     *file_size = info->size;
01151 
01152     return 0;
01153 err:
01154     return ~0;
01155 }
01156 
01179 int request_get_uploaded_file(request_t *rq, const char *name, size_t idx,
01180     char local_filename[U_FILENAME_MAX], char client_filename[U_FILENAME_MAX],
01181     char mime_type[MIME_TYPE_BUFSZ], size_t *file_size)
01182 {
01183     var_t *v;
01184     upload_info_t *info;
01185     const char *tmp_fqn;
01186 
01187     dbg_err_if (rq == NULL);
01188     dbg_err_if (name == NULL);
01189     dbg_err_if (idx >= vars_count(rq->uploads));
01190     dbg_err_if (local_filename == NULL);
01191     dbg_err_if (client_filename == NULL);
01192     dbg_err_if (mime_type == NULL);
01193     dbg_err_if (file_size == NULL);
01194 
01195     v = vars_geti(rq->uploads, name, idx);
01196     dbg_err_if(v == NULL);
01197 
01198     return request_get_uploaded_filev(rq, v, local_filename, client_filename,
01199         mime_type, file_size);
01200 err:
01201     return ~0;
01202 }
01203 
01204 static int request_parse_multipart_chunk(request_t *rq, io_t *io, 
01205     const char *boundary, int *eof)
01206 {
01207     enum { PRMSZ = 512, BUFSZ = 4096 };
01208     header_t *h = NULL;
01209     io_t *tmpio = NULL;
01210     var_t *v = NULL;
01211     char name[PRMSZ], filename[PRMSZ], buf[BUFSZ];
01212     size_t bound_len, len;
01213     int found;
01214     ssize_t rc;
01215 
01216     /* create an header object to parse MIME part headers */
01217     dbg_err_if(header_create(&h));
01218 
01219     /* read header lines until the first blank line */
01220     dbg_err_if(header_load(h, io));
01221 
01222     warn_err_ifm(is_multipart_mixed(h), 
01223         "multipart/mixed content is not supported yet");
01224 
01225     /* HTTP should never use cte */
01226     warn_err_ifm(is_encoded(h), 
01227         "encoded file upload is not supported");
01228 
01229     dbg_err_if(parse_content_disposition(h, name, filename, PRMSZ));
01230 
01231     /* shortcut */
01232     bound_len = strlen(boundary);
01233 
01234     if(filename[0] != '\0')
01235     {
01236         dbg_err_if(BUFSZ <= bound_len);
01237 
01238         /* open a temporary file to dump uploaded data */
01239         dbg_err_if(u_tmpfile_open(&tmpio));
01240 
01241         for(found = 0; !found; /* nothing */)
01242         {
01243             rc = read_until(io, boundary, buf, BUFSZ, &found);
01244             dbg_err_if(rc <= 0); /* on error or eof exit */
01245 
01246             /* write all but the last bound_len + 2 (\r\n) bytes */
01247             if(found)
01248             {
01249                 rc -= (bound_len + 2);
01250                 dbg_err_if(rc < 0);
01251             }
01252 
01253             /* write to the temp file */
01254             dbg_err_if(io_write(tmpio, buf, rc) < 0);
01255         }
01256 
01257         /* save the path/name of the tmp file to buf */
01258         dbg_err_if(io_name_get(tmpio, buf, BUFSZ));
01259 
01260         /* flush and free */
01261         io_free(tmpio); tmpio = NULL;
01262 
01263         /* add this file to the uploaded file list */
01264         dbg_err_if(request_add_uploaded_file(rq, name, filename, buf, 
01265             header_get_field_value(h, "Content-Type")));
01266 
01267         /* could be "\r\n" for not-ending boundaries or "--\r\n" */
01268         dbg_err_if(io_gets(io, buf, BUFSZ) <= 0);
01269 
01270         if(strncmp(buf, "--", 2) == 0)
01271             *eof = 1; /* end of MIME stuff */
01272 
01273     } else {
01274         /* read the value of the variable (all until the next boundary) */
01275         rc = read_until(io, boundary, buf, BUFSZ, &found);
01276         dbg_err_if(rc <= 0); /* on error or eof exit */
01277 
01278         /* write all but the last bound_len + 2 (\r\n) bytes */
01279         warn_err_ifm(!found, "malformed request or BUFSZ too small");
01280 
01281         rc -= (bound_len + 2);
01282         dbg_err_if(rc < 0);
01283 
01284         /* zero-term the buffer (removing the boundary) */
01285         buf[rc] = 0;
01286 
01287         /* add a new binary var to request arguments list */
01288         dbg_err_if(var_bin_create(name, buf, rc, &v));
01289         dbg_if(vars_add(rq->args, v));
01290 
01291         /* could be "\r\n" for not-ending boundaries or "--\r\n" */
01292         dbg_err_if(io_gets(io, buf, BUFSZ) <= 0);
01293 
01294         if(strncmp(buf, "--", 2) == 0)
01295             *eof = 1; /* end of MIME stuff */
01296     }
01297 
01298     header_free(h);
01299 
01300     return 0;
01301 err:
01302     if(tmpio)
01303         io_free(tmpio);
01304     if(h)
01305         header_free(h);
01306     return ~0;
01307 }
01308 
01309 static int request_parse_multipart_data(request_t *rq)
01310 {
01311     enum { BOUNDARY_BUFSZ = 128, BUFSZ = 1024 }; 
01312     char boundary[BOUNDARY_BUFSZ], buf[BUFSZ];
01313     int eof;
01314 
01315     /* boundaries always start with -- */
01316     strcpy(boundary, "--");
01317 
01318     dbg_err_if(request_get_fieldparam(rq, "Content-Type", "boundary",
01319         boundary + 2, BOUNDARY_BUFSZ - 2));
01320 
01321     dbg_err_if(strlen(boundary) == 0);
01322 
01323     /* skip the MIME preamble (usually not used in HTTP) */
01324     for(;;)
01325     {
01326         dbg_err_if(io_gets(rq->io, buf, BUFSZ) <= 0);
01327         if(!strncmp(buf, boundary, strlen(boundary)))
01328             break; /* boundary found */
01329     }
01330 
01331     /* cycle on each MIME part */
01332     for(eof = 0; eof == 0; )
01333         dbg_err_if(request_parse_multipart_chunk(rq, rq->io, boundary, &eof));
01334 
01335     return 0;
01336 err:
01337     return ~0;
01338 }
01339 
01340 static int request_cb_close_socket(talarm_t *al, void *arg)
01341 {
01342     io_t *io = (io_t*)arg;
01343 
01344     u_unused_args(al);
01345 
01346     warn("[%x] connection timed out, closing", io);
01347     
01348     /* close the stream (but not free it) */
01349     io_close(io);
01350 
01351     return 0;
01352 }
01353 
01354 int request_parse_data(request_t *rq)
01355 {
01356     talarm_t *al = NULL;
01357     int rc = HTTP_STATUS_BAD_REQUEST;
01358 
01359     if(rq->method == HM_POST)
01360     {
01361         /* set a timeout to abort POST if it takes too much... */
01362         dbg_err_if(timerm_add(rq->post_timeout, request_cb_close_socket, 
01363             (void*)rq->io, &al));
01364 
01365         /* Content-Length is required when using POST */
01366         dbg_err_if(request_set_content_length(rq) && 
01367             (rc = HTTP_STATUS_LENGTH_REQUIRED));
01368 
01369         if(rq->content_length == 0)
01370             return 0; /* no data posted */
01371 
01372         /* abort if the client is pushing too much data */
01373         dbg_err_if(rq->content_length > rq->post_maxsize &&
01374             (rc = HTTP_STATUS_REQUEST_TOO_LARGE));
01375 
01376         if(request_is_multipart_formdata(rq))
01377         { 
01378             /* some vars may be urlencoded */
01379             dbg_err_if(request_parse_query_args(rq));
01380 
01381             /* <form enctype="multipart/form-data" ...> */
01382             dbg_err_if(request_parse_multipart_data(rq));
01383         } else {
01384             /* <form [enctype="application/x-www-form-urlencoded"] ...> */
01385             dbg_err_if(request_parse_urlencoded_data(rq));
01386         }
01387 
01388         /* post timeout not expired, clear it */
01389         dbg_if(timerm_del(al)); al = NULL;
01390     } else {
01391         /* parse urlencoded variables and set var_t* array */
01392         dbg_err_if(request_parse_query_args(rq));
01393     }
01394 
01395     return 0;
01396 err:
01397     return rc;
01398 }
01399 
01400 /*
01401  * Parse request object \p rq.
01402  *
01403  * \param rq            request object
01404  * \param is_valid_uri  URI validation function
01405  * \param arg           argument to is_valid_uri
01406  *
01407  * \return \c 0 if successful, non-zero on error
01408  */
01409 int request_parse_header(request_t *rq, 
01410         int (*is_valid_uri)(void*, const char *, size_t),
01411         void* arg)
01412 {
01413     enum { BUFSZ = 4096 };
01414     const char WP[] = " \t\r\n";
01415     char ln[BUFSZ], *pp, *method, *uri, *proto;
01416     talarm_t *al = NULL;
01417     int rc = HTTP_STATUS_BAD_REQUEST;
01418     
01419     dbg_err_if (rq == NULL);
01420     dbg_err_if (rq->io == NULL); /* must call rq_bind before rq_parse */
01421 
01422     /* wait at most N seconds to receive the request */
01423     dbg_err_if(timerm_add(rq->idle_timeout, request_cb_close_socket, 
01424         (void*)rq->io, &al));
01425 
01426     if(!rq->cgi)
01427     {
01428         /* cp the first line */
01429         dbg_err_if(io_gets(rq->io, ln, BUFSZ) <= 0);
01430 
01431         /* save the verbatim request line */
01432         dbg_err_if(request_set_client_request(rq, ln));
01433 
01434         method = strtok_r(ln, WP, &pp); 
01435         dbg_err_if(!method || request_set_method(rq, method));
01436 
01437         uri = strtok_r(NULL, WP, &pp);
01438         dbg_err_if(!uri || request_set_uri(rq, uri, is_valid_uri, arg));
01439 
01440         /* HTTP/0.9 not supported yet */ 
01441         proto = strtok_r(NULL, WP, &pp);
01442         dbg_err_if(!proto || request_set_proto(rq, proto)); 
01443 
01444         dbg_err_if(header_load(rq->header, rq->io));
01445     } else
01446         dbg_err_if(header_load_from_cgienv(rq->header));
01447 
01448     /* set if-modified-since time_t value */
01449     dbg_err_if(request_parse_ims(rq));
01450 
01451     /* parse "Cookie:" fields and set the cookies vars_t */
01452     dbg_err_if(request_parse_cookies(rq));
01453 
01454     /* Content-Length is required when using POST */
01455     if(request_get_method(rq) == HM_POST)
01456         dbg_err_if(request_set_content_length(rq));
01457 
01458     /* idle timeout not expired, clear it */
01459     dbg_if(timerm_del(al)); al = NULL;
01460 
01461     /* parse URL encoded or POSTed data */
01462     /* dbg_err_if((rc = request_parse_data(rq))); */
01463 
01464     return 0;
01465 err:
01466     if(al)
01467         timerm_del(al);
01468     return rc;
01469 }
01470 
01482 int request_get_method(request_t *rq)
01483 {
01484     dbg_return_if (rq == NULL, HM_UNKNOWN);
01485 
01486     return rq->method;
01487 }
01488 
01500 const char* request_get_protocol(request_t *rq)
01501 {
01502     dbg_return_if (rq == NULL, "unknown");
01503 
01504     return rq->protocol;
01505 }
01506 
01517 const char *request_get_resolved_filename(request_t *rq)
01518 {
01519     dbg_return_if (rq == NULL, NULL);
01520 
01521     return rq->resolved_filename;
01522 }
01523 
01534 const char *request_get_resolved_path_info(request_t *rq)
01535 {
01536     dbg_return_if (rq == NULL, NULL);
01537 
01538     return rq->resolved_path_info;
01539 }
01540 
01541 int request_print(request_t *rq)
01542 {
01543     dbg_return_if (rq == NULL, ~0);
01544 
01545     dbg("method: %u", rq->method);
01546     dbg("uri: %s", rq->uri);
01547     dbg("proto: %s", rq->protocol);
01548     dbg("filename: %s", rq->filename);
01549     dbg("resolved filename: %s", rq->resolved_filename);
01550     dbg("path_info: %s", rq->path_info);
01551     dbg("resolved path_info: %s", rq->resolved_path_info);
01552     dbg("query: %s", rq->query);
01553 
01554     return 0;
01555 }
01556 
01557 static int request_load_config(request_t *rq)
01558 {
01559     u_config_t *c;
01560     const char *v;
01561 
01562     dbg_err_if (rq == NULL);
01563     dbg_err_if (rq->http == NULL);
01564     dbg_err_if (http_get_config(rq->http) == NULL);
01565 
01566     c = http_get_config(rq->http);
01567     
01568     /* defaults */
01569     rq->idle_timeout = REQUEST_DEFAULT_IDLE_TIMEOUT;
01570     rq->post_timeout = REQUEST_DEFAULT_POST_TIMEOUT;
01571     rq->post_maxsize = REQUEST_DEFAULT_POST_MAXSIZE;
01572 
01573     /* idle timeout */
01574     if((v = u_config_get_subkey_value(c, "idle_timeout")) != NULL)
01575         rq->idle_timeout = MAX(1, atoi(v));
01576 
01577     /* post timeout */
01578     if((v = u_config_get_subkey_value(c, "post_timeout")) != NULL)
01579         rq->post_timeout = MAX(5, atoi(v));
01580 
01581     /* post maxsize */
01582     if((v = u_config_get_subkey_value(c, "post_maxsize")) != NULL)
01583         rq->post_maxsize = MAX(1024, atoi(v));
01584 
01585     return 0;
01586 err:
01587     return ~0;
01588 }
01589 
01590 int request_create(http_t *http, request_t **prq)
01591 {
01592     request_t *rq = NULL;
01593 
01594     dbg_return_if (prq == NULL, ~0);
01595     dbg_return_if (http == NULL, ~0);
01596 
01597     rq = u_zalloc(sizeof(request_t));
01598     dbg_err_if(rq == NULL);
01599 
01600     dbg_err_if(header_create(&rq->header));
01601 
01602     dbg_err_if(vars_create(&rq->args));
01603     dbg_err_if(vars_create(&rq->cookies));
01604     dbg_err_if(vars_create(&rq->uploads));
01605 
01606     rq->http = http;
01607 
01608     dbg_err_if(request_load_config(rq));
01609 
01610     *prq = rq;
01611 
01612     return 0;
01613 err:
01614     if(rq)
01615         request_free(rq);
01616     return ~0;
01617 }
01618 
01619 static int request_unlink_uploads(var_t *v, void * arg)
01620 {
01621     dbg_err_if (v == NULL);
01622 
01623     u_unused_args(arg);
01624 
01625     if(var_get_opaque(v) && var_get_value(v))
01626     {   /* it's a file var, unlink v->value */
01627         unlink(var_get_value(v));
01628     }
01629 
01630 err:
01631     return 0;
01632 }
01633 
01634 int request_free(request_t *rq)
01635 {
01636     if (rq)
01637     {
01638         /* free internal stuff */
01639         request_clear_uri(rq);
01640 
01641         if(rq->header)
01642             header_free(rq->header);
01643 
01644         if(rq->io)
01645             io_free(rq->io);
01646 
01647         if(rq->uploads)
01648         {
01649             /* unlink uploaded files (if any) */
01650             vars_foreach(rq->uploads, request_unlink_uploads, NULL);
01651             vars_free(rq->uploads);
01652         }
01653         
01654         if(rq->cookies)
01655             vars_free(rq->cookies);
01656 
01657         if(rq->args)
01658             vars_free(rq->args);
01659 
01660         U_FREE(rq);
01661     }
01662 
01663     return 0;
01664 }
01665 
01666 /* save the local address struct (ip and port) in the request obj */
01667 int request_set_addr(request_t *rq, addr_t *addr)
01668 {
01669     dbg_return_if (rq == NULL, ~0);
01670     dbg_return_if (addr == NULL, ~0);
01671 
01672     memcpy(&rq->local_addr, addr, sizeof(addr_t));
01673 
01674     return 0;
01675 }
01676 
01677 /* save the peer address struct (ip and port) in the request obj */
01678 int request_set_peer_addr(request_t *rq, addr_t *addr)
01679 {
01680     dbg_return_if (rq == NULL, ~0);
01681     dbg_return_if (addr == NULL, ~0);
01682 
01683     memcpy(&rq->peer_addr, addr, sizeof(addr_t));
01684 
01685     return 0;
01686 }
01687 
01698 addr_t* request_get_addr(request_t *rq)
01699 {
01700     dbg_return_if (rq == NULL, NULL);
01701 
01702     return &rq->local_addr;
01703 }
01704 
01715 addr_t* request_get_peer_addr(request_t *rq)
01716 {
01717     dbg_return_if (rq == NULL, NULL);
01718 
01719     return &rq->peer_addr;
01720 }
01721 
01733 header_t* request_get_header(request_t *rq)
01734 {
01735     dbg_return_if (rq == NULL, NULL);
01736 
01737     return rq->header;
01738 }
01739 
01751 field_t* request_get_field(request_t *rq, const char *name)
01752 {
01753     dbg_return_if (rq == NULL, NULL);
01754     dbg_return_if (name == NULL, NULL);
01755 
01756     return header_get_field(rq->header, name);
01757 }
01758 
01770 const char* request_get_field_value(request_t *rq, const char *name)
01771 {
01772     dbg_return_if (rq == NULL, NULL);
01773     dbg_return_if (name == NULL, NULL);
01774 
01775     return header_get_field_value(rq->header, name);
01776 }
01777