"Fossies" - the Fresh Open Source Software Archive

Member "httperf-0.9.0/src/http.c" (7 Apr 2007, 10217 Bytes) of package /linux/www/old/httperf-0.9.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "http.c" see the Fossies "Dox" file reference documentation.

    1 /*
    2     httperf -- a tool for measuring web server performance
    3     Copyright 2000-2007 Hewlett-Packard Company and Contributors listed in
    4     AUTHORS file. Originally contributed by David Mosberger-Tang
    5 
    6     This file is part of httperf, a web server performance measurment
    7     tool.
    8 
    9     This program is free software; you can redistribute it and/or
   10     modify it under the terms of the GNU General Public License as
   11     published by the Free Software Foundation; either version 2 of the
   12     License, or (at your option) any later version.
   13     
   14     In addition, as a special exception, the copyright holders give
   15     permission to link the code of this work with the OpenSSL project's
   16     "OpenSSL" library (or with modified versions of it that use the same
   17     license as the "OpenSSL" library), and distribute linked combinations
   18     including the two.  You must obey the GNU General Public License in
   19     all respects for all of the code used other than "OpenSSL".  If you
   20     modify this file, you may extend this exception to your version of the
   21     file, but you are not obligated to do so.  If you do not wish to do
   22     so, delete this exception statement from your version.
   23 
   24     This program is distributed in the hope that it will be useful,
   25     but WITHOUT ANY WARRANTY; without even the implied warranty of
   26     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   27     General Public License for more details.
   28 
   29     You should have received a copy of the GNU General Public License
   30     along with this program; if not, write to the Free Software
   31     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  
   32     02110-1301, USA
   33 */
   34 
   35 #include <assert.h>
   36 #include <ctype.h>
   37 #include <errno.h>
   38 #include <stdio.h>
   39 #include <stdlib.h>
   40 #include <string.h>
   41 
   42 #include <sys/types.h>
   43 
   44 #include <httperf.h>
   45 #include <http.h>
   46 #include <call.h>
   47 #include <event.h>
   48 #include <conn.h>
   49 
   50 /* Read a CRLF terminated line of characters into c->reply.line.
   51    Returns 1 when the line is complete, 0 when the line is incomplete
   52    and more data is needed.  */
   53 static int
   54 get_line (Call *c, char **bufp, size_t *buf_lenp)
   55 {
   56   size_t to_copy, buf_len = *buf_lenp;
   57   Conn *s = c->conn;
   58   char *buf = *bufp;
   59   const char *eol;
   60   int has_lf;
   61 
   62   if (buf_len <= 0)
   63     return 0;
   64 
   65   /* Note that core.c guarantees that BUF is '\0' terminated. */
   66   eol = strchr (buf, '\n');
   67   if (eol)
   68     ++eol;
   69   else
   70     eol = buf + buf_len;
   71 
   72   to_copy = eol - buf;
   73   buf_len -= to_copy;
   74   if (s->line.iov_len + to_copy >= sizeof (s->line_buf))
   75     {
   76       fprintf (stderr,
   77            "%s.get_line: truncating header from %lu to %lu bytes\n",
   78            prog_name, (u_long) (s->line.iov_len + to_copy),
   79            (u_long) sizeof (s->line_buf));
   80       to_copy = sizeof (s->line_buf) - 1 - s->line.iov_len;
   81     }
   82   memcpy ((char *) s->line.iov_base + s->line.iov_len, buf, to_copy);
   83   s->line.iov_len += to_copy;
   84 
   85   has_lf = ((char *) s->line.iov_base)[s->line.iov_len - 1] == '\n';
   86 
   87   *bufp = (char *) eol;
   88   *buf_lenp = buf_len;
   89 
   90   if (has_lf || s->line.iov_len == sizeof (s->line_buf) - 1)
   91     {
   92       /* We got a full header line.  Chop off \r\n at the tail if
   93      necessary.  */
   94       if (((char *) s->line.iov_base)[s->line.iov_len - 1] == '\n')
   95     {
   96       --s->line.iov_len;
   97       if (((char *) s->line.iov_base)[s->line.iov_len - 1] == '\r')
   98         --s->line.iov_len;
   99     }
  100       ((char *) s->line.iov_base)[s->line.iov_len] = '\0';
  101       return 1;
  102     }
  103   return 0;
  104 }
  105 
  106 static void
  107 parse_status_line (Call *c, char **bufp, size_t *buf_lenp)
  108 {
  109   char *buf, *buf_start = *bufp;
  110   u_int major, minor, status;
  111   Conn *s = c->conn;
  112   Any_Type arg;
  113 
  114   s->is_chunked = 0;
  115 
  116   /* default to "infinite" content length: */
  117   s->content_length = ~(size_t) 0;
  118 
  119   if (!get_line (c, bufp, buf_lenp))
  120     return;
  121 
  122   buf = c->conn->line.iov_base;
  123   if (sscanf (buf, "HTTP/%u.%u %u ", &major, &minor, &status) == 3)
  124     {
  125       c->reply.version = 0x10000*major + minor;
  126       c->reply.status = status;
  127     }
  128   else
  129     {
  130       c->reply.version = 0x10000;       /* default to 1.0 */
  131       c->reply.status = 599;
  132       if (c->reply.status == 599)
  133     fprintf (stderr, "%s.parse_status_line: invalid status line `%s'!!\n",
  134          prog_name, buf);
  135     }
  136   if (DBG > 0)
  137     fprintf (stderr,
  138          "parse_status_line.%lu: reply is HTTP/%u.%u, status = %d\n",
  139          c->id, c->reply.version / 0x10000, c->reply.version & 0xffff,
  140          c->reply.status);
  141 
  142   /* Determine whether we should be expecting a message body.  HEAD
  143      never includes an entity.  For other methods, things depend on
  144      the status code.  */
  145 
  146   if (strcmp ((char *) c->req.iov[IE_METHOD].iov_base, "HEAD") == 0)
  147     s->has_body = 0;
  148   else
  149     {
  150       s->has_body = 1;
  151       switch (status / 100)
  152     {
  153     case 1: /* informational */
  154       s->has_body = 0;
  155       if (status == 100)
  156         {
  157           arg.l = c->reply.status;
  158           event_signal (EV_CALL_RECV_START, (Object *) c, arg);
  159           s->state = S_REPLY_CONTINUE;
  160           goto done;
  161         }
  162       break;
  163 
  164     case 2: /* success */
  165     case 3: /* redirection */
  166       switch (status)
  167         {
  168         case 204: /* No Content */
  169         case 205: /* Reset Content */
  170         case 304: /* Not Modified */
  171           s->has_body = 0;
  172           break;
  173         }
  174       break;
  175 
  176     case 4: /* client errors */
  177     case 5: /* server errors */
  178       break;
  179 
  180     default:
  181       fprintf (stderr, "%s.parse_status_line: bad status %u\n",
  182            prog_name, status);
  183       break;
  184     }
  185     }
  186   arg.l = c->reply.status;
  187   event_signal (EV_CALL_RECV_START, (Object *) c, arg);
  188   if (s->state >= S_CLOSING)
  189     return;
  190   s->state = S_REPLY_HEADER;
  191 
  192  done:
  193   c->reply.header_bytes += *bufp - buf_start;
  194   s->line.iov_len = 0;      
  195 }
  196 
  197 static void
  198 parse_headers (Call *c, char **bufp, size_t *buf_lenp)
  199 {
  200   char *hdr, *buf_start = *bufp;
  201   Conn *s = c->conn;
  202   size_t hdr_len;
  203   Any_Type arg;
  204 
  205   while (get_line (c, bufp, buf_lenp) > 0)
  206     {
  207       hdr = s->line.iov_base;
  208       hdr_len = s->line.iov_len;
  209 
  210       if (!hdr_len)
  211     {
  212       /* empty header implies end of headers */
  213       if (s->has_body)
  214         if (s->is_chunked)
  215           {
  216         s->content_length = 0;
  217         s->state = S_REPLY_CHUNKED;
  218           }
  219         else
  220           s->state = S_REPLY_DATA;
  221       else if (s->state == S_REPLY_CONTINUE)
  222         s->state = S_REPLY_HEADER;
  223       else
  224         s->state = S_REPLY_DONE;
  225       break;
  226     }
  227 
  228       /* process line as a regular header: */
  229       switch (tolower (*hdr))
  230     {
  231     case 'c':
  232       if (strncasecmp (hdr, "content-length:", 15) == 0)
  233         {
  234           hdr += 15;
  235           s->content_length = strtoul (hdr, 0, 10);
  236           if (!s->content_length)
  237         s->has_body = 0;
  238         }
  239       break;
  240 
  241     case 't':
  242       if (strncasecmp (hdr, "transfer-encoding:", 18) == 0)
  243         {
  244           hdr += 18;
  245           while (isspace (*hdr))
  246         ++hdr;
  247           if (strcasecmp (hdr, "chunked") == 0)
  248         s->is_chunked = 1;
  249           else
  250         fprintf (stderr, "%s.parse_headers: unknown transfer "
  251              "encoding `%s'\n", prog_name, hdr);
  252         }
  253       break;
  254     }
  255       arg.vp = &s->line;
  256       event_signal (EV_CALL_RECV_HDR, (Object *) c, arg);
  257       if (s->state >= S_CLOSING)
  258     return;
  259       s->line.iov_len = 0;      
  260     }
  261   c->reply.header_bytes += *bufp - buf_start;
  262 }
  263 
  264 static void
  265 parse_footers (Call *c, char **bufp, size_t *buf_lenp)
  266 {
  267   char *hdr, *buf_start = *bufp;
  268   Conn *s = c->conn;
  269   size_t hdr_len;
  270   Any_Type arg;
  271 
  272   while (get_line (c, bufp, buf_lenp) > 0)
  273     {
  274       hdr = s->line.iov_base;
  275       hdr_len = s->line.iov_len;
  276 
  277       if (!hdr_len)
  278     {
  279       /* empty footer implies end of footers */
  280       s->state = S_REPLY_DONE;
  281       break;
  282     }
  283       /* process line as a regular footer: */
  284       arg.vp = &s->line;
  285       event_signal (EV_CALL_RECV_FOOTER, (Object *) c, arg);
  286       if (s->state >= S_CLOSING)
  287     return;
  288       s->line.iov_len = 0;      
  289     }
  290   c->reply.footer_bytes += *bufp - buf_start;
  291 }
  292 
  293 static int
  294 parse_data (Call *c, char **bufp, size_t *buf_lenp)
  295 {
  296   size_t bytes_needed, buf_len = *buf_lenp;
  297   Conn *s = c->conn;
  298   char *buf = *bufp;
  299   struct iovec iov;
  300   Any_Type arg;
  301 
  302   bytes_needed = (s->content_length - c->reply.content_bytes);
  303 
  304   if (buf_len > bytes_needed)
  305     buf_len = bytes_needed;
  306 
  307   iov.iov_base = (caddr_t) buf;
  308   iov.iov_len = buf_len;
  309   arg.vp = &iov;
  310   event_signal (EV_CALL_RECV_DATA, (Object *) c, arg);
  311 
  312   c->reply.content_bytes += buf_len;
  313   *bufp = buf + buf_len;
  314   *buf_lenp -= buf_len;
  315 
  316   return (buf_len == bytes_needed);
  317 }
  318 
  319 static void
  320 xfer_chunked  (Call *c, char **bufp, size_t *buf_lenp)
  321 {
  322   Conn *s = c->conn;
  323   size_t chunk_length;
  324   char *end;
  325 
  326   while (*buf_lenp > 0 && s->state < S_CLOSING)
  327     {
  328       if (c->reply.content_bytes >= s->content_length)
  329     {
  330       /* need to parse next chunk length line: */
  331       if (!get_line (c, bufp, buf_lenp))
  332         return;             /* need more data */
  333       if (s->line.iov_len == 0)
  334         continue;               /* skip over empty line */
  335 
  336       errno = 0;
  337       chunk_length = strtoul (s->line.iov_base, &end, 16);
  338       s->line.iov_len = 0;
  339       if (errno == ERANGE || end == s->line.iov_base)
  340         {
  341           fprintf (stderr, "%s.xfer_chunked: bad chunk line `%s'\n",
  342                prog_name, (char *) s->line.iov_base);
  343           continue;
  344         }
  345 
  346       if (chunk_length == 0)
  347         {
  348           /* a final chunk of zero bytes indicates the end of the reply */
  349           s->state = S_REPLY_FOOTER;
  350           return;
  351         }
  352       s->content_length += chunk_length;
  353     }
  354       parse_data (c, bufp, buf_lenp);
  355     }
  356 }
  357 
  358 void
  359 http_process_reply_bytes (Call *c, char **bufp, size_t *buf_lenp)
  360 {
  361   Conn *s = c->conn;
  362   struct iovec iov;
  363   Any_Type arg;
  364 
  365   iov.iov_base = *bufp;
  366   iov.iov_len = *buf_lenp;
  367   arg.vp = &iov;
  368   event_signal (EV_CALL_RECV_RAW_DATA, (Object *) c, arg);
  369 
  370   do
  371     {
  372       switch (s->state)
  373     {
  374     case S_REPLY_STATUS:
  375       parse_status_line (c, bufp, buf_lenp);
  376       break;
  377 
  378     case S_REPLY_HEADER:
  379       parse_headers (c, bufp, buf_lenp);
  380       break;
  381 
  382     case S_REPLY_FOOTER:
  383       parse_footers (c, bufp, buf_lenp);
  384       break;
  385 
  386     case S_REPLY_DATA:
  387       if (parse_data (c, bufp, buf_lenp) && s->state < S_CLOSING)
  388         s->state = S_REPLY_DONE;
  389       break;
  390 
  391     case S_REPLY_CONTINUE:
  392       parse_headers (c, bufp, buf_lenp);
  393       break;
  394 
  395     case S_REPLY_CHUNKED:
  396       xfer_chunked (c, bufp, buf_lenp);
  397       break;
  398 
  399     case S_REPLY_DONE:
  400       return;
  401 
  402     default:
  403       fprintf (stderr, "%s.http_process_reply_bytes: bad state %d\n",
  404            prog_name, s->state);
  405       exit (1);
  406     }
  407     }
  408   while (*buf_lenp > 0 && s->state < S_CLOSING);
  409 }