annotate src/c/http.c @ 1843:0da75c01a993

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