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