diff --git a/Makefile b/Makefile index 31ef66ec..fa75c0a0 100644 --- a/Makefile +++ b/Makefile @@ -54,6 +54,7 @@ LIB_SOURCES = src/libhttp.c \ src/httplib_consume_socket.c \ src/httplib_delete_file.c \ src/httplib_download.c \ + src/httplib_forward_body_data.c \ src/httplib_free_context.c \ src/httplib_get_first_ssl_listener_index.c \ src/httplib_get_rel_url_at_current_server.c \ diff --git a/src/httplib_forward_body_data.c b/src/httplib_forward_body_data.c new file mode 100644 index 00000000..415d2c4d --- /dev/null +++ b/src/httplib_forward_body_data.c @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2016 Lammert Bies + * Copyright (c) 2013-2016 the Civetweb developers + * Copyright (c) 2004-2013 Sergey Lyubka + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + + +#include "libhttp-private.h" + + + +/* + * int XX_httplib_forward_body_data( struct mg_connection *conn, FILE *fp, SOCKET sock, SSL *ssl ); + * + * The function XX_httplib_forward_body_data() forwards body data to the + * client. + */ + +#if !defined(NO_CGI) || !defined(NO_FILES) +int XX_httplib_forward_body_data( struct mg_connection *conn, FILE *fp, SOCKET sock, SSL *ssl ) { + + const char *expect; + const char *body; + char buf[MG_BUF_LEN]; + int to_read; + int nread; + int success = 0; + int64_t buffered_len; + double timeout = -1.0; + + if (!conn) { return 0; } + if (conn->ctx->config[REQUEST_TIMEOUT]) { + timeout = atoi(conn->ctx->config[REQUEST_TIMEOUT]) / 1000.0; + } + + expect = mg_get_header(conn, "Expect"); + /* assert(fp != NULL); */ + if (!fp) { + XX_httplib_send_http_error(conn, 500, "%s", "Error: NULL File"); + return 0; + } + + if (conn->content_len == -1 && !conn->is_chunked) { + /* Content length is not specified by the client. */ + XX_httplib_send_http_error(conn, 411, "%s", "Error: Client did not specify content length"); + } else if ((expect != NULL) + && (mg_strcasecmp(expect, "100-continue") != 0)) { + /* Client sent an "Expect: xyz" header and xyz is not 100-continue. */ + XX_httplib_send_http_error(conn, 417, "Error: Can not fulfill expectation %s", expect); + } else { + if (expect != NULL) { + (void)mg_printf(conn, "%s", "HTTP/1.1 100 Continue\r\n\r\n"); + conn->status_code = 100; + } else conn->status_code = 200; + + buffered_len = (int64_t)(conn->data_len) - (int64_t)conn->request_len + - conn->consumed_content; + + /* assert(buffered_len >= 0); */ + /* assert(conn->consumed_content == 0); */ + + if ((buffered_len < 0) || (conn->consumed_content != 0)) { + XX_httplib_send_http_error(conn, 500, "%s", "Error: Size mismatch"); + return 0; + } + + if (buffered_len > 0) { + if ((int64_t)buffered_len > conn->content_len) { + buffered_len = (int)conn->content_len; + } + body = conn->buf + conn->request_len + conn->consumed_content; + XX_httplib_push_all(conn->ctx, fp, sock, ssl, body, (int64_t)buffered_len); + conn->consumed_content += buffered_len; + } + + nread = 0; + while (conn->consumed_content < conn->content_len) { + to_read = sizeof(buf); + if ((int64_t)to_read > conn->content_len - conn->consumed_content) { + to_read = (int)(conn->content_len - conn->consumed_content); + } + nread = XX_httplib_pull(NULL, conn, buf, to_read, timeout); + if (nread <= 0 || XX_httplib_push_all(conn->ctx, fp, sock, ssl, buf, nread) != nread) break; + conn->consumed_content += nread; + } + + if (conn->consumed_content == conn->content_len) success = (nread >= 0); + + /* Each error code path in this function must send an error */ + if (!success) { + /* NOTE: Maybe some data has already been sent. */ + /* TODO (low): If some data has been sent, a correct error + * reply can no longer be sent, so just close the connection */ + XX_httplib_send_http_error(conn, 500, "%s", ""); + } + } + + return success; + +} /* XX_httplib_forward_body_data */ +#endif diff --git a/src/libhttp-private.h b/src/libhttp-private.h index ec04ebbd..b96fe628 100644 --- a/src/libhttp-private.h +++ b/src/libhttp-private.h @@ -942,6 +942,7 @@ void XX_httplib_process_new_connection( struct mg_connection *conn ); void XX_httplib_produce_socket( struct mg_context *ctx, const struct socket *sp ); int XX_httplib_pull( FILE *fp, struct mg_connection *conn, char *buf, int len, double timeout ); int XX_httplib_pull_all( FILE *fp, struct mg_connection *conn, char *buf, int len ); +int64_t XX_httplib_push_all( struct mg_context *ctx, FILE *fp, SOCKET sock, SSL *ssl, const char *buf, int64_t len ); int XX_httplib_put_dir( struct mg_connection *conn, const char *path ); void XX_httplib_put_file( struct mg_connection *conn, const char *path ); int XX_httplib_read_request( FILE *fp, struct mg_connection *conn, char *buf, int bufsiz, int *nread ); diff --git a/src/libhttp.c b/src/libhttp.c index df2e7d99..ad85c7dc 100644 --- a/src/libhttp.c +++ b/src/libhttp.c @@ -2674,7 +2674,7 @@ static int push(struct mg_context *ctx, FILE *fp, SOCKET sock, SSL *ssl, const c } -static int64_t push_all(struct mg_context *ctx, FILE *fp, SOCKET sock, SSL *ssl, const char *buf, int64_t len) { +int64_t XX_httplib_push_all(struct mg_context *ctx, FILE *fp, SOCKET sock, SSL *ssl, const char *buf, int64_t len) { double timeout = -1.0; int64_t n; @@ -2701,7 +2701,7 @@ static int64_t push_all(struct mg_context *ctx, FILE *fp, SOCKET sock, SSL *ssl, return nwritten; -} /* push_all */ +} /* XX_httplib_push_all */ /* Read from IO channel - opened file descriptor, socket, or SSL descriptor. @@ -3041,7 +3041,7 @@ int mg_write(struct mg_connection *conn, const void *buf, size_t len) { } allowed = conn->throttle - conn->last_throttle_bytes; if (allowed > (int64_t)len) allowed = (int64_t)len; - if ((total = push_all(conn->ctx, + if ((total = XX_httplib_push_all(conn->ctx, NULL, conn->client.sock, conn->ssl, @@ -3053,7 +3053,7 @@ int mg_write(struct mg_connection *conn, const void *buf, size_t len) { allowed = (conn->throttle > ((int64_t)len - total)) ? (int64_t)len - total : conn->throttle; - if ((n = push_all(conn->ctx, + if ((n = XX_httplib_push_all(conn->ctx, NULL, conn->client.sock, conn->ssl, @@ -3070,7 +3070,7 @@ int mg_write(struct mg_connection *conn, const void *buf, size_t len) { } } - else total = push_all(conn->ctx, NULL, conn->client.sock, conn->ssl, (const char *)buf, (int64_t)len); + else total = XX_httplib_push_all(conn->ctx, NULL, conn->client.sock, conn->ssl, (const char *)buf, (int64_t)len); return (int)total; } @@ -5439,88 +5439,3 @@ int XX_httplib_is_not_modified( const struct mg_connection *conn, const struct f } /* XX_httplib_is_not_modified */ #endif /* !NO_CACHING */ - - -#if !defined(NO_CGI) || !defined(NO_FILES) -int XX_httplib_forward_body_data( struct mg_connection *conn, FILE *fp, SOCKET sock, SSL *ssl ) { - - const char *expect; - const char *body; - char buf[MG_BUF_LEN]; - int to_read; - int nread; - int success = 0; - int64_t buffered_len; - double timeout = -1.0; - - if (!conn) { return 0; } - if (conn->ctx->config[REQUEST_TIMEOUT]) { - timeout = atoi(conn->ctx->config[REQUEST_TIMEOUT]) / 1000.0; - } - - expect = mg_get_header(conn, "Expect"); - /* assert(fp != NULL); */ - if (!fp) { - XX_httplib_send_http_error(conn, 500, "%s", "Error: NULL File"); - return 0; - } - - if (conn->content_len == -1 && !conn->is_chunked) { - /* Content length is not specified by the client. */ - XX_httplib_send_http_error(conn, 411, "%s", "Error: Client did not specify content length"); - } else if ((expect != NULL) - && (mg_strcasecmp(expect, "100-continue") != 0)) { - /* Client sent an "Expect: xyz" header and xyz is not 100-continue. */ - XX_httplib_send_http_error(conn, 417, "Error: Can not fulfill expectation %s", expect); - } else { - if (expect != NULL) { - (void)mg_printf(conn, "%s", "HTTP/1.1 100 Continue\r\n\r\n"); - conn->status_code = 100; - } else conn->status_code = 200; - - buffered_len = (int64_t)(conn->data_len) - (int64_t)conn->request_len - - conn->consumed_content; - - /* assert(buffered_len >= 0); */ - /* assert(conn->consumed_content == 0); */ - - if ((buffered_len < 0) || (conn->consumed_content != 0)) { - XX_httplib_send_http_error(conn, 500, "%s", "Error: Size mismatch"); - return 0; - } - - if (buffered_len > 0) { - if ((int64_t)buffered_len > conn->content_len) { - buffered_len = (int)conn->content_len; - } - body = conn->buf + conn->request_len + conn->consumed_content; - push_all(conn->ctx, fp, sock, ssl, body, (int64_t)buffered_len); - conn->consumed_content += buffered_len; - } - - nread = 0; - while (conn->consumed_content < conn->content_len) { - to_read = sizeof(buf); - if ((int64_t)to_read > conn->content_len - conn->consumed_content) { - to_read = (int)(conn->content_len - conn->consumed_content); - } - nread = XX_httplib_pull(NULL, conn, buf, to_read, timeout); - if (nread <= 0 || push_all(conn->ctx, fp, sock, ssl, buf, nread) != nread) break; - conn->consumed_content += nread; - } - - if (conn->consumed_content == conn->content_len) success = (nread >= 0); - - /* Each error code path in this function must send an error */ - if (!success) { - /* NOTE: Maybe some data has already been sent. */ - /* TODO (low): If some data has been sent, a correct error - * reply can no longer be sent, so just close the connection */ - XX_httplib_send_http_error(conn, 500, "%s", ""); - } - } - - return success; - -} /* XX_httplib_forward_body_data */ -#endif