commit dc6f6a872e5f3c18a230f44dfc6d31ab50a397a7
parent 42c501a9c40dca7df9752fa4d4ac4a7a1ce5e4a9
Author: Christian Grothoff <christian@grothoff.org>
Date: Mon, 22 Oct 2012 11:07:36 +0000
Trying to fix issue reported by Matthieu:
>>
I face an issue while handling big https posts with libmicrohttpd 0.9.22.
I'm using an "external" select based on MHD_get_fdset and MHD_run.
Let's assume 16K of encrypted data arrive on the socket.
When the data arrives, my select correctly returns and MHD_run is called.
Do_read will first call gnu_tls to decrypt the data.
Apparently gnu_tls reads the full 16K so starting here, from the point of
view of socket/select, there is no more data to read.
Do_read however presents a buffer of only 9K to gnu_tls to retrieve the data
(9K as the result of a try_grow_read_buffer call).
So gnu_tls returns 9K of clear data and keeps the rest 7K in its own buffer
I think.
Do_read handles the 9K and then MHD_run returns;
Since from the socket point of view no data is pending read, select will
then wait until it finally timeout.
Only after this timeout, MHD_run is run again and the remaining 7K are
correctly processed.
I found two dirty workarounds for this issue :
1/ increase the MDH_BUF_INC_SIZE to an arbitrarily large value so it will
always be greater than gnu_tls own buffers
2/ run multiple MHD_run after each select (let's say 10 times)
What do you think of this issue ?
What could be a cleaner way of handling this ?
<<
Approach: detect that we *might* be in this situation because TLS fills
the entire buffer we give to gnuTLS. If so, set timeout to 0 and read
again.
Diffstat:
3 files changed, 46 insertions(+), 4 deletions(-)
diff --git a/ChangeLog b/ChangeLog
@@ -1,3 +1,8 @@
+Mon Oct 22 13:05:01 CEST 2012
+ Immediately do a second read if we get a full buffer from
+ TLS as there might be more data in the TLS buffers even if
+ there is no activity on the socket. -CG
+
Tue Oct 16 01:33:55 CEST 2012
Consistently use "#ifdef" and "#ifndef" WINDOWS, and not
sometimes "#if". -CG
diff --git a/src/daemon/daemon.c b/src/daemon/daemon.c
@@ -386,6 +386,7 @@ recv_tls_adapter (struct MHD_Connection *connection, void *other, size_t i)
{
int res;
+ connection->tls_read_ready = MHD_NO;
res = gnutls_record_recv (connection->tls_session, other, i);
if ( (GNUTLS_E_AGAIN == res) ||
(GNUTLS_E_INTERRUPTED == res) )
@@ -401,6 +402,8 @@ recv_tls_adapter (struct MHD_Connection *connection, void *other, size_t i)
errno = EPIPE;
return res;
}
+ if (res == i)
+ connection->tls_read_ready = MHD_YES;
return res;
}
@@ -606,7 +609,15 @@ MHD_handle_connection (void *data)
tv.tv_usec = 0;
tvp = &tv;
}
-
+#if HTTPS_SUPPORT
+ if (MHD_YES == con->tls_read_ready)
+ {
+ /* do not block (more data may be inside of TLS buffers waiting for us) */
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ tvp = &tv;
+ }
+#endif
if (0 == (con->daemon->options & MHD_USE_POLL))
{
/* use select */
@@ -629,7 +640,11 @@ MHD_handle_connection (void *data)
break;
}
/* call appropriate connection handler if necessary */
- if (FD_ISSET (con->socket_fd, &rs))
+ if ( (FD_ISSET (con->socket_fd, &rs))
+#if HTTPS_SUPPORT
+ || (MHD_YES == con->tls_read_ready)
+#endif
+ )
con->read_handler (con);
if (FD_ISSET (con->socket_fd, &ws))
con->write_handler (con);
@@ -660,7 +675,11 @@ MHD_handle_connection (void *data)
#endif
break;
}
- if (0 != (p[0].revents & POLLIN))
+ if ( (0 != (p[0].revents & POLLIN))
+#if HTTPS_SUPPORT
+ || (MHD_YES == con->tls_read_ready)
+#endif
+ )
con->read_handler (con);
if (0 != (p[0].revents & POLLOUT))
con->write_handler (con);
@@ -1271,6 +1290,14 @@ MHD_get_timeout (struct MHD_Daemon *daemon,
have_timeout = MHD_NO;
for (pos = daemon->connections_head; NULL != pos; pos = pos->next)
{
+#if HTTPS_SUPPORT
+ if (MHD_YES == pos->tls_read_ready)
+ {
+ earliest_deadline = 0;
+ have_timeout = MHD_YES;
+ break;
+ }
+#endif
if (0 != pos->connection_timeout)
{
if ( (! have_timeout) ||
@@ -1397,7 +1424,11 @@ MHD_select (struct MHD_Daemon *daemon,
ds = pos->socket_fd;
if (ds != -1)
{
- if (FD_ISSET (ds, &rs))
+ if ( (FD_ISSET (ds, &rs))
+#if HTTPS_SUPPORT
+ || (MHD_YES == pos->tls_read_ready)
+#endif
+ )
pos->read_handler (pos);
if (FD_ISSET (ds, &ws))
pos->write_handler (pos);
diff --git a/src/daemon/internal.h b/src/daemon/internal.h
@@ -750,6 +750,12 @@ struct MHD_Connection
*/
int cipher;
+ /**
+ * Could it be that we are ready to read due to TLS buffers
+ * even though the socket is not?
+ */
+ int tls_read_ready;
+
#endif
};