annotate src/c/request.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 4d0b80dd4c37
children 27fdd78bd2f5
rev   line source
adamc@1268 1 #include "config.h"
adamc@1268 2
adamc@853 3 #include <stdio.h>
adamc@853 4 #include <string.h>
adamc@853 5 #include <stdlib.h>
adamc@853 6 #include <sys/types.h>
adamc@853 7 #include <sys/socket.h>
adamc@853 8 #include <netinet/in.h>
adamc@853 9 #include <unistd.h>
adamc@853 10 #include <signal.h>
adamc@853 11
adamc@853 12 #include <pthread.h>
adamc@853 13
adamc@853 14 #include "urweb.h"
adamc@853 15
adamc@853 16 #define MAX_RETRIES 5
adamc@853 17
adamc@1152 18 void *memmem(const void *b1, size_t len1, const void *b2, size_t len2);
adamc@1152 19
adam@1327 20 static int try_rollback(uw_context ctx, int will_retry, void *logger_data, uw_logger log_error) {
adam@1327 21 int r = uw_rollback(ctx, will_retry);
adamc@853 22
adamc@853 23 if (r) {
adamc@856 24 log_error(logger_data, "Error running SQL ROLLBACK\n");
adamc@853 25 uw_reset(ctx);
adamc@1115 26 uw_write(ctx, "HTTP/1.1 500 Internal Server Error\r\n");
adamc@853 27 uw_write(ctx, "Content-type: text/plain\r\n\r\n");
adamc@853 28 uw_write(ctx, "Error running SQL ROLLBACK\n");
adamc@1115 29 uw_set_error_message(ctx, "Database error; you are probably out of storage space.");
adamc@853 30 }
adamc@853 31
adamc@853 32 return r;
adamc@853 33 }
adamc@853 34
adam@1446 35 uw_context uw_request_new_context(int id, uw_app *app, void *logger_data, uw_logger log_error, uw_logger log_debug) {
adam@1446 36 uw_context ctx = uw_init(id, logger_data, log_debug);
adamc@853 37 int retries_left = MAX_RETRIES;
adamc@1094 38 uw_set_app(ctx, app);
adamc@853 39
adamc@853 40 while (1) {
adamc@853 41 failure_kind fk = uw_begin_init(ctx);
adamc@853 42
adamc@853 43 if (fk == SUCCESS) {
adamc@856 44 log_debug(logger_data, "Database connection initialized.\n");
adamc@853 45 break;
adamc@853 46 } else if (fk == BOUNDED_RETRY) {
adamc@853 47 if (retries_left) {
adamc@856 48 log_debug(logger_data, "Initialization error triggers bounded retry: %s\n", uw_error_message(ctx));
adamc@853 49 --retries_left;
adamc@853 50 } else {
adamc@856 51 log_error(logger_data, "Fatal initialization error (out of retries): %s\n", uw_error_message(ctx));
adamc@853 52 uw_free(ctx);
adamc@853 53 return NULL;
adamc@853 54 }
adamc@853 55 } else if (fk == UNLIMITED_RETRY)
adamc@856 56 log_debug(logger_data, "Initialization error triggers unlimited retry: %s\n", uw_error_message(ctx));
adamc@853 57 else if (fk == FATAL) {
adamc@856 58 log_error(logger_data, "Fatal initialization error: %s\n", uw_error_message(ctx));
adamc@853 59 uw_free(ctx);
adamc@853 60 return NULL;
adamc@853 61 } else {
adamc@856 62 log_error(logger_data, "Unknown uw_begin_init return code!\n");
adamc@853 63 uw_free(ctx);
adamc@853 64 return NULL;
adamc@853 65 }
adamc@853 66 }
adamc@853 67
adamc@853 68 return ctx;
adamc@853 69 }
adamc@853 70
adam@1308 71 static void *ticker(void *data) {
adam@1308 72 while (1) {
adam@1308 73 usleep(100000);
adam@1308 74 ++uw_time;
adam@1308 75 }
adam@1308 76
adam@1308 77 return NULL;
adam@1308 78 }
adam@1308 79
adam@1349 80 typedef struct {
adam@1349 81 uw_app *app;
adam@1349 82 void *logger_data;
adam@1349 83 uw_logger log_error, log_debug;
adam@1349 84 } loggers;
adam@1349 85
adam@1349 86 typedef struct {
adam@1446 87 int id;
adam@1349 88 loggers *ls;
adam@1349 89 uw_periodic pdic;
adam@1349 90 } periodic;
adam@1349 91
adam@1349 92 static void *periodic_loop(void *data) {
adam@1349 93 periodic *p = (periodic *)data;
adam@1446 94 uw_context ctx = uw_request_new_context(p->id, p->ls->app, p->ls->logger_data, p->ls->log_error, p->ls->log_debug);
adam@1349 95
adam@1349 96 if (!ctx)
adam@1349 97 exit(1);
adam@1349 98
adam@1349 99 while (1) {
adam@1418 100 int retries_left = MAX_RETRIES;
adam@1418 101
adam@1349 102 failure_kind r;
adam@1349 103 do {
adam@1418 104 uw_reset(ctx);
adam@1349 105 r = uw_runCallback(ctx, p->pdic.callback);
adam@1418 106 if (r == BOUNDED_RETRY)
adam@1418 107 --retries_left;
adam@1418 108 else if (r == UNLIMITED_RETRY)
adam@1418 109 p->ls->log_debug(p->ls->logger_data, "Error triggers unlimited retry in periodic: %s\n", uw_error_message(ctx));
adam@1418 110 else if (r == BOUNDED_RETRY)
adam@1418 111 p->ls->log_debug(p->ls->logger_data, "Error triggers bounded retry in periodic: %s\n", uw_error_message(ctx));
adam@1418 112 else if (r == FATAL)
adam@1418 113 p->ls->log_error(p->ls->logger_data, "Fatal error: %s\n", uw_error_message(ctx));
adam@1418 114 if (r == FATAL || r == BOUNDED_RETRY || r == UNLIMITED_RETRY)
adam@1437 115 if (try_rollback(ctx, 0, p->ls->logger_data, p->ls->log_error))
adam@1437 116 return NULL;
adam@1418 117 } while (r == UNLIMITED_RETRY || (r == BOUNDED_RETRY && retries_left > 0));
adam@1418 118
adam@1418 119 if (r != FATAL && r != BOUNDED_RETRY)
adam@1418 120 uw_commit(ctx);
adam@1349 121
adam@1349 122 sleep(p->pdic.period);
adam@1349 123 };
adam@1349 124 }
adam@1349 125
adam@1522 126 static unsigned long long stackSize;
adam@1522 127
adam@1522 128 int pthread_create_big(pthread_t *outThread, void *foo, void *threadFunc, void *arg)
adam@1522 129 {
adam@1522 130 int err;
adam@1522 131 pthread_attr_t stackSizeAttribute;
adam@1522 132
adam@1522 133 err = pthread_attr_init(&stackSizeAttribute);
adam@1522 134 if (err) return err;
adam@1522 135
adam@1522 136 if (stackSize > 0) {
adam@1522 137 err = pthread_attr_setstacksize(&stackSizeAttribute, stackSize);
adam@1522 138 if (err) return err;
adam@1522 139 }
adam@1522 140
adam@1522 141 return pthread_create(outThread, &stackSizeAttribute, threadFunc, arg);
adam@1522 142 }
adam@1522 143
adamc@1094 144 void uw_request_init(uw_app *app, void *logger_data, uw_logger log_error, uw_logger log_debug) {
adamc@853 145 uw_context ctx;
adamc@853 146 failure_kind fk;
adam@1349 147 uw_periodic *ps;
adam@1349 148 loggers *ls = malloc(sizeof(loggers));
adam@1446 149 int id;
adam@1522 150 char *stackSize_s;
adam@1522 151
adam@1522 152 if ((stackSize_s = getenv("URWEB_STACK_SIZE")) != NULL && stackSize_s[0] != 0) {
adam@1522 153 stackSize = atoll(stackSize_s);
adam@1522 154
adam@1522 155 if (stackSize <= 0) {
adam@1522 156 fprintf(stderr, "Invalid stack size \"%s\"\n", stackSize_s);
adam@1522 157 exit(1);
adam@1522 158 }
adam@1522 159 }
adam@1349 160
adam@1349 161 ls->app = app;
adam@1349 162 ls->logger_data = logger_data;
adam@1349 163 ls->log_error = log_error;
adam@1349 164 ls->log_debug = log_debug;
adamc@853 165
adamc@853 166 uw_global_init();
adamc@1094 167 uw_app_init(app);
adamc@853 168
adam@1308 169 {
adam@1308 170 pthread_t thread;
adam@1308 171
adam@1522 172 if (uw_time_max && pthread_create_big(&thread, NULL, ticker, NULL)) {
adam@1308 173 fprintf(stderr, "Error creating ticker thread\n");
adam@1308 174 exit(1);
adam@1308 175 }
adam@1308 176 }
adam@1308 177
adam@1446 178 ctx = uw_request_new_context(0, app, logger_data, log_error, log_debug);
adamc@853 179
adamc@853 180 if (!ctx)
adamc@853 181 exit(1);
adamc@853 182
adamc@853 183 for (fk = uw_initialize(ctx); fk == UNLIMITED_RETRY; fk = uw_initialize(ctx)) {
adamc@856 184 log_debug(logger_data, "Unlimited retry during init: %s\n", uw_error_message(ctx));
adam@1327 185 uw_rollback(ctx, 1);
adamc@853 186 uw_reset(ctx);
adamc@853 187 }
adamc@853 188
adamc@853 189 if (fk != SUCCESS) {
adamc@856 190 log_error(logger_data, "Failed to initialize database! %s\n", uw_error_message(ctx));
adam@1327 191 uw_rollback(ctx, 0);
adamc@853 192 exit(1);
adamc@853 193 }
adamc@853 194
adamc@853 195 uw_free(ctx);
adam@1349 196
adam@1446 197 id = 1;
adam@1349 198 for (ps = app->periodics; ps->callback; ++ps) {
adam@1349 199 pthread_t thread;
adam@1349 200 periodic *arg = malloc(sizeof(periodic));
adam@1446 201 arg->id = id++;
adam@1349 202 arg->ls = ls;
adam@1349 203 arg->pdic = *ps;
adam@1349 204
adam@1522 205 if (pthread_create_big(&thread, NULL, periodic_loop, arg)) {
adam@1349 206 fprintf(stderr, "Error creating periodic thread\n");
adam@1349 207 exit(1);
adam@1349 208 }
adam@1349 209 }
adamc@853 210 }
adamc@853 211
adamc@853 212
adamc@853 213 typedef struct uw_rc {
adam@1370 214 size_t path_copy_size, queryString_size;
adam@1370 215 char *path_copy, *queryString;
adamc@853 216 } *uw_request_context;
adamc@853 217
adamc@853 218 uw_request_context uw_new_request_context(void) {
adamc@853 219 uw_request_context r = malloc(sizeof(struct uw_rc));
adam@1371 220 r->path_copy_size = 0;
adam@1371 221 r->queryString_size = 1;
adamc@853 222 r->path_copy = malloc(0);
adam@1371 223 r->queryString = malloc(1);
adamc@853 224 return r;
adamc@853 225 }
adamc@853 226
adamc@853 227 void uw_free_request_context(uw_request_context r) {
adamc@853 228 free(r->path_copy);
adam@1370 229 free(r->queryString);
adamc@853 230 free(r);
adamc@853 231 }
adamc@853 232
adamc@854 233 request_result uw_request(uw_request_context rc, uw_context ctx,
adamc@854 234 char *method, char *path, char *query_string,
adamc@854 235 char *body, size_t body_len,
adamc@856 236 void (*on_success)(uw_context), void (*on_failure)(uw_context),
adamc@856 237 void *logger_data, uw_logger log_error, uw_logger log_debug,
adamc@863 238 int sock,
adamc@863 239 int (*send)(int sockfd, const void *buf, size_t len),
adamc@863 240 int (*close)(int fd)) {
adamc@853 241 int retries_left = MAX_RETRIES;
adamc@853 242 failure_kind fk;
adamc@1134 243 int is_post = 0;
adamc@853 244 char *boundary = NULL;
adamc@1134 245 size_t boundary_len = 0;
adamc@854 246 char *inputs;
adamc@1094 247 const char *prefix = uw_get_url_prefix(ctx);
adamc@1169 248 char *s;
adam@1294 249 int had_error = 0;
adam@1294 250 char errmsg[ERROR_BUF_LEN];
adamc@1169 251
adam@1417 252 uw_reset(ctx);
adam@1446 253
adam@1371 254 rc->queryString[0] = 0;
adam@1371 255
adamc@1169 256 for (s = path; *s; ++s) {
adamc@1169 257 if (s[0] == '%' && s[1] == '2' && s[2] == '7') {
adamc@1169 258 s[0] = '\'';
adamc@1169 259 memmove(s+1, s+3, strlen(s+3)+1);
adamc@1169 260 }
adamc@1169 261 }
adamc@853 262
adamc@1066 263 uw_set_currentUrl(ctx, path);
adamc@1066 264
adamc@854 265 if (!strcmp(method, "POST")) {
adamc@853 266 char *clen_s = uw_Basis_requestHeader(ctx, "Content-length");
adamc@853 267 if (!clen_s) {
adamc@1037 268 clen_s = "0";
adamc@1037 269 /*log_error(logger_data, "No Content-length with POST\n");
adamc@1037 270 return FAILED;*/
adamc@853 271 }
adamc@853 272 int clen = atoi(clen_s);
adamc@853 273 if (clen < 0) {
adamc@856 274 log_error(logger_data, "Negative Content-length with POST\n");
adamc@853 275 return FAILED;
adamc@853 276 }
adamc@853 277
adamc@854 278 if (body_len < clen) {
adamc@856 279 log_error(logger_data, "Request doesn't contain all POST data (according to Content-Length)\n");
adamc@853 280 return FAILED;
adamc@853 281 }
adamc@853 282
adamc@853 283 is_post = 1;
adam@1386 284 uw_isPost(ctx);
adamc@853 285
adamc@853 286 clen_s = uw_Basis_requestHeader(ctx, "Content-type");
adamc@853 287 if (clen_s && !strncasecmp(clen_s, "multipart/form-data", 19)) {
adamc@853 288 if (strncasecmp(clen_s + 19, "; boundary=", 11)) {
adamc@856 289 log_error(logger_data, "Bad multipart boundary spec");
adamc@853 290 return FAILED;
adamc@853 291 }
adamc@853 292
adamc@853 293 boundary = clen_s + 28;
adamc@853 294 boundary[0] = '-';
adamc@853 295 boundary[1] = '-';
adamc@853 296 boundary_len = strlen(boundary);
adam@1347 297 } else if (clen_s && strcasecmp(clen_s, "application/x-www-form-urlencoded")) {
adam@1347 298 uw_Basis_postBody pb = {clen_s, body};
adam@1347 299 uw_postBody(ctx, pb);
adamc@853 300 }
adamc@854 301 } else if (strcmp(method, "GET")) {
adamc@856 302 log_error(logger_data, "Not ready for non-GET/POST command: %s\n", method);
adamc@853 303 return FAILED;
adamc@853 304 }
adamc@853 305
adamc@1094 306 if (!strncmp(path, prefix, strlen(prefix))
adamc@1094 307 && !strcmp(path + strlen(prefix), ".msgs")) {
adamc@853 308 char *id = uw_Basis_requestHeader(ctx, "UrWeb-Client");
adamc@853 309 char *pass = uw_Basis_requestHeader(ctx, "UrWeb-Pass");
adamc@853 310
adamc@853 311 if (sock < 0) {
adamc@856 312 log_error(logger_data, ".msgs requested, but not socket supplied\n");
adamc@853 313 return FAILED;
adamc@853 314 }
adamc@853 315
adamc@853 316 if (id && pass) {
adamc@853 317 unsigned idn = atoi(id);
adamc@864 318 uw_client_connect(idn, atoi(pass), sock, send, close, logger_data, log_error);
adam@1320 319 log_debug(logger_data, "Processed request for messages by client %u\n\n", idn);
adamc@853 320 return KEEP_OPEN;
adamc@853 321 }
adamc@853 322 else {
adamc@856 323 log_error(logger_data, "Missing fields in .msgs request: %s, %s\n\n", id, pass);
adamc@853 324 return FAILED;
adamc@853 325 }
adamc@853 326 }
adamc@853 327
adamc@853 328 if (boundary) {
adamc@854 329 char *part = body, *after_sub_headers, *header, *after_header;
adamc@853 330 size_t part_len;
adamc@853 331
adamc@853 332 part = strstr(part, boundary);
adamc@853 333 if (!part) {
adamc@856 334 log_error(logger_data, "Missing first multipart boundary\n");
adamc@853 335 return FAILED;
adamc@853 336 }
adamc@853 337 part += boundary_len;
adamc@853 338
adamc@853 339 while (1) {
adamc@853 340 char *name = NULL, *filename = NULL, *type = NULL;
adamc@853 341
adamc@853 342 if (part[0] == '-' && part[1] == '-')
adamc@853 343 break;
adamc@853 344
adamc@853 345 if (*part != '\r') {
adamc@856 346 log_error(logger_data, "No \\r after multipart boundary\n");
adamc@853 347 return FAILED;
adamc@853 348 }
adamc@853 349 ++part;
adamc@853 350 if (*part != '\n') {
adamc@856 351 log_error(logger_data, "No \\n after multipart boundary\n");
adamc@853 352 return FAILED;
adamc@853 353 }
adamc@853 354 ++part;
adamc@853 355
adamc@853 356 if (!(after_sub_headers = strstr(part, "\r\n\r\n"))) {
adamc@856 357 log_error(logger_data, "Missing end of headers after multipart boundary\n");
adamc@853 358 return FAILED;
adamc@853 359 }
adamc@853 360 after_sub_headers[2] = 0;
adamc@853 361 after_sub_headers += 4;
adamc@853 362
adamc@1134 363 for (header = part; (after_header = strstr(header, "\r\n")); header = after_header + 2) {
adamc@853 364 char *colon, *after_colon;
adamc@853 365
adamc@853 366 *after_header = 0;
adamc@853 367 if (!(colon = strchr(header, ':'))) {
adamc@856 368 log_error(logger_data, "Missing colon in multipart sub-header\n");
adamc@853 369 return FAILED;
adamc@853 370 }
adamc@853 371 *colon++ = 0;
adamc@853 372 if (*colon++ != ' ') {
adamc@856 373 log_error(logger_data, "No space after colon in multipart sub-header\n");
adamc@853 374 return FAILED;
adamc@853 375 }
adamc@853 376
adamc@853 377 if (!strcasecmp(header, "Content-Disposition")) {
adamc@853 378 if (strncmp(colon, "form-data; ", 11)) {
adamc@856 379 log_error(logger_data, "Multipart data is not \"form-data\"\n");
adamc@853 380 return FAILED;
adamc@853 381 }
adamc@853 382
adamc@1134 383 for (colon += 11; (after_colon = strchr(colon, '=')); colon = after_colon) {
adamc@853 384 char *data;
adamc@853 385 after_colon[0] = 0;
adamc@853 386 if (after_colon[1] != '"') {
adamc@856 387 log_error(logger_data, "Disposition setting is missing initial quote\n");
adamc@853 388 return FAILED;
adamc@853 389 }
adamc@853 390 data = after_colon+2;
adamc@853 391 if (!(after_colon = strchr(data, '"'))) {
adamc@856 392 log_error(logger_data, "Disposition setting is missing final quote\n");
adamc@853 393 return FAILED;
adamc@853 394 }
adamc@853 395 after_colon[0] = 0;
adamc@853 396 ++after_colon;
adamc@853 397 if (after_colon[0] == ';' && after_colon[1] == ' ')
adamc@853 398 after_colon += 2;
adamc@853 399
adamc@853 400 if (!strcasecmp(colon, "name"))
adamc@853 401 name = data;
adamc@853 402 else if (!strcasecmp(colon, "filename"))
adamc@853 403 filename = data;
adamc@853 404 }
adamc@853 405 } else if (!strcasecmp(header, "Content-Type")) {
adamc@853 406 type = colon;
adamc@853 407 }
adamc@853 408 }
adamc@853 409
adamc@854 410 part = memmem(after_sub_headers, body + body_len - after_sub_headers, boundary, boundary_len);
adamc@853 411 if (!part) {
adamc@856 412 log_error(logger_data, "Missing boundary after multipart payload\n");
adamc@853 413 return FAILED;
adamc@853 414 }
adamc@853 415 part[-2] = 0;
adamc@853 416 part_len = part - after_sub_headers - 2;
adamc@853 417 part[0] = 0;
adamc@853 418 part += boundary_len;
adamc@853 419
adamc@853 420 if (filename) {
adamc@853 421 uw_Basis_file f = {filename, type, {part_len, after_sub_headers}};
adamc@853 422
adamc@853 423 if (uw_set_file_input(ctx, name, f)) {
adamc@856 424 log_error(logger_data, "%s\n", uw_error_message(ctx));
adamc@853 425 return FAILED;
adamc@853 426 }
adamc@853 427 } else if (uw_set_input(ctx, name, after_sub_headers)) {
adamc@856 428 log_error(logger_data, "%s\n", uw_error_message(ctx));
adamc@853 429 return FAILED;
adamc@853 430 }
adamc@853 431 }
adamc@853 432 }
adam@1347 433 else if (!uw_hasPostBody(ctx)) {
adamc@854 434 inputs = is_post ? body : query_string;
adamc@853 435
adamc@853 436 if (inputs) {
adamc@853 437 char *name, *value;
adam@1370 438 int len = strlen(inputs);
adam@1370 439
adam@1370 440 if (len+1 > rc->queryString_size) {
adam@1370 441 rc->queryString_size = len+1;
adam@1370 442 rc->queryString = realloc(rc->queryString, len+1);
adam@1370 443 }
adam@1370 444 strcpy(rc->queryString, inputs);
adamc@853 445
adamc@853 446 while (*inputs) {
adamc@853 447 name = inputs;
adamc@1134 448 if ((inputs = strchr(inputs, '&')))
adamc@853 449 *inputs++ = 0;
adamc@853 450 else
adamc@853 451 inputs = strchr(name, 0);
adamc@853 452
adamc@1134 453 if ((value = strchr(name, '='))) {
adamc@853 454 *value++ = 0;
adamc@853 455 if (uw_set_input(ctx, name, value)) {
adamc@856 456 log_error(logger_data, "%s\n", uw_error_message(ctx));
adamc@853 457 return FAILED;
adamc@853 458 }
adamc@853 459 }
adamc@853 460 else if (uw_set_input(ctx, name, "")) {
adamc@856 461 log_error(logger_data, "%s\n", uw_error_message(ctx));
adamc@853 462 return FAILED;
adamc@853 463 }
adamc@853 464 }
adamc@853 465 }
adamc@853 466 }
adamc@853 467
adamc@853 468 while (1) {
adam@1371 469 uw_setQueryString(ctx, rc->queryString);
adam@1371 470
adam@1294 471 if (!had_error) {
adam@1294 472 size_t path_len = strlen(path);
adamc@853 473
adam@1294 474 on_success(ctx);
adamc@853 475
adam@1294 476 if (path_len + 1 > rc->path_copy_size) {
adam@1294 477 rc->path_copy_size = path_len + 1;
adam@1294 478 rc->path_copy = realloc(rc->path_copy, rc->path_copy_size);
adam@1294 479 }
adam@1294 480 strcpy(rc->path_copy, path);
adam@1308 481
adam@1308 482 uw_set_deadline(ctx, uw_time + uw_time_max);
adam@1294 483 fk = uw_begin(ctx, rc->path_copy);
adam@1308 484 } else {
adam@1308 485 uw_set_deadline(ctx, uw_time + uw_time_max);
adam@1294 486 fk = uw_begin_onError(ctx, errmsg);
adam@1308 487 }
adam@1294 488
adamc@1065 489 if (fk == SUCCESS || fk == RETURN_INDIRECTLY) {
adamc@853 490 uw_commit(ctx);
adam@1294 491 if (uw_has_error(ctx) && !had_error) {
adamc@1131 492 log_error(logger_data, "Fatal error: %s\n", uw_error_message(ctx));
adamc@1131 493 uw_reset_keep_error_message(ctx);
adamc@1131 494 on_failure(ctx);
adam@1294 495
adam@1294 496 if (uw_get_app(ctx)->on_error) {
adam@1294 497 had_error = 1;
adam@1294 498 strcpy(errmsg, uw_error_message(ctx));
adam@1294 499 } else {
adam@1294 500 uw_write_header(ctx, "Content-type: text/html\r\n");
adam@1294 501 uw_write(ctx, "<html><head><title>Fatal Error</title></head><body>");
adam@1294 502 uw_write(ctx, "Fatal error: ");
adam@1294 503 uw_write(ctx, uw_error_message(ctx));
adam@1294 504 uw_write(ctx, "\n</body></html>");
adamc@1131 505
adam@1294 506 return FAILED;
adam@1294 507 }
adamc@1131 508 } else
adam@1294 509 return had_error ? FAILED : SERVED;
adamc@853 510 } else if (fk == BOUNDED_RETRY) {
adamc@853 511 if (retries_left) {
adamc@856 512 log_debug(logger_data, "Error triggers bounded retry: %s\n", uw_error_message(ctx));
adamc@853 513 --retries_left;
adamc@853 514 }
adamc@853 515 else {
adamc@856 516 log_error(logger_data, "Fatal error (out of retries): %s\n", uw_error_message(ctx));
adamc@853 517
adam@1327 518 try_rollback(ctx, 0, logger_data, log_error);
adamc@853 519
adam@1294 520 if (!had_error && uw_get_app(ctx)->on_error) {
adam@1294 521 had_error = 1;
adam@1294 522 strcpy(errmsg, uw_error_message(ctx));
adam@1294 523 } else {
adam@1294 524 uw_reset_keep_error_message(ctx);
adam@1294 525 on_failure(ctx);
adam@1294 526 uw_write_header(ctx, "Content-type: text/plain\r\n");
adam@1294 527 uw_write(ctx, "Fatal error (out of retries): ");
adam@1294 528 uw_write(ctx, uw_error_message(ctx));
adam@1294 529 uw_write(ctx, "\n");
adam@1294 530
adam@1294 531 return FAILED;
adam@1294 532 }
adamc@853 533 }
adamc@853 534 } else if (fk == UNLIMITED_RETRY)
adamc@856 535 log_debug(logger_data, "Error triggers unlimited retry: %s\n", uw_error_message(ctx));
adamc@853 536 else if (fk == FATAL) {
adamc@856 537 log_error(logger_data, "Fatal error: %s\n", uw_error_message(ctx));
adamc@853 538
adam@1327 539 try_rollback(ctx, 0, logger_data, log_error);
adamc@853 540
adam@1294 541 if (uw_get_app(ctx)->on_error && !had_error) {
adam@1294 542 had_error = 1;
adam@1294 543 strcpy(errmsg, uw_error_message(ctx));
adam@1294 544 } else {
adam@1294 545 uw_reset_keep_error_message(ctx);
adam@1294 546 on_failure(ctx);
adam@1294 547 uw_write_header(ctx, "Content-type: text/html\r\n");
adam@1294 548 uw_write(ctx, "<html><head><title>Fatal Error</title></head><body>");
adam@1294 549 uw_write(ctx, "Fatal error: ");
adam@1294 550 uw_write(ctx, uw_error_message(ctx));
adam@1294 551 uw_write(ctx, "\n</body></html>");
adamc@853 552
adam@1294 553 return FAILED;
adam@1294 554 }
adamc@853 555 } else {
adamc@856 556 log_error(logger_data, "Unknown uw_handle return code!\n");
adamc@853 557
adam@1327 558 try_rollback(ctx, 0, logger_data, log_error);
adamc@853 559
adam@1294 560 if (uw_get_app(ctx)->on_error && !had_error) {
adam@1294 561 had_error = 1;
adam@1294 562 strcpy(errmsg, "Unknown uw_handle return code");
adam@1294 563 } else {
adam@1294 564 uw_reset_keep_request(ctx);
adam@1294 565 on_failure(ctx);
adam@1294 566 uw_write_header(ctx, "Content-type: text/plain\r\n");
adam@1294 567 uw_write(ctx, "Unknown uw_handle return code!\n");
adamc@853 568
adam@1294 569 return FAILED;
adam@1294 570 }
adamc@853 571 }
adamc@853 572
adam@1327 573 if (try_rollback(ctx, 1, logger_data, log_error))
adamc@853 574 return FAILED;
adamc@853 575
adamc@853 576 uw_reset_keep_request(ctx);
adamc@853 577 }
adamc@853 578 }
adamc@853 579
adamc@853 580 void *client_pruner(void *data) {
adamc@856 581 loggers *ls = (loggers *)data;
adam@1446 582 uw_context ctx = uw_request_new_context(0, ls->app, ls->logger_data, ls->log_error, ls->log_debug);
adamc@853 583
adamc@853 584 if (!ctx)
adamc@853 585 exit(1);
adamc@853 586
adamc@853 587 while (1) {
adamc@853 588 uw_prune_clients(ctx);
adamc@853 589 sleep(5);
adamc@853 590 }
adamc@853 591 }