annotate src/c/http.c @ 1915:52e88e139b25

Add Connection and Content-length headers to raw HTTP responses
author Adam Chlipala <adam@chlipala.net>
date Wed, 27 Nov 2013 15:28:55 -0500
parents 6a2cc604a322
children 398298ca82b9
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
adam@1914 22 int uw_backlog = SOMAXCONN;
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@1915 207 if (rr != KEEP_OPEN) {
adam@1915 208 char clen[100];
adam@1915 209
adam@1915 210 uw_write_header(ctx, "Connection: close\r\n");
adam@1915 211 sprintf(clen, "Content-length: %d\r\n", uw_pagelen(ctx));
adam@1915 212 uw_write_header(ctx, clen);
adam@1915 213 uw_send(ctx, sock);
adam@1915 214 }
adamc@138 215
adamc@853 216 if (rr == SERVED || rr == FAILED)
adamc@853 217 close(sock);
adamc@853 218 else if (rr != KEEP_OPEN)
adamc@853 219 fprintf(stderr, "Illegal uw_request return code: %d\n", rr);
adamc@853 220
adamc@138 221 break;
adamc@138 222 }
adamc@116 223 }
adamc@116 224
adamc@854 225 done:
adamc@311 226 uw_reset(ctx);
adamc@116 227 }
adamc@1138 228
adamc@1138 229 return NULL;
adamc@116 230 }
adamc@102 231
adamc@477 232 static void help(char *cmd) {
adam@1779 233 printf("Usage: %s [-p <port>] [-a <IP address>] [-t <thread count>]\n", cmd);
adamc@477 234 }
adamc@477 235
adamc@502 236 static void sigint(int signum) {
adamc@502 237 printf("Exiting....\n");
adamc@502 238 exit(0);
adamc@502 239 }
adamc@502 240
adamc@1094 241 static loggers ls = {&uw_application, NULL, log_error, log_debug};
adamc@856 242
adamc@138 243 int main(int argc, char *argv[]) {
adamc@116 244 // The skeleton for this function comes from Beej's sockets tutorial.
adamc@138 245 int sockfd; // listen on sock_fd
adamc@116 246 struct sockaddr_in my_addr;
adamc@116 247 struct sockaddr_in their_addr; // connector's address information
adamc@1134 248 socklen_t sin_size;
adamc@1134 249 int yes = 1, uw_port = 8080, nthreads = 1, i, *names, opt;
adamc@502 250
adamc@502 251 signal(SIGINT, sigint);
adamc@505 252 signal(SIGPIPE, SIG_IGN);
adamc@505 253
adam@1779 254 my_addr.sin_addr.s_addr = INADDR_ANY; // auto-fill with my IP
adam@1779 255 memset(my_addr.sin_zero, '\0', sizeof my_addr.sin_zero);
adam@1779 256
adam@1779 257 while ((opt = getopt(argc, argv, "hp:a:t:")) != -1) {
adamc@472 258 switch (opt) {
adamc@472 259 case '?':
adamc@472 260 fprintf(stderr, "Unknown command-line option");
adamc@477 261 help(argv[0]);
adamc@472 262 return 1;
adamc@138 263
adamc@477 264 case 'h':
adamc@477 265 help(argv[0]);
adamc@477 266 return 0;
adamc@477 267
adamc@472 268 case 'p':
adamc@472 269 uw_port = atoi(optarg);
adamc@472 270 if (uw_port <= 0) {
adamc@472 271 fprintf(stderr, "Invalid port number\n");
adamc@477 272 help(argv[0]);
adamc@472 273 return 1;
adamc@472 274 }
adamc@472 275 break;
adamc@472 276
adam@1779 277 case 'a':
adam@1779 278 if (!inet_pton(AF_INET, optarg, &my_addr.sin_addr)) {
adam@1779 279 fprintf(stderr, "Invalid IP address\n");
adam@1779 280 help(argv[0]);
adam@1779 281 return 1;
adam@1779 282 }
adam@1779 283 break;
adam@1779 284
adamc@472 285 case 't':
adamc@472 286 nthreads = atoi(optarg);
adamc@472 287 if (nthreads <= 0) {
adamc@472 288 fprintf(stderr, "Invalid thread count\n");
adamc@477 289 help(argv[0]);
adamc@472 290 return 1;
adamc@472 291 }
adamc@472 292 break;
adamc@472 293
adamc@472 294 default:
adamc@472 295 fprintf(stderr, "Unexpected getopt() behavior\n");
adamc@472 296 return 1;
adamc@472 297 }
adamc@138 298 }
adamc@138 299
adamc@1094 300 uw_request_init(&uw_application, NULL, log_error, log_debug);
adamc@687 301
adamc@138 302 names = calloc(nthreads, sizeof(int));
adamc@116 303
adamc@116 304 sockfd = socket(PF_INET, SOCK_STREAM, 0); // do some error checking!
adamc@116 305
adamc@116 306 if (sockfd < 0) {
adamc@116 307 fprintf(stderr, "Listener socket creation failed\n");
adamc@116 308 return 1;
adamc@116 309 }
adamc@116 310
adamc@116 311 if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) < 0) {
adamc@116 312 fprintf(stderr, "Listener socket option setting failed\n");
adamc@116 313 return 1;
adamc@116 314 }
adamc@116 315
adamc@116 316 my_addr.sin_family = AF_INET; // host byte order
adamc@311 317 my_addr.sin_port = htons(uw_port); // short, network byte order
adamc@116 318
adamc@116 319 if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof my_addr) < 0) {
adamc@116 320 fprintf(stderr, "Listener socket bind failed\n");
adamc@116 321 return 1;
adamc@116 322 }
adamc@116 323
adamc@311 324 if (listen(sockfd, uw_backlog) < 0) {
adamc@116 325 fprintf(stderr, "Socket listen failed\n");
adamc@116 326 return 1;
adamc@116 327 }
adamc@116 328
adamc@116 329 sin_size = sizeof their_addr;
adamc@116 330
adamc@311 331 printf("Listening on port %d....\n", uw_port);
adamc@116 332
adamc@667 333 {
adamc@667 334 pthread_t thread;
adamc@667 335
adam@1522 336 if (pthread_create_big(&thread, NULL, client_pruner, &ls)) {
adamc@667 337 fprintf(stderr, "Error creating pruner thread\n");
adamc@667 338 return 1;
adamc@667 339 }
adamc@667 340 }
adamc@667 341
adamc@138 342 for (i = 0; i < nthreads; ++i) {
adamc@138 343 pthread_t thread;
adamc@138 344 names[i] = i;
adam@1522 345 if (pthread_create_big(&thread, NULL, worker, &names[i])) {
adamc@138 346 fprintf(stderr, "Error creating worker thread #%d\n", i);
adamc@138 347 return 1;
adamc@138 348 }
adamc@138 349 }
adamc@138 350
adamc@116 351 while (1) {
adamc@138 352 int new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
adamc@116 353
adamc@116 354 if (new_fd < 0) {
adamc@116 355 fprintf(stderr, "Socket accept failed\n");
adamc@116 356 return 1;
adamc@116 357 }
adamc@116 358
adamc@116 359 printf("Accepted connection.\n");
adamc@138 360
adamc@859 361 uw_enqueue(new_fd);
adamc@116 362 }
adamc@102 363 }
adamc@1121 364
adamc@1121 365 void *uw_init_client_data() {
adamc@1121 366 return NULL;
adamc@1121 367 }
adamc@1121 368
adamc@1121 369 void uw_free_client_data(void *data) {
adamc@1121 370 }
adamc@1121 371
adamc@1121 372 void uw_copy_client_data(void *dst, void *src) {
adamc@1121 373 }
adamc@1121 374
adamc@1121 375 void uw_do_expunge(uw_context ctx, uw_Basis_client cli, void *data) {
adamc@1121 376 if (uw_get_app(ctx)->db_begin(ctx))
adamc@1121 377 uw_error(ctx, FATAL, "Error running SQL BEGIN");
adamc@1121 378 uw_get_app(ctx)->expunger(ctx, cli);
adam@1672 379 uw_commit(ctx);
adamc@1121 380 }
adamc@1121 381
adamc@1121 382 void uw_post_expunge(uw_context ctx, void *data) {
adamc@1121 383 }
adam@1320 384
adam@1320 385 int uw_supports_direct_status = 1;