1 /*
2  * This is a simple driver for the http-parser package.  It is
3  * intended to read one HTTP request after another from a file,
4  * nothing more.
5  *
6  * For "feature parity" with the Haskell code in RFC2616.hs, we
7  * allocate and populate a simple structure describing each request,
8  * since that's the sort of thing that many real applications would
9  * themselves do and the library doesn't do this for us.
10  *
11  * For the http-parser source, see
12  * https://github.com/joyent/http-parser/blob/master/http_parser.c
13  */
14 
15 /*
16  * Turn off this preprocessor symbol to have the callbacks do nothing
17  * at all, which "improves performance" by about 50%.
18  */
19 /*#define LOOK_BUSY*/
20 
21 #include <assert.h>
22 #include <fcntl.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/stat.h>
27 #include <sys/types.h>
28 #include <unistd.h>
29 
30 #include "http_parser.h"
31 
32 struct http_string {
33     size_t len;
34     char value[0];
35 };
36 
37 struct http_header {
38     struct http_string *name;
39     struct http_string *value;
40     struct http_header *next;
41 };
42 
43 struct http_request {
44     struct http_string *method;
45     struct http_string *uri;
46     struct http_header *headers, *last;
47 };
48 
49 struct data {
50     size_t count;
51     struct http_request req;
52 };
53 
xmalloc(size_t size)54 static void *xmalloc(size_t size)
55 {
56     void *ptr;
57 
58     if ((ptr = malloc(size)) == NULL) {
59 	perror("malloc");
60 	exit(1);
61     }
62 
63     return ptr;
64 }
65 
xstrdup(const char * src,size_t len,size_t extra)66 static struct http_string *xstrdup(const char *src, size_t len, size_t extra)
67 {
68     struct http_string *dst = xmalloc(sizeof(*dst) + len + extra);
69     memcpy(dst->value, src, len);
70     dst->len = len;
71     return dst;
72 }
73 
xstrcat(struct http_string ** dst,const char * src,size_t len)74 static void xstrcat(struct http_string **dst, const char *src, size_t len)
75 {
76     struct http_string *p;
77 
78     if (*dst == NULL) {
79 	*dst = xstrdup(src, len, 0);
80 	return;
81     }
82 
83     p = xstrdup((*dst)->value, (*dst)->len, len);
84     memcpy(p->value + (*dst)->len, src, len);
85     p->len += len;
86     free(*dst);
87     *dst = p;
88 }
89 
begin(http_parser * p)90 static int begin(http_parser *p)
91 {
92     struct data *data = p->data;
93 
94     data->count++;
95 
96     return 0;
97 }
98 
url(http_parser * p,const char * at,size_t len)99 static int url(http_parser *p, const char *at, size_t len)
100 {
101 #ifdef LOOK_BUSY
102     struct data *data = p->data;
103 
104     xstrcat(&data->req.uri, at, len);
105 #endif
106 
107     return 0;
108 }
109 
header_field(http_parser * p,const char * at,size_t len)110 static int header_field(http_parser *p, const char *at, size_t len)
111 {
112 #ifdef LOOK_BUSY
113     struct data *data = p->data;
114 
115     if (data->req.last && data->req.last->value == NULL) {
116 	xstrcat(&data->req.last->name, at, len);
117     } else {
118 	struct http_header *hdr = xmalloc(sizeof(*hdr));
119 
120 	hdr->name = xstrdup(at, len, 0);
121 	hdr->value = NULL;
122 	hdr->next = NULL;
123 
124 	if (data->req.last != NULL)
125 	    data->req.last->next = hdr;
126 	data->req.last = hdr;
127 	if (data->req.headers == NULL)
128 	    data->req.headers = hdr;
129     }
130 #endif
131 
132     return 0;
133 }
134 
header_value(http_parser * p,const char * at,size_t len)135 static int header_value(http_parser *p, const char *at, size_t len)
136 {
137 #ifdef LOOK_BUSY
138     struct data *data = p->data;
139 
140     xstrcat(&data->req.last->value, at, len);
141 #endif
142 
143     return 0;
144 }
145 
complete(http_parser * p)146 static int complete(http_parser *p)
147 {
148 #ifdef LOOK_BUSY
149     struct data *data = p->data;
150     struct http_header *hdr, *next;
151 
152     free(data->req.method);
153     free(data->req.uri);
154 
155     for (hdr = data->req.headers; hdr != NULL; hdr = next) {
156 	next = hdr->next;
157 	free(hdr->name);
158 	free(hdr->value);
159 	free(hdr);
160 	hdr = next;
161     }
162 
163     data->req.method = NULL;
164     data->req.uri = NULL;
165     data->req.headers = NULL;
166     data->req.last = NULL;
167 #endif
168 
169     /* Bludgeon http_parser into understanding that we really want to
170      * keep parsing after a request that in principle ought to close
171      * the "connection". */
172     if (!http_should_keep_alive(p)) {
173 	p->http_major = 1;
174 	p->http_minor = 1;
175 	p->flags &= ~6;
176     }
177 
178     return 0;
179 }
180 
parse(const char * path,int fd)181 static void parse(const char *path, int fd)
182 {
183     struct data data;
184     http_parser_settings s;
185     http_parser p;
186     ssize_t nread;
187 
188     memset(&s, 0, sizeof(s));
189     s.on_message_begin = begin;
190     s.on_url = url;
191     s.on_header_field = header_field;
192     s.on_header_value = header_value;
193     s.on_message_complete = complete;
194 
195     p.data = &data;
196 
197     http_parser_init(&p, HTTP_REQUEST);
198 
199     data.count = 0;
200     data.req.method = NULL;
201     data.req.uri = NULL;
202     data.req.headers = NULL;
203     data.req.last = NULL;
204 
205     do {
206 	char buf[HTTP_MAX_HEADER_SIZE];
207 	size_t np;
208 
209 	nread = read(fd, buf, sizeof(buf));
210 
211 	np = http_parser_execute(&p, &s, buf, nread);
212 	if (np != nread) {
213 	    fprintf(stderr, "%s: parse failed\n", path);
214 	    break;
215 	}
216     } while (nread > 0);
217 
218     printf("%ld\n", (unsigned long) data.count);
219 }
220 
main(int argc,char ** argv)221 int main(int argc, char **argv)
222 {
223     int i;
224 
225     for (i = 1; i < argc; i++) {
226 	int fd;
227 
228 	fd = open(argv[i], O_RDONLY);
229 	if (fd == -1) {
230 	    perror(argv[i]);
231 	    continue;
232 	}
233 	parse(argv[i], fd);
234 	close(fd);
235     }
236 
237     return 0;
238 }
239 
240 /*
241  * Local Variables:
242  * c-file-style: "stroustrup"
243  * End:
244  */
245