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.