tinywebd

annotate src/main.c @ 3:852a745503cf

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