M.V.
M.V.

Reputation: 95

How to determine when data is finished on an SSL socket

I'm writing an application that creates multiple non-blocking SSL connections to an https server, in each one I send a request and read the server's response. my problem is, whatever I do, I can't determine when the response data is finished. here's part of my code responsible for sending and receiving data:

....
fd_set connectionfds;
struct timeval timeout2;
FD_ZERO(&connectionfds);
FD_SET(socket_server, &connectionfds);
timeout2.tv_usec = 0;
timeout2.tv_sec = 1;
while(1)
{
    r=BIO_read(io,buf,BUFSIZZ-1);
    if (r>0){
        //gather data
        continue;
    }
    else if (SSL_get_error(ssl, r)==SSL_ERROR_WANT_READ){
        int ret = select(socket_server + 1, &connectionfds, NULL, NULL, &timeout2);
        if (ret <= 0){
            break;
        }
        continue;
    }
    else{
        break;
    }
}
// use whole gathered data
....

my problem with above code is, if I set select timeout to a small time, I can't guarantee that all data is received (because some servers are really slow), and if I set timeout to a long time (5-10 seconds), my socket is getting stucked in wait-state for a long time and I can't use the response before that. I tried to make this work by using "BIO_should_read()" or "BIO_pending()" functions, but none of them is giving me what I want. so, Is there a way to determine when exactly there is nothing else to read on the SSL socket?

Upvotes: 1

Views: 1524

Answers (2)

Marian
Marian

Reputation: 7472

As Steffen said, there is no notion of "end-of-data" in SSL and TCP/IP stream. Modern http servers are using a "keep-alive" connection and do not disconnect after answering your request. Instead they are using additional info from where you need to infer the length of the answer. It is either a "Content-lenght" header, or they use "Transfer-Encoding:chunked" followed by chunks of data. Each chunk is preceded by size (as hexadecimal number). A chunk with the size equal to zero means the end of the answer.

For you, the easiest way to get something usable may be to send request as a HTTP 1.0 request (not HTTP 1.1). If you issue a request like this:

GET /myrequestdir/myrequestfile HTTP/1.0

the server will close the connection right after answering your request.

In case you need HTTP 1.1, here are two examples, how the server can answer with a "Hello." message:

HTTP/1.1 200 OK
Content-Length:6

Hello.

or with a chunked connection:

HTTP/1.1 200 OK
Transfer-Encoding: chunked

6
Hello.

0

Upvotes: 5

Steffen Ullrich
Steffen Ullrich

Reputation: 123320

Why do you use BIO_read, not SSL_read? In my opinion it is a bad idea to use the SSL layer with SSL_get_error and then not use the same layer with SSL_read, but instead the underlying BIO layer with BIO_read. You also do not handle the case, where SSL_get_error might return no error or SSL_ERROR_WANT_READ (in case of (re)negotiations).

As for your question, on how do you know when data is finished: SSL is like the underlying TCP a stream based protocol, e.g. contrary to packet based protocols like UDP there is no "end of data packet". So your data are finished, if the SSL connection is finished. If you want to have something else you have to adapt your application protocol to make a packet layer on top of the stream layer.

Or did you mean, that you want to know, when the current SSL frame is done? While SSL should be used as a streaming protocol it sends its data in variable sized frames with a size up to 16k. Each SSL_write will result in a new frame (and maybe more if you send more send 16k at once) but each SSL_read will only read a single frame, e.g. it will return an error (SSL_WANT_*) if it did not get the whole frame or otherwise return the decrypted content of the frame. And that's how you know the frame was completely read.

Upvotes: 1

Related Questions