Like th_http but arms a client-side receive-timeout watchdog. If the * single-threaded server wedges, recv() returns instead of blocking forever, so * the test FAILs deterministically rather than hanging CI. 0 on connect/timeout. */
| 1858 | * single-threaded server wedges, recv() returns instead of blocking forever, so |
| 1859 | * the test FAILs deterministically rather than hanging CI. 0 on connect/timeout. */ |
| 1860 | static int th_http_deadline(int port, const char *request, char *resp, size_t respsz, |
| 1861 | int timeout_ms) { |
| 1862 | char *prepared = th_request_with_ui_headers(port, request); |
| 1863 | if (!prepared) |
| 1864 | return 0; |
| 1865 | th_sock_t s = th_connect(port); |
| 1866 | if (s == TH_SOCK_BAD) { |
| 1867 | free(prepared); |
| 1868 | return 0; |
| 1869 | } |
| 1870 | #ifdef _WIN32 |
| 1871 | DWORD tv = (DWORD)timeout_ms; |
| 1872 | setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof(tv)); |
| 1873 | #else |
| 1874 | struct timeval tv; |
| 1875 | tv.tv_sec = timeout_ms / 1000; |
| 1876 | tv.tv_usec = (timeout_ms % 1000) * 1000; |
| 1877 | setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); |
| 1878 | #endif |
| 1879 | if (th_send_all(s, prepared, strlen(prepared)) != 0) { |
| 1880 | free(prepared); |
| 1881 | th_sock_close(s); |
| 1882 | return 0; |
| 1883 | } |
| 1884 | free(prepared); |
| 1885 | int n = th_recv_until_close(s, resp, respsz); |
| 1886 | th_sock_close(s); |
| 1887 | return n; |
| 1888 | } |
| 1889 | |
| 1890 | /* #798 was a single-threaded-server wedge: list_projects never returned and the |
| 1891 | * whole UI stopped answering. Assert the running server answers list_projects |
no test coverage detected