multi-event: fix timeout of pending transfers
No binary (build failed)
—
(log yok)
| @@ -224,3 +224,7 @@ This allows testing of connection reuse in mixed `STARTTLS` needs. | |
| ## `CURL_DBG_SUPPRESS_CONNECT_HDS` | |
| Existence of this variable suppresses the collection of CONNECT headers. | |
| + | |
| + | ## `CURL_DBG_MAX_TOTAL_CONNECTIONS` |
| + | |
| + | Set the `max_total_connections` for a `multi` handle. |
| @@ -47,6 +47,7 @@ | |
| #include "http2.h" | |
| #include "socketpair.h" | |
| #include "bufref.h" | |
| + | #include "curlx/strparse.h" |
| /* initial multi->xfers table size for a full multi */ | |
| #define CURL_XFER_TABLE_SIZE 128 | |
| @@ -283,8 +284,18 @@ struct Curl_multi *Curl_multi_handle(uint32_t xfer_table_size, | |
| goto error; | |
| #ifdef DEBUGBUILD | |
| - | if(getenv("CURL_DEBUG")) |
| - | multi->admin->set.verbose = TRUE; |
| + | { |
| + | const char *p; |
| + | if(getenv("CURL_DEBUG")) |
| + | multi->admin->set.verbose = TRUE; |
| + | p = getenv("CURL_DBG_MAX_TOTAL_CONNECTIONS"); |
| + | if(p) { |
| + | curl_off_t l; |
| + | if(!curlx_str_number(&p, &l, UINT32_MAX)) { |
| + | multi->max_total_connections = (uint32_t)l; |
| + | } |
| + | } |
| + | } |
| #endif | |
| Curl_uint32_tbl_add(&multi->xfers, multi->admin, &multi->admin->mid); | |
| Curl_uint32_bset_add(&multi->process, multi->admin->mid); | |
| @@ -3206,9 +3217,15 @@ static CURLMcode multi_run_dirty(struct Curl_multi *multi, | |
| CURL_TRC_M(data, "multi_run_dirty"); | |
| if(!Curl_uint32_bset_contains(&multi->process, mid)) { | |
| - | /* We are no longer processing this transfer */ |
| - | Curl_uint32_bset_remove(&multi->dirty, mid); |
| - | continue; |
| + | if(Curl_uint32_bset_contains(&multi->pending, mid)) { |
| + | /* Something happened on the pending transfer, act on it. */ |
| + | move_pending_to_connect(multi, data); |
| + | } |
| + | else { |
| + | /* We are no longer processing this transfer */ |
| + | Curl_uint32_bset_remove(&multi->dirty, mid); |
| + | continue; |
| + | } |
| } | |
| (*pnum)++; | |
| @@ -716,3 +716,30 @@ class TestDownload: | |
| f'got {r.exit_code}\n{r.dump_logs()}' | |
| if r.exit_code == 0: | |
| r.check_response(http_status=431) | |
| + | |
| + | # download 2 http/1.1 resources, limited to a single connection max |
| + | @pytest.mark.skipif(condition=not Env.curl_is_debug(), reason="needs curl debug") |
| + | def test_02_37_pending_timeout(self, env: Env, httpd): |
| + | # Run 2 http/1.1 transfers in parallel with a connection limit of 1 |
| + | # - xfer 0 has a delay response, making xfer 1 pending during its run |
| + | # - xfer 1 will timeout while pending, waiting on the connection |
| + | proto = 'http/1.1' |
| + | run_env = os.environ.copy() |
| + | run_env['CURL_DBG_MAX_TOTAL_CONNECTIONS'] = '1' |
| + | curl = CurlClient(env=env, run_env=run_env) |
| + | url1 = f'https://{env.authority_for(env.domain1, proto)}' \ |
| + | '/curltest/tweak/?&delay=3s' |
| + | url2 = f'https://{env.authority_for(env.domain1, proto)}/data.json' |
| + | r = curl.http_download(urls=[url1, url2], alpn_proto=proto, extra_args=[ |
| + | '--http1.1', '--parallel' |
| + | ], url_options={ |
| + | url1: ['--max-time', '10'], |
| + | url2: ['--max-time', '1'] |
| + | }) |
| + | r.check_exit_code(28) |
| + | xfers = {stat['xfer_id']: stat for stat in r.stats} |
| + | assert xfers[0]['http_code'] == 200, f'{r.stats[0]}' # succeeded |
| + | assert xfers[1]['exitcode'] == 28, f'{r.stats[1]}' # timed out |
| + | m = re.search(r'timed out after (\d+) milliseconds', xfers[1]['errormsg']) |
| + | assert m, f'{r.stats[1]}' |
| + | assert int(m.group(1)) < 3000, f'{r.stats[1]}' |