Skip to content

curl closes socket in the connection cache upon reuse if CURLOPT_CONNECT_ONLY & CURL_MAXCONNECTS is set to 1 #3983

@anonymousbug94

Description

@anonymousbug94

Changes in curl 7.64.1 close cause curl to close the socket in the connection cache upon reuse if CURLOPT_CONNECT_ONLY and CURLOPT_MAXCONNECTS are both set to 1 - this makes using curl_send and curl_recv impossible because curl reports "Failed to get recent socket"

I did this

  1. create an easy handle
  2. Set options to CURLOPT_URL ("http://www.example.com"), CURLOPT_VERBOSE (1L), CURLOPT_NOPROGRESS (1L), CURLOPT_CONNECT_ONLY (1L), and CURLOPT_MAXCONNECTS (1L)
  3. Execute the following
    a. call curl_easy_perform - watch it succeed.
    b. use curl_send to send the following "GET / HTTP/1.1\r\nHost: www.example.com\r\n"
    c. use curl_recv to recv the response
  4. repeat step 3. curl informs you that the "connection cache is full, closing the oldest one", curl_easy_perform will succeed, but curl_send will fail with error 1 unsupported protocol

I expected the following

The handle can be reused for both send and recv and the socket doesn't get closed.

curl/libcurl version

7.64.1.
tested on 7.60, 7.63, 7.64.0 - all work, the problem seems isolated to 7.64.1

[curl -V output]

operating system

Linux and others

demonstration

this sample demonstrates the problem

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

int SendRecv(CURL* curl, char* buffer, size_t buffersize)
{
    size_t nsize = 0, size = 0;
    char getMsg[] = "GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n";

    CURLcode res = curl_easy_send(curl, getMsg, strlen(getMsg), &size);
    printf("send curl code:%s (%d),\n%s\n", curl_easy_strerror(res), res, getMsg);
    memset(buffer, 0, buffersize);

    do
    {
      res = curl_easy_recv(curl, buffer + nsize, buffersize - nsize, &size);
      nsize += size;
    } while (buffersize - nsize > 0 && res == CURLE_AGAIN);

    buffer[size < 300 ? size : 300] = 0; // just show some of it for illustrative purposes
    printf("recv curl code:%s (%d)\n%s\n", curl_easy_strerror(res), res, buffer);
}

int main(int argc, char* argv[])
{
    const char* url = "http://www.example.com";
    CURL* curl = NULL;
    CURLcode res;
    char buffer[4096];

    res = curl_global_init(CURL_GLOBAL_DEFAULT);
    if( res != CURLE_OK )
    {
        goto bail;
    }

    curl = curl_easy_init();
    if (curl == NULL)
    {
      goto bail;
    };

    curl_easy_setopt(curl, CURLOPT_URL, url);
    curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
    curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
    curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 1L);
    curl_easy_setopt(curl, CURLOPT_MAXCONNECTS, 1L);

    res = curl_easy_perform(curl);
    if (res != CURLE_OK)
    {
      printf("\n curl_easy_perform failed! %s (%d).\n", curl_easy_strerror(res), res);
      goto bail;
    }
    printf("\n curl_easy_perform succeeded!\n");
    SendRecv(curl, buffer, sizeof(buffer));

    res = curl_easy_perform(curl);
    if (res != CURLE_OK)
    {
      printf("\n curl_easy_perform failed! %s (%d).\n", curl_easy_strerror(res), res);
      goto bail;
    }
    printf("\n curl_easy_perform succeeded!\n");
    SendRecv(curl, buffer, sizeof(buffer));

bail:
    curl_easy_cleanup(curl);
    curl_global_cleanup();
    return 0;
}

problem

I tracked the problem to the the below code

diff --git a/lib/conncache.c b/lib/conncache.c
index 39302ba7b..d61b3bcf7 100644
--- a/lib/conncache.c
+++ b/lib/conncache.c
@@ -477,7 +477,7 @@ Curl_conncache_extract_bundle(struct Curl_easy *data,
   while(curr) {
     conn = curr->ptr;

-    if(!CONN_INUSE(conn) && !conn->data) {
+    if(!CONN_INUSE(conn)/* && !conn->data*/) {
       /* Set higher score for the age passed since the connection was used */
       score = Curl_timediff(now, conn->now);

@@ -535,7 +535,7 @@ Curl_conncache_extract_oldest(struct Curl_easy *data)
     while(curr) {
       conn = curr->ptr;

-      if(!CONN_INUSE(conn) && !conn->data) {
+      if(!CONN_INUSE(conn) /* && !conn->data */) { 
         /* Set higher score for the age passed since the connection was used */
         score = Curl_timediff(now, conn->now);

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions