fixing Mantis 1260

This commit is contained in:
Christian Grothoff
2007-12-16 10:27:29 +00:00
parent 7bf6af9146
commit baeae167a1
7 changed files with 695 additions and 132 deletions
+8
View File
@@ -1,3 +1,11 @@
Sun Dec 16 03:24:13 MST 2007
Implemented handling of chunked (HTTP 1.1) uploads.
Note that the upload callback must be able to
process chunks in the size uploaded by the client,
MHD will not "join" small chunks into a big
contiguous block of memory (even if buffer space
would be available). - CG
Wed Dec 5 21:39:35 MST 2007
Fixed race in multi-threaded server mode.
Fixed handling of POST data when receiving a
+1 -1
View File
@@ -53,7 +53,7 @@ feature.
For http/1.1-compliance:
========================
connection.c:
- support chunked requests from clients (#1260, ARCH, TEST)
- support sending of chunked responses (#1260, TEST)
For POST:
=========
+7
View File
@@ -48,6 +48,7 @@ check_PROGRAMS = \
daemontest_postform \
daemontest_post_loop \
daemontest_put \
daemontest_put_chunked \
daemontest_large_put \
daemontest_get11 \
daemontest_post11 \
@@ -94,6 +95,12 @@ daemontest_put_LDADD = \
$(top_builddir)/src/daemon/libmicrohttpd.la \
@LIBCURL@
daemontest_put_chunked_SOURCES = \
daemontest_put_chunked.c
daemontest_put_chunked_LDADD = \
$(top_builddir)/src/daemon/libmicrohttpd.la \
@LIBCURL@
daemontest_get11_SOURCES = \
daemontest_get.c
daemontest_get11_LDADD = \
+261 -119
View File
@@ -152,7 +152,7 @@ MHD_queue_response (struct MHD_Connection *connection,
if ((connection == NULL) ||
(response == NULL) ||
(connection->response != NULL) ||
(connection->bodyReceived == MHD_NO) || (connection->headersReceived == MHD_NO))
(connection->have_received_body == MHD_NO) || (connection->have_received_headers == MHD_NO))
return MHD_NO;
MHD_increment_response_rc (response);
connection->response = response;
@@ -162,7 +162,7 @@ MHD_queue_response (struct MHD_Connection *connection,
{
/* if this is a "HEAD" request, pretend that we
have already sent the full message body */
connection->messagePos = response->total_size;
connection->response_write_position = response->total_size;
}
return MHD_YES;
}
@@ -179,12 +179,12 @@ MHD_need_100_continue (struct MHD_Connection *connection)
return ((connection->version != NULL) &&
(0 == strcasecmp (connection->version,
MHD_HTTP_VERSION_1_1)) &&
(connection->headersReceived == MHD_YES) &&
(connection->have_received_headers == MHD_YES) &&
(NULL != (expect = MHD_lookup_connection_value (connection,
MHD_HEADER_KIND,
MHD_HTTP_HEADER_EXPECT)))
&& (0 == strcasecmp (expect, "100-continue"))
&& (connection->continuePos < strlen (HTTP_100_CONTINUE)));
&& (connection->continue_message_write_offset < strlen (HTTP_100_CONTINUE)));
}
/**
@@ -221,10 +221,10 @@ ready_response (struct MHD_Connection *connection)
response = connection->response;
ret = response->crc (response->crc_cls,
connection->messagePos,
connection->response_write_position,
response->data,
MIN (response->data_buffer_size,
response->total_size - connection->messagePos));
response->total_size - connection->response_write_position));
if (ret == -1)
{
/* end of message, signal other side by closing! */
@@ -233,11 +233,11 @@ ready_response (struct MHD_Connection *connection)
MHD_DLOG (connection->daemon, "Closing connection (end of response)\n");
#endif
#endif
response->total_size = connection->messagePos;
response->total_size = connection->response_write_position;
connection_close_error (connection);
return MHD_NO;
}
response->data_start = connection->messagePos;
response->data_start = connection->response_write_position;
response->data_size = ret;
if (ret == 0)
{
@@ -278,8 +278,8 @@ MHD_connection_get_fdset (struct MHD_Connection *connection,
if (fd == -1)
return MHD_YES;
if ((connection->read_close == MHD_NO) &&
((connection->headersReceived == MHD_NO) ||
(connection->readLoc < connection->read_buffer_size)))
((connection->have_received_headers == MHD_NO) ||
(connection->read_buffer_offset < connection->read_buffer_size)))
{
FD_SET (fd, read_fd_set);
if (fd > *max_fd)
@@ -288,8 +288,8 @@ MHD_connection_get_fdset (struct MHD_Connection *connection,
else
{
if ((connection->read_close == MHD_NO) &&
((connection->headersReceived == MHD_YES) &&
(connection->readLoc == connection->read_buffer_size)))
((connection->have_received_headers == MHD_YES) &&
(connection->read_buffer_offset == connection->read_buffer_size)))
{
/* try growing the read buffer, just in case */
buf = MHD_pool_reallocate (connection->pool,
@@ -342,8 +342,8 @@ MHD_excessive_data_handler (struct MHD_Connection *connection,
/* die, header far too long to be reasonable */
connection->read_close = MHD_YES;
connection->headersReceived = MHD_YES;
connection->bodyReceived = MHD_YES;
connection->have_received_headers = MHD_YES;
connection->have_received_body = MHD_YES;
#if HAVE_MESSAGES
MHD_DLOG (connection->daemon,
"Received excessively long header, closing connection.\n");
@@ -368,17 +368,17 @@ MHD_get_next_header_line (struct MHD_Connection *connection)
char *rbuf;
size_t pos;
if (connection->readLoc == 0)
if (connection->read_buffer_offset == 0)
return NULL;
pos = 0;
rbuf = connection->read_buffer;
while ((pos < connection->readLoc - 1) &&
while ((pos < connection->read_buffer_offset - 1) &&
(rbuf[pos] != '\r') && (rbuf[pos] != '\n'))
pos++;
if (pos == connection->readLoc - 1)
if (pos == connection->read_buffer_offset - 1)
{
/* not found, consider growing... */
if (connection->readLoc == connection->read_buffer_size)
if (connection->read_buffer_offset == connection->read_buffer_size)
{
rbuf = MHD_pool_reallocate (connection->pool,
connection->read_buffer,
@@ -407,7 +407,7 @@ MHD_get_next_header_line (struct MHD_Connection *connection)
rbuf[pos++] = '\0';
connection->read_buffer += pos;
connection->read_buffer_size -= pos;
connection->readLoc -= pos;
connection->read_buffer_offset -= pos;
return rbuf;
}
@@ -586,14 +586,16 @@ parse_initial_message_line (struct MHD_Connection *connection, char *line)
/**
* This function is designed to parse the input buffer of a given connection.
* This function is designed to parse the input buffer of a given
* connection for HTTP headers -- and in the case of chunked encoding,
* also for HTTP "footers".
*
* Once the header is complete, it should have set the
* headers_received, url and method values and set
* headersReceived to MHD_YES. If no body is expected, it should
* also set "bodyReceived" to MHD_YES. Otherwise, it should
* set "uploadSize" to the expected size of the body. If the
* size of the body is unknown, it should be set to -1.
* have_received_headers to MHD_YES. If no body is expected, it
* should also set "have_received_body" to MHD_YES. Otherwise, it
* should set "remaining_upload_size" to the expected size of the
* body. If the size of the body is unknown, it should be set to -1.
*/
static void
MHD_parse_connection_headers (struct MHD_Connection *connection)
@@ -606,8 +608,9 @@ MHD_parse_connection_headers (struct MHD_Connection *connection)
unsigned long long cval;
struct MHD_Response *response;
if ( (connection->bodyReceived == MHD_YES) ||
(connection->headersReceived == MHD_YES) )
if ( ( (connection->have_received_body == MHD_YES) &&
(connection->have_chunked_upload == MHD_NO) ) ||
(connection->have_received_headers == MHD_YES) )
abort ();
colon = NULL; /* make gcc happy */
last = NULL;
@@ -656,7 +659,7 @@ MHD_parse_connection_headers (struct MHD_Connection *connection)
if (strlen (line) == 0)
{
/* end of header */
connection->headersReceived = MHD_YES;
connection->have_received_headers = MHD_YES;
clen = MHD_lookup_connection_value (connection,
MHD_HEADER_KIND,
MHD_HTTP_HEADER_CONTENT_LENGTH);
@@ -671,8 +674,8 @@ MHD_parse_connection_headers (struct MHD_Connection *connection)
#endif
goto DIE;
}
connection->uploadSize = cval;
connection->bodyReceived = cval == 0 ? MHD_YES : MHD_NO;
connection->remaining_upload_size = cval;
connection->have_received_body = cval == 0 ? MHD_YES : MHD_NO;
}
else
{
@@ -681,14 +684,29 @@ MHD_parse_connection_headers (struct MHD_Connection *connection)
MHD_HTTP_HEADER_TRANSFER_ENCODING))
{
/* this request does not have a body */
connection->uploadSize = 0;
connection->bodyReceived = MHD_YES;
connection->remaining_upload_size = 0;
connection->have_received_body = MHD_YES;
}
else
{
connection->uploadSize = -1; /* unknown size */
connection->bodyReceived = MHD_NO;
}
if (connection->have_chunked_upload == MHD_NO)
{
connection->remaining_upload_size = -1; /* unknown size */
if (0 == strcasecmp(MHD_lookup_connection_value(connection,
MHD_HEADER_KIND,
MHD_HTTP_HEADER_TRANSFER_ENCODING),
"chunked"))
connection->have_chunked_upload = MHD_YES;
}
else
{
/* we were actually processing the footers at the
END of a chunked encoding; give connection
handler an extra chance... */
connection->have_chunked_upload = MHD_NO; /* no more! */
MHD_call_connection_handler(connection);
}
}
}
if ((0 != (MHD_USE_PEDANTIC_CHECKS & connection->daemon->options))
&& (NULL != connection->version)
@@ -698,7 +716,7 @@ MHD_parse_connection_headers (struct MHD_Connection *connection)
MHD_HTTP_HEADER_HOST)))
{
/* die, http 1.1 request without host and we are pedantic */
connection->bodyReceived = MHD_YES;
connection->have_received_body = MHD_YES;
connection->read_close = MHD_YES;
#if HAVE_MESSAGES
MHD_DLOG (connection->daemon,
@@ -780,54 +798,177 @@ MHD_call_connection_handler (struct MHD_Connection *connection)
{
struct MHD_Access_Handler *ah;
unsigned int processed;
unsigned int available;
unsigned int used;
int instant_retry;
unsigned int i;
if (connection->response != NULL)
return; /* already queued a response */
if (connection->headersReceived == MHD_NO)
if (connection->have_received_headers == MHD_NO)
abort (); /* bad timing... */
ah = MHD_find_access_handler (connection);
processed = connection->readLoc;
if (MHD_NO == ah->dh (ah->dh_cls,
connection,
connection->url,
connection->method,
connection->version,
connection->read_buffer, &processed,
&connection->client_context))
do
{
/* serious internal error, close connection */
instant_retry = MHD_NO;
available = connection->read_buffer_offset;
if (connection->have_chunked_upload == MHD_YES)
{
if ( (connection->current_chunk_offset == connection->current_chunk_size) &&
(connection->current_chunk_offset != 0) &&
(available >= 2) )
{
/* skip new line at the *end* of a chunk */
i = 0;
if ( (connection->read_buffer[i] == '\r') ||
(connection->read_buffer[i] == '\n') )
i++; /* skip 1st part of line feed */
if ( (connection->read_buffer[i] == '\r') ||
(connection->read_buffer[i] == '\n') )
i++; /* skip 2nd part of line feed */
if (i == 0)
{
/* malformed encoding */
#if HAVE_MESSAGES
MHD_DLOG (connection->daemon,
"Internal application error, closing connection.\n");
MHD_DLOG (connection->daemon,
"Received malformed HTTP request (bad chunked encoding), closing connection.\n");
#endif
connection_close_error (connection);
return;
}
/* dh left "processed" bytes in buffer for next time... */
memmove (connection->read_buffer,
&connection->read_buffer[connection->readLoc - processed],
processed);
if (connection->uploadSize != -1)
connection->uploadSize -= (connection->readLoc - processed);
connection->readLoc = processed;
if ((connection->uploadSize == 0) ||
((connection->readLoc == 0) &&
(connection->uploadSize == -1) && (connection->socket_fd == -1)))
{
connection->bodyReceived = MHD_YES;
if (connection->read_buffer != NULL)
MHD_pool_reallocate (connection->pool,
connection->read_buffer,
(connection->read_buffer ==
NULL) ? 0 : connection->read_buffer_size + 1,
0);
connection->readLoc = 0;
connection->read_buffer_size = 0;
connection->read_buffer = NULL;
}
connection_close_error (connection);
return;
}
connection->read_buffer_offset -= i;
available -= i;
memmove(connection->read_buffer,
&connection->read_buffer[i],
available);
connection->current_chunk_offset = 0;
connection->current_chunk_size = 0;
}
if (connection->current_chunk_offset < connection->current_chunk_size)
{
/* we are in the middle of a chunk, give
as much as possible to the client (without
crossing chunk boundaries) */
processed = connection->current_chunk_size - connection->current_chunk_offset;
if (processed > available)
processed = available;
available -= processed;
if (available > 0)
instant_retry = MHD_YES;
}
else
{
/* we need to read chunk boundaries */
i = 0;
while (i < available)
{
if ( (connection->read_buffer[i] == '\r') ||
(connection->read_buffer[i] == '\n') )
break;
i++;
if (i >= 6)
break;
}
if (i >= available)
return; /* need more data... */
/* The following if-statement is a bit crazy -- we
use the second clause only for the side-effect,
0-terminating the buffer for the following sscanf
attempts; yes, there should be only a single
"="-sign (assignment!) in the read_buffer[i]-line. */
if ( (i >= 6) ||
((connection->read_buffer[i] = '\0')) ||
( (1 != sscanf(connection->read_buffer,
"%X",
&connection->current_chunk_size)) &&
(1 != sscanf(connection->read_buffer,
"%x",
&connection->current_chunk_size)) ) )
{
/* malformed encoding */
#if HAVE_MESSAGES
MHD_DLOG (connection->daemon,
"Received malformed HTTP request (bad chunked encoding), closing connection.\n");
#endif
connection_close_error (connection);
return;
}
i++;
if ( (connection->read_buffer[i] == '\r') ||
(connection->read_buffer[i] == '\n') )
i++; /* skip 2nd part of line feed */
memmove(connection->read_buffer,
&connection->read_buffer[i],
available - i);
connection->read_buffer_offset -= i;
connection->current_chunk_offset = 0;
instant_retry = MHD_YES;
if (connection->current_chunk_size == 0)
{
/* we're back to reading HEADERS (footers!) */
connection->have_received_body = MHD_YES;
connection->remaining_upload_size = 0;
connection->have_received_headers = MHD_NO;
MHD_parse_connection_headers(connection);
return;
}
continue;
}
}
else
{
/* no chunked encoding, give all to the client */
processed = available;
available = 0;
}
used = processed;
ah = MHD_find_access_handler (connection);
if (MHD_NO == ah->dh (ah->dh_cls,
connection,
connection->url,
connection->method,
connection->version,
connection->read_buffer, &processed,
&connection->client_context))
{
/* serious internal error, close connection */
#if HAVE_MESSAGES
MHD_DLOG (connection->daemon,
"Internal application error, closing connection.\n");
#endif
connection_close_error (connection);
return;
}
if (processed != 0)
instant_retry = MHD_NO; /* client did not process everything */
used -= processed;
if (connection->have_chunked_upload == MHD_YES)
connection->current_chunk_offset += used;
/* dh left "processed" bytes in buffer for next time... */
if (used > 0)
memmove (connection->read_buffer,
&connection->read_buffer[used],
processed + available);
if (connection->remaining_upload_size != -1)
connection->remaining_upload_size -= used;
connection->read_buffer_offset = processed + available;
if ((connection->remaining_upload_size == 0) ||
((connection->read_buffer_offset == 0) &&
(connection->remaining_upload_size == -1) && (connection->socket_fd == -1)))
{
connection->have_received_body = MHD_YES;
if (connection->read_buffer != NULL)
MHD_pool_reallocate (connection->pool,
connection->read_buffer,
(connection->read_buffer ==
NULL) ? 0 : connection->read_buffer_size + 1,
0);
connection->read_buffer_offset = 0;
connection->read_buffer_size = 0;
connection->read_buffer = NULL;
}
} while (instant_retry == MHD_YES);
}
/**
* This function handles a particular connection when it has been
* determined that there is data to be read off a socket. All implementations
@@ -850,8 +991,8 @@ MHD_connection_handle_read (struct MHD_Connection *connection)
connection_close_error (connection);
return MHD_NO;
}
if ((connection->readLoc >= connection->read_buffer_size) &&
(connection->headersReceived == MHD_NO))
if ((connection->read_buffer_offset >= connection->read_buffer_size) &&
(connection->have_received_headers == MHD_NO))
{
/* need to grow read buffer */
tmp = MHD_pool_reallocate (connection->pool,
@@ -873,7 +1014,7 @@ MHD_connection_handle_read (struct MHD_Connection *connection)
connection->read_buffer_size =
connection->read_buffer_size * 2 + MHD_BUF_INC_SIZE;
}
if (connection->readLoc >= connection->read_buffer_size)
if (connection->read_buffer_offset >= connection->read_buffer_size)
{
#if HAVE_MESSAGES
MHD_DLOG (connection->daemon, "Unexpected call to %s.\n", __FUNCTION__);
@@ -881,8 +1022,8 @@ MHD_connection_handle_read (struct MHD_Connection *connection)
return MHD_NO;
}
bytes_read = RECV (connection->socket_fd,
&connection->read_buffer[connection->readLoc],
connection->read_buffer_size - connection->readLoc,
&connection->read_buffer[connection->read_buffer_offset],
connection->read_buffer_size - connection->read_buffer_offset,
MSG_NOSIGNAL);
if (bytes_read < 0)
{
@@ -899,7 +1040,7 @@ MHD_connection_handle_read (struct MHD_Connection *connection)
{
/* other side closed connection */
connection->read_close = MHD_YES;
if ((connection->headersReceived == MHD_YES) && (connection->readLoc > 0))
if ((connection->have_received_headers == MHD_YES) && (connection->read_buffer_offset > 0))
MHD_call_connection_handler (connection);
#if DEBUG_CLOSE
#if HAVE_MESSAGES
@@ -908,18 +1049,18 @@ MHD_connection_handle_read (struct MHD_Connection *connection)
#endif
#endif
shutdown (connection->socket_fd, SHUT_RD);
if ( (connection->headersReceived == MHD_NO) ||
(connection->bodyReceived == MHD_NO) ) {
if ( (connection->have_received_headers == MHD_NO) ||
(connection->have_received_body == MHD_NO) ) {
/* no request => no response! */
CLOSE (connection->socket_fd);
connection->socket_fd = -1;
}
return MHD_YES;
}
connection->readLoc += bytes_read;
if (connection->headersReceived == MHD_NO)
connection->read_buffer_offset += bytes_read;
if (connection->have_received_headers == MHD_NO)
MHD_parse_connection_headers (connection);
if ((connection->headersReceived == MHD_YES) && (connection->method != NULL))
if ((connection->have_received_headers == MHD_YES) && (connection->method != NULL))
MHD_call_connection_handler (connection);
return MHD_YES;
}
@@ -1035,8 +1176,8 @@ MHD_build_header_response (struct MHD_Connection *connection)
if (off != size)
abort ();
connection->write_buffer = data;
connection->writeLoc = size;
connection->writePos = 0;
connection->write_buffer_append_offset = size;
connection->write_buffer_send_offset = 0;
connection->write_buffer_size = size + 1;
return MHD_YES;
}
@@ -1057,8 +1198,8 @@ MHD_connection_handle_write (struct MHD_Connection *connection)
if (MHD_need_100_continue (connection))
{
ret = SEND (connection->socket_fd,
&HTTP_100_CONTINUE[connection->continuePos],
strlen (HTTP_100_CONTINUE) - connection->continuePos,
&HTTP_100_CONTINUE[connection->continue_message_write_offset],
strlen (HTTP_100_CONTINUE) - connection->continue_message_write_offset,
MSG_NOSIGNAL);
if (ret < 0)
{
@@ -1074,9 +1215,9 @@ MHD_connection_handle_write (struct MHD_Connection *connection)
#if DEBUG_SEND_DATA
fprintf (stderr,
"Sent 100 continue response: `%.*s'\n",
ret, &HTTP_100_CONTINUE[connection->continuePos]);
ret, &HTTP_100_CONTINUE[connection->continue_message_write_offset]);
#endif
connection->continuePos += ret;
connection->continue_message_write_offset += ret;
return MHD_YES;
}
response = connection->response;
@@ -1087,7 +1228,7 @@ MHD_connection_handle_write (struct MHD_Connection *connection)
#endif
return MHD_NO;
}
if (MHD_NO == connection->headersSent)
if (MHD_NO == connection->have_sent_headers)
{
if ((connection->write_buffer == NULL) &&
(MHD_NO == MHD_build_header_response (connection)))
@@ -1101,8 +1242,8 @@ MHD_connection_handle_write (struct MHD_Connection *connection)
return MHD_NO;
}
ret = SEND (connection->socket_fd,
&connection->write_buffer[connection->writePos],
connection->writeLoc - connection->writePos,
&connection->write_buffer[connection->write_buffer_send_offset],
connection->write_buffer_append_offset - connection->write_buffer_send_offset,
MSG_NOSIGNAL);
if (ret < 0)
{
@@ -1118,14 +1259,14 @@ MHD_connection_handle_write (struct MHD_Connection *connection)
#if DEBUG_SEND_DATA
fprintf (stderr,
"Sent HEADER response: `%.*s'\n",
ret, &connection->write_buffer[connection->writePos]);
ret, &connection->write_buffer[connection->write_buffer_send_offset]);
#endif
connection->writePos += ret;
if (connection->writeLoc == connection->writePos)
connection->write_buffer_send_offset += ret;
if (connection->write_buffer_append_offset == connection->write_buffer_send_offset)
{
connection->writeLoc = 0;
connection->writePos = 0;
connection->headersSent = MHD_YES;
connection->write_buffer_append_offset = 0;
connection->write_buffer_send_offset = 0;
connection->have_sent_headers = MHD_YES;
MHD_pool_reallocate (connection->pool,
connection->write_buffer,
connection->write_buffer_size, 0);
@@ -1134,24 +1275,24 @@ MHD_connection_handle_write (struct MHD_Connection *connection)
}
return MHD_YES;
}
if (response->total_size < connection->messagePos)
if (response->total_size < connection->response_write_position)
abort (); /* internal error */
if (response->crc != NULL)
pthread_mutex_lock (&response->mutex);
/* prepare send buffer */
if ((response->crc != NULL) &&
((response->data_start > connection->messagePos) ||
((response->data_start > connection->response_write_position) ||
(response->data_start + response->data_size <=
connection->messagePos)) && (MHD_YES != ready_response (connection)))
connection->response_write_position)) && (MHD_YES != ready_response (connection)))
{
pthread_mutex_unlock (&response->mutex);
return MHD_YES;
}
/* transmit */
ret = SEND (connection->socket_fd,
&response->data[connection->messagePos - response->data_start],
response->data_size - (connection->messagePos -
&response->data[connection->response_write_position - response->data_start],
response->data_size - (connection->response_write_position -
response->data_start),
MSG_NOSIGNAL);
if (response->crc != NULL)
@@ -1171,15 +1312,15 @@ MHD_connection_handle_write (struct MHD_Connection *connection)
fprintf (stderr,
"Sent DATA response: `%.*s'\n",
ret,
&response->data[connection->messagePos - response->data_start]);
&response->data[connection->response_write_position - response->data_start]);
#endif
connection->messagePos += ret;
if (connection->messagePos > response->total_size)
connection->response_write_position += ret;
if (connection->response_write_position > response->total_size)
abort (); /* internal error */
if (connection->messagePos == response->total_size)
if (connection->response_write_position == response->total_size)
{
if ((connection->bodyReceived == MHD_NO) ||
(connection->headersReceived == MHD_NO))
if ((connection->have_received_body == MHD_NO) ||
(connection->have_received_headers == MHD_NO))
abort (); /* internal error */
MHD_destroy_response (response);
if (connection->daemon->notify_completed != NULL)
@@ -1192,14 +1333,15 @@ MHD_connection_handle_write (struct MHD_Connection *connection)
MHD_HEADER_KIND,
MHD_HTTP_HEADER_CONNECTION);
connection->client_context = NULL;
connection->continuePos = 0;
connection->continue_message_write_offset = 0;
connection->responseCode = 0;
connection->response = NULL;
connection->headers_received = NULL;
connection->headersReceived = MHD_NO;
connection->headersSent = MHD_NO;
connection->bodyReceived = MHD_NO;
connection->messagePos = 0;
connection->have_received_headers = MHD_NO;
connection->have_sent_headers = MHD_NO;
connection->have_received_body = MHD_NO;
connection->response_write_position = 0;
connection->have_chunked_upload = MHD_NO;
connection->method = NULL;
connection->url = NULL;
if ((end != NULL) && (0 == strcasecmp (end, "close")))
@@ -1230,10 +1372,10 @@ MHD_connection_handle_write (struct MHD_Connection *connection)
connection->read_buffer = NULL;
connection->write_buffer = NULL;
connection->read_buffer_size = 0;
connection->readLoc = 0;
connection->read_buffer_offset = 0;
connection->write_buffer_size = 0;
connection->writePos = 0;
connection->writeLoc = 0;
connection->write_buffer_send_offset = 0;
connection->write_buffer_append_offset = 0;
MHD_pool_destroy (connection->pool);
connection->pool = NULL;
}
+2 -2
View File
@@ -227,7 +227,7 @@ MHD_handle_connection (void *data)
(FD_ISSET (con->socket_fd, &ws)) &&
(MHD_YES != MHD_connection_handle_write (con))))
break;
if ((con->headersReceived == MHD_YES) && (con->response == NULL))
if ((con->have_received_headers == MHD_YES) && (con->response == NULL))
MHD_call_connection_handler (con);
if ((con->socket_fd != -1) &&
((FD_ISSET (con->socket_fd, &rs)) ||
@@ -438,7 +438,7 @@ MHD_cleanup_connections (struct MHD_Daemon *daemon)
}
if ( (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
((pos->headersReceived == MHD_YES) && (pos->response == NULL)) )
((pos->have_received_headers == MHD_YES) && (pos->response == NULL)) )
MHD_call_connection_handler (pos);
prev = pos;
+382
View File
@@ -0,0 +1,382 @@
/*
This file is part of libmicrohttpd
(C) 2007 Christian Grothoff
libmicrohttpd is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
by the Free Software Foundation; either version 2, or (at your
option) any later version.
libmicrohttpd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with libmicrohttpd; see the file COPYING. If not, write to the
Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
*/
/**
* @file daemontest_put_chunked.c
* @brief Testcase for libmicrohttpd PUT operations with chunked encoding
* for the upload data
* @author Christian Grothoff
*/
#include "config.h"
#include <curl/curl.h>
#include <microhttpd.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#ifndef WINDOWS
#include <unistd.h>
#endif
struct CBC
{
char *buf;
size_t pos;
size_t size;
};
static size_t
putBuffer (void *stream, size_t size, size_t nmemb, void *ptr)
{
unsigned int *pos = ptr;
unsigned int wrt;
wrt = size * nmemb;
if (wrt > 8 - (*pos))
wrt = 8 - (*pos);
if (wrt > 4)
wrt = 4; /* only send half at first => force multiple chunks! */
memcpy (stream, &("Hello123"[*pos]), wrt);
(*pos) += wrt;
return wrt;
}
static size_t
copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
{
struct CBC *cbc = ctx;
if (cbc->pos + size * nmemb > cbc->size)
return 0; /* overflow */
memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
cbc->pos += size * nmemb;
return size * nmemb;
}
static int
ahc_echo (void *cls,
struct MHD_Connection *connection,
const char *url,
const char *method,
const char *version,
const char *upload_data, unsigned int *upload_data_size,
void **unused)
{
int *done = cls;
struct MHD_Response *response;
int ret;
int have;
if (0 != strcmp ("PUT", method))
return MHD_NO; /* unexpected method */
if ((*done) < 8)
{
have = *upload_data_size;
if (have + *done > 8)
{
printf ("Invalid upload data `%8s'!\n", upload_data);
return MHD_NO;
}
if (0 == memcmp(upload_data,
&"Hello123"[*done],
have))
{
*done += have;
*upload_data_size = 0;
}
else
{
printf ("Invalid upload data `%8s'!\n", upload_data);
return MHD_NO;
}
return MHD_YES;
}
response = MHD_create_response_from_data (strlen (url),
(void *) url, MHD_NO, MHD_YES);
ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
MHD_destroy_response (response);
return ret;
}
static int
testInternalPut ()
{
struct MHD_Daemon *d;
CURL *c;
char buf[2048];
struct CBC cbc;
unsigned int pos = 0;
int done_flag = 0;
CURLcode errornum;
cbc.buf = buf;
cbc.size = 2048;
cbc.pos = 0;
d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
11080,
NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END);
if (d == NULL)
return 1;
c = curl_easy_init ();
curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11080/hello_world");
curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
curl_easy_setopt (c, CURLOPT_READDATA, &pos);
curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
/*
// by not giving the file size, we force chunking!
curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L);
*/
curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
curl_easy_setopt (c, CURLOPT_TIMEOUT, 15L);
curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 15L);
// NOTE: use of CONNECTTIMEOUT without also
// setting NOSIGNAL results in really weird
// crashes on my system!
curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
if (CURLE_OK != (errornum = curl_easy_perform (c)))
{
fprintf (stderr,
"curl_easy_perform failed: `%s'\n",
curl_easy_strerror (errornum));
curl_easy_cleanup (c);
MHD_stop_daemon (d);
return 2;
}
curl_easy_cleanup (c);
MHD_stop_daemon (d);
if (cbc.pos != strlen ("/hello_world"))
return 4;
if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
return 8;
return 0;
}
static int
testMultithreadedPut ()
{
struct MHD_Daemon *d;
CURL *c;
char buf[2048];
struct CBC cbc;
unsigned int pos = 0;
int done_flag = 0;
CURLcode errornum;
cbc.buf = buf;
cbc.size = 2048;
cbc.pos = 0;
d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG,
11081,
NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END);
if (d == NULL)
return 16;
c = curl_easy_init ();
curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world");
curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
curl_easy_setopt (c, CURLOPT_READDATA, &pos);
curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
/*
// by not giving the file size, we force chunking!
curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L);
*/
curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
curl_easy_setopt (c, CURLOPT_TIMEOUT, 15L);
curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 15L);
// NOTE: use of CONNECTTIMEOUT without also
// setting NOSIGNAL results in really weird
// crashes on my system!
curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
if (CURLE_OK != (errornum = curl_easy_perform (c)))
{
fprintf (stderr,
"curl_easy_perform failed: `%s'\n",
curl_easy_strerror (errornum));
curl_easy_cleanup (c);
MHD_stop_daemon (d);
return 32;
}
curl_easy_cleanup (c);
MHD_stop_daemon (d);
if (cbc.pos != strlen ("/hello_world"))
return 64;
if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
return 128;
return 0;
}
static int
testExternalPut ()
{
struct MHD_Daemon *d;
CURL *c;
char buf[2048];
struct CBC cbc;
CURLM *multi;
CURLMcode mret;
fd_set rs;
fd_set ws;
fd_set es;
int max;
int running;
struct CURLMsg *msg;
time_t start;
struct timeval tv;
unsigned int pos = 0;
int done_flag = 0;
multi = NULL;
cbc.buf = buf;
cbc.size = 2048;
cbc.pos = 0;
d = MHD_start_daemon (MHD_USE_DEBUG,
11082,
NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END);
if (d == NULL)
return 256;
c = curl_easy_init ();
curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11082/hello_world");
curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
curl_easy_setopt (c, CURLOPT_READDATA, &pos);
curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
/*
// by not giving the file size, we force chunking!
curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L);
*/
curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
curl_easy_setopt (c, CURLOPT_TIMEOUT, 15L);
curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 15L);
// NOTE: use of CONNECTTIMEOUT without also
// setting NOSIGNAL results in really weird
// crashes on my system!
curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
multi = curl_multi_init ();
if (multi == NULL)
{
curl_easy_cleanup (c);
MHD_stop_daemon (d);
return 512;
}
mret = curl_multi_add_handle (multi, c);
if (mret != CURLM_OK)
{
curl_multi_cleanup (multi);
curl_easy_cleanup (c);
MHD_stop_daemon (d);
return 1024;
}
start = time (NULL);
while ((time (NULL) - start < 5) && (multi != NULL))
{
max = 0;
FD_ZERO (&rs);
FD_ZERO (&ws);
FD_ZERO (&es);
curl_multi_perform (multi, &running);
mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
if (mret != CURLM_OK)
{
curl_multi_remove_handle (multi, c);
curl_multi_cleanup (multi);
curl_easy_cleanup (c);
MHD_stop_daemon (d);
return 2048;
}
if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
{
curl_multi_remove_handle (multi, c);
curl_multi_cleanup (multi);
curl_easy_cleanup (c);
MHD_stop_daemon (d);
return 4096;
}
tv.tv_sec = 0;
tv.tv_usec = 1000;
select (max + 1, &rs, &ws, &es, &tv);
curl_multi_perform (multi, &running);
if (running == 0)
{
msg = curl_multi_info_read (multi, &running);
if (msg == NULL)
break;
if (msg->msg == CURLMSG_DONE)
{
if (msg->data.result != CURLE_OK)
printf ("%s failed at %s:%d: `%s'\n",
"curl_multi_perform",
__FILE__,
__LINE__, curl_easy_strerror (msg->data.result));
curl_multi_remove_handle (multi, c);
curl_multi_cleanup (multi);
curl_easy_cleanup (c);
c = NULL;
multi = NULL;
}
}
MHD_run (d);
}
if (multi != NULL)
{
curl_multi_remove_handle (multi, c);
curl_easy_cleanup (c);
curl_multi_cleanup (multi);
}
MHD_stop_daemon (d);
if (cbc.pos != strlen ("/hello_world"))
return 8192;
if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
return 16384;
return 0;
}
int
main (int argc, char *const *argv)
{
unsigned int errorCount = 0;
if (0 != curl_global_init (CURL_GLOBAL_WIN32))
return 2;
errorCount += testInternalPut ();
if (0)
{
errorCount += testMultithreadedPut ();
errorCount += testExternalPut ();
}
if (errorCount != 0)
fprintf (stderr, "Error (code: %u)\n", errorCount);
curl_global_cleanup ();
return errorCount != 0; /* 0 == pass */
}
+34 -10
View File
@@ -273,7 +273,7 @@ struct MHD_Connection
* Position where we currently append data in
* read_buffer (last valid position).
*/
size_t readLoc;
size_t read_buffer_offset;
/**
* Size of write_buffer (in bytes).
@@ -283,32 +283,33 @@ struct MHD_Connection
/**
* Offset where we are with sending from write_buffer.
*/
size_t writePos;
size_t write_buffer_send_offset;
/**
* Last valid location in write_buffer.
* Last valid location in write_buffer (where do we
* append and up to where is it safe to send?)
*/
size_t writeLoc;
size_t write_buffer_append_offset;
/**
* Current write position in the actual response
* (excluding headers, content only; should be 0
* while sending headers).
*/
size_t messagePos;
size_t response_write_position;
/**
* Remaining (!) number of bytes in the upload.
* Set to -1 for unknown (connection will close
* to indicate end of upload).
*/
size_t uploadSize;
size_t remaining_upload_size;
/**
* Position in the 100 CONTINUE message that
* we need to send when receiving http 1.1 requests.
*/
size_t continuePos;
size_t continue_message_write_offset;
/**
* Length of the foreign address.
@@ -343,18 +344,18 @@ struct MHD_Connection
* possible that the NEXT request is already
* (partially) waiting in the read buffer.
*/
int headersReceived;
int have_received_headers;
/**
* Have we finished receiving the data from a
* potential file-upload?
*/
int bodyReceived;
int have_received_body;
/**
* Have we finished sending all of the headers yet?
*/
int headersSent;
int have_sent_headers;
/**
* HTTP response code. Only valid if response object
@@ -371,6 +372,29 @@ struct MHD_Connection
*/
int response_unready;
/**
* Are we receiving with chunked encoding? This will be set to
* MHD_YES after we parse the headers and are processing the body
* with chunks. After we are done with the body and we are
* processing the footers; once the footers are also done, this will
* be set to MHD_NO again (before the final call to the handler).
*/
int have_chunked_upload;
/**
* If we are receiving with chunked encoding, where are we right
* now? Set to 0 if we are waiting to receive the chunk size;
* otherwise, this is the size of the current chunk. A value of
* zero is also used when we're at the end of the chunks.
*/
unsigned int current_chunk_size;
/**
* If we are receiving with chunked encoding, where are we currently
* with respect to the current chunk (at what offset / position)?
*/
unsigned int current_chunk_offset;
};