annotate src/c/http.c @ 856:86ec89baee01

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