Skip to content

HTTP/2: After a timeout, RST_STREAM is not sent immediately #17611

@mkauf

Description

@mkauf

I did this

When a curl transfer times out (CURLOPT_TIMEOUT), an RST_STREAM frame should be sent to the server immediately to notify the server. This worked with curl 7.87.0 but broke with curl 7.88.0. It's still broken in the current master branch.

"git bisect" points to the commit ead2b2d.

Test program to reproduce this (based on multi-single.c):

#include <stdio.h>
#include <string.h>
#include <unistd.h>

#include <curl/curl.h>

int main(void)
{
  CURL *http_handle;
  CURLM *multi_handle;
  int still_running = 1;

  curl_global_init(CURL_GLOBAL_DEFAULT);

  http_handle = curl_easy_init();

  curl_easy_setopt(http_handle, CURLOPT_URL, "https://localhost/?sleep=10s");
  curl_easy_setopt(http_handle, CURLOPT_SSL_VERIFYPEER, 0L);
  curl_easy_setopt(http_handle, CURLOPT_SSL_VERIFYHOST, 0L);
  curl_easy_setopt(http_handle, CURLOPT_TIMEOUT, 5L);
  curl_easy_setopt(http_handle, CURLOPT_VERBOSE, 1L);

  multi_handle = curl_multi_init();

  curl_multi_add_handle(multi_handle, http_handle);

  do {
    CURLMcode mc = curl_multi_perform(multi_handle, &still_running);

    if(!mc)
      mc = curl_multi_poll(multi_handle, NULL, 0, 1000, NULL);

    if(mc) {
      fprintf(stderr, "curl_multi_poll() failed, code %d.\n", (int)mc);
      break;
    }

  } while(still_running);

  curl_multi_remove_handle(multi_handle, http_handle);

  curl_easy_cleanup(http_handle);

  sleep(4);

  curl_multi_cleanup(multi_handle);

  curl_global_cleanup();

  return 0;
}

The server sleeps for 10 seconds before sending the response.

To test it, set the environment variable SSLKEYLOGFILE and capture + analyze the network traffic with Wireshark: https://everything.curl.dev/usingcurl/tls/sslkeylogfile.html

  • With curl 7.87.0, an RST_STREAM frame is sent approximately after 5 seconds (timeout).
  • curl 7.88.0 does not send an RST_STREAM frame at all.
  • The current master branch sends an RST_STREAM frame after approximately 9 seconds (timeout of 5 seconds + sleep time of 4 seconds).

I expected the following

After a timeout, curl notifies HTTP/2 servers immediately by sending an RST_STREAM frame.

curl/libcurl version

current master branch (commit 1cdac95)

operating system

Fedora Linux 42

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions