tinywebd

annotate src/main.c @ 1:f425a9805d17

foo
author John Tsiombikas <nuclear@member.fsf.org>
date Tue, 14 Apr 2015 08:32:51 +0300
parents 9c9e24956d99
children 852a745503cf
rev   line source
nuclear@0 1 #include <stdio.h>
nuclear@0 2 #include <stdlib.h>
nuclear@0 3 #include <string.h>
nuclear@0 4 #include <errno.h>
nuclear@0 5 #include <unistd.h>
nuclear@0 6 #include <fcntl.h>
nuclear@0 7 #include <sys/select.h>
nuclear@0 8 #include <sys/types.h>
nuclear@0 9 #include <sys/socket.h>
nuclear@0 10 #include <arpa/inet.h>
nuclear@1 11 #include "http.h"
nuclear@1 12
nuclear@1 13 /* HTTP version */
nuclear@1 14 #define HTTP_VER_MAJOR 1
nuclear@1 15 #define HTTP_VER_MINOR 1
nuclear@1 16 #define HTTP_VER_STR "1.1"
nuclear@1 17
nuclear@1 18 /* maximum request length: 64mb */
nuclear@1 19 #define MAX_REQ_LENGTH (65536 * 1024)
nuclear@0 20
nuclear@0 21 struct client {
nuclear@0 22 int s;
nuclear@0 23 char *rcvbuf;
nuclear@0 24 int bufsz;
nuclear@0 25 struct client *next;
nuclear@0 26 };
nuclear@0 27
nuclear@0 28 int start_server(void);
nuclear@0 29 int accept_conn(int lis);
nuclear@0 30 int handle_client(struct client *c);
nuclear@1 31 void respond_error(struct client *c, int errcode);
nuclear@1 32 int parse_args(int argc, char **argv);
nuclear@0 33
nuclear@0 34 static int lis;
nuclear@0 35 static int port = 8080;
nuclear@0 36 static struct client *clist;
nuclear@0 37
nuclear@0 38 int main(int argc, char **argv)
nuclear@0 39 {
nuclear@1 40 if(parse_args(argc, argv) == -1) {
nuclear@1 41 return 1;
nuclear@1 42 }
nuclear@1 43
nuclear@0 44 if((lis = start_server()) == -1) {
nuclear@0 45 return 1;
nuclear@0 46 }
nuclear@0 47
nuclear@0 48 for(;;) {
nuclear@0 49 struct client *c, dummy;
nuclear@0 50 int maxfd = lis;
nuclear@0 51 fd_set rdset;
nuclear@0 52
nuclear@0 53 FD_ZERO(&rdset);
nuclear@0 54 FD_SET(lis, &rdset);
nuclear@0 55
nuclear@0 56 c = clist;
nuclear@0 57 while(c) {
nuclear@0 58 if(c->s > maxfd) {
nuclear@0 59 maxfd = c->s;
nuclear@0 60 }
nuclear@0 61 FD_SET(c->s, &rdset);
nuclear@0 62 c = c->next;
nuclear@0 63 }
nuclear@0 64
nuclear@0 65 while(select(maxfd + 1, &rdset, 0, 0, 0) == -1 && errno == EINTR);
nuclear@0 66
nuclear@0 67 c = clist;
nuclear@0 68 while(c) {
nuclear@0 69 if(FD_ISSET(c->s, &rdset)) {
nuclear@0 70 handle_client(c);
nuclear@0 71 }
nuclear@0 72 c = c->next;
nuclear@0 73 }
nuclear@0 74
nuclear@0 75 if(FD_ISSET(lis, &rdset)) {
nuclear@0 76 accept_conn(lis);
nuclear@0 77 }
nuclear@0 78
nuclear@0 79 dummy.next = clist;
nuclear@0 80 c = &dummy;
nuclear@0 81
nuclear@0 82 while(c->next) {
nuclear@0 83 struct client *n = c->next;
nuclear@0 84
nuclear@0 85 if(n->s == -1) {
nuclear@0 86 /* marked for removal */
nuclear@0 87 c->next = n->next;
nuclear@0 88 free(n);
nuclear@0 89 } else {
nuclear@0 90 c = c->next;
nuclear@0 91 }
nuclear@0 92 }
nuclear@0 93 }
nuclear@0 94 }
nuclear@0 95
nuclear@0 96 int start_server(void)
nuclear@0 97 {
nuclear@0 98 int s;
nuclear@0 99 struct sockaddr_in sa;
nuclear@0 100
nuclear@0 101 if((s = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
nuclear@0 102 perror("failed to create listening socket");
nuclear@0 103 return -1;
nuclear@0 104 }
nuclear@0 105 fcntl(s, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK);
nuclear@0 106
nuclear@0 107 memset(&sa, 0, sizeof sa);
nuclear@0 108 sa.sin_family = AF_INET;
nuclear@0 109 sa.sin_addr.s_addr = INADDR_ANY;
nuclear@0 110 sa.sin_port = htons(port);
nuclear@0 111
nuclear@0 112 if(bind(s, (struct sockaddr*)&sa, sizeof sa) == -1) {
nuclear@0 113 fprintf(stderr, "failed to bind socket to port %d: %s\n", port, strerror(errno));
nuclear@0 114 return -1;
nuclear@0 115 }
nuclear@0 116 listen(s, 16);
nuclear@0 117
nuclear@0 118 return s;
nuclear@0 119 }
nuclear@0 120
nuclear@0 121 int accept_conn(int lis)
nuclear@0 122 {
nuclear@0 123 int s;
nuclear@0 124 struct client *c;
nuclear@0 125 struct sockaddr_in addr;
nuclear@0 126 socklen_t addr_sz = sizeof addr;
nuclear@0 127
nuclear@0 128 if((s = accept(lis, (struct sockaddr*)&addr, &addr_sz)) == -1) {
nuclear@0 129 perror("failed to accept incoming connection");
nuclear@0 130 return -1;
nuclear@0 131 }
nuclear@0 132 fcntl(s, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK);
nuclear@0 133
nuclear@0 134 if(!(c = malloc(sizeof *c))) {
nuclear@0 135 perror("failed to allocate memory while accepting connection");
nuclear@0 136 return -1;
nuclear@0 137 }
nuclear@0 138 c->s = s;
nuclear@0 139 c->rcvbuf = 0;
nuclear@0 140 c->bufsz = 0;
nuclear@0 141 c->next = clist;
nuclear@0 142 clist = c;
nuclear@0 143 return 0;
nuclear@0 144 }
nuclear@0 145
nuclear@1 146 void close_conn(struct client *c)
nuclear@1 147 {
nuclear@1 148 close(c->s);
nuclear@1 149 c->s = -1; /* mark it for removal */
nuclear@1 150 free(c->rcvbuf);
nuclear@1 151 }
nuclear@1 152
nuclear@1 153 static char *skip_space(char *s, char *endp)
nuclear@1 154 {
nuclear@1 155 while(s < endp && *s && isspace(*s))
nuclear@1 156 ++s;
nuclear@1 157 return s;
nuclear@1 158 }
nuclear@1 159
nuclear@0 160 int handle_client(struct client *c)
nuclear@0 161 {
nuclear@1 162 char *reqp, *hdrp, *bodyp, *endp, *eol;
nuclear@1 163 char req_type[32];
nuclear@0 164 static char buf[2048];
nuclear@0 165 int rdsz;
nuclear@0 166
nuclear@0 167 while((rdsz = recv(c->s, buf, sizeof buf, 0)) > 0) {
nuclear@0 168 if(c->rcvbuf) {
nuclear@0 169 int newsz = c->bufsz + rdsz;
nuclear@1 170 if(newsz > MAX_REQ_LENGTH) {
nuclear@1 171 respond_error(c, 413);
nuclear@1 172 return -1;
nuclear@1 173 }
nuclear@1 174
nuclear@1 175 char *newbuf = realloc(buf, newsz + 1);
nuclear@0 176 if(!newbuf) {
nuclear@0 177 fprintf(stderr, "failed to allocate %d byte buffer\n", newsz);
nuclear@1 178 respond_error(c, 503);
nuclear@1 179 return -1;
nuclear@0 180 }
nuclear@0 181
nuclear@0 182 memcpy(newbuf + c->bufsz, buf, rdsz);
nuclear@1 183 newbuf[newsz] = 0;
nuclear@0 184
nuclear@0 185 c->rcvbuf = newbuf;
nuclear@0 186 c->bufsz = newsz;
nuclear@0 187 }
nuclear@0 188 }
nuclear@0 189
nuclear@1 190 endp = c->rcvbuf + c->bufsz;
nuclear@1 191 if((reqp = skip_space(buf, endp)) >= endp) {
nuclear@1 192 return 0; /* incomplete have to read more ... */
nuclear@1 193 }
nuclear@1 194
nuclear@1 195
nuclear@1 196 /* we only support GET and HEAD at this point, so freak out on anything else */
nuclear@1 197
nuclear@0 198 /* TODO: parse header, drop on invalid, determine if the request
nuclear@0 199 * is complete and process
nuclear@0 200 */
nuclear@1 201 }
nuclear@0 202
nuclear@1 203 void respond_error(struct client *c, int errcode)
nuclear@1 204 {
nuclear@1 205 char buf[512];
nuclear@1 206
nuclear@1 207 sprintf(buf, HTTP_VER_STR " %d %s\r\n\r\n", errcode, http_strmsg(errcode));
nuclear@1 208
nuclear@1 209 send(c->s, buf, strlen(buf), 0);
nuclear@1 210 close_conn(c);
nuclear@1 211 }
nuclear@1 212
nuclear@1 213
nuclear@1 214 static void print_help(const char *argv0)
nuclear@1 215 {
nuclear@1 216 printf("Usage: %s [options]\n", argv0);
nuclear@1 217 printf("Options:\n");
nuclear@1 218 printf(" -p <port> set the TCP/IP port number to use\n");
nuclear@1 219 printf(" -h print usage help and exit\n");
nuclear@1 220 }
nuclear@1 221
nuclear@1 222 int parse_args(int argc, char **argv)
nuclear@1 223 {
nuclear@1 224 int i;
nuclear@1 225
nuclear@1 226 for(i=1; i<argc; i++) {
nuclear@1 227 if(argv[i][0] == '-' && argv[i][2] == 0) {
nuclear@1 228 switch(argv[i][1]) {
nuclear@1 229 case 'p':
nuclear@1 230 if((port = atoi(argv[++i])) == 0) {
nuclear@1 231 fprintf(stderr, "-p must be followed by a valid port number\n");
nuclear@1 232 return -1;
nuclear@1 233 }
nuclear@1 234 break;
nuclear@1 235
nuclear@1 236 case 'h':
nuclear@1 237 print_help(argv[0]);
nuclear@1 238 exit(0);
nuclear@1 239
nuclear@1 240 default:
nuclear@1 241 fprintf(stderr, "unrecognized option: %s\n", argv[i]);
nuclear@1 242 return -1;
nuclear@1 243 }
nuclear@1 244 } else {
nuclear@1 245 fprintf(stderr, "unexpected argument: %s\n", argv[i]);
nuclear@1 246 return -1;
nuclear@1 247 }
nuclear@0 248 }
nuclear@0 249 return 0;
nuclear@0 250 }