diff options
Diffstat (limited to 'ot_http.c')
| -rw-r--r-- | ot_http.c | 76 |
1 files changed, 54 insertions, 22 deletions
| @@ -39,9 +39,33 @@ ssize_t g_stats_path_len; | |||
| 39 | 39 | ||
| 40 | enum { SUCCESS_HTTP_HEADER_LENGTH = 80, SUCCESS_HTTP_SIZE_OFF = 17 }; | 40 | enum { SUCCESS_HTTP_HEADER_LENGTH = 80, SUCCESS_HTTP_SIZE_OFF = 17 }; |
| 41 | 41 | ||
| 42 | /* Give up a connection: the one place that knows everything hanging off it. | 42 | struct http_data *http_adopt(const int64_t sock) { |
| 43 | The slot is released before anything is freed, so that a late event or the | 43 | struct http_data *cookie = fdstate_cookie(sock), *heap; |
| 44 | expiry sweep finds a closed descriptor rather than a freed cookie. */ | 44 | int interest; |
| 45 | |||
| 46 | if (!cookie || !(cookie->flag & STRUCT_HTTP_FLAG_ONSTACK)) | ||
| 47 | return cookie; | ||
| 48 | |||
| 49 | if (!(heap = malloc(sizeof(*heap)))) | ||
| 50 | return NULL; | ||
| 51 | |||
| 52 | *heap = *cookie; | ||
| 53 | heap->flag &= ~STRUCT_HTTP_FLAG_ONSTACK; | ||
| 54 | |||
| 55 | /* Publishing the cookie is what lets another thread reach this connection, | ||
| 56 | so nothing may touch it afterwards, not even to read what it asked for */ | ||
| 57 | interest = heap->interest; | ||
| 58 | fdstate_set_cookie((int)sock, heap); | ||
| 59 | |||
| 60 | if (event_add((int)sock, interest, fdstate_generation((int)sock)) == -1) { | ||
| 61 | fdstate_set_cookie((int)sock, cookie); | ||
| 62 | free(heap); | ||
| 63 | return NULL; | ||
| 64 | } | ||
| 65 | return heap; | ||
| 66 | } | ||
| 67 | |||
| 68 | /* Give up a connection: the one place that knows everything hanging off it. */ | ||
| 45 | void http_close(const int64_t sock) { | 69 | void http_close(const int64_t sock) { |
| 46 | struct http_data *cookie = fdstate_cookie(sock); | 70 | struct http_data *cookie = fdstate_cookie(sock); |
| 47 | 71 | ||
| @@ -54,7 +78,8 @@ void http_close(const int64_t sock) { | |||
| 54 | buf_reset(&cookie->request); | 78 | buf_reset(&cookie->request); |
| 55 | if (cookie->flag & (STRUCT_HTTP_FLAG_WAITINGFORTASK | STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER)) | 79 | if (cookie->flag & (STRUCT_HTTP_FLAG_WAITINGFORTASK | STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER)) |
| 56 | mutex_workqueue_canceltask(sock); | 80 | mutex_workqueue_canceltask(sock); |
| 57 | free(cookie); | 81 | if (!(cookie->flag & STRUCT_HTTP_FLAG_ONSTACK)) |
| 82 | free(cookie); | ||
| 58 | } | 83 | } |
| 59 | 84 | ||
| 60 | event_close((int)sock); | 85 | event_close((int)sock); |
| @@ -118,11 +143,9 @@ static void http_senddata(const int64_t sock, struct ot_workstruct *ws) { | |||
| 118 | batch_add(cookie->batch, outbuf, ws->reply_size - written_size); | 143 | batch_add(cookie->batch, outbuf, ws->reply_size - written_size); |
| 119 | 144 | ||
| 120 | /* writeable short data sockets just have a tcp timeout */ | 145 | /* writeable short data sockets just have a tcp timeout */ |
| 121 | if (!ws->keep_alive) { | 146 | if (!ws->keep_alive) |
| 122 | fdstate_set_expiry(sock, 0); | 147 | fdstate_set_expiry(sock, 0); |
| 123 | http_interest_drop(sock, cookie, EVENT_IN); | 148 | http_interest_set(sock, cookie, EVENT_OUT); |
| 124 | } | ||
| 125 | http_interest_add(sock, cookie, EVENT_OUT); | ||
| 126 | } | 149 | } |
| 127 | } | 150 | } |
| 128 | 151 | ||
| @@ -135,21 +158,20 @@ static void http_senddata(const int64_t sock, struct ot_workstruct *ws) { | |||
| 135 | #define HTTPERROR_403_IP return http_issue_error(sock, ws, CODE_HTTPERROR_403_IP) | 158 | #define HTTPERROR_403_IP return http_issue_error(sock, ws, CODE_HTTPERROR_403_IP) |
| 136 | #define HTTPERROR_404 return http_issue_error(sock, ws, CODE_HTTPERROR_404) | 159 | #define HTTPERROR_404 return http_issue_error(sock, ws, CODE_HTTPERROR_404) |
| 137 | #define HTTPERROR_500 return http_issue_error(sock, ws, CODE_HTTPERROR_500) | 160 | #define HTTPERROR_500 return http_issue_error(sock, ws, CODE_HTTPERROR_500) |
| 138 | static void http_interest(const int64_t sock, struct http_data *cookie, int what) { | 161 | /* A connection is either being read or being written, never both, so what it |
| 162 | waits for is set outright rather than added to and taken away again. The | ||
| 163 | event layer only hears about it when the answer changes. */ | ||
| 164 | void http_interest_set(const int64_t sock, struct http_data *cookie, int what) { | ||
| 139 | if (cookie->interest == what) | 165 | if (cookie->interest == what) |
| 140 | return; | 166 | return; |
| 141 | cookie->interest = what; | 167 | cookie->interest = what; |
| 168 | /* Still on a stack, so not in the event loop yet: whoever adopts this | ||
| 169 | connection registers whatever it asked for last */ | ||
| 170 | if (cookie->flag & STRUCT_HTTP_FLAG_ONSTACK) | ||
| 171 | return; | ||
| 142 | event_modify((int)sock, what, fdstate_generation((int)sock)); | 172 | event_modify((int)sock, what, fdstate_generation((int)sock)); |
| 143 | } | 173 | } |
| 144 | 174 | ||
| 145 | void http_interest_add(const int64_t sock, struct http_data *cookie, int what) { | ||
| 146 | http_interest(sock, cookie, cookie->interest | what); | ||
| 147 | } | ||
| 148 | |||
| 149 | void http_interest_drop(const int64_t sock, struct http_data *cookie, int what) { | ||
| 150 | http_interest(sock, cookie, cookie->interest & ~what); | ||
| 151 | } | ||
| 152 | |||
| 153 | ssize_t http_issue_error(const int64_t sock, struct ot_workstruct *ws, int code) { | 175 | ssize_t http_issue_error(const int64_t sock, struct ot_workstruct *ws, int code) { |
| 154 | const char *error_code[] = {"302 Found", "400 Invalid Request", "400 Invalid Request", "400 Invalid Request", "402 Payment Required", | 176 | const char *error_code[] = {"302 Found", "400 Invalid Request", "400 Invalid Request", "400 Invalid Request", "402 Payment Required", |
| 155 | "403 Not Modest", "403 Access Denied", "404 Not Found", "500 Internal Server Error"}; | 177 | "403 Not Modest", "403 Access Denied", "404 Not Found", "500 Internal Server Error"}; |
| @@ -194,7 +216,7 @@ ssize_t http_sendiovecdata(const int64_t sock, struct ot_workstruct *ws, size_t | |||
| 194 | 216 | ||
| 195 | /* If we came here, wait for the answer is over */ | 217 | /* If we came here, wait for the answer is over */ |
| 196 | if (cookie->flag & STRUCT_HTTP_FLAG_WAITINGFORTASK) { | 218 | if (cookie->flag & STRUCT_HTTP_FLAG_WAITINGFORTASK) { |
| 197 | http_interest_drop(sock, cookie, EVENT_IN); | 219 | http_interest_set(sock, cookie, 0); |
| 198 | cookie->flag &= ~STRUCT_HTTP_FLAG_WAITINGFORTASK; | 220 | cookie->flag &= ~STRUCT_HTTP_FLAG_WAITINGFORTASK; |
| 199 | } | 221 | } |
| 200 | 222 | ||
| @@ -245,7 +267,7 @@ ssize_t http_sendiovecdata(const int64_t sock, struct ot_workstruct *ws, size_t | |||
| 245 | } | 267 | } |
| 246 | 268 | ||
| 247 | fdstate_set_expiry(sock, time_future_seconds(OT_CLIENT_TIMEOUT_SEND)); | 269 | fdstate_set_expiry(sock, time_future_seconds(OT_CLIENT_TIMEOUT_SEND)); |
| 248 | http_interest_add(sock, cookie, EVENT_OUT); | 270 | http_interest_set(sock, cookie, EVENT_OUT); |
| 249 | return 0; | 271 | return 0; |
| 250 | } | 272 | } |
| 251 | 273 | ||
| @@ -334,13 +356,17 @@ static ssize_t http_handle_stats(const int64_t sock, struct ot_workstruct *ws, c | |||
| 334 | } | 356 | } |
| 335 | #endif | 357 | #endif |
| 336 | #endif | 358 | #endif |
| 337 | /* Pass this task to the worker thread */ | ||
| 338 | cookie->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK | STRUCT_HTTP_FLAG_CHUNKED; | 359 | cookie->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK | STRUCT_HTTP_FLAG_CHUNKED; |
| 339 | 360 | ||
| 340 | /* Clients waiting for us should not easily timeout */ | 361 | /* Clients waiting for us should not easily timeout */ |
| 341 | fdstate_set_expiry(sock, 0); | 362 | fdstate_set_expiry(sock, 0); |
| 363 | http_interest_set(sock, cookie, 0); | ||
| 364 | |||
| 365 | /* Everything this connection needs has been written, so it can be given | ||
| 366 | a home of its own and only then be made reachable by a worker thread */ | ||
| 367 | if (!http_adopt(sock)) | ||
| 368 | HTTPERROR_500; | ||
| 342 | fullscrape_deliver(sock, format); | 369 | fullscrape_deliver(sock, format); |
| 343 | http_interest_drop(sock, cookie, EVENT_IN); | ||
| 344 | return ws->reply_size = -2; | 370 | return ws->reply_size = -2; |
| 345 | } | 371 | } |
| 346 | #endif | 372 | #endif |
| @@ -352,6 +378,9 @@ static ssize_t http_handle_stats(const int64_t sock, struct ot_workstruct *ws, c | |||
| 352 | 378 | ||
| 353 | /* Complex stats also include expensive memory debugging tools */ | 379 | /* Complex stats also include expensive memory debugging tools */ |
| 354 | fdstate_set_expiry(sock, 0); | 380 | fdstate_set_expiry(sock, 0); |
| 381 | |||
| 382 | if (!http_adopt(sock)) | ||
| 383 | HTTPERROR_500; | ||
| 355 | stats_deliver(sock, mode); | 384 | stats_deliver(sock, mode); |
| 356 | return ws->reply_size = -2; | 385 | return ws->reply_size = -2; |
| 357 | } | 386 | } |
| @@ -434,8 +463,11 @@ static ssize_t http_handle_fullscrape(const int64_t sock, struct ot_workstruct * | |||
| 434 | cookie->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK | STRUCT_HTTP_FLAG_CHUNKED; | 463 | cookie->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK | STRUCT_HTTP_FLAG_CHUNKED; |
| 435 | /* Clients waiting for us should not easily timeout */ | 464 | /* Clients waiting for us should not easily timeout */ |
| 436 | fdstate_set_expiry(sock, 0); | 465 | fdstate_set_expiry(sock, 0); |
| 466 | http_interest_set(sock, cookie, 0); | ||
| 467 | |||
| 468 | if (!http_adopt(sock)) | ||
| 469 | HTTPERROR_500; | ||
| 437 | fullscrape_deliver(sock, format); | 470 | fullscrape_deliver(sock, format); |
| 438 | http_interest_drop(sock, cookie, EVENT_IN); | ||
| 439 | return ws->reply_size = -2; | 471 | return ws->reply_size = -2; |
| 440 | } | 472 | } |
| 441 | #endif | 473 | #endif |
