2 ashd - A Sane HTTP Daemon
3 Copyright (C) 2008 Fredrik Tolf <fredrik@dolda2000.com>
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.
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.
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/>.
23 #include <sys/select.h>
24 #include <sys/socket.h>
25 #include <netinet/in.h>
40 struct blocker *n, *p;
46 static struct blocker *blockers;
48 static int block(int fd, int ev)
71 static int listensock4(int port)
73 struct sockaddr_in name;
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)
83 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &valbuf, sizeof(valbuf));
84 if(bind(fd, (struct sockaddr *)&name, sizeof(name))) {
88 if(listen(fd, 16) < 0) {
95 static int listensock6(int port)
97 struct sockaddr_in6 name;
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)
107 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &valbuf, sizeof(valbuf));
108 if(bind(fd, (struct sockaddr *)&name, sizeof(name))) {
112 if(listen(fd, 16) < 0) {
119 static char *slowreadhead(int fd)
129 sizebuf(buf, buf.d + 1);
130 ret = recv(fd, buf.b + buf.d, 1, MSG_DONTWAIT);
132 if((ret < 0) && (errno == EAGAIN)) {
138 last = buf.b[buf.d++];
143 } else if(last == '\r') {
156 #define SKIPNL(ptr) ({ \
160 if(*(ptr) != '\n') { \
167 static struct htreq *parseraw(char *buf)
170 char *method, *url, *ver;
173 if((nl = strchr(buf, '\n')) == NULL)
175 if(((p = strchr(buf, ' ')) == NULL) || (p > nl))
179 if(((p2 = strchr(p, ' ')) == NULL) || (p2 > nl))
184 if(strncmp(p, "HTTP/", 5))
187 for(; ((*p >= '0') && (*p <= '9')) || (*p == '.'); p++);
191 req = mkreq(method, url, ver);
198 if((nl = strchr(p, '\n')) == NULL)
200 if(((p2 = strchr(p, ':')) == NULL) || (p2 > nl))
203 for(; (*p2 == ' ') || (*p2 == '\t'); p2++);
204 for(nl = p2; (*nl != '\r') && (*nl != '\n'); nl++);
207 reqappheader(req, p, p2);
213 static void serve(struct muth *muth, va_list args)
221 if((hb = slowreadhead(fd)) == NULL)
223 if((req = parseraw(hb)) == NULL)
227 printf("\"%s\", \"%s\", \"%s\"\n", req->method, req->url, req->ver);
237 static void listenloop(struct muth *muth, va_list args)
241 struct sockaddr_storage name;
245 namelen = sizeof(name);
247 ns = accept(ss, (struct sockaddr *)&name, &namelen);
249 flog(LOG_ERR, "accept: %s", strerror(errno));
259 static void ioloop(void)
262 fd_set rfds, wfds, efds;
263 struct blocker *bl, *nbl;
267 while(blockers != NULL) {
272 for(bl = blockers; bl; bl = bl->n) {
274 FD_SET(bl->fd, &rfds);
275 if(bl->ev & EV_WRITE)
276 FD_SET(bl->fd, &wfds);
277 FD_SET(bl->fd, &efds);
281 ret = select(maxfd + 1, &rfds, &wfds, &efds, NULL);
284 flog(LOG_CRIT, "ioloop: select errored out: %s", strerror(errno));
285 /* To avoid CPU hogging in case it's bad, which it
290 for(bl = blockers; bl; bl = nbl) {
293 if(FD_ISSET(bl->fd, &rfds))
295 if(FD_ISSET(bl->fd, &wfds))
297 if(FD_ISSET(bl->fd, &efds))
305 int main(int argc, char **argv)
309 if((fd = listensock6(8080)) < 0) {
310 flog(LOG_ERR, "could not listen on IPv6: %s", strerror(errno));
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));
320 mustart(listenloop, fd);