diff options
| author | Dirk Engling <erdgeist@erdgeist.org> | 2026-11-06 03:01:46 +0100 |
|---|---|---|
| committer | Dirk Engling <erdgeist@erdgeist.org> | 2026-11-06 03:01:46 +0100 |
| commit | 8bae93ce181086769175e5c3fb0f0ef66fb9ca89 (patch) | |
| tree | da86b766bd37f1a71d0847fd3ebbd93b1f95ec76 | |
| parent | 2cecc9da20afb55613589dafc854e9f4d1a74069 (diff) | |
Stop reading a connection a worker has taken over
| -rw-r--r-- | src/ot_http.c | 4 | ||||
| -rw-r--r-- | src/ot_tcp.c | 5 | ||||
| -rw-r--r-- | src/trackerlogic.h | 3 | ||||
| -rw-r--r-- | tests/integration/test_keepalive.py | 19 |
4 files changed, 31 insertions, 0 deletions
diff --git a/src/ot_http.c b/src/ot_http.c index e6a8db7..29f41a3 100644 --- a/src/ot_http.c +++ b/src/ot_http.c | |||
| @@ -367,6 +367,7 @@ static ssize_t http_handle_stats(const int64_t sock, struct ot_workstruct *ws, c | |||
| 367 | a home of its own and only then be made reachable by a worker thread */ | 367 | a home of its own and only then be made reachable by a worker thread */ |
| 368 | if (!http_adopt(sock)) | 368 | if (!http_adopt(sock)) |
| 369 | HTTPERROR_500; | 369 | HTTPERROR_500; |
| 370 | ws->handed_off = 1; | ||
| 370 | fullscrape_deliver(sock, format); | 371 | fullscrape_deliver(sock, format); |
| 371 | return ws->reply_size = -2; | 372 | return ws->reply_size = -2; |
| 372 | } | 373 | } |
| @@ -383,6 +384,7 @@ static ssize_t http_handle_stats(const int64_t sock, struct ot_workstruct *ws, c | |||
| 383 | 384 | ||
| 384 | if (!http_adopt(sock)) | 385 | if (!http_adopt(sock)) |
| 385 | HTTPERROR_500; | 386 | HTTPERROR_500; |
| 387 | ws->handed_off = 1; | ||
| 386 | stats_deliver(sock, mode); | 388 | stats_deliver(sock, mode); |
| 387 | return ws->reply_size = -2; | 389 | return ws->reply_size = -2; |
| 388 | } | 390 | } |
| @@ -477,6 +479,7 @@ static ssize_t http_handle_fullscrape(const int64_t sock, struct ot_workstruct * | |||
| 477 | 479 | ||
| 478 | if (!http_adopt(sock)) | 480 | if (!http_adopt(sock)) |
| 479 | HTTPERROR_500; | 481 | HTTPERROR_500; |
| 482 | ws->handed_off = 1; | ||
| 480 | fullscrape_deliver(sock, format); | 483 | fullscrape_deliver(sock, format); |
| 481 | return ws->reply_size = -2; | 484 | return ws->reply_size = -2; |
| 482 | } | 485 | } |
| @@ -742,6 +745,7 @@ ssize_t http_handle_request(const int64_t sock, struct ot_workstruct *ws) { | |||
| 742 | subroutine gets to answer: an error reply finishes with the connection | 745 | subroutine gets to answer: an error reply finishes with the connection |
| 743 | on its way out, and ws outlives the connection it was serving */ | 746 | on its way out, and ws outlives the connection it was serving */ |
| 744 | ws->keep_alive = 0; | 747 | ws->keep_alive = 0; |
| 748 | ws->handed_off = 0; | ||
| 745 | #ifdef WANT_KEEPALIVE | 749 | #ifdef WANT_KEEPALIVE |
| 746 | if ((write_ptr = http_header(ws->request, ws->header_size, "connection")) && (*write_ptr == 'K' || *write_ptr == 'k')) | 750 | if ((write_ptr = http_header(ws->request, ws->header_size, "connection")) && (*write_ptr == 'K' || *write_ptr == 'k')) |
| 747 | ws->keep_alive = 1; | 751 | ws->keep_alive = 1; |
diff --git a/src/ot_tcp.c b/src/ot_tcp.c index 1351b92..f93795b 100644 --- a/src/ot_tcp.c +++ b/src/ot_tcp.c | |||
| @@ -79,6 +79,11 @@ void tcp_read(const int64_t sock, struct ot_workstruct *ws) { | |||
| 79 | ws->request = cookie->request.data; | 79 | ws->request = cookie->request.data; |
| 80 | ws->request_size = (ssize_t)cookie->request.size; | 80 | ws->request_size = (ssize_t)cookie->request.size; |
| 81 | http_handle_request(sock, ws); | 81 | http_handle_request(sock, ws); |
| 82 | |||
| 83 | /* A worker owns the connection now, and the buffer this loop reads hangs | ||
| 84 | off it, so there is nothing here left to look at */ | ||
| 85 | if (ws->handed_off) | ||
| 86 | return; | ||
| 82 | #ifdef WANT_KEEPALIVE | 87 | #ifdef WANT_KEEPALIVE |
| 83 | if (!ws->keep_alive) | 88 | if (!ws->keep_alive) |
| 84 | #endif | 89 | #endif |
diff --git a/src/trackerlogic.h b/src/trackerlogic.h index 4f970ba..d03d3b1 100644 --- a/src/trackerlogic.h +++ b/src/trackerlogic.h | |||
| @@ -256,6 +256,9 @@ struct ot_workstruct { | |||
| 256 | uint16_t rand48_state[3]; | 256 | uint16_t rand48_state[3]; |
| 257 | 257 | ||
| 258 | int keep_alive; | 258 | int keep_alive; |
| 259 | |||
| 260 | /* Set when a request path gave the connection to a worker thread. */ | ||
| 261 | int handed_off; | ||
| 259 | }; | 262 | }; |
| 260 | 263 | ||
| 261 | /* Everything a thread that serves requests needs for itself. Set up and torn | 264 | /* Everything a thread that serves requests needs for itself. Set up and torn |
diff --git a/tests/integration/test_keepalive.py b/tests/integration/test_keepalive.py index 01f02a5..2c724fc 100644 --- a/tests/integration/test_keepalive.py +++ b/tests/integration/test_keepalive.py | |||
| @@ -7,6 +7,7 @@ the request has moved on, so whether to keep the connection has to be | |||
| 7 | remembered on the connection itself. | 7 | remembered on the connection itself. |
| 8 | """ | 8 | """ |
| 9 | 9 | ||
| 10 | import time | ||
| 10 | import unittest | 11 | import unittest |
| 11 | import urllib.parse | 12 | import urllib.parse |
| 12 | 13 | ||
| @@ -106,6 +107,24 @@ class KeepAlive(TrackerTestCase): | |||
| 106 | finally: | 107 | finally: |
| 107 | c.close() | 108 | c.close() |
| 108 | 109 | ||
| 110 | def test_a_split_request_answered_by_a_worker_does_not_spin(self): | ||
| 111 | """A reply that a worker produces does not go through the send path, | ||
| 112 | so the request stays in the buffer the read loop is reading. The loop | ||
| 113 | used to find the same request again on every pass, queue a task for | ||
| 114 | it, and never hand the thread back to the event loop.""" | ||
| 115 | for path in ("/stats", "/scrape"): | ||
| 116 | c = self.tracker.connect() | ||
| 117 | try: | ||
| 118 | c.sock.sendall(("GET %s HTTP/1.1\r\nConnection: keep-alive\r\n" % path).encode()) | ||
| 119 | time.sleep(0.2) | ||
| 120 | c.sock.sendall(b"\r\n") | ||
| 121 | self.assertIn(b"200", c.reply()[0], "%s was not answered" % path) | ||
| 122 | finally: | ||
| 123 | c.close() | ||
| 124 | |||
| 125 | answer = self.tracker.announce(self.hash_for("after-" + path), port=6881, left=0) | ||
| 126 | self.assertNotIn(b"failure reason", answer, "the tracker stopped serving after %s" % path) | ||
| 127 | |||
| 109 | 128 | ||
| 110 | if __name__ == "__main__": | 129 | if __name__ == "__main__": |
| 111 | unittest.main() | 130 | unittest.main() |
