annotate src/c/http.c @ 1779:7095e1b7240b

HTTP daemons now take '-a' option to set IP address to listen on
author Adam Chlipala <adam@chlipala.net>
date Sat, 23 Jun 2012 09:46:40 -0400
parents ea131de361d9
children 3d922a28370b
rev   line source
adamc@1268 1 #include "config.h"
adamc@1268 2
adamc@116 3 #include <stdio.h>
adamc@116 4 #include <string.h>
adamc@502 5 #include <stdlib.h>
adamc@116 6 #include <sys/types.h>
adamc@116 7 #include <sys/socket.h>
adamc@116 8 #include <netinet/in.h>
adam@1779 9 #include <arpa/inet.h>
adamc@472 10 #include <unistd.h>
adamc@502 11 #include <signal.h>
adamc@856 12 #include <stdarg.h>
adamc@116 13
adamc@138 14 #include <pthread.h>
adamc@138 15
adamc@244 16 #include "urweb.h"
adamc@853 17 #include "request.h"
adamc@859 18 #include "queue.h"
adamc@117 19
adamc@1094 20 extern uw_app uw_application;
adamc@1094 21
adamc@311 22 int uw_backlog = 10;
adamc@116 23
adamc@854 24 static char *get_header(void *data, const char *h) {
adamc@854 25 char *s = data;
adamc@854 26 int len = strlen(h);
adamc@854 27 char *p;
adamc@854 28
adamc@1134 29 while ((p = strchr(s, ':'))) {
adamc@854 30 if (p - s == len && !strncasecmp(s, h, len)) {
adamc@854 31 return p + 2;
adamc@854 32 } else {
adamc@854 33 if ((s = strchr(p, 0)) && s[1] != 0)
adamc@854 34 s += 2;
adamc@854 35 else
adamc@854 36 return NULL;
adamc@854 37 }
adamc@854 38 }
adamc@854 39
adamc@854 40 return NULL;
adamc@854 41 }
adamc@854 42
adamc@856 43 static void on_success(uw_context ctx) {
adamc@856 44 uw_write_header(ctx, "HTTP/1.1 200 OK\r\n");
adamc@856 45 }
adamc@856 46
adamc@856 47 static void on_failure(uw_context ctx) {
adamc@856 48 uw_write_header(ctx, "HTTP/1.1 500 Internal Server Error\r\n");
adamc@856 49 }
adamc@856 50
adamc@856 51 static void log_error(void *data, const char *fmt, ...) {
adamc@856 52 va_list ap;
adamc@856 53 va_start(ap, fmt);
adamc@856 54
adamc@856 55 vfprintf(stderr, fmt, ap);
adamc@856 56 }
adamc@856 57
adamc@856 58 static void log_debug(void *data, const char *fmt, ...) {
adamc@856 59 va_list ap;
adamc@856 60 va_start(ap, fmt);
adamc@856 61
adamc@856 62 vprintf(fmt, ap);
adamc@856 63 }
adamc@856 64
adamc@853 65 static void *worker(void *data) {
adamc@853 66 int me = *(int *)data;
adam@1446 67 uw_context ctx = uw_request_new_context(me, &uw_application, NULL, log_error, log_debug);
adamc@853 68 size_t buf_size = 2;
adamc@853 69 char *buf = malloc(buf_size);
adamc@853 70 uw_request_context rc = uw_new_request_context();
adamc@698 71
adamc@272 72 while (1) {
adamc@853 73 char *back = buf;
adamc@859 74 int sock = uw_dequeue();
adamc@138 75
adamc@138 76 printf("Handling connection with thread #%d.\n", me);
adamc@138 77
adamc@138 78 while (1) {
adamc@737 79 int r;
adamc@854 80 char *method, *path, *query_string, *headers, *body, *s, *s2;
adamc@737 81
adamc@742 82 if (back - buf == buf_size - 1) {
adamc@737 83 char *new_buf;
adamc@737 84 buf_size *= 2;
adamc@737 85 new_buf = realloc(buf, buf_size);
adamc@737 86 back = new_buf + (back - buf);
adamc@737 87 buf = new_buf;
adamc@737 88 }
adamc@737 89
adamc@742 90 r = recv(sock, back, buf_size - 1 - (back - buf), 0);
adamc@138 91
adamc@138 92 if (r < 0) {
adamc@138 93 fprintf(stderr, "Recv failed\n");
adamc@138 94 break;
adamc@138 95 }
adamc@138 96
adamc@138 97 if (r == 0) {
adamc@138 98 printf("Connection closed.\n");
adamc@138 99 break;
adamc@138 100 }
adamc@138 101
adamc@138 102 back += r;
adamc@138 103 *back = 0;
adamc@730 104
adamc@854 105 if ((body = strstr(buf, "\r\n\r\n"))) {
adamc@853 106 request_result rr;
adamc@138 107
adamc@854 108 body[0] = body[1] = 0;
adamc@854 109 body += 4;
adamc@854 110
adamc@854 111 if ((s = strcasestr(buf, "\r\nContent-Length: ")) && s < body) {
adamc@853 112 int clen;
adamc@737 113
adamc@854 114 if (sscanf(s + 18, "%d\r\n", &clen) != 1) {
adamc@853 115 fprintf(stderr, "Malformed Content-Length header\n");
adamc@167 116 break;
adamc@167 117 }
adamc@167 118
adamc@854 119 while (back - body < clen) {
adamc@854 120 if (back - buf == buf_size - 1) {
adamc@854 121 char *new_buf;
adamc@854 122 buf_size *= 2;
adamc@854 123 new_buf = realloc(buf, buf_size);
adamc@854 124
adamc@854 125 back = new_buf + (back - buf);
adamc@854 126 body = new_buf + (body - buf);
adamc@854 127 s = new_buf + (s - buf);
adamc@854 128
adamc@854 129 buf = new_buf;
adamc@854 130 }
adamc@854 131
adamc@854 132 r = recv(sock, back, buf_size - 1 - (back - buf), 0);
adamc@854 133
adamc@854 134 if (r < 0) {
adamc@854 135 fprintf(stderr, "Recv failed\n");
adamc@854 136 close(sock);
adamc@854 137 goto done;
adamc@854 138 }
adamc@854 139
adamc@854 140 if (r == 0) {
adamc@854 141 fprintf(stderr, "Connection closed.\n");
adamc@854 142 close(sock);
adamc@854 143 goto done;
adamc@854 144 }
adamc@854 145
adamc@854 146 back += r;
adamc@854 147 *back = 0;
adamc@854 148 }
adamc@167 149 }
adamc@138 150
adamc@1119 151 body[-4] = '\r';
adamc@1119 152 body[-3] = '\n';
adamc@1119 153
adamc@854 154 if (!(s = strstr(buf, "\r\n"))) {
adamc@854 155 fprintf(stderr, "No newline in request\n");
adamc@854 156 close(sock);
adamc@854 157 goto done;
adamc@854 158 }
adamc@854 159
adamc@1119 160 body[-4] = body[-3] = 0;
adamc@1119 161
adamc@854 162 *s = 0;
adamc@854 163 headers = s + 2;
adamc@854 164 method = s = buf;
adamc@854 165
adam@1426 166 strsep(&s, " ");
adam@1426 167 if (!s) {
adamc@854 168 fprintf(stderr, "No first space in HTTP command\n");
adamc@854 169 close(sock);
adamc@854 170 goto done;
adamc@854 171 }
adamc@854 172 path = s;
adamc@854 173
adamc@1134 174 if ((s = strchr(path, ' ')))
adamc@854 175 *s = 0;
adamc@854 176
adamc@1134 177 if ((s = strchr(path, '?'))) {
adamc@854 178 *s = 0;
adamc@854 179 query_string = s+1;
adamc@854 180 }
adamc@854 181 else
adamc@854 182 query_string = NULL;
adamc@854 183
adamc@854 184 s = headers;
adamc@1134 185 while ((s2 = strchr(s, '\r'))) {
adamc@854 186 s = s2;
adamc@854 187
adamc@854 188 if (s[1] == 0)
adamc@854 189 break;
adamc@854 190
adamc@854 191 *s = 0;
adamc@854 192 s += 2;
adamc@854 193 }
adamc@854 194
adamc@854 195 uw_set_headers(ctx, get_header, headers);
adamc@854 196
adam@1415 197 printf("Serving URI %s....\n", path);
adamc@856 198 rr = uw_request(rc, ctx, method, path, query_string, body, back - body,
adamc@856 199 on_success, on_failure,
adamc@856 200 NULL, log_error, log_debug,
adamc@863 201 sock, uw_really_send, close);
adam@1447 202 if (rr != KEEP_OPEN) uw_send(ctx, sock);
adamc@138 203
adamc@853 204 if (rr == SERVED || rr == FAILED)
adamc@853 205 close(sock);
adamc@853 206 else if (rr != KEEP_OPEN)
adamc@853 207 fprintf(stderr, "Illegal uw_request return code: %d\n", rr);
adamc@853 208
adamc@138 209 break;
adamc@138 210 }
adamc@116 211 }
adamc@116 212
adamc@854 213 done:
adamc@311 214 uw_reset(ctx);
adamc@116 215 }
adamc@1138 216
adamc@1138 217 return NULL;
adamc@116 218 }
adamc@102 219
adamc@477 220 static void help(char *cmd) {
adam@1779 221 printf("Usage: %s [-p <port>] [-a <IP address>] [-t <thread count>]\n", cmd);
adamc@477 222 }
adamc@477 223
adamc@502 224 static void sigint(int signum) {
adamc@502 225 printf("Exiting....\n");
adamc@502 226 exit(0);
adamc@502 227 }
adamc@502 228
adamc@1094 229 static loggers ls = {&uw_application, NULL, log_error, log_debug};
adamc@856 230
adamc@138 231 int main(int argc, char *argv[]) {
adamc@116 232 // The skeleton for this function comes from Beej's sockets tutorial.
adamc@138 233 int sockfd; // listen on sock_fd
adamc@116 234 struct sockaddr_in my_addr;
adamc@116 235 struct sockaddr_in their_addr; // connector's address information
adamc@1134 236 socklen_t sin_size;
adamc@1134 237 int yes = 1, uw_port = 8080, nthreads = 1, i, *names, opt;
adamc@502 238
adamc@502 239 signal(SIGINT, sigint);
adamc@505 240 signal(SIGPIPE, SIG_IGN);
adamc@505 241
adam@1779 242 my_addr.sin_addr.s_addr = INADDR_ANY; // auto-fill with my IP
adam@1779 243 memset(my_addr.sin_zero, '\0', sizeof my_addr.sin_zero);
adam@1779 244
adam@1779 245 while ((opt = getopt(argc, argv, "hp:a:t:")) != -1) {
adamc@472 246 switch (opt) {
adamc@472 247 case '?':
adamc@472 248 fprintf(stderr, "Unknown command-line option");
adamc@477 249 help(argv[0]);
adamc@472 250 return 1;
adamc@138 251
adamc@477 252 case 'h':
adamc@477 253 help(argv[0]);
adamc@477 254 return 0;
adamc@477 255
adamc@472 256 case 'p':
adamc@472 257 uw_port = atoi(optarg);
adamc@472 258 if (uw_port <= 0) {
adamc@472 259 fprintf(stderr, "Invalid port number\n");
adamc@477 260 help(argv[0]);
adamc@472 261 return 1;
adamc@472 262 }
adamc@472 263 break;
adamc@472 264
adam@1779 265 case 'a':
adam@1779 266 if (!inet_pton(AF_INET, optarg, &my_addr.sin_addr)) {
adam@1779 267 fprintf(stderr, "Invalid IP address\n");
adam@1779 268 help(argv[0]);
adam@1779 269 return 1;
adam@1779 270 }
adam@1779 271 break;
adam@1779 272
adamc@472 273 case 't':
adamc@472 274 nthreads = atoi(optarg);
adamc@472 275 if (nthreads <= 0) {
adamc@472 276 fprintf(stderr, "Invalid thread count\n");
adamc@477 277 help(argv[0]);
adamc@472 278 return 1;
adamc@472 279 }
adamc@472 280 break;
adamc@472 281
adamc@472 282 default:
adamc@472 283 fprintf(stderr, "Unexpected getopt() behavior\n");
adamc@472 284 return 1;
adamc@472 285 }
adamc@138 286 }
adamc@138 287
adamc@1094 288 uw_request_init(&uw_application, NULL, log_error, log_debug);
adamc@687 289
adamc@138 290 names = calloc(nthreads, sizeof(int));
adamc@116 291
adamc@116 292 sockfd = socket(PF_INET, SOCK_STREAM, 0); // do some error checking!
adamc@116 293
adamc@116 294 if (sockfd < 0) {
adamc@116 295 fprintf(stderr, "Listener socket creation failed\n");
adamc@116 296 return 1;
adamc@116 297 }
adamc@116 298
adamc@116 299 if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) < 0) {
adamc@116 300 fprintf(stderr, "Listener socket option setting failed\n");
adamc@116 301 return 1;
adamc@116 302 }
adamc@116 303
adamc@116 304 my_addr.sin_family = AF_INET; // host byte order
adamc@311 305 my_addr.sin_port = htons(uw_port); // short, network byte order
adamc@116 306
adamc@116 307 if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof my_addr) < 0) {
adamc@116 308 fprintf(stderr, "Listener socket bind failed\n");
adamc@116 309 return 1;
adamc@116 310 }
adamc@116 311
adamc@311 312 if (listen(sockfd, uw_backlog) < 0) {
adamc@116 313 fprintf(stderr, "Socket listen failed\n");
adamc@116 314 return 1;
adamc@116 315 }
adamc@116 316
adamc@116 317 sin_size = sizeof their_addr;
adamc@116 318
adamc@311 319 printf("Listening on port %d....\n", uw_port);
adamc@116 320
adamc@667 321 {
adamc@667 322 pthread_t thread;
adamc@667 323
adam@1522 324 if (pthread_create_big(&thread, NULL, client_pruner, &ls)) {
adamc@667 325 fprintf(stderr, "Error creating pruner thread\n");
adamc@667 326 return 1;
adamc@667 327 }
adamc@667 328 }
adamc@667 329
adamc@138 330 for (i = 0; i < nthreads; ++i) {
adamc@138 331 pthread_t thread;
adamc@138 332 names[i] = i;
adam@1522 333 if (pthread_create_big(&thread, NULL, worker, &names[i])) {
adamc@138 334 fprintf(stderr, "Error creating worker thread #%d\n", i);
adamc@138 335 return 1;
adamc@138 336 }
adamc@138 337 }
adamc@138 338
adamc@116 339 while (1) {
adamc@138 340 int new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
adamc@116 341
adamc@116 342 if (new_fd < 0) {
adamc@116 343 fprintf(stderr, "Socket accept failed\n");
adamc@116 344 return 1;
adamc@116 345 }
adamc@116 346
adamc@116 347 printf("Accepted connection.\n");
adamc@138 348
adamc@859 349 uw_enqueue(new_fd);
adamc@116 350 }
adamc@102 351 }
adamc@1121 352
adamc@1121 353 void *uw_init_client_data() {
adamc@1121 354 return NULL;
adamc@1121 355 }
adamc@1121 356
adamc@1121 357 void uw_free_client_data(void *data) {
adamc@1121 358 }
adamc@1121 359
adamc@1121 360 void uw_copy_client_data(void *dst, void *src) {
adamc@1121 361 }
adamc@1121 362
adamc@1121 363 void uw_do_expunge(uw_context ctx, uw_Basis_client cli, void *data) {
adamc@1121 364 if (uw_get_app(ctx)->db_begin(ctx))
adamc@1121 365 uw_error(ctx, FATAL, "Error running SQL BEGIN");
adamc@1121 366 uw_get_app(ctx)->expunger(ctx, cli);
adam@1672 367 uw_commit(ctx);
adamc@1121 368 }
adamc@1121 369
adamc@1121 370 void uw_post_expunge(uw_context ctx, void *data) {
adamc@1121 371 }
adam@1320 372
adam@1320 373 int uw_supports_direct_status = 1;