annotate src/c/http.c @ 1739:c414850f206f

Add support for -boot flag, which allows in-tree execution of Ur/Web The boot flag rewrites most hardcoded paths to point to the build directory, and also forces static compilation. This is convenient for developing Ur/Web, or if you cannot 'sudo make install' Ur/Web. The following changes were made: * Header files were moved to include/urweb instead of include; this lets FFI users point their C_INCLUDE_PATH at this directory at write <urweb/urweb.h>. For internal Ur/Web executables, we simply pass -I$PATH/include/urweb as normal. * Differentiate between LIB and SRCLIB; SRCLIB is Ur and JavaScript source files, while LIB is compiled products from libtool. For in-tree compilation these live in different places. * No longer reference Config for paths; instead use Settings; these settings can be changed dynamically by Compiler.enableBoot () (TODO: add a disableBoot function.) * config.h is now generated directly in include/urweb/config.h, for consistency's sake (especially since it gets installed along with the rest of the headers!) * All of the autotools build products got updated. * The linkStatic field in protocols now only contains the name of the build product, and not the absolute path. Future users have to be careful not to reference the Settings files to early, lest they get an old version (this was the source of two bugs during development of this patch.)
author Edward Z. Yang <ezyang@mit.edu>
date Wed, 02 May 2012 17:17:57 -0400
parents ea131de361d9
children 7095e1b7240b
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>
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@859 17 #include "queue.h"
adamc@117 18
adamc@1094 19 extern uw_app uw_application;
adamc@1094 20
adamc@311 21 int uw_backlog = 10;
adamc@116 22
adamc@854 23 static char *get_header(void *data, const char *h) {
adamc@854 24 char *s = data;
adamc@854 25 int len = strlen(h);
adamc@854 26 char *p;
adamc@854 27
adamc@1134 28 while ((p = strchr(s, ':'))) {
adamc@854 29 if (p - s == len && !strncasecmp(s, h, len)) {
adamc@854 30 return p + 2;
adamc@854 31 } else {
adamc@854 32 if ((s = strchr(p, 0)) && s[1] != 0)
adamc@854 33 s += 2;
adamc@854 34 else
adamc@854 35 return NULL;
adamc@854 36 }
adamc@854 37 }
adamc@854 38
adamc@854 39 return NULL;
adamc@854 40 }
adamc@854 41
adamc@856 42 static void on_success(uw_context ctx) {
adamc@856 43 uw_write_header(ctx, "HTTP/1.1 200 OK\r\n");
adamc@856 44 }
adamc@856 45
adamc@856 46 static void on_failure(uw_context ctx) {
adamc@856 47 uw_write_header(ctx, "HTTP/1.1 500 Internal Server Error\r\n");
adamc@856 48 }
adamc@856 49
adamc@856 50 static void log_error(void *data, const char *fmt, ...) {
adamc@856 51 va_list ap;
adamc@856 52 va_start(ap, fmt);
adamc@856 53
adamc@856 54 vfprintf(stderr, fmt, ap);
adamc@856 55 }
adamc@856 56
adamc@856 57 static void log_debug(void *data, const char *fmt, ...) {
adamc@856 58 va_list ap;
adamc@856 59 va_start(ap, fmt);
adamc@856 60
adamc@856 61 vprintf(fmt, ap);
adamc@856 62 }
adamc@856 63
adamc@853 64 static void *worker(void *data) {
adamc@853 65 int me = *(int *)data;
adam@1446 66 uw_context ctx = uw_request_new_context(me, &uw_application, NULL, log_error, log_debug);
adamc@853 67 size_t buf_size = 2;
adamc@853 68 char *buf = malloc(buf_size);
adamc@853 69 uw_request_context rc = uw_new_request_context();
adamc@698 70
adamc@272 71 while (1) {
adamc@853 72 char *back = buf;
adamc@859 73 int sock = uw_dequeue();
adamc@138 74
adamc@138 75 printf("Handling connection with thread #%d.\n", me);
adamc@138 76
adamc@138 77 while (1) {
adamc@737 78 int r;
adamc@854 79 char *method, *path, *query_string, *headers, *body, *s, *s2;
adamc@737 80
adamc@742 81 if (back - buf == buf_size - 1) {
adamc@737 82 char *new_buf;
adamc@737 83 buf_size *= 2;
adamc@737 84 new_buf = realloc(buf, buf_size);
adamc@737 85 back = new_buf + (back - buf);
adamc@737 86 buf = new_buf;
adamc@737 87 }
adamc@737 88
adamc@742 89 r = recv(sock, back, buf_size - 1 - (back - buf), 0);
adamc@138 90
adamc@138 91 if (r < 0) {
adamc@138 92 fprintf(stderr, "Recv failed\n");
adamc@138 93 break;
adamc@138 94 }
adamc@138 95
adamc@138 96 if (r == 0) {
adamc@138 97 printf("Connection closed.\n");
adamc@138 98 break;
adamc@138 99 }
adamc@138 100
adamc@138 101 back += r;
adamc@138 102 *back = 0;
adamc@730 103
adamc@854 104 if ((body = strstr(buf, "\r\n\r\n"))) {
adamc@853 105 request_result rr;
adamc@138 106
adamc@854 107 body[0] = body[1] = 0;
adamc@854 108 body += 4;
adamc@854 109
adamc@854 110 if ((s = strcasestr(buf, "\r\nContent-Length: ")) && s < body) {
adamc@853 111 int clen;
adamc@737 112
adamc@854 113 if (sscanf(s + 18, "%d\r\n", &clen) != 1) {
adamc@853 114 fprintf(stderr, "Malformed Content-Length header\n");
adamc@167 115 break;
adamc@167 116 }
adamc@167 117
adamc@854 118 while (back - body < clen) {
adamc@854 119 if (back - buf == buf_size - 1) {
adamc@854 120 char *new_buf;
adamc@854 121 buf_size *= 2;
adamc@854 122 new_buf = realloc(buf, buf_size);
adamc@854 123
adamc@854 124 back = new_buf + (back - buf);
adamc@854 125 body = new_buf + (body - buf);
adamc@854 126 s = new_buf + (s - buf);
adamc@854 127
adamc@854 128 buf = new_buf;
adamc@854 129 }
adamc@854 130
adamc@854 131 r = recv(sock, back, buf_size - 1 - (back - buf), 0);
adamc@854 132
adamc@854 133 if (r < 0) {
adamc@854 134 fprintf(stderr, "Recv failed\n");
adamc@854 135 close(sock);
adamc@854 136 goto done;
adamc@854 137 }
adamc@854 138
adamc@854 139 if (r == 0) {
adamc@854 140 fprintf(stderr, "Connection closed.\n");
adamc@854 141 close(sock);
adamc@854 142 goto done;
adamc@854 143 }
adamc@854 144
adamc@854 145 back += r;
adamc@854 146 *back = 0;
adamc@854 147 }
adamc@167 148 }
adamc@138 149
adamc@1119 150 body[-4] = '\r';
adamc@1119 151 body[-3] = '\n';
adamc@1119 152
adamc@854 153 if (!(s = strstr(buf, "\r\n"))) {
adamc@854 154 fprintf(stderr, "No newline in request\n");
adamc@854 155 close(sock);
adamc@854 156 goto done;
adamc@854 157 }
adamc@854 158
adamc@1119 159 body[-4] = body[-3] = 0;
adamc@1119 160
adamc@854 161 *s = 0;
adamc@854 162 headers = s + 2;
adamc@854 163 method = s = buf;
adamc@854 164
adam@1426 165 strsep(&s, " ");
adam@1426 166 if (!s) {
adamc@854 167 fprintf(stderr, "No first space in HTTP command\n");
adamc@854 168 close(sock);
adamc@854 169 goto done;
adamc@854 170 }
adamc@854 171 path = s;
adamc@854 172
adamc@1134 173 if ((s = strchr(path, ' ')))
adamc@854 174 *s = 0;
adamc@854 175
adamc@1134 176 if ((s = strchr(path, '?'))) {
adamc@854 177 *s = 0;
adamc@854 178 query_string = s+1;
adamc@854 179 }
adamc@854 180 else
adamc@854 181 query_string = NULL;
adamc@854 182
adamc@854 183 s = headers;
adamc@1134 184 while ((s2 = strchr(s, '\r'))) {
adamc@854 185 s = s2;
adamc@854 186
adamc@854 187 if (s[1] == 0)
adamc@854 188 break;
adamc@854 189
adamc@854 190 *s = 0;
adamc@854 191 s += 2;
adamc@854 192 }
adamc@854 193
adamc@854 194 uw_set_headers(ctx, get_header, headers);
adamc@854 195
adam@1415 196 printf("Serving URI %s....\n", path);
adamc@856 197 rr = uw_request(rc, ctx, method, path, query_string, body, back - body,
adamc@856 198 on_success, on_failure,
adamc@856 199 NULL, log_error, log_debug,
adamc@863 200 sock, uw_really_send, close);
adam@1447 201 if (rr != KEEP_OPEN) uw_send(ctx, sock);
adamc@138 202
adamc@853 203 if (rr == SERVED || rr == FAILED)
adamc@853 204 close(sock);
adamc@853 205 else if (rr != KEEP_OPEN)
adamc@853 206 fprintf(stderr, "Illegal uw_request return code: %d\n", rr);
adamc@853 207
adamc@138 208 break;
adamc@138 209 }
adamc@116 210 }
adamc@116 211
adamc@854 212 done:
adamc@311 213 uw_reset(ctx);
adamc@116 214 }
adamc@1138 215
adamc@1138 216 return NULL;
adamc@116 217 }
adamc@102 218
adamc@477 219 static void help(char *cmd) {
adamc@477 220 printf("Usage: %s [-p <port>] [-t <thread-count>]\n", cmd);
adamc@477 221 }
adamc@477 222
adamc@502 223 static void sigint(int signum) {
adamc@502 224 printf("Exiting....\n");
adamc@502 225 exit(0);
adamc@502 226 }
adamc@502 227
adamc@1094 228 static loggers ls = {&uw_application, NULL, log_error, log_debug};
adamc@856 229
adamc@138 230 int main(int argc, char *argv[]) {
adamc@116 231 // The skeleton for this function comes from Beej's sockets tutorial.
adamc@138 232 int sockfd; // listen on sock_fd
adamc@116 233 struct sockaddr_in my_addr;
adamc@116 234 struct sockaddr_in their_addr; // connector's address information
adamc@1134 235 socklen_t sin_size;
adamc@1134 236 int yes = 1, uw_port = 8080, nthreads = 1, i, *names, opt;
adamc@502 237
adamc@502 238 signal(SIGINT, sigint);
adamc@505 239 signal(SIGPIPE, SIG_IGN);
adamc@505 240
adamc@477 241 while ((opt = getopt(argc, argv, "hp:t:")) != -1) {
adamc@472 242 switch (opt) {
adamc@472 243 case '?':
adamc@472 244 fprintf(stderr, "Unknown command-line option");
adamc@477 245 help(argv[0]);
adamc@472 246 return 1;
adamc@138 247
adamc@477 248 case 'h':
adamc@477 249 help(argv[0]);
adamc@477 250 return 0;
adamc@477 251
adamc@472 252 case 'p':
adamc@472 253 uw_port = atoi(optarg);
adamc@472 254 if (uw_port <= 0) {
adamc@472 255 fprintf(stderr, "Invalid port number\n");
adamc@477 256 help(argv[0]);
adamc@472 257 return 1;
adamc@472 258 }
adamc@472 259 break;
adamc@472 260
adamc@472 261 case 't':
adamc@472 262 nthreads = atoi(optarg);
adamc@472 263 if (nthreads <= 0) {
adamc@472 264 fprintf(stderr, "Invalid thread count\n");
adamc@477 265 help(argv[0]);
adamc@472 266 return 1;
adamc@472 267 }
adamc@472 268 break;
adamc@472 269
adamc@472 270 default:
adamc@472 271 fprintf(stderr, "Unexpected getopt() behavior\n");
adamc@472 272 return 1;
adamc@472 273 }
adamc@138 274 }
adamc@138 275
adamc@1094 276 uw_request_init(&uw_application, NULL, log_error, log_debug);
adamc@687 277
adamc@138 278 names = calloc(nthreads, sizeof(int));
adamc@116 279
adamc@116 280 sockfd = socket(PF_INET, SOCK_STREAM, 0); // do some error checking!
adamc@116 281
adamc@116 282 if (sockfd < 0) {
adamc@116 283 fprintf(stderr, "Listener socket creation failed\n");
adamc@116 284 return 1;
adamc@116 285 }
adamc@116 286
adamc@116 287 if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) < 0) {
adamc@116 288 fprintf(stderr, "Listener socket option setting failed\n");
adamc@116 289 return 1;
adamc@116 290 }
adamc@116 291
adamc@116 292 my_addr.sin_family = AF_INET; // host byte order
adamc@311 293 my_addr.sin_port = htons(uw_port); // short, network byte order
adamc@116 294 my_addr.sin_addr.s_addr = INADDR_ANY; // auto-fill with my IP
adamc@116 295 memset(my_addr.sin_zero, '\0', sizeof my_addr.sin_zero);
adamc@116 296
adamc@116 297 if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof my_addr) < 0) {
adamc@116 298 fprintf(stderr, "Listener socket bind failed\n");
adamc@116 299 return 1;
adamc@116 300 }
adamc@116 301
adamc@311 302 if (listen(sockfd, uw_backlog) < 0) {
adamc@116 303 fprintf(stderr, "Socket listen failed\n");
adamc@116 304 return 1;
adamc@116 305 }
adamc@116 306
adamc@116 307 sin_size = sizeof their_addr;
adamc@116 308
adamc@311 309 printf("Listening on port %d....\n", uw_port);
adamc@116 310
adamc@667 311 {
adamc@667 312 pthread_t thread;
adamc@667 313
adam@1522 314 if (pthread_create_big(&thread, NULL, client_pruner, &ls)) {
adamc@667 315 fprintf(stderr, "Error creating pruner thread\n");
adamc@667 316 return 1;
adamc@667 317 }
adamc@667 318 }
adamc@667 319
adamc@138 320 for (i = 0; i < nthreads; ++i) {
adamc@138 321 pthread_t thread;
adamc@138 322 names[i] = i;
adam@1522 323 if (pthread_create_big(&thread, NULL, worker, &names[i])) {
adamc@138 324 fprintf(stderr, "Error creating worker thread #%d\n", i);
adamc@138 325 return 1;
adamc@138 326 }
adamc@138 327 }
adamc@138 328
adamc@116 329 while (1) {
adamc@138 330 int new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
adamc@116 331
adamc@116 332 if (new_fd < 0) {
adamc@116 333 fprintf(stderr, "Socket accept failed\n");
adamc@116 334 return 1;
adamc@116 335 }
adamc@116 336
adamc@116 337 printf("Accepted connection.\n");
adamc@138 338
adamc@859 339 uw_enqueue(new_fd);
adamc@116 340 }
adamc@102 341 }
adamc@1121 342
adamc@1121 343 void *uw_init_client_data() {
adamc@1121 344 return NULL;
adamc@1121 345 }
adamc@1121 346
adamc@1121 347 void uw_free_client_data(void *data) {
adamc@1121 348 }
adamc@1121 349
adamc@1121 350 void uw_copy_client_data(void *dst, void *src) {
adamc@1121 351 }
adamc@1121 352
adamc@1121 353 void uw_do_expunge(uw_context ctx, uw_Basis_client cli, void *data) {
adamc@1121 354 if (uw_get_app(ctx)->db_begin(ctx))
adamc@1121 355 uw_error(ctx, FATAL, "Error running SQL BEGIN");
adamc@1121 356 uw_get_app(ctx)->expunger(ctx, cli);
adam@1672 357 uw_commit(ctx);
adamc@1121 358 }
adamc@1121 359
adamc@1121 360 void uw_post_expunge(uw_context ctx, void *data) {
adamc@1121 361 }
adam@1320 362
adam@1320 363 int uw_supports_direct_status = 1;