Home > Enterprise >  HTTP keep connection alive without blocking on socket receive?
HTTP keep connection alive without blocking on socket receive?

Time:11-28

I am using the below code to make a HTTPS GET request.

I'd like to keep the connection alive to make multiple requests without having to connect each time, so I set "Connection: Keep-Alive\r\n". However, once I did this, the behaviour changed and the code now blocks on BIO_read() (equivalent to ::recv()). Now I cannot return to process the received data.

How can I only connect once, but not block on BIO_read()/::recv()?

Here is the code:

    #define APIKEY "/api/someversion/feature?key1=value1"
    #define HOST "api.somewebsite.com"
    #define PORT "443"

    //
    //  Initialize the variables
    //
    BIO* bio;
    SSL* ssl;
    SSL_CTX* ctx;

    //
    //   Registers the SSL/TLS ciphers and digests.
    //
    //   Basically start the security layer.
    //
    SSL_library_init();

    //
    //  Creates a new SSL_CTX object as a framework to establish TLS/SSL
    //  or DTLS enabled connections
    //
    ctx = SSL_CTX_new(SSLv23_client_method());

    //
    //  -> Error check
    //
    if (ctx == NULL)
    {
        printf("Ctx is null\n");
    }

    //
    //   Creates a new BIO chain consisting of an SSL BIO
    //
    bio = BIO_new_ssl_connect(ctx);

    // Server Name Indication (SNI):
    BIO_get_ssl(bio, &ssl);
    SSL_set_tlsext_host_name(ssl, HOST);

    //
    //  Use the variable from the beginning of the file to create a 
    //  string that contains the URL to the site that you want to connect
    //  to while also specifying the port.
    //
    BIO_set_conn_hostname(bio, HOST ":" PORT);

    //
    //   Attempts to connect the supplied BIO
    //
    if(BIO_do_connect(bio) <= 0)
    {
        printf("Failed connection\n");
        std::abort();
    }
    else
    {
        printf("Connected\n");
    }

    //
    //  The bare minimum to make a HTTP request.
    //
    char* write_buf = "GET " APIKEY " HTTP/1.1\r\n"
                      "Host: " HOST "\r\n"
                      "Connection: Keep-Alive\r\n"
                      "\r\n";

    //
    //   Attempts to write len bytes from buf to BIO
    //
    if(BIO_write(bio, write_buf, strlen(write_buf)) <= 0)
    {
        //
        //  Handle failed writes here
        //
        if(!BIO_should_retry(bio))
        {
            // Not worth implementing, but worth knowing.
        }

        //
        //  -> Let us know about the failed writes
        //
        printf("Failed write\n");
    }

    //
    //  Variables used to read the response from the server
    //
    int size;
    char buf[1024];

    //
    //  Read the response message
    //
    for(;;)
    {
        //
        //  Get chunks of the response 1023 at the time.
        //
        //  THE CODE BLOCKS HERE
        //
        size = BIO_read(bio, buf, 1023);

        //
        //  If no more data, then exit the loop
        //
        if(size <= 0)
        {
            break;
        }

        //
        //  Terminate the string with a 0, to let know C when the string 
        //  ends.
        //
        buf[size] = 0;

        //
        //  ->  Print out the response
        //
        printf("%s", buf);
    }

    //
    //  Clean after ourselves
    //
    BIO_free_all(bio);
    SSL_CTX_free(ctx);

CodePudding user response:

HTTP/2 uses, according to the specification, persistent connections. So it should work to replace HTTP 1.1 with HTTP/2. Note, that you then have to remove the Connection: Keep-Alive line, as it is prohibited in HTTP/2. It was just a hint anyway, and didn't guarantee a persistent connection (see MDN Web Docs).

CodePudding user response:

You have to pay attention to what you read. Parse the response headers. There would be wither a "Content-Length: xxx", or "Transfer-Encoding: chunked". That woukd give you an information on where the body ends.

  • Related