Added basic support for parsing requests.
[ashd.git] / src / htparser.c
CommitLineData
f0bbedf7
FT
1/*
2 ashd - A Sane HTTP Daemon
3 Copyright (C) 2008 Fredrik Tolf <fredrik@dolda2000.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 3 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
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19#include <stdlib.h>
20#include <unistd.h>
21#include <stdio.h>
f4cdf919
FT
22#include <string.h>
23#include <sys/select.h>
24#include <sys/socket.h>
25#include <netinet/in.h>
26#include <errno.h>
f0bbedf7
FT
27
28#ifdef HAVE_CONFIG_H
29#include <config.h>
30#endif
31#include <utils.h>
f4cdf919
FT
32#include <mt.h>
33#include <log.h>
66987955 34#include <req.h>
f4cdf919
FT
35
36#define EV_READ 1
37#define EV_WRITE 2
38
39struct blocker {
40 struct blocker *n, *p;
41 int fd;
42 int ev;
43 struct muth *th;
44};
45
46static struct blocker *blockers;
47
48static int block(int fd, int ev)
49{
50 struct blocker *bl;
51 int rv;
52
53 omalloc(bl);
54 bl->fd = fd;
55 bl->ev = ev;
56 bl->th = current;
57 bl->n = blockers;
58 if(blockers)
59 blockers->p = bl;
60 blockers = bl;
61 rv = yield();
62 if(bl->n)
63 bl->n->p = bl->p;
64 if(bl->p)
65 bl->p->n = bl->n;
66 if(bl == blockers)
67 blockers = bl->n;
68 return(rv);
69}
70
71static int listensock4(int port)
72{
73 struct sockaddr_in name;
74 int fd;
75 int valbuf;
76
77 memset(&name, 0, sizeof(name));
78 name.sin_family = AF_INET;
79 name.sin_port = htons(port);
80 if((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
81 return(-1);
82 valbuf = 1;
83 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &valbuf, sizeof(valbuf));
84 if(bind(fd, (struct sockaddr *)&name, sizeof(name))) {
85 close(fd);
86 return(-1);
87 }
88 if(listen(fd, 16) < 0) {
89 close(fd);
90 return(-1);
91 }
92 return(fd);
93}
94
95static int listensock6(int port)
96{
97 struct sockaddr_in6 name;
98 int fd;
99 int valbuf;
100
101 memset(&name, 0, sizeof(name));
102 name.sin6_family = AF_INET6;
103 name.sin6_port = htons(port);
104 if((fd = socket(PF_INET6, SOCK_STREAM, 0)) < 0)
105 return(-1);
106 valbuf = 1;
107 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &valbuf, sizeof(valbuf));
108 if(bind(fd, (struct sockaddr *)&name, sizeof(name))) {
109 close(fd);
110 return(-1);
111 }
112 if(listen(fd, 16) < 0) {
113 close(fd);
114 return(-1);
115 }
116 return(fd);
117}
118
66987955
FT
119static char *slowreadhead(int fd)
120{
121 int ret;
122 struct charbuf buf;
123 int nl;
124 char last;
125
126 bufinit(buf);
127 nl = 0;
128 while(1) {
129 sizebuf(buf, buf.d + 1);
130 ret = recv(fd, buf.b + buf.d, 1, MSG_DONTWAIT);
131 if(ret <= 0) {
132 if((ret < 0) && (errno == EAGAIN)) {
133 block(fd, EV_READ);
134 continue;
135 }
136 goto err;
137 }
138 last = buf.b[buf.d++];
139 if(last == '\n') {
140 if(nl)
141 break;
142 nl = 1;
143 } else if(last == '\r') {
144 } else {
145 nl = 0;
146 }
147 }
148 bufadd(buf, 0);
149 return(buf.b);
150
151err:
152 buffree(buf);
153 return(NULL);
154}
155
156#define SKIPNL(ptr) ({ \
157 int __buf__; \
158 if(*(ptr) == '\r') \
159 *((ptr)++) = 0; \
160 if(*(ptr) != '\n') { \
161 __buf__ = 0; \
162 } else { \
163 *((ptr)++) = 0; \
164 __buf__ = 1; \
165 } \
166 __buf__;})
167static struct htreq *parseraw(char *buf)
168{
169 char *p, *p2, *nl;
170 char *method, *url, *ver;
171 struct htreq *req;
172
173 if((nl = strchr(buf, '\n')) == NULL)
174 return(NULL);
175 if(((p = strchr(buf, ' ')) == NULL) || (p > nl))
176 return(NULL);
177 method = buf;
178 *(p++) = 0;
179 if(((p2 = strchr(p, ' ')) == NULL) || (p2 > nl))
180 return(NULL);
181 url = p;
182 p = p2;
183 *(p++) = 0;
184 if(strncmp(p, "HTTP/", 5))
185 return(NULL);
186 ver = (p += 5);
187 for(; ((*p >= '0') && (*p <= '9')) || (*p == '.'); p++);
188 if(!SKIPNL(p))
189 return(NULL);
190
191 req = mkreq(method, url, ver);
192 while(1) {
193 if(SKIPNL(p)) {
194 if(*p)
195 return(NULL);
196 break;
197 }
198 if((nl = strchr(p, '\n')) == NULL)
199 return(NULL);
200 if(((p2 = strchr(p, ':')) == NULL) || (p2 > nl))
201 return(NULL);
202 *(p2++) = 0;
203 for(; (*p2 == ' ') || (*p2 == '\t'); p2++);
204 for(nl = p2; (*nl != '\r') && (*nl != '\n'); nl++);
205 if(!SKIPNL(nl))
206 return(NULL);
207 reqappheader(req, p, p2);
208 p = nl;
209 }
210 return(req);
211}
212
213static void serve(struct muth *muth, va_list args)
214{
215 vavar(int, fd);
216 char *hb;
217 struct htreq *req;
218
219 hb = NULL;
220 while(1) {
221 if((hb = slowreadhead(fd)) == NULL)
222 goto out;
223 if((req = parseraw(hb)) == NULL)
224 goto out;
225 free(hb);
226 hb = NULL;
227 printf("\"%s\", \"%s\", \"%s\"\n", req->method, req->url, req->ver);
228 freereq(req);
229 }
230
231out:
232 if(hb != NULL)
233 free(hb);
234 close(fd);
235}
236
f4cdf919
FT
237static void listenloop(struct muth *muth, va_list args)
238{
239 vavar(int, ss);
240 int ns;
241 struct sockaddr_storage name;
242 socklen_t namelen;
243
244 while(1) {
245 namelen = sizeof(name);
246 block(ss, EV_READ);
247 ns = accept(ss, (struct sockaddr *)&name, &namelen);
66987955
FT
248 if(ns < 0) {
249 flog(LOG_ERR, "accept: %s", strerror(errno));
250 goto out;
251 }
252 mustart(serve, ns);
f4cdf919 253 }
66987955
FT
254
255out:
256 close(ss);
f4cdf919
FT
257}
258
259static void ioloop(void)
260{
261 int ret;
262 fd_set rfds, wfds, efds;
263 struct blocker *bl, *nbl;
264 int maxfd;
265 int ev;
266
66987955 267 while(blockers != NULL) {
f4cdf919
FT
268 FD_ZERO(&rfds);
269 FD_ZERO(&wfds);
270 FD_ZERO(&efds);
271 maxfd = 0;
272 for(bl = blockers; bl; bl = bl->n) {
273 if(bl->ev & EV_READ)
274 FD_SET(bl->fd, &rfds);
275 if(bl->ev & EV_WRITE)
276 FD_SET(bl->fd, &wfds);
277 FD_SET(bl->fd, &efds);
278 if(bl->fd > maxfd)
279 maxfd = bl->fd;
280 }
281 ret = select(maxfd + 1, &rfds, &wfds, &efds, NULL);
282 if(ret < 0) {
283 if(errno != EINTR) {
284 flog(LOG_CRIT, "ioloop: select errored out: %s", strerror(errno));
285 /* To avoid CPU hogging in case it's bad, which it
286 * probably is. */
287 sleep(1);
288 }
289 }
290 for(bl = blockers; bl; bl = nbl) {
291 nbl = bl->n;
292 ev = 0;
293 if(FD_ISSET(bl->fd, &rfds))
294 ev |= EV_READ;
295 if(FD_ISSET(bl->fd, &wfds))
296 ev |= EV_WRITE;
297 if(FD_ISSET(bl->fd, &efds))
298 ev = -1;
66987955
FT
299 if(ev != 0)
300 resume(bl->th, ev);
f4cdf919
FT
301 }
302 }
303}
f0bbedf7
FT
304
305int main(int argc, char **argv)
306{
f4cdf919
FT
307 int fd;
308
309 if((fd = listensock6(8080)) < 0) {
310 flog(LOG_ERR, "could not listen on IPv6: %s", strerror(errno));
311 return(1);
312 }
313 mustart(listenloop, fd);
314 if((fd = listensock4(8080)) < 0) {
315 if(errno != EADDRINUSE) {
316 flog(LOG_ERR, "could not listen on IPv4: %s", strerror(errno));
317 return(1);
318 }
319 } else {
320 mustart(listenloop, fd);
321 }
322 ioloop();
323 return(0);
f0bbedf7 324}