adamc@1268: #include "config.h" adamc@1268: adamc@853: #include adamc@853: #include adamc@853: #include adamc@853: #include adamc@853: #include adamc@853: #include adamc@853: #include adamc@853: #include adamc@853: adamc@853: #include adamc@853: adamc@853: #include "urweb.h" grrwlf@1997: #include "request.h" adamc@853: adamc@853: #define MAX_RETRIES 5 adamc@853: adamc@1152: void *memmem(const void *b1, size_t len1, const void *b2, size_t len2); adamc@1152: adam@1327: static int try_rollback(uw_context ctx, int will_retry, void *logger_data, uw_logger log_error) { adam@1327: int r = uw_rollback(ctx, will_retry); adamc@853: adamc@853: if (r) { adamc@856: log_error(logger_data, "Error running SQL ROLLBACK\n"); adamc@853: uw_reset(ctx); adamc@1115: uw_write(ctx, "HTTP/1.1 500 Internal Server Error\r\n"); adamc@853: uw_write(ctx, "Content-type: text/plain\r\n\r\n"); adamc@853: uw_write(ctx, "Error running SQL ROLLBACK\n"); adamc@1115: uw_set_error_message(ctx, "Database error; you are probably out of storage space."); adamc@853: } adamc@853: adamc@853: return r; adamc@853: } adamc@853: grrwlf@1997: uw_context uw_request_new_context(int id, uw_app *app, uw_loggers *ls) { grrwlf@1997: void *logger_data = ls->logger_data; grrwlf@1997: uw_logger log_debug = ls->log_debug; grrwlf@1997: uw_logger log_error = ls->log_error; grrwlf@1997: uw_context ctx = uw_init(id, ls); adamc@853: int retries_left = MAX_RETRIES; adamc@1094: uw_set_app(ctx, app); adamc@853: adamc@853: while (1) { adamc@853: failure_kind fk = uw_begin_init(ctx); adamc@853: adamc@853: if (fk == SUCCESS) { adamc@856: log_debug(logger_data, "Database connection initialized.\n"); adamc@853: break; adamc@853: } else if (fk == BOUNDED_RETRY) { adamc@853: if (retries_left) { adamc@856: log_debug(logger_data, "Initialization error triggers bounded retry: %s\n", uw_error_message(ctx)); adamc@853: --retries_left; adamc@853: } else { adamc@856: log_error(logger_data, "Fatal initialization error (out of retries): %s\n", uw_error_message(ctx)); adamc@853: uw_free(ctx); adamc@853: return NULL; adamc@853: } adamc@853: } else if (fk == UNLIMITED_RETRY) adamc@856: log_debug(logger_data, "Initialization error triggers unlimited retry: %s\n", uw_error_message(ctx)); adamc@853: else if (fk == FATAL) { adamc@856: log_error(logger_data, "Fatal initialization error: %s\n", uw_error_message(ctx)); adamc@853: uw_free(ctx); adamc@853: return NULL; adamc@853: } else { adamc@856: log_error(logger_data, "Unknown uw_begin_init return code!\n"); adamc@853: uw_free(ctx); adamc@853: return NULL; adamc@853: } adamc@853: } adamc@853: adamc@853: return ctx; adamc@853: } adamc@853: adam@1308: static void *ticker(void *data) { adam@1308: while (1) { adam@1308: usleep(100000); adam@1308: ++uw_time; adam@1308: } adam@1308: adam@1308: return NULL; adam@1308: } adam@1308: adam@1349: typedef struct { grrwlf@1997: int id; grrwlf@1997: uw_loggers *ls; grrwlf@1997: uw_periodic pdic; adam@1349: uw_app *app; adam@1349: } periodic; adam@1349: adam@1349: static void *periodic_loop(void *data) { adam@1349: periodic *p = (periodic *)data; grrwlf@1997: uw_context ctx = uw_request_new_context(p->id, p->app, p->ls); adam@1349: adam@1349: if (!ctx) adam@1349: exit(1); adam@1349: adam@1349: while (1) { adam@1418: int retries_left = MAX_RETRIES; adam@1418: adam@1349: failure_kind r; adam@1349: do { adam@1418: uw_reset(ctx); adam@1349: r = uw_runCallback(ctx, p->pdic.callback); adam@1418: if (r == BOUNDED_RETRY) adam@1418: --retries_left; adam@1418: else if (r == UNLIMITED_RETRY) adam@1418: p->ls->log_debug(p->ls->logger_data, "Error triggers unlimited retry in periodic: %s\n", uw_error_message(ctx)); adam@1418: else if (r == BOUNDED_RETRY) adam@1418: p->ls->log_debug(p->ls->logger_data, "Error triggers bounded retry in periodic: %s\n", uw_error_message(ctx)); adam@1418: else if (r == FATAL) adam@1418: p->ls->log_error(p->ls->logger_data, "Fatal error: %s\n", uw_error_message(ctx)); adam@1418: if (r == FATAL || r == BOUNDED_RETRY || r == UNLIMITED_RETRY) adam@1437: if (try_rollback(ctx, 0, p->ls->logger_data, p->ls->log_error)) adam@1437: return NULL; adam@1418: } while (r == UNLIMITED_RETRY || (r == BOUNDED_RETRY && retries_left > 0)); adam@1418: adam@1938: if (r != FATAL && r != BOUNDED_RETRY) { adam@1938: if (uw_commit(ctx)) adam@1938: r = UNLIMITED_RETRY; adam@1938: } adam@1349: adam@1349: sleep(p->pdic.period); adam@1349: }; adam@1349: } adam@1349: adam@1522: static unsigned long long stackSize; adam@1522: adam@1522: int pthread_create_big(pthread_t *outThread, void *foo, void *threadFunc, void *arg) adam@1522: { adam@1882: if (stackSize > 0) { adam@1882: int err; adam@1882: pthread_attr_t stackSizeAttribute; adam@1522: adam@1882: err = pthread_attr_init(&stackSizeAttribute); adam@1882: if (err) return err; adam@1522: adam@1522: err = pthread_attr_setstacksize(&stackSizeAttribute, stackSize); adam@1522: if (err) return err; adam@1882: adam@1882: return pthread_create(outThread, &stackSizeAttribute, threadFunc, arg); adam@1882: } else { adam@1882: return pthread_create(outThread, NULL, threadFunc, arg); adam@1522: } adam@1522: } adam@1522: grrwlf@1997: void uw_request_init(uw_app *app, uw_loggers* ls) { adamc@853: uw_context ctx; adamc@853: failure_kind fk; adam@1349: uw_periodic *ps; adam@1446: int id; adam@1522: char *stackSize_s; adam@1522: grrwlf@1997: uw_logger log_debug = ls->log_debug; grrwlf@1997: uw_logger log_error = ls->log_error; grrwlf@1997: void* logger_data = ls->logger_data; grrwlf@1997: adam@1522: if ((stackSize_s = getenv("URWEB_STACK_SIZE")) != NULL && stackSize_s[0] != 0) { adam@1522: stackSize = atoll(stackSize_s); adam@1522: adam@1522: if (stackSize <= 0) { adam@1522: fprintf(stderr, "Invalid stack size \"%s\"\n", stackSize_s); adam@1522: exit(1); adam@1522: } adam@1522: } adam@1349: adamc@853: uw_global_init(); adamc@1094: uw_app_init(app); adamc@853: adam@1308: { adam@1308: pthread_t thread; adam@1308: adam@1522: if (uw_time_max && pthread_create_big(&thread, NULL, ticker, NULL)) { adam@1308: fprintf(stderr, "Error creating ticker thread\n"); adam@1308: exit(1); adam@1308: } adam@1308: } adam@1308: grrwlf@1997: ctx = uw_request_new_context(0, app, ls); adamc@853: adamc@853: if (!ctx) adamc@853: exit(1); adamc@853: adamc@853: for (fk = uw_initialize(ctx); fk == UNLIMITED_RETRY; fk = uw_initialize(ctx)) { adamc@856: log_debug(logger_data, "Unlimited retry during init: %s\n", uw_error_message(ctx)); adam@1327: uw_rollback(ctx, 1); adamc@853: uw_reset(ctx); adamc@853: } adamc@853: adamc@853: if (fk != SUCCESS) { adamc@856: log_error(logger_data, "Failed to initialize database! %s\n", uw_error_message(ctx)); adam@1327: uw_rollback(ctx, 0); adamc@853: exit(1); adamc@853: } adamc@853: adamc@853: uw_free(ctx); adam@1349: adam@1446: id = 1; adam@1349: for (ps = app->periodics; ps->callback; ++ps) { adam@1349: pthread_t thread; adam@1349: periodic *arg = malloc(sizeof(periodic)); adam@1446: arg->id = id++; adam@1349: arg->ls = ls; adam@1349: arg->pdic = *ps; grrwlf@1997: arg->app = app; adam@1349: adam@1522: if (pthread_create_big(&thread, NULL, periodic_loop, arg)) { adam@1349: fprintf(stderr, "Error creating periodic thread\n"); adam@1349: exit(1); adam@1349: } adam@1349: } adamc@853: } adamc@853: adamc@853: adam@2017: struct uw_rc { adam@1370: size_t path_copy_size, queryString_size; adam@1370: char *path_copy, *queryString; adam@2017: }; adamc@853: adamc@853: uw_request_context uw_new_request_context(void) { adamc@853: uw_request_context r = malloc(sizeof(struct uw_rc)); adam@1371: r->path_copy_size = 0; adam@1371: r->queryString_size = 1; adamc@853: r->path_copy = malloc(0); adam@1371: r->queryString = malloc(1); adamc@853: return r; adamc@853: } adamc@853: adamc@853: void uw_free_request_context(uw_request_context r) { adamc@853: free(r->path_copy); adam@1370: free(r->queryString); adamc@853: free(r); adamc@853: } adamc@853: adamc@854: request_result uw_request(uw_request_context rc, uw_context ctx, adamc@854: char *method, char *path, char *query_string, adamc@854: char *body, size_t body_len, adamc@856: void (*on_success)(uw_context), void (*on_failure)(uw_context), adamc@856: void *logger_data, uw_logger log_error, uw_logger log_debug, adamc@863: int sock, grrwlf@1997: int (*send)(int sockfd, const void *buf, ssize_t len), adamc@863: int (*close)(int fd)) { adamc@853: int retries_left = MAX_RETRIES; adamc@853: failure_kind fk; adamc@1134: int is_post = 0; adamc@853: char *boundary = NULL; adamc@1134: size_t boundary_len = 0; adamc@854: char *inputs; adamc@1094: const char *prefix = uw_get_url_prefix(ctx); adamc@1169: char *s; adam@1788: int had_error = 0, is_fancy = 0; adam@1294: char errmsg[ERROR_BUF_LEN]; adamc@1169: adam@1417: uw_reset(ctx); adam@1446: adam@1371: rc->queryString[0] = 0; adam@1371: adamc@1169: for (s = path; *s; ++s) { adamc@1169: if (s[0] == '%' && s[1] == '2' && s[2] == '7') { adamc@1169: s[0] = '\''; adamc@1169: memmove(s+1, s+3, strlen(s+3)+1); adamc@1169: } adamc@1169: } adamc@853: adamc@1066: uw_set_currentUrl(ctx, path); adamc@1066: adamc@854: if (!strcmp(method, "POST")) { adamc@853: char *clen_s = uw_Basis_requestHeader(ctx, "Content-length"); adamc@853: if (!clen_s) { adamc@1037: clen_s = "0"; adamc@1037: /*log_error(logger_data, "No Content-length with POST\n"); adamc@1037: return FAILED;*/ adamc@853: } adamc@853: int clen = atoi(clen_s); adamc@853: if (clen < 0) { adamc@856: log_error(logger_data, "Negative Content-length with POST\n"); adamc@853: return FAILED; adamc@853: } adamc@853: adamc@854: if (body_len < clen) { adamc@856: log_error(logger_data, "Request doesn't contain all POST data (according to Content-Length)\n"); adamc@853: return FAILED; adamc@853: } adamc@853: adamc@853: is_post = 1; adam@1386: uw_isPost(ctx); adamc@853: adamc@853: clen_s = uw_Basis_requestHeader(ctx, "Content-type"); adam@1788: adam@1788: if (!clen_s || strcasecmp(clen_s, "application/x-www-form-urlencoded")) adam@1788: is_fancy = 1; adam@1788: adamc@853: if (clen_s && !strncasecmp(clen_s, "multipart/form-data", 19)) { adamc@853: if (strncasecmp(clen_s + 19, "; boundary=", 11)) { adamc@856: log_error(logger_data, "Bad multipart boundary spec"); adamc@853: return FAILED; adamc@853: } adamc@853: adamc@853: boundary = clen_s + 28; adamc@853: boundary[0] = '-'; adamc@853: boundary[1] = '-'; adamc@853: boundary_len = strlen(boundary); adam@1787: } else if (clen_s) { adam@1788: uw_Basis_postBody pb = {clen_s, body, body_len}; adam@1347: uw_postBody(ctx, pb); adamc@853: } adamc@854: } else if (strcmp(method, "GET")) { adamc@856: log_error(logger_data, "Not ready for non-GET/POST command: %s\n", method); adamc@853: return FAILED; adamc@853: } adamc@853: adamc@1094: if (!strncmp(path, prefix, strlen(prefix)) adamc@1094: && !strcmp(path + strlen(prefix), ".msgs")) { adamc@853: char *id = uw_Basis_requestHeader(ctx, "UrWeb-Client"); adamc@853: char *pass = uw_Basis_requestHeader(ctx, "UrWeb-Pass"); adamc@853: adamc@853: if (sock < 0) { adamc@856: log_error(logger_data, ".msgs requested, but not socket supplied\n"); adamc@853: return FAILED; adamc@853: } adamc@853: adamc@853: if (id && pass) { adamc@853: unsigned idn = atoi(id); adamc@864: uw_client_connect(idn, atoi(pass), sock, send, close, logger_data, log_error); adam@1320: log_debug(logger_data, "Processed request for messages by client %u\n\n", idn); adamc@853: return KEEP_OPEN; adamc@853: } adamc@853: else { adamc@856: log_error(logger_data, "Missing fields in .msgs request: %s, %s\n\n", id, pass); adamc@853: return FAILED; adamc@853: } adamc@853: } adamc@853: adamc@853: if (boundary) { adamc@854: char *part = body, *after_sub_headers, *header, *after_header; adamc@853: size_t part_len; adamc@853: adamc@853: part = strstr(part, boundary); adamc@853: if (!part) { adamc@856: log_error(logger_data, "Missing first multipart boundary\n"); adamc@853: return FAILED; adamc@853: } adamc@853: part += boundary_len; adamc@853: adamc@853: while (1) { adamc@853: char *name = NULL, *filename = NULL, *type = NULL; adamc@853: adamc@853: if (part[0] == '-' && part[1] == '-') adamc@853: break; adamc@853: adamc@853: if (*part != '\r') { adamc@856: log_error(logger_data, "No \\r after multipart boundary\n"); adamc@853: return FAILED; adamc@853: } adamc@853: ++part; adamc@853: if (*part != '\n') { adamc@856: log_error(logger_data, "No \\n after multipart boundary\n"); adamc@853: return FAILED; adamc@853: } adamc@853: ++part; adamc@853: adamc@853: if (!(after_sub_headers = strstr(part, "\r\n\r\n"))) { adamc@856: log_error(logger_data, "Missing end of headers after multipart boundary\n"); adamc@853: return FAILED; adamc@853: } adamc@853: after_sub_headers[2] = 0; adamc@853: after_sub_headers += 4; adamc@853: adamc@1134: for (header = part; (after_header = strstr(header, "\r\n")); header = after_header + 2) { adamc@853: char *colon, *after_colon; adamc@853: adamc@853: *after_header = 0; adamc@853: if (!(colon = strchr(header, ':'))) { adamc@856: log_error(logger_data, "Missing colon in multipart sub-header\n"); adamc@853: return FAILED; adamc@853: } adamc@853: *colon++ = 0; adamc@853: if (*colon++ != ' ') { adamc@856: log_error(logger_data, "No space after colon in multipart sub-header\n"); adamc@853: return FAILED; adamc@853: } adamc@853: adamc@853: if (!strcasecmp(header, "Content-Disposition")) { adamc@853: if (strncmp(colon, "form-data; ", 11)) { adamc@856: log_error(logger_data, "Multipart data is not \"form-data\"\n"); adamc@853: return FAILED; adamc@853: } adamc@853: adamc@1134: for (colon += 11; (after_colon = strchr(colon, '=')); colon = after_colon) { adamc@853: char *data; adamc@853: after_colon[0] = 0; adamc@853: if (after_colon[1] != '"') { adamc@856: log_error(logger_data, "Disposition setting is missing initial quote\n"); adamc@853: return FAILED; adamc@853: } adamc@853: data = after_colon+2; adamc@853: if (!(after_colon = strchr(data, '"'))) { adamc@856: log_error(logger_data, "Disposition setting is missing final quote\n"); adamc@853: return FAILED; adamc@853: } adamc@853: after_colon[0] = 0; adamc@853: ++after_colon; adamc@853: if (after_colon[0] == ';' && after_colon[1] == ' ') adamc@853: after_colon += 2; adamc@853: adamc@853: if (!strcasecmp(colon, "name")) adamc@853: name = data; adamc@853: else if (!strcasecmp(colon, "filename")) adamc@853: filename = data; adamc@853: } adamc@853: } else if (!strcasecmp(header, "Content-Type")) { adamc@853: type = colon; adamc@853: } adamc@853: } adamc@853: adamc@854: part = memmem(after_sub_headers, body + body_len - after_sub_headers, boundary, boundary_len); adamc@853: if (!part) { adamc@856: log_error(logger_data, "Missing boundary after multipart payload\n"); adamc@853: return FAILED; adamc@853: } adamc@853: part[-2] = 0; adamc@853: part_len = part - after_sub_headers - 2; adamc@853: part[0] = 0; adamc@853: part += boundary_len; adamc@853: adamc@853: if (filename) { adamc@853: uw_Basis_file f = {filename, type, {part_len, after_sub_headers}}; adamc@853: adamc@853: if (uw_set_file_input(ctx, name, f)) { adamc@856: log_error(logger_data, "%s\n", uw_error_message(ctx)); adamc@853: return FAILED; adamc@853: } adamc@853: } else if (uw_set_input(ctx, name, after_sub_headers)) { adamc@856: log_error(logger_data, "%s\n", uw_error_message(ctx)); adamc@853: return FAILED; adamc@853: } adamc@853: } adamc@853: } adam@1788: else if (!is_fancy) { adamc@854: inputs = is_post ? body : query_string; adamc@853: adamc@853: if (inputs) { adamc@853: char *name, *value; adam@1370: int len = strlen(inputs); adam@1370: adam@1370: if (len+1 > rc->queryString_size) { grrwlf@2068: char *qs = realloc(rc->queryString, len+1); grrwlf@2068: if(qs == NULL) { grrwlf@2063: log_error(logger_data, "queryString is too long (not enough memory)\n"); grrwlf@2063: return FAILED; grrwlf@2063: } grrwlf@2068: rc->queryString = qs; adam@1370: rc->queryString_size = len+1; adam@1370: } adam@1370: strcpy(rc->queryString, inputs); adamc@853: adamc@853: while (*inputs) { adamc@853: name = inputs; adamc@1134: if ((inputs = strchr(inputs, '&'))) adamc@853: *inputs++ = 0; adamc@853: else adamc@853: inputs = strchr(name, 0); adamc@853: adamc@1134: if ((value = strchr(name, '='))) { adamc@853: *value++ = 0; adamc@853: if (uw_set_input(ctx, name, value)) { adamc@856: log_error(logger_data, "%s\n", uw_error_message(ctx)); adamc@853: return FAILED; adamc@853: } adamc@853: } adamc@853: else if (uw_set_input(ctx, name, "")) { adamc@856: log_error(logger_data, "%s\n", uw_error_message(ctx)); adamc@853: return FAILED; adamc@853: } adamc@853: } adamc@853: } adamc@853: } adamc@853: adamc@853: while (1) { adam@1371: uw_setQueryString(ctx, rc->queryString); adam@1371: adam@1294: if (!had_error) { adam@1294: size_t path_len = strlen(path); adamc@853: adam@1294: on_success(ctx); adamc@853: adam@1294: if (path_len + 1 > rc->path_copy_size) { grrwlf@2068: char *pc = realloc(rc->path_copy, path_len + 1); grrwlf@2068: if(pc == NULL) { grrwlf@2063: log_error(logger_data, "Path is too long (not enough memory)\n"); grrwlf@2063: return FAILED; grrwlf@2063: } grrwlf@2068: rc->path_copy = pc; adam@1294: rc->path_copy_size = path_len + 1; adam@1294: } adam@1294: strcpy(rc->path_copy, path); adam@1308: adam@1308: uw_set_deadline(ctx, uw_time + uw_time_max); adam@1294: fk = uw_begin(ctx, rc->path_copy); adam@1308: } else { adam@1308: uw_set_deadline(ctx, uw_time + uw_time_max); adam@1294: fk = uw_begin_onError(ctx, errmsg); adam@1308: } adam@1294: adamc@1065: if (fk == SUCCESS || fk == RETURN_INDIRECTLY) { adamc@853: uw_commit(ctx); adam@1294: if (uw_has_error(ctx) && !had_error) { adamc@1131: log_error(logger_data, "Fatal error: %s\n", uw_error_message(ctx)); adamc@1131: uw_reset_keep_error_message(ctx); adamc@1131: on_failure(ctx); adam@1294: adam@1294: if (uw_get_app(ctx)->on_error) { adam@1294: had_error = 1; adam@1294: strcpy(errmsg, uw_error_message(ctx)); adam@1294: } else { grrwlf@2030: try_rollback(ctx, 0, logger_data, log_error); grrwlf@2030: adam@1294: uw_write_header(ctx, "Content-type: text/html\r\n"); adam@1294: uw_write(ctx, "Fatal Error"); adam@1294: uw_write(ctx, "Fatal error: "); adam@1294: uw_write(ctx, uw_error_message(ctx)); adam@1294: uw_write(ctx, "\n"); adamc@1131: adam@1294: return FAILED; adam@1294: } adamc@1131: } else adam@1294: return had_error ? FAILED : SERVED; adamc@853: } else if (fk == BOUNDED_RETRY) { adamc@853: if (retries_left) { adamc@856: log_debug(logger_data, "Error triggers bounded retry: %s\n", uw_error_message(ctx)); adamc@853: --retries_left; adamc@853: } adamc@853: else { adamc@856: log_error(logger_data, "Fatal error (out of retries): %s\n", uw_error_message(ctx)); adamc@853: adam@1294: if (!had_error && uw_get_app(ctx)->on_error) { adam@1294: had_error = 1; adam@1294: strcpy(errmsg, uw_error_message(ctx)); adam@1294: } else { grrwlf@2030: try_rollback(ctx, 0, logger_data, log_error); grrwlf@2030: adam@1294: uw_reset_keep_error_message(ctx); adam@1294: on_failure(ctx); adam@1294: uw_write_header(ctx, "Content-type: text/plain\r\n"); adam@1294: uw_write(ctx, "Fatal error (out of retries): "); adam@1294: uw_write(ctx, uw_error_message(ctx)); adam@1294: uw_write(ctx, "\n"); adam@1774: adam@1294: return FAILED; adam@1294: } adamc@853: } adamc@853: } else if (fk == UNLIMITED_RETRY) adamc@856: log_debug(logger_data, "Error triggers unlimited retry: %s\n", uw_error_message(ctx)); adamc@853: else if (fk == FATAL) { adamc@856: log_error(logger_data, "Fatal error: %s\n", uw_error_message(ctx)); adamc@853: adam@1294: if (uw_get_app(ctx)->on_error && !had_error) { adam@1294: had_error = 1; adam@1294: strcpy(errmsg, uw_error_message(ctx)); adam@1294: } else { grrwlf@2030: try_rollback(ctx, 0, logger_data, log_error); grrwlf@2030: adam@1294: uw_reset_keep_error_message(ctx); adam@1294: on_failure(ctx); adam@1294: uw_write_header(ctx, "Content-type: text/html\r\n"); adam@1294: uw_write(ctx, "Fatal Error"); adam@1294: uw_write(ctx, "Fatal error: "); adam@1294: uw_write(ctx, uw_error_message(ctx)); adam@1294: uw_write(ctx, "\n"); adamc@853: adam@1294: return FAILED; adam@1294: } adamc@853: } else { adamc@856: log_error(logger_data, "Unknown uw_handle return code!\n"); adamc@853: adam@1294: if (uw_get_app(ctx)->on_error && !had_error) { adam@1294: had_error = 1; adam@1294: strcpy(errmsg, "Unknown uw_handle return code"); adam@1294: } else { grrwlf@2030: try_rollback(ctx, 0, logger_data, log_error); grrwlf@2030: adam@1294: uw_reset_keep_request(ctx); adam@1294: on_failure(ctx); adam@1294: uw_write_header(ctx, "Content-type: text/plain\r\n"); adam@1294: uw_write(ctx, "Unknown uw_handle return code!\n"); adamc@853: adam@1294: return FAILED; adam@1294: } adamc@853: } adamc@853: adam@1327: if (try_rollback(ctx, 1, logger_data, log_error)) adamc@853: return FAILED; adamc@853: adamc@853: uw_reset_keep_request(ctx); adamc@853: } adamc@853: } adamc@853: adamc@853: void *client_pruner(void *data) { grrwlf@1997: pruner_data *pd = (pruner_data *)data; grrwlf@1997: uw_context ctx = uw_request_new_context(0, pd->app, pd->loggers); adamc@853: adamc@853: if (!ctx) adamc@853: exit(1); adamc@853: adamc@853: while (1) { adamc@853: uw_prune_clients(ctx); adamc@853: sleep(5); adamc@853: } adamc@853: }