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 size_t readhead(int fd, struct charbuf *buf)
128 while(!(off < buf->d)) {
129 sizebuf(*buf, buf->d + 1024);
130 ret = recv(fd, buf->b + buf->d, buf->s - buf->d, MSG_DONTWAIT);
132 if((ret < 0) && (errno == EAGAIN)) {
140 return(buf->b[off++]);
163 #define SKIPNL(ptr) ({ \
167 if(*(ptr) != '\n') { \
174 static struct htreq *parseraw(char *buf)
177 char *method, *url, *ver;
180 if((nl = strchr(buf, '\n')) == NULL)
182 if(((p = strchr(buf, ' ')) == NULL) || (p > nl))
186 if(((p2 = strchr(p, ' ')) == NULL) || (p2 > nl))
191 if(strncmp(p, "HTTP/", 5))
194 for(; ((*p >= '0') && (*p <= '9')) || (*p == '.'); p++);
198 req = mkreq(method, url, ver);
205 if((nl = strchr(p, '\n')) == NULL)
207 if(((p2 = strchr(p, ':')) == NULL) || (p2 > nl))
210 for(; (*p2 == ' ') || (*p2 == '\t'); p2++);
211 for(nl = p2; (*nl != '\r') && (*nl != '\n'); nl++);
214 reqappheader(req, p, p2);
224 static void serve(struct muth *muth, va_list args)
234 if((headoff = readhead(fd, &buf)) < 0)
236 if((req = parseraw(buf.b)) == NULL)
238 printf("\"%s\", \"%s\", \"%s\", \"%s\"\n", req->method, req->url, req->ver, getheader(req, "host"));
247 static void listenloop(struct muth *muth, va_list args)
251 struct sockaddr_storage name;
255 namelen = sizeof(name);
257 ns = accept(ss, (struct sockaddr *)&name, &namelen);
259 flog(LOG_ERR, "accept: %s", strerror(errno));
269 static void ioloop(void)
272 fd_set rfds, wfds, efds;
273 struct blocker *bl, *nbl;
277 while(blockers != NULL) {
282 for(bl = blockers; bl; bl = bl->n) {
284 FD_SET(bl->fd, &rfds);
285 if(bl->ev & EV_WRITE)
286 FD_SET(bl->fd, &wfds);
287 FD_SET(bl->fd, &efds);
291 ret = select(maxfd + 1, &rfds, &wfds, &efds, NULL);
294 flog(LOG_CRIT, "ioloop: select errored out: %s", strerror(errno));
295 /* To avoid CPU hogging in case it's bad, which it
300 for(bl = blockers; bl; bl = nbl) {
303 if(FD_ISSET(bl->fd, &rfds))
305 if(FD_ISSET(bl->fd, &wfds))
307 if(FD_ISSET(bl->fd, &efds))
315 int main(int argc, char **argv)
319 if((fd = listensock6(8080)) < 0) {
320 flog(LOG_ERR, "could not listen on IPv6: %s", strerror(errno));
323 mustart(listenloop, fd);
324 if((fd = listensock4(8080)) < 0) {
325 if(errno != EADDRINUSE) {
326 flog(LOG_ERR, "could not listen on IPv4: %s", strerror(errno));
330 mustart(listenloop, fd);