/[jscoverage]/trunk/http-message.c
ViewVC logotype

Annotation of /trunk/http-message.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 116 - (hide annotations)
Sat May 31 21:42:36 2008 UTC (11 years, 3 months ago) by siliconforks
File MIME type: text/plain
File size: 20128 byte(s)
Add jscoverage-server.

1 siliconforks 116 /*
2     http-message.c - HTTP message object
3     Copyright (C) 2008 siliconforks.com
4    
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9    
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13     GNU General Public License for more details.
14    
15     You should have received a copy of the GNU General Public License along
16     with this program; if not, write to the Free Software Foundation, Inc.,
17     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18     */
19    
20     #include <config.h>
21    
22     #include "http-server.h"
23    
24     #include <assert.h>
25     #include <errno.h>
26     #include <string.h>
27    
28     #include <sys/socket.h>
29    
30     #include "stream.h"
31     #include "util.h"
32    
33     enum ChunkedBodyState {
34     CHUNKED_BODY_CHUNK_SIZE, CHUNKED_BODY_CHUNK_DATA, CHUNKED_BODY_TRAILER, CHUNKED_BODY_DONE
35     };
36    
37     struct HTTPMessage {
38     char * start_line;
39     HTTPHeader * headers;
40    
41     /* used for sending and receiving */
42     struct HTTPConnection * connection;
43    
44     /* used only for receiving */
45     bool has_content_length;
46     bool is_chunked;
47     size_t bytes_remaining;
48     enum ChunkedBodyState chunked_body_state;
49     Stream * chunk_buffer;
50    
51     /* used only for sending */
52     bool is_started;
53     };
54    
55     HTTPMessage * HTTPMessage_new(HTTPConnection * connection) {
56     HTTPMessage * message = xmalloc(sizeof(HTTPMessage));
57     message->start_line = NULL;
58     message->headers = NULL;
59     message->connection = connection;
60    
61     message->has_content_length = false;
62     message->is_chunked = false;
63     message->bytes_remaining = 0;
64     message->chunked_body_state = CHUNKED_BODY_CHUNK_SIZE;
65     message->chunk_buffer = NULL;
66    
67     message->is_started = false;
68     return message;
69     }
70    
71     void HTTPMessage_delete(HTTPMessage * message) {
72     free(message->start_line);
73    
74     HTTPHeader * h = message->headers;
75     while (h != NULL) {
76     HTTPHeader * doomed = h;
77     h = h->next;
78     free(doomed->name);
79     free(doomed->value);
80     free(doomed);
81     }
82    
83     if (message->chunk_buffer != NULL) {
84     Stream_delete(message->chunk_buffer);
85     }
86    
87     free(message);
88     }
89    
90     HTTPConnection * HTTPMessage_get_connection(const HTTPMessage * message) {
91     return message->connection;
92     }
93    
94     void HTTPMessage_add_header(HTTPMessage * message, const char * name, const char * value) {
95     HTTPHeader * last = NULL;
96     for (HTTPHeader * h = message->headers; h != NULL; h = h->next) {
97     if (strcmp(h->name, name) == 0) {
98     char * new_value;
99     xasprintf(&new_value, "%s, %s", h->value, value);
100     free(h->value);
101     h->value = new_value;
102     return;
103     }
104     last = h;
105     }
106    
107     HTTPHeader * header = xmalloc(sizeof(HTTPHeader));
108     header->name = xstrdup(name);
109     header->value = xstrdup(value);
110     header->next = NULL;
111     if (last == NULL) {
112     message->headers = header;
113     }
114     else {
115     last->next = header;
116     }
117     }
118    
119     void HTTPMessage_set_header(HTTPMessage * message, const char * name, const char * value) {
120     for (HTTPHeader * h = message->headers; h != NULL; h = h->next) {
121     if (strcmp(name, h->name) == 0) {
122     free(h->value);
123     h->value = xstrdup(value);
124     return;
125     }
126     }
127     HTTPMessage_add_header(message, name, value);
128     }
129    
130     void HTTPMessage_set_content_length(HTTPMessage * message, size_t value) {
131     char * s;
132     xasprintf(&s, "%u", value);
133     HTTPMessage_set_header(message, HTTP_CONTENT_LENGTH, s);
134     free(s);
135     }
136    
137     const char * HTTPMessage_find_header(const HTTPMessage * message, const char * name) {
138     for (HTTPHeader * h = message->headers; h != NULL; h = h->next) {
139     if (strcasecmp(h->name, name) == 0) {
140     return h->value;
141     }
142     }
143     return NULL;
144     }
145    
146     const HTTPHeader * HTTPMessage_get_headers(const HTTPMessage * message) {
147     return message->headers;
148     }
149    
150     const char * HTTPMessage_get_start_line(const HTTPMessage * message) {
151     return message->start_line;
152     }
153    
154     void HTTPMessage_set_start_line(HTTPMessage * message, const char * start_line) {
155     free(message->start_line);
156     message->start_line = xstrdup(start_line);
157     }
158    
159     static int read_line(Stream * stream, HTTPConnection * connection) __attribute__((warn_unused_result));
160    
161     static int read_line(Stream * stream, HTTPConnection * connection) {
162     for (;;) {
163     int octet;
164     int result = HTTPConnection_read_octet(connection, &octet);
165     if (result != 0) {
166     return result;
167     }
168    
169     /* check for end of input */
170     if (octet == -1) {
171     return 0;
172     }
173    
174     char c = (char) octet;
175     Stream_write_char(stream, c);
176     if (c == '\n') {
177     return 0;
178     }
179     }
180     }
181    
182     static int read_header(Stream * stream, HTTPConnection * connection) __attribute__((warn_unused_result));
183    
184     static int read_header(Stream * stream, HTTPConnection * connection) {
185     int c;
186    
187     do {
188     int result = read_line(stream, connection);
189     if (result != 0) {
190     return result;
191     }
192    
193     /* check for blank line ending the headers */
194     if (stream->length == 0 ||
195     (stream->length == 1 && stream->data[0] == '\n') ||
196     (stream->length == 2 && stream->data[0] == '\r' && stream->data[1] == '\n')) {
197     break;
198     }
199    
200     result = HTTPConnection_peek_octet(connection, &c);
201     if (result != 0) {
202     return result;
203     }
204     }
205     while (c == ' ' || c == '\t');
206    
207     return 0;
208     }
209    
210     static bool is_lws(uint8_t c) {
211     return c == '\r' || c == '\n' || c == ' ' || c == '\t';
212     }
213    
214     static bool is_separator(uint8_t c) {
215     /* RFC 2616 2.2 */
216     return strchr("()<>@,;:\\\"/[]?={} \t", c) != NULL;
217     }
218    
219     static bool is_token_char(uint8_t c) {
220     /* RFC 2616 2.2 */
221     return 32 <= c && c <= 126 && ! is_separator(c);
222     }
223    
224     static bool is_text(uint8_t c) {
225     return ! (c <= 31 || c == 127);
226     }
227    
228     static void skip_lws(const uint8_t ** p) {
229     while (**p != '\0' && is_lws(**p)) {
230     (*p)++;
231     }
232     }
233    
234     static uint8_t * parse_token(const uint8_t ** p) {
235     const uint8_t * start = *p;
236     while (**p != '\0' && is_token_char(**p)) {
237     (*p)++;
238     }
239    
240     if (*p == start) {
241     return NULL;
242     }
243    
244     return (uint8_t *) xstrndup((char *) start, *p - start);
245     }
246    
247     static uint8_t * parse_quoted_string(const uint8_t ** p) {
248     const uint8_t * start = *p;
249    
250     if (**p != '"') {
251     return NULL;
252     }
253     (*p)++;
254    
255     while (**p != '\0' && **p != '"') {
256     if (**p == '\\') {
257     (*p)++;
258     if (**p < 1 || **p > 127) {
259     return NULL;
260     }
261     (*p)++;
262     }
263     else if (is_text(**p)) {
264     (*p)++;
265     }
266     else {
267     return NULL;
268     }
269     }
270    
271     if (**p != '"') {
272     return NULL;
273     }
274     (*p)++;
275    
276     return (uint8_t *) xstrndup((char *) start, *p - start);
277     }
278    
279     static bool stream_contains_nul(const Stream * stream) {
280     for (size_t i = 0; i < stream->length; i++) {
281     if (stream->data[i] == '\0') {
282     return true;
283     }
284     }
285     return false;
286     }
287    
288     int HTTPMessage_read_start_line_and_headers(HTTPMessage * message) {
289     Stream * stream = Stream_new(0);
290    
291     /* read the start line */
292     int result = read_line(stream, message->connection);
293     if (result != 0) {
294     Stream_delete(stream);
295     return -1;
296     }
297    
298     /* check for NUL byte */
299     if (stream_contains_nul(stream)) {
300     Stream_delete(stream);
301     return -1;
302     }
303    
304     message->start_line = xstrndup((char *) stream->data, stream->length);
305    
306     /* read the headers - RFC 2616 4.2 */
307     message->headers = NULL;
308     for (;;) {
309     Stream_reset(stream);
310     result = read_header(stream, message->connection);
311     if (result != 0) {
312     Stream_delete(stream);
313     return -1;
314     }
315    
316     /* check for CRLF (or similar) to terminate headers */
317     if (stream->length == 0 ||
318     (stream->length == 1 && stream->data[0] == '\n') ||
319     (stream->length == 2 && stream->data[0] == '\r' && stream->data[1] == '\n')) {
320     break;
321     }
322    
323     /* check for NUL byte */
324     if (stream_contains_nul(stream)) {
325     Stream_delete(stream);
326     return -1;
327     }
328    
329     /* NUL-terminate the header */
330     Stream_write_char(stream, '\0');
331    
332     const uint8_t * p = stream->data;
333    
334     char * name = (char *) parse_token(&p);
335     if (name == NULL) {
336     Stream_delete(stream);
337     return -1;
338     }
339    
340     skip_lws(&p);
341    
342     /* expect colon */
343     if (*p != ':') {
344     free(name);
345     Stream_delete(stream);
346     return -1;
347     }
348    
349     /* skip over colon */
350     p++;
351    
352     skip_lws(&p);
353    
354     if (*p == '\0') {
355     free(name);
356     Stream_delete(stream);
357     return -1;
358     }
359    
360     /* skip backward over LWS, starting from the last char in the buffer */
361     uint8_t * end = stream->data + stream->length - 2;
362     while (end > p && is_lws(*end)) {
363     end--;
364     }
365    
366     char * value = xstrndup((char *) p, end - p + 1);
367    
368     HTTPMessage_add_header(message, name, value);
369     free(name);
370     free(value);
371     }
372    
373     Stream_delete(stream);
374    
375     /*
376     RFC 2616 4.3:
377     - a request has a body iff the request headers include Content-Length or Transfer-Encoding
378     - a response has a body iff the request is not HEAD and the response is not 1xx, 204, 304
379     */
380    
381     const char * content_length = HTTPMessage_find_header(message, HTTP_CONTENT_LENGTH);
382     if (content_length != NULL) {
383     size_t value = 0;
384     for (const char * p = content_length; *p != '\0'; p++) {
385     /* check for overflow */
386     if (SIZE_MAX / 10 < value) {
387     return -1;
388     }
389     value *= 10;
390    
391     uint8_t digit = *p;
392    
393     /* check that it contains only decimal digits */
394     if (digit < '0' || digit > '9') {
395     return -1;
396     }
397    
398     size_t digit_value = digit - '0';
399    
400     /* check for overflow */
401     if (SIZE_MAX - digit_value < value) {
402     return -1;
403     }
404     value += digit_value;
405     }
406    
407     message->bytes_remaining = value;
408     message->has_content_length = true;
409     }
410    
411     const char * transfer_encoding = HTTPMessage_find_header(message, HTTP_TRANSFER_ENCODING);
412     if (transfer_encoding != NULL) {
413     uint8_t * token = NULL;
414    
415     const uint8_t * p = (const uint8_t *) transfer_encoding;
416     result = 0;
417     for (;;) {
418     skip_lws(&p);
419    
420     if (! is_token_char(*p)) {
421     result = -1;
422     break;
423     }
424    
425     free(token);
426     token = parse_token(&p);
427    
428     skip_lws(&p);
429    
430     while (*p == ';') {
431     p++;
432    
433     skip_lws(&p);
434    
435     if (! is_token_char(*p)) {
436     result = -1;
437     break;
438     }
439     uint8_t * attribute = parse_token(&p);
440     free(attribute);
441    
442     skip_lws(&p);
443    
444     if (*p != '=') {
445     result = -1;
446     break;
447     }
448     p++;
449    
450     skip_lws(&p);
451    
452     if (*p == '"') {
453     uint8_t * value = parse_quoted_string(&p);
454     if (value == NULL) {
455     result = -1;
456     break;
457     }
458     free(value);
459     }
460     else if (is_token_char(*p)) {
461     uint8_t * value = parse_token(&p);
462     free(value);
463     }
464     else {
465     result = -1;
466     break;
467     }
468    
469     skip_lws(&p);
470     }
471    
472     if (result == -1) {
473     break;
474     }
475    
476     if (*p != ',') {
477     break;
478     }
479    
480     p++;
481     }
482    
483     if (result == 0 && *p == '\0' && token != NULL && strcasecmp((char *) token, "chunked") == 0) {
484     message->is_chunked = true;
485     message->chunk_buffer = Stream_new(0);
486     }
487    
488     free(token);
489     }
490    
491     return result;
492     }
493    
494     int HTTPMessage_write_start_line_and_headers(HTTPMessage * message) {
495     int result = 0;
496    
497     if (message->is_started) {
498     return result;
499     }
500    
501     message->is_started = true;
502    
503     /* send the start line */
504     assert(message->start_line != NULL);
505     size_t length = strlen(message->start_line);
506     assert(length >= 2 && message->start_line[length - 2] == '\r' && message->start_line[length - 1] == '\n');
507     result = HTTPConnection_write(message->connection, message->start_line, length);
508     if (result != 0) {
509     return result;
510     }
511    
512     /* send the headers */
513     HTTPMessage_set_header(message, HTTP_CONNECTION, "close");
514     for (HTTPHeader * h = message->headers; h != NULL; h = h->next) {
515     result = HTTPConnection_write(message->connection, h->name, strlen(h->name));
516     if (result != 0) {
517     return result;
518     }
519     result = HTTPConnection_write(message->connection, ": ", 2);
520     if (result != 0) {
521     return result;
522     }
523     result = HTTPConnection_write(message->connection, h->value, strlen(h->value));
524     if (result != 0) {
525     return result;
526     }
527     result = HTTPConnection_write(message->connection, "\r\n", 2);
528     if (result != 0) {
529     return result;
530     }
531     }
532    
533     result = HTTPConnection_write(message->connection, "\r\n", 2);
534     return result;
535     }
536    
537     bool HTTPMessage_has_sent_headers(const HTTPMessage * message) {
538     return message->is_started;
539     }
540    
541     int HTTPMessage_write(HTTPMessage * message, const void * p, size_t size) {
542     int result = 0;
543     result = HTTPMessage_write_start_line_and_headers(message);
544     if (result != 0) {
545     return result;
546     }
547     result = HTTPConnection_write(message->connection, p, size);
548     return result;
549     }
550    
551     int HTTPMessage_flush(HTTPMessage * message) {
552     int result = 0;
553     result = HTTPMessage_write_start_line_and_headers(message);
554     if (result != 0) {
555     return result;
556     }
557     result = HTTPConnection_flush(message->connection);
558     return result;
559     }
560    
561     static int read_chunk_size_line(HTTPMessage * message) __attribute__((warn_unused_result));
562    
563     static int read_chunk_size_line(HTTPMessage * message) {
564     Stream_reset(message->chunk_buffer);
565     int result = read_line(message->chunk_buffer, message->connection);
566     if (result != 0) {
567     return result;
568     }
569     if (message->chunk_buffer->length < 2) {
570     return -1;
571     }
572     return 0;
573     }
574    
575     static int read_chunk_size(Stream * stream, size_t * chunk_size) __attribute__((warn_unused_result));
576    
577     static int read_chunk_size(Stream * stream, size_t * chunk_size) {
578     size_t value = 0;
579     for (size_t i = 0; i < stream->length; i++) {
580     uint8_t digit = stream->data[i];
581    
582     /* check that it contains only hexadecimal digits */
583     size_t digit_value;
584     if ('0' <= digit && digit <= '9') {
585     digit_value = digit - '0';
586     }
587     else if ('a' <= digit && digit <= 'f') {
588     digit_value = digit - 'a' + 10;
589     }
590     else if ('A' <= digit && digit <= 'F') {
591     digit_value = digit - 'A' + 10;
592     }
593     else if (is_lws(digit) || digit == ';') {
594     break;
595     }
596     else {
597     return -1;
598     }
599    
600     /* check for overflow */
601     if (SIZE_MAX / 16 < value) {
602     return -1;
603     }
604     value *= 16;
605    
606     /* check for overflow */
607     if (SIZE_MAX - digit_value < value) {
608     return -1;
609     }
610     value += digit_value;
611     }
612    
613     *chunk_size = value;
614     return 0;
615     }
616    
617     static int read_chunked_message_body(HTTPMessage * message, void * p, size_t capacity, size_t * bytes_read) {
618     int result = 0;
619     *bytes_read = 0;
620    
621     if (message->chunked_body_state == CHUNKED_BODY_DONE) {
622     return 0;
623     }
624    
625     uint8_t * s = p;
626     int c;
627     for (*bytes_read = 0; *bytes_read < capacity; (*bytes_read)++) {
628     switch (message->chunked_body_state) {
629     case CHUNKED_BODY_CHUNK_SIZE:
630     if (message->chunk_buffer->length == 0) {
631     /* read a `chunk-size' line */
632     result = read_chunk_size_line(message);
633     if (result != 0) {
634     return result;
635     }
636    
637     message->bytes_remaining = message->chunk_buffer->length;
638     }
639    
640     if (message->bytes_remaining == 0) {
641     return -1;
642     }
643    
644     /* serve from the chunk buffer */
645     s[*bytes_read] = message->chunk_buffer->data[message->chunk_buffer->length - message->bytes_remaining];
646     message->bytes_remaining--;
647    
648     if (message->bytes_remaining == 0) {
649     size_t chunk_size;
650     result = read_chunk_size(message->chunk_buffer, &chunk_size);
651     if (result != 0) {
652     return result;
653     }
654     Stream_reset(message->chunk_buffer);
655     if (chunk_size == 0) {
656     message->chunked_body_state = CHUNKED_BODY_TRAILER;
657     }
658     else if (SIZE_MAX - 2 < chunk_size) {
659     /* overflow */
660     return -1;
661     }
662     else {
663     message->chunked_body_state = CHUNKED_BODY_CHUNK_DATA;
664     message->bytes_remaining = chunk_size + 2;
665     }
666     }
667    
668     break;
669     case CHUNKED_BODY_CHUNK_DATA:
670     /* serve from the chunk */
671     result = HTTPConnection_read_octet(message->connection, &c);
672     if (result != 0) {
673     return result;
674     }
675     if (c == -1) {
676     result = -1;
677     message->chunked_body_state = CHUNKED_BODY_DONE;
678     return result;
679     }
680     s[*bytes_read] = (uint8_t) c;
681     message->bytes_remaining--;
682    
683     if (message->bytes_remaining == 0) {
684     message->chunked_body_state = CHUNKED_BODY_CHUNK_SIZE;
685     }
686    
687     break;
688     case CHUNKED_BODY_TRAILER:
689     if (message->chunk_buffer->length == 0) {
690     /* read a header */
691     result = read_header(message->chunk_buffer, message->connection);
692     if (result != 0) {
693     return result;
694     }
695     message->bytes_remaining = message->chunk_buffer->length;
696     }
697    
698     if (message->bytes_remaining == 0) {
699     message->chunked_body_state = CHUNKED_BODY_DONE;
700     return result;
701     }
702    
703     /* serve from the chunk buffer */
704     s[*bytes_read] = message->chunk_buffer->data[message->chunk_buffer->length - message->bytes_remaining];
705     message->bytes_remaining--;
706    
707     if (message->bytes_remaining == 0) {
708     size_t length = message->chunk_buffer->length;
709     uint8_t * chunk_buffer = message->chunk_buffer->data;
710     if (length == 0 ||
711     (length == 1 && chunk_buffer[0] == '\n') ||
712     (length == 2 && chunk_buffer[0] == '\r' && chunk_buffer[1] == '\n')) {
713     message->chunked_body_state = CHUNKED_BODY_DONE;
714     return result;
715     }
716     Stream_reset(message->chunk_buffer);
717     }
718    
719     break;
720     default:
721     break;
722     }
723     }
724    
725     return result;
726     }
727    
728     static int read_chunked_entity_body(HTTPMessage * message, void * p, size_t capacity, size_t * bytes_read) {
729     int result = 0;
730     *bytes_read = 0;
731    
732     if (message->chunked_body_state == CHUNKED_BODY_DONE) {
733     return result;
734     }
735    
736     uint8_t * s = p;
737     for (*bytes_read = 0; *bytes_read < capacity; (*bytes_read)++) {
738     if (message->bytes_remaining == 0) {
739     result = read_chunk_size_line(message);
740     if (result != 0) {
741     break;
742     }
743     size_t chunk_size;
744     result = read_chunk_size(message->chunk_buffer, &chunk_size);
745     if (result != 0) {
746     break;
747     }
748     message->bytes_remaining = chunk_size;
749     if (chunk_size == 0) {
750     message->chunked_body_state = CHUNKED_BODY_DONE;
751     break;
752     }
753     }
754    
755     int c;
756     result = HTTPConnection_read_octet(message->connection, &c);
757     if (result != 0) {
758     break;
759     }
760     if (c == -1) {
761     result = -1;
762     message->chunked_body_state = CHUNKED_BODY_DONE;
763     break;
764     }
765     s[*bytes_read] = (uint8_t) c;
766     message->bytes_remaining--;
767     }
768    
769     return result;
770     }
771    
772     int HTTPMessage_read_message_body(HTTPMessage * message, void * p, size_t capacity, size_t * bytes_read) {
773     if (message->is_chunked) {
774     return read_chunked_message_body(message, p, capacity, bytes_read);
775     }
776    
777     int result = 0;
778     uint8_t * s = p;
779     for (*bytes_read = 0; *bytes_read < capacity; (*bytes_read)++) {
780     if (message->has_content_length && message->bytes_remaining == 0) {
781     break;
782     }
783    
784     int c;
785     result = HTTPConnection_read_octet(message->connection, &c);
786     if (result != 0) {
787     break;
788     }
789     if (c == -1) {
790     break;
791     }
792     s[*bytes_read] = (uint8_t) c;
793     message->bytes_remaining--;
794     }
795     return result;
796     }
797    
798     int HTTPMessage_read_entity_body(HTTPMessage * message, void * p, size_t capacity, size_t * bytes_read) {
799     if (message->is_chunked) {
800     return read_chunked_entity_body(message, p, capacity, bytes_read);
801     }
802    
803     return HTTPMessage_read_message_body(message, p, capacity, bytes_read);
804     }
805    
806     int HTTPMessage_read_entire_entity_body(HTTPMessage * message, Stream * input_stream) {
807     int result = 0;
808     uint8_t * buffer[8192];
809     for (;;) {
810     size_t bytes_read;
811     result = HTTPMessage_read_entity_body(message, buffer, 8192, &bytes_read);
812     if (result != 0) {
813     break;
814     }
815     if (bytes_read == 0) {
816     break;
817     }
818     Stream_write(input_stream, buffer, bytes_read);
819     }
820     return result;
821     }

  ViewVC Help
Powered by ViewVC 1.1.24