Hi, On Sat, Nov 15, 2014 at 2:01 AM, Samuel Hurst <[email protected]> wrote:
> Hi Tatsuhiro, > > On 14/11/2014 15:42, Tatsuhiro Tsujikawa wrote: > > I found the bug that curl unconditionally sends Upgrade headers > > to server even after the connection was upgraded to HTTP/2. The > > attached patch fixes this bug. > > Thanks for the patch, it does solve the initial issue but similarly to > my own fix I've been working on today it seems to expose a different > issue. It seems that the client is sending two HEADERS frames for two > resources in very short order, and when the server attempts to respond > the client is sending RST_STREAM with a flagged protocol error. > > The below is a paraphrased wireshark log showing the issue (sadly > can't attach the full trace as it contains sensitive information): > > E.g. > Source | Destination | Protocol | Info (Stream IDs) > 10.0.2.15 | 172.29.1.10 | HTTP | GET /1/IS.mp4 HTTP/1.1 > 172.29.1.10 | 10.0.2.15 | HTTP | HTTP/1.1 101 Switching Protocols > 10.0.2.15 | 172.29.1.10 | HTTP2 | Magic > 172.29.1.10 | 10.0.2.15 | HTTP2 | HEADERS (1) [Status200], DATA > (1), DATA (1) > 10.0.2.15 | 172.29.1.10 | HTTP2 | SETTINGS > 172.29.1.10 | 10.0.2.15 | HTTP2 | SETTINGS [ACK above] > 10.0.2.15 | 172.29.1.10 | HTTP2 | HEADERS (3) [GET /1/V1.mp4] > 10.0.2.15 | 172.29.1.10 | HTTP2 | HEADERS (5) [GET /1/A1.mp4] > 172.29.1.10 | 10.0.2.15 | HTTP2 | HEADERS (3) [Status200], DATA (3) > 10.0.2.15 | 172.29.1.10 | HTTP2 | RST_STREAM (3) > 172.29.1.10 | 10.0.2.15 | HTTP2 | HEADERS (5) [Status200], DATA (5) > ... > > I only see the behaviour when I see two HEADERS frames sent before the > server could respond. Otherwise transmission is fine. I'm also not so > sure whether the second 0x1-flagged DATA frame shows up, my version of > Wireshark is having some issues with HTTP2 packets with long > content-lengths. > > Settings are whatever nghttp2 uses as defaults as I am not explicitly > setting them. (This seems to be max number of concurrent streams as > 100 and window size 65535) > > I'm actually on holiday next week so any further discussion on this > will probably come from my colleague Lucas Pardue. > > As far as I know, libcurl HTTP/2 code does not handle concurrent request handling yet. So appearing more than 1 concurrent requests in same HTTP/2 connection is unexpected, but you see this happening. I'm wandering how to reproduce this issue. It seems curl tool does not do this. It may be due to curl multi interface, so I just experimented with multi-app.c example program (attached in this mail). But I could not see this issue in there. I'm totally new to curl multi interface, so could you send me your code (or something stripped version to delete confidential parts, but enough to make reproduce this issue)? Best regards, Tatsuhiro Tsujikawa > > > Best Regards, > Sam > > ------------------------------------------------------------------- > List admin: http://cool.haxx.se/list/listinfo/curl-library > Etiquette: http://curl.haxx.se/mail/etiquette.html >
/*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | * / __| | | | |_) | | * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * * Copyright (C) 1998 - 2011, Daniel Stenberg, <[email protected]>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://curl.haxx.se/docs/copyright.html. * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ***************************************************************************/ /* This is an example application source code using the multi interface. */ #include <stdio.h> #include <string.h> /* somewhat unix-specific */ #include <sys/time.h> #include <unistd.h> /* curl stuff */ #include <curl/curl.h> /* * Download a HTTP file and upload an FTP file simultaneously. */ #define HANDLECOUNT 2 /* Number of simultaneous transfers */ #define HTTP_HANDLE1 0 /* Index for the HTTP transfer#1 */ #define HTTP_HANDLE2 1 /* Index for the HTTP transfer#2 */ int main(void) { CURL *handles[HANDLECOUNT]; CURLM *multi_handle; int still_running; /* keep number of running handles */ int i; CURLMsg *msg; /* for picking up messages with the transfer status */ int msgs_left; /* how many messages are left */ /* Allocate one CURL handle per transfer */ for (i=0; i<HANDLECOUNT; i++) handles[i] = curl_easy_init(); /* set the options (I left out a few, you'll get the point anyway) */ curl_easy_setopt(handles[HTTP_HANDLE1], CURLOPT_URL, "http://localhost:3000/a"); curl_easy_setopt(handles[HTTP_HANDLE1], CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); curl_easy_setopt(handles[HTTP_HANDLE2], CURLOPT_URL, "http://localhost:3000/b"); curl_easy_setopt(handles[HTTP_HANDLE2], CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); /* init a multi stack */ multi_handle = curl_multi_init(); curl_multi_setopt(multi_handle, CURLMOPT_MAX_HOST_CONNECTIONS, 1); /* add the individual transfers */ for (i=0; i<HANDLECOUNT; i++) curl_multi_add_handle(multi_handle, handles[i]); /* we start some action by calling perform right away */ curl_multi_perform(multi_handle, &still_running); do { struct timeval timeout; int rc; /* select() return code */ fd_set fdread; fd_set fdwrite; fd_set fdexcep; int maxfd = -1; long curl_timeo = -1; FD_ZERO(&fdread); FD_ZERO(&fdwrite); FD_ZERO(&fdexcep); /* set a suitable timeout to play around with */ timeout.tv_sec = 1; timeout.tv_usec = 0; curl_multi_timeout(multi_handle, &curl_timeo); if(curl_timeo >= 0) { timeout.tv_sec = curl_timeo / 1000; if(timeout.tv_sec > 1) timeout.tv_sec = 1; else timeout.tv_usec = (curl_timeo % 1000) * 1000; } /* get file descriptors from the transfers */ curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd); /* In a real-world program you OF COURSE check the return code of the function calls. On success, the value of maxfd is guaranteed to be greater or equal than -1. We call select(maxfd + 1, ...), specially in case of (maxfd == -1), we call select(0, ...), which is basically equal to sleep. */ rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout); switch(rc) { case -1: /* select error */ break; case 0: /* timeout */ default: /* action */ curl_multi_perform(multi_handle, &still_running); break; } } while(still_running); /* See how the transfers went */ while ((msg = curl_multi_info_read(multi_handle, &msgs_left))) { if (msg->msg == CURLMSG_DONE) { int idx, found = 0; /* Find out which handle this message is about */ for (idx=0; idx<HANDLECOUNT; idx++) { found = (msg->easy_handle == handles[idx]); if(found) break; } switch (idx) { case HTTP_HANDLE1: printf("HTTP transfer#1 completed with status %d\n", msg->data.result); break; case HTTP_HANDLE2: printf("HTTP transfer#2 completed with status %d\n", msg->data.result); break; } } } curl_multi_cleanup(multi_handle); /* Free the CURL handles */ for (i=0; i<HANDLECOUNT; i++) curl_easy_cleanup(handles[i]); return 0; }
------------------------------------------------------------------- List admin: http://cool.haxx.se/list/listinfo/curl-library Etiquette: http://curl.haxx.se/mail/etiquette.html
