comparison src/c/request.c @ 856:86ec89baee01

cgi protocol
author Adam Chlipala <adamc@hcoop.net>
date Tue, 23 Jun 2009 17:59:23 -0400
parents 158d980889ac
children 305bc0a431de
comparison
equal deleted inserted replaced
855:28e42b22424d 856:86ec89baee01
15 15
16 #include "urweb.h" 16 #include "urweb.h"
17 17
18 #define MAX_RETRIES 5 18 #define MAX_RETRIES 5
19 19
20 static int try_rollback(uw_context ctx) { 20 static int try_rollback(uw_context ctx, void *logger_data, uw_logger log_error) {
21 int r = uw_rollback(ctx); 21 int r = uw_rollback(ctx);
22 22
23 if (r) { 23 if (r) {
24 printf("Error running SQL ROLLBACK\n"); 24 log_error(logger_data, "Error running SQL ROLLBACK\n");
25 uw_reset(ctx); 25 uw_reset(ctx);
26 uw_write(ctx, "HTTP/1.1 500 Internal Server Error\n\r"); 26 uw_write(ctx, "HTTP/1.1 500 Internal Server Error\n\r");
27 uw_write(ctx, "Content-type: text/plain\r\n\r\n"); 27 uw_write(ctx, "Content-type: text/plain\r\n\r\n");
28 uw_write(ctx, "Error running SQL ROLLBACK\n"); 28 uw_write(ctx, "Error running SQL ROLLBACK\n");
29 } 29 }
30 30
31 return r; 31 return r;
32 } 32 }
33 33
34 uw_context uw_request_new_context() { 34 uw_context uw_request_new_context(void *logger_data, uw_logger log_error, uw_logger log_debug) {
35 uw_context ctx = uw_init(); 35 uw_context ctx = uw_init();
36 int retries_left = MAX_RETRIES; 36 int retries_left = MAX_RETRIES;
37 37
38 while (1) { 38 while (1) {
39 failure_kind fk = uw_begin_init(ctx); 39 failure_kind fk = uw_begin_init(ctx);
40 40
41 if (fk == SUCCESS) { 41 if (fk == SUCCESS) {
42 printf("Database connection initialized.\n"); 42 log_debug(logger_data, "Database connection initialized.\n");
43 break; 43 break;
44 } else if (fk == BOUNDED_RETRY) { 44 } else if (fk == BOUNDED_RETRY) {
45 if (retries_left) { 45 if (retries_left) {
46 printf("Initialization error triggers bounded retry: %s\n", uw_error_message(ctx)); 46 log_debug(logger_data, "Initialization error triggers bounded retry: %s\n", uw_error_message(ctx));
47 --retries_left; 47 --retries_left;
48 } else { 48 } else {
49 printf("Fatal initialization error (out of retries): %s\n", uw_error_message(ctx)); 49 log_error(logger_data, "Fatal initialization error (out of retries): %s\n", uw_error_message(ctx));
50 uw_free(ctx); 50 uw_free(ctx);
51 return NULL; 51 return NULL;
52 } 52 }
53 } else if (fk == UNLIMITED_RETRY) 53 } else if (fk == UNLIMITED_RETRY)
54 printf("Initialization error triggers unlimited retry: %s\n", uw_error_message(ctx)); 54 log_debug(logger_data, "Initialization error triggers unlimited retry: %s\n", uw_error_message(ctx));
55 else if (fk == FATAL) { 55 else if (fk == FATAL) {
56 printf("Fatal initialization error: %s\n", uw_error_message(ctx)); 56 log_error(logger_data, "Fatal initialization error: %s\n", uw_error_message(ctx));
57 uw_free(ctx); 57 uw_free(ctx);
58 return NULL; 58 return NULL;
59 } else { 59 } else {
60 printf("Unknown uw_begin_init return code!\n"); 60 log_error(logger_data, "Unknown uw_begin_init return code!\n");
61 uw_free(ctx); 61 uw_free(ctx);
62 return NULL; 62 return NULL;
63 } 63 }
64 } 64 }
65 65
76 int uw_hash_blocksize = HASH_BLOCKSIZE; 76 int uw_hash_blocksize = HASH_BLOCKSIZE;
77 77
78 static int password[PASSSIZE]; 78 static int password[PASSSIZE];
79 static unsigned char private_key[KEYSIZE]; 79 static unsigned char private_key[KEYSIZE];
80 80
81 static void init_crypto() { 81 static void init_crypto(void *logger_data, uw_logger log_error) {
82 KEYGEN kg = {{HASH_ALGORITHM, HASH_ALGORITHM}}; 82 KEYGEN kg = {{HASH_ALGORITHM, HASH_ALGORITHM}};
83 int i; 83 int i;
84 84
85 assert(mhash_get_block_size(HASH_ALGORITHM) == HASH_BLOCKSIZE); 85 assert(mhash_get_block_size(HASH_ALGORITHM) == HASH_BLOCKSIZE);
86 86
88 password[i] = rand(); 88 password[i] = rand();
89 89
90 if (mhash_keygen_ext(KEYGEN_ALGORITHM, kg, 90 if (mhash_keygen_ext(KEYGEN_ALGORITHM, kg,
91 private_key, sizeof(private_key), 91 private_key, sizeof(private_key),
92 (unsigned char*)password, sizeof(password)) < 0) { 92 (unsigned char*)password, sizeof(password)) < 0) {
93 printf("Key generation failed\n"); 93 log_error(logger_data, "Key generation failed\n");
94 exit(1); 94 exit(1);
95 } 95 }
96 } 96 }
97 97
98 void uw_request_init() { 98 void uw_request_init(void *logger_data, uw_logger log_error, uw_logger log_debug) {
99 uw_context ctx; 99 uw_context ctx;
100 failure_kind fk; 100 failure_kind fk;
101 101
102 uw_global_init(); 102 uw_global_init();
103 103
104 ctx = uw_request_new_context(); 104 ctx = uw_request_new_context(logger_data, log_error, log_debug);
105 105
106 if (!ctx) 106 if (!ctx)
107 exit(1); 107 exit(1);
108 108
109 for (fk = uw_initialize(ctx); fk == UNLIMITED_RETRY; fk = uw_initialize(ctx)) { 109 for (fk = uw_initialize(ctx); fk == UNLIMITED_RETRY; fk = uw_initialize(ctx)) {
110 printf("Unlimited retry during init: %s\n", uw_error_message(ctx)); 110 log_debug(logger_data, "Unlimited retry during init: %s\n", uw_error_message(ctx));
111 uw_db_rollback(ctx); 111 uw_db_rollback(ctx);
112 uw_reset(ctx); 112 uw_reset(ctx);
113 } 113 }
114 114
115 if (fk != SUCCESS) { 115 if (fk != SUCCESS) {
116 printf("Failed to initialize database! %s\n", uw_error_message(ctx)); 116 log_error(logger_data, "Failed to initialize database! %s\n", uw_error_message(ctx));
117 uw_db_rollback(ctx); 117 uw_db_rollback(ctx);
118 exit(1); 118 exit(1);
119 } 119 }
120 120
121 uw_free(ctx); 121 uw_free(ctx);
122 122
123 init_crypto(); 123 init_crypto(logger_data, log_error);
124 } 124 }
125 125
126 void uw_sign(const char *in, char *out) { 126 void uw_sign(const char *in, char *out) {
127 MHASH td; 127 MHASH td;
128 128
129 td = mhash_hmac_init(HASH_ALGORITHM, private_key, sizeof(private_key), 129 td = mhash_hmac_init(HASH_ALGORITHM, private_key, sizeof(private_key),
130 mhash_get_hash_pblock(HASH_ALGORITHM)); 130 mhash_get_hash_pblock(HASH_ALGORITHM));
131 131
132 mhash(td, in, strlen(in)); 132 mhash(td, in, strlen(in));
133 if (mhash_hmac_deinit(td, out) < 0) 133 if (mhash_hmac_deinit(td, out) < 0)
134 printf("Signing failed"); 134 fprintf(stderr, "Signing failed\n");
135 } 135 }
136 136
137 typedef struct uw_rc { 137 typedef struct uw_rc {
138 size_t path_copy_size; 138 size_t path_copy_size;
139 char *path_copy; 139 char *path_copy;
152 } 152 }
153 153
154 request_result uw_request(uw_request_context rc, uw_context ctx, 154 request_result uw_request(uw_request_context rc, uw_context ctx,
155 char *method, char *path, char *query_string, 155 char *method, char *path, char *query_string,
156 char *body, size_t body_len, 156 char *body, size_t body_len,
157 void (*on_success)(uw_context), void (*on_failure)(uw_context),
158 void *logger_data, uw_logger log_error, uw_logger log_debug,
157 int sock) { 159 int sock) {
158 int retries_left = MAX_RETRIES; 160 int retries_left = MAX_RETRIES;
159 char *s; 161 char *s;
160 failure_kind fk; 162 failure_kind fk;
161 int is_post = 0, do_normal_send = 1; 163 int is_post = 0, do_normal_send = 1;
164 char *inputs; 166 char *inputs;
165 167
166 if (!strcmp(method, "POST")) { 168 if (!strcmp(method, "POST")) {
167 char *clen_s = uw_Basis_requestHeader(ctx, "Content-length"); 169 char *clen_s = uw_Basis_requestHeader(ctx, "Content-length");
168 if (!clen_s) { 170 if (!clen_s) {
169 fprintf(stderr, "No Content-length with POST\n"); 171 log_error(logger_data, "No Content-length with POST\n");
170 return FAILED; 172 return FAILED;
171 } 173 }
172 int clen = atoi(clen_s); 174 int clen = atoi(clen_s);
173 if (clen < 0) { 175 if (clen < 0) {
174 fprintf(stderr, "Negative Content-length with POST\n"); 176 log_error(logger_data, "Negative Content-length with POST\n");
175 return FAILED; 177 return FAILED;
176 } 178 }
177 179
178 if (body_len < clen) { 180 if (body_len < clen) {
179 fprintf(stderr, "Request doesn't contain all POST data (according to Content-Length)\n"); 181 log_error(logger_data, "Request doesn't contain all POST data (according to Content-Length)\n");
180 return FAILED; 182 return FAILED;
181 } 183 }
182 184
183 is_post = 1; 185 is_post = 1;
184 186
185 clen_s = uw_Basis_requestHeader(ctx, "Content-type"); 187 clen_s = uw_Basis_requestHeader(ctx, "Content-type");
186 if (clen_s && !strncasecmp(clen_s, "multipart/form-data", 19)) { 188 if (clen_s && !strncasecmp(clen_s, "multipart/form-data", 19)) {
187 if (strncasecmp(clen_s + 19, "; boundary=", 11)) { 189 if (strncasecmp(clen_s + 19, "; boundary=", 11)) {
188 fprintf(stderr, "Bad multipart boundary spec"); 190 log_error(logger_data, "Bad multipart boundary spec");
189 return FAILED; 191 return FAILED;
190 } 192 }
191 193
192 boundary = clen_s + 28; 194 boundary = clen_s + 28;
193 boundary[0] = '-'; 195 boundary[0] = '-';
194 boundary[1] = '-'; 196 boundary[1] = '-';
195 boundary_len = strlen(boundary); 197 boundary_len = strlen(boundary);
196 } 198 }
197 } else if (strcmp(method, "GET")) { 199 } else if (strcmp(method, "GET")) {
198 fprintf(stderr, "Not ready for non-GET/POST command: %s\n", method); 200 log_error(logger_data, "Not ready for non-GET/POST command: %s\n", method);
199 return FAILED; 201 return FAILED;
200 } 202 }
201 203
202 if (!strcmp(path, "/.msgs")) { 204 if (!strcmp(path, "/.msgs")) {
203 char *id = uw_Basis_requestHeader(ctx, "UrWeb-Client"); 205 char *id = uw_Basis_requestHeader(ctx, "UrWeb-Client");
204 char *pass = uw_Basis_requestHeader(ctx, "UrWeb-Pass"); 206 char *pass = uw_Basis_requestHeader(ctx, "UrWeb-Pass");
205 207
206 if (sock < 0) { 208 if (sock < 0) {
207 fprintf(stderr, ".msgs requested, but not socket supplied\n"); 209 log_error(logger_data, ".msgs requested, but not socket supplied\n");
208 return FAILED; 210 return FAILED;
209 } 211 }
210 212
211 if (id && pass) { 213 if (id && pass) {
212 unsigned idn = atoi(id); 214 unsigned idn = atoi(id);
213 uw_client_connect(idn, atoi(pass), sock); 215 uw_client_connect(idn, atoi(pass), sock);
214 fprintf(stderr, "Processed request for messages by client %u\n\n", idn); 216 log_error(logger_data, "Processed request for messages by client %u\n\n", idn);
215 return KEEP_OPEN; 217 return KEEP_OPEN;
216 } 218 }
217 else { 219 else {
218 fprintf(stderr, "Missing fields in .msgs request: %s, %s\n\n", id, pass); 220 log_error(logger_data, "Missing fields in .msgs request: %s, %s\n\n", id, pass);
219 return FAILED; 221 return FAILED;
220 } 222 }
221 } 223 }
222 224
223 if (boundary) { 225 if (boundary) {
224 char *part = body, *after_sub_headers, *header, *after_header; 226 char *part = body, *after_sub_headers, *header, *after_header;
225 size_t part_len; 227 size_t part_len;
226 228
227 part = strstr(part, boundary); 229 part = strstr(part, boundary);
228 if (!part) { 230 if (!part) {
229 fprintf(stderr, "Missing first multipart boundary\n"); 231 log_error(logger_data, "Missing first multipart boundary\n");
230 return FAILED; 232 return FAILED;
231 } 233 }
232 part += boundary_len; 234 part += boundary_len;
233 235
234 while (1) { 236 while (1) {
236 238
237 if (part[0] == '-' && part[1] == '-') 239 if (part[0] == '-' && part[1] == '-')
238 break; 240 break;
239 241
240 if (*part != '\r') { 242 if (*part != '\r') {
241 fprintf(stderr, "No \\r after multipart boundary\n"); 243 log_error(logger_data, "No \\r after multipart boundary\n");
242 return FAILED; 244 return FAILED;
243 } 245 }
244 ++part; 246 ++part;
245 if (*part != '\n') { 247 if (*part != '\n') {
246 fprintf(stderr, "No \\n after multipart boundary\n"); 248 log_error(logger_data, "No \\n after multipart boundary\n");
247 return FAILED; 249 return FAILED;
248 } 250 }
249 ++part; 251 ++part;
250 252
251 if (!(after_sub_headers = strstr(part, "\r\n\r\n"))) { 253 if (!(after_sub_headers = strstr(part, "\r\n\r\n"))) {
252 fprintf(stderr, "Missing end of headers after multipart boundary\n"); 254 log_error(logger_data, "Missing end of headers after multipart boundary\n");
253 return FAILED; 255 return FAILED;
254 } 256 }
255 after_sub_headers[2] = 0; 257 after_sub_headers[2] = 0;
256 after_sub_headers += 4; 258 after_sub_headers += 4;
257 259
258 for (header = part; after_header = strstr(header, "\r\n"); header = after_header + 2) { 260 for (header = part; after_header = strstr(header, "\r\n"); header = after_header + 2) {
259 char *colon, *after_colon; 261 char *colon, *after_colon;
260 262
261 *after_header = 0; 263 *after_header = 0;
262 if (!(colon = strchr(header, ':'))) { 264 if (!(colon = strchr(header, ':'))) {
263 fprintf(stderr, "Missing colon in multipart sub-header\n"); 265 log_error(logger_data, "Missing colon in multipart sub-header\n");
264 return FAILED; 266 return FAILED;
265 } 267 }
266 *colon++ = 0; 268 *colon++ = 0;
267 if (*colon++ != ' ') { 269 if (*colon++ != ' ') {
268 fprintf(stderr, "No space after colon in multipart sub-header\n"); 270 log_error(logger_data, "No space after colon in multipart sub-header\n");
269 return FAILED; 271 return FAILED;
270 } 272 }
271 273
272 if (!strcasecmp(header, "Content-Disposition")) { 274 if (!strcasecmp(header, "Content-Disposition")) {
273 if (strncmp(colon, "form-data; ", 11)) { 275 if (strncmp(colon, "form-data; ", 11)) {
274 fprintf(stderr, "Multipart data is not \"form-data\"\n"); 276 log_error(logger_data, "Multipart data is not \"form-data\"\n");
275 return FAILED; 277 return FAILED;
276 } 278 }
277 279
278 for (colon += 11; after_colon = strchr(colon, '='); colon = after_colon) { 280 for (colon += 11; after_colon = strchr(colon, '='); colon = after_colon) {
279 char *data; 281 char *data;
280 after_colon[0] = 0; 282 after_colon[0] = 0;
281 if (after_colon[1] != '"') { 283 if (after_colon[1] != '"') {
282 fprintf(stderr, "Disposition setting is missing initial quote\n"); 284 log_error(logger_data, "Disposition setting is missing initial quote\n");
283 return FAILED; 285 return FAILED;
284 } 286 }
285 data = after_colon+2; 287 data = after_colon+2;
286 if (!(after_colon = strchr(data, '"'))) { 288 if (!(after_colon = strchr(data, '"'))) {
287 fprintf(stderr, "Disposition setting is missing final quote\n"); 289 log_error(logger_data, "Disposition setting is missing final quote\n");
288 return FAILED; 290 return FAILED;
289 } 291 }
290 after_colon[0] = 0; 292 after_colon[0] = 0;
291 ++after_colon; 293 ++after_colon;
292 if (after_colon[0] == ';' && after_colon[1] == ' ') 294 if (after_colon[0] == ';' && after_colon[1] == ' ')
302 } 304 }
303 } 305 }
304 306
305 part = memmem(after_sub_headers, body + body_len - after_sub_headers, boundary, boundary_len); 307 part = memmem(after_sub_headers, body + body_len - after_sub_headers, boundary, boundary_len);
306 if (!part) { 308 if (!part) {
307 fprintf(stderr, "Missing boundary after multipart payload\n"); 309 log_error(logger_data, "Missing boundary after multipart payload\n");
308 return FAILED; 310 return FAILED;
309 } 311 }
310 part[-2] = 0; 312 part[-2] = 0;
311 part_len = part - after_sub_headers - 2; 313 part_len = part - after_sub_headers - 2;
312 part[0] = 0; 314 part[0] = 0;
314 316
315 if (filename) { 317 if (filename) {
316 uw_Basis_file f = {filename, type, {part_len, after_sub_headers}}; 318 uw_Basis_file f = {filename, type, {part_len, after_sub_headers}};
317 319
318 if (uw_set_file_input(ctx, name, f)) { 320 if (uw_set_file_input(ctx, name, f)) {
319 fprintf(stderr, "%s\n", uw_error_message(ctx)); 321 log_error(logger_data, "%s\n", uw_error_message(ctx));
320 return FAILED; 322 return FAILED;
321 } 323 }
322 } else if (uw_set_input(ctx, name, after_sub_headers)) { 324 } else if (uw_set_input(ctx, name, after_sub_headers)) {
323 fprintf(stderr, "%s\n", uw_error_message(ctx)); 325 log_error(logger_data, "%s\n", uw_error_message(ctx));
324 return FAILED; 326 return FAILED;
325 } 327 }
326 } 328 }
327 } 329 }
328 else { 330 else {
339 inputs = strchr(name, 0); 341 inputs = strchr(name, 0);
340 342
341 if (value = strchr(name, '=')) { 343 if (value = strchr(name, '=')) {
342 *value++ = 0; 344 *value++ = 0;
343 if (uw_set_input(ctx, name, value)) { 345 if (uw_set_input(ctx, name, value)) {
344 fprintf(stderr, "%s\n", uw_error_message(ctx)); 346 log_error(logger_data, "%s\n", uw_error_message(ctx));
345 return FAILED; 347 return FAILED;
346 } 348 }
347 } 349 }
348 else if (uw_set_input(ctx, name, "")) { 350 else if (uw_set_input(ctx, name, "")) {
349 fprintf(stderr, "%s\n", uw_error_message(ctx)); 351 log_error(logger_data, "%s\n", uw_error_message(ctx));
350 return FAILED; 352 return FAILED;
351 } 353 }
352 } 354 }
353 } 355 }
354 } 356 }
355 357
356 printf("Serving URI %s....\n", path); 358 log_debug(logger_data, "Serving URI %s....\n", path);
357 359
358 while (1) { 360 while (1) {
359 size_t path_len = strlen(path); 361 size_t path_len = strlen(path);
360 362
361 uw_write_header(ctx, "HTTP/1.1 200 OK\r\n"); 363 on_success(ctx);
362 364
363 if (path_len + 1 > rc->path_copy_size) { 365 if (path_len + 1 > rc->path_copy_size) {
364 rc->path_copy_size = path_len + 1; 366 rc->path_copy_size = path_len + 1;
365 rc->path_copy = realloc(rc->path_copy, rc->path_copy_size); 367 rc->path_copy = realloc(rc->path_copy, rc->path_copy_size);
366 } 368 }
369 if (fk == SUCCESS || fk == RETURN_BLOB) { 371 if (fk == SUCCESS || fk == RETURN_BLOB) {
370 uw_commit(ctx); 372 uw_commit(ctx);
371 return SERVED; 373 return SERVED;
372 } else if (fk == BOUNDED_RETRY) { 374 } else if (fk == BOUNDED_RETRY) {
373 if (retries_left) { 375 if (retries_left) {
374 printf("Error triggers bounded retry: %s\n", uw_error_message(ctx)); 376 log_debug(logger_data, "Error triggers bounded retry: %s\n", uw_error_message(ctx));
375 --retries_left; 377 --retries_left;
376 } 378 }
377 else { 379 else {
378 printf("Fatal error (out of retries): %s\n", uw_error_message(ctx)); 380 log_error(logger_data, "Fatal error (out of retries): %s\n", uw_error_message(ctx));
379 381
380 try_rollback(ctx); 382 try_rollback(ctx, logger_data, log_error);
381 383
382 uw_reset_keep_error_message(ctx); 384 uw_reset_keep_error_message(ctx);
383 uw_write_header(ctx, "HTTP/1.1 500 Internal Server Error\n\r"); 385 on_failure(ctx);
384 uw_write_header(ctx, "Content-type: text/plain\r\n"); 386 uw_write_header(ctx, "Content-type: text/plain\r\n");
385 uw_write(ctx, "Fatal error (out of retries): "); 387 uw_write(ctx, "Fatal error (out of retries): ");
386 uw_write(ctx, uw_error_message(ctx)); 388 uw_write(ctx, uw_error_message(ctx));
387 uw_write(ctx, "\n"); 389 uw_write(ctx, "\n");
388 390
389 return FAILED; 391 return FAILED;
390 } 392 }
391 } else if (fk == UNLIMITED_RETRY) 393 } else if (fk == UNLIMITED_RETRY)
392 printf("Error triggers unlimited retry: %s\n", uw_error_message(ctx)); 394 log_debug(logger_data, "Error triggers unlimited retry: %s\n", uw_error_message(ctx));
393 else if (fk == FATAL) { 395 else if (fk == FATAL) {
394 printf("Fatal error: %s\n", uw_error_message(ctx)); 396 log_error(logger_data, "Fatal error: %s\n", uw_error_message(ctx));
395 397
396 try_rollback(ctx); 398 try_rollback(ctx, logger_data, log_error);
397 399
398 uw_reset_keep_error_message(ctx); 400 uw_reset_keep_error_message(ctx);
399 uw_write_header(ctx, "HTTP/1.1 500 Internal Server Error\r\n"); 401 on_failure(ctx);
400 uw_write_header(ctx, "Content-type: text/html\r\n"); 402 uw_write_header(ctx, "Content-type: text/html\r\n");
401 uw_write(ctx, "<html><head><title>Fatal Error</title></head><body>"); 403 uw_write(ctx, "<html><head><title>Fatal Error</title></head><body>");
402 uw_write(ctx, "Fatal error: "); 404 uw_write(ctx, "Fatal error: ");
403 uw_write(ctx, uw_error_message(ctx)); 405 uw_write(ctx, uw_error_message(ctx));
404 uw_write(ctx, "\n</body></html>"); 406 uw_write(ctx, "\n</body></html>");
405 407
406 return FAILED; 408 return FAILED;
407 } else { 409 } else {
408 printf("Unknown uw_handle return code!\n"); 410 log_error(logger_data, "Unknown uw_handle return code!\n");
409 411
410 try_rollback(ctx); 412 try_rollback(ctx, logger_data, log_error);
411 413
412 uw_reset_keep_request(ctx); 414 uw_reset_keep_request(ctx);
413 uw_write_header(ctx, "HTTP/1.1 500 Internal Server Error\n\r"); 415 on_failure(ctx);
414 uw_write_header(ctx, "Content-type: text/plain\r\n"); 416 uw_write_header(ctx, "Content-type: text/plain\r\n");
415 uw_write(ctx, "Unknown uw_handle return code!\n"); 417 uw_write(ctx, "Unknown uw_handle return code!\n");
416 418
417 return FAILED; 419 return FAILED;
418 } 420 }
419 421
420 if (try_rollback(ctx)) 422 if (try_rollback(ctx, logger_data, log_error))
421 return FAILED; 423 return FAILED;
422 424
423 uw_reset_keep_request(ctx); 425 uw_reset_keep_request(ctx);
424 } 426 }
425 } 427 }
426 428
429 typedef struct {
430 void *logger_data;
431 uw_logger log_error, log_debug;
432 } loggers;
433
427 void *client_pruner(void *data) { 434 void *client_pruner(void *data) {
428 uw_context ctx = uw_request_new_context(); 435 loggers *ls = (loggers *)data;
436 uw_context ctx = uw_request_new_context(ls->logger_data, ls->log_error, ls->log_debug);
429 437
430 if (!ctx) 438 if (!ctx)
431 exit(1); 439 exit(1);
432 440
433 while (1) { 441 while (1) {