darwinssl: Fix send glitchiness with data > 32 or so KB
An ambiguity in the SSLWrite() documentation lead to a bad inference in the code where we assumed SSLWrite() returned the amount of bytes written to the socket, when that is not actually true; it returns the amount of data that is buffered for writing to the socket if it returns errSSLWouldBlock. Now darwinssl_send() returns CURLE_AGAIN if data is buffered but not written. Reference URL: http://curl.haxx.se/mail/lib-2013-02/0145.html
This commit is contained in:
parent
bd93062ee5
commit
66aa9bf52d
@ -739,6 +739,7 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn,
|
|||||||
return CURLE_OUT_OF_MEMORY;
|
return CURLE_OUT_OF_MEMORY;
|
||||||
}
|
}
|
||||||
#endif /* defined(__MAC_10_8) || defined(__IPHONE_5_0) */
|
#endif /* defined(__MAC_10_8) || defined(__IPHONE_5_0) */
|
||||||
|
connssl->ssl_write_buffered_length = 0UL; /* reset buffered write length */
|
||||||
|
|
||||||
/* check to see if we've been told to use an explicit SSL/TLS version */
|
/* check to see if we've been told to use an explicit SSL/TLS version */
|
||||||
#if defined(__MAC_10_8) || defined(__IPHONE_5_0)
|
#if defined(__MAC_10_8) || defined(__IPHONE_5_0)
|
||||||
@ -1424,22 +1425,58 @@ static ssize_t darwinssl_send(struct connectdata *conn,
|
|||||||
/*struct SessionHandle *data = conn->data;*/
|
/*struct SessionHandle *data = conn->data;*/
|
||||||
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
|
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
|
||||||
size_t processed = 0UL;
|
size_t processed = 0UL;
|
||||||
OSStatus err = SSLWrite(connssl->ssl_ctx, mem, len, &processed);
|
OSStatus err;
|
||||||
|
|
||||||
if(err != noErr) {
|
/* The SSLWrite() function works a little differently than expected. The
|
||||||
|
fourth argument (processed) is currently documented in Apple's
|
||||||
|
documentation as: "On return, the length, in bytes, of the data actually
|
||||||
|
written."
|
||||||
|
|
||||||
|
Now, one could interpret that as "written to the socket," but actually,
|
||||||
|
it returns the amount of data that was written to a buffer internal to
|
||||||
|
the SSLContextRef instead. So it's possible for SSLWrite() to return
|
||||||
|
errSSLWouldBlock and a number of bytes "written" because those bytes were
|
||||||
|
encrypted and written to a buffer, not to the socket.
|
||||||
|
|
||||||
|
So if this happens, then we need to keep calling SSLWrite() over and
|
||||||
|
over again with no new data until it quits returning errSSLWouldBlock. */
|
||||||
|
|
||||||
|
/* Do we have buffered data to write from the last time we were called? */
|
||||||
|
if(connssl->ssl_write_buffered_length) {
|
||||||
|
/* Write the buffered data: */
|
||||||
|
err = SSLWrite(connssl->ssl_ctx, NULL, 0UL, &processed);
|
||||||
switch (err) {
|
switch (err) {
|
||||||
case errSSLWouldBlock: /* return how much we sent (if anything) */
|
case noErr:
|
||||||
if(processed)
|
/* processed is always going to be 0 because we didn't write to
|
||||||
return (ssize_t)processed;
|
the buffer, so return how much was written to the socket */
|
||||||
|
processed = connssl->ssl_write_buffered_length;
|
||||||
|
connssl->ssl_write_buffered_length = 0UL;
|
||||||
|
break;
|
||||||
|
case errSSLWouldBlock: /* argh, try again */
|
||||||
*curlcode = CURLE_AGAIN;
|
*curlcode = CURLE_AGAIN;
|
||||||
return -1;
|
return -1L;
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
failf(conn->data, "SSLWrite() return error %d", err);
|
failf(conn->data, "SSLWrite() returned error %d", err);
|
||||||
*curlcode = CURLE_SEND_ERROR;
|
*curlcode = CURLE_SEND_ERROR;
|
||||||
return -1;
|
return -1L;
|
||||||
break;
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* We've got new data to write: */
|
||||||
|
err = SSLWrite(connssl->ssl_ctx, mem, len, &processed);
|
||||||
|
if(err != noErr) {
|
||||||
|
switch (err) {
|
||||||
|
case errSSLWouldBlock:
|
||||||
|
/* Data was buffered but not sent, we have to tell the caller
|
||||||
|
to try sending again, and remember how much was buffered */
|
||||||
|
connssl->ssl_write_buffered_length = len;
|
||||||
|
*curlcode = CURLE_AGAIN;
|
||||||
|
return -1L;
|
||||||
|
default:
|
||||||
|
failf(conn->data, "SSLWrite() returned error %d", err);
|
||||||
|
*curlcode = CURLE_SEND_ERROR;
|
||||||
|
return -1L;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (ssize_t)processed;
|
return (ssize_t)processed;
|
||||||
@ -1462,18 +1499,18 @@ static ssize_t darwinssl_recv(struct connectdata *conn,
|
|||||||
if(processed)
|
if(processed)
|
||||||
return (ssize_t)processed;
|
return (ssize_t)processed;
|
||||||
*curlcode = CURLE_AGAIN;
|
*curlcode = CURLE_AGAIN;
|
||||||
return -1;
|
return -1L;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case errSSLClosedGraceful: /* they're done; fail gracefully */
|
case errSSLClosedGraceful: /* they're done; fail gracefully */
|
||||||
*curlcode = CURLE_OK;
|
*curlcode = CURLE_OK;
|
||||||
return -1;
|
return -1L;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
failf(conn->data, "SSLRead() return error %d", err);
|
failf(conn->data, "SSLRead() return error %d", err);
|
||||||
*curlcode = CURLE_RECV_ERROR;
|
*curlcode = CURLE_RECV_ERROR;
|
||||||
return -1;
|
return -1L;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -326,6 +326,7 @@ struct ssl_connect_data {
|
|||||||
curl_socket_t ssl_sockfd;
|
curl_socket_t ssl_sockfd;
|
||||||
ssl_connect_state connecting_state;
|
ssl_connect_state connecting_state;
|
||||||
bool ssl_direction; /* true if writing, false if reading */
|
bool ssl_direction; /* true if writing, false if reading */
|
||||||
|
size_t ssl_write_buffered_length;
|
||||||
#endif /* USE_DARWINSSL */
|
#endif /* USE_DARWINSSL */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user