"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 }