summaryrefslogtreecommitdiff
path: root/main/http.c
diff options
context:
space:
mode:
authorTimo Teräs <timo.teras@iki.fi>2016-06-02 22:10:06 +0300
committerTimo Teräs <timo.teras@iki.fi>2016-11-15 22:25:14 +0200
commit070a51bf7c00f49bb82d26e889b88906a9b2fd0c (patch)
treefddd2462220284d9dd7abba8ec2c1c0d68a68159 /main/http.c
parent0cc14597b29203259b5e6ae4496f9f6d4f4e76f2 (diff)
Implement internal abstraction for iostreams
fopencookie/funclose is a non-standard API and should not be used in portable software. Additionally, the way FILE's fd is used in non-blocking mode is undefined behaviour and cannot be relied on. This introduces internal abstraction for io streams, that allows implementing the desired virtualization of read/write operations with necessary timeout handling. ASTERISK-24515 #close ASTERISK-24517 #close Change-Id: Id916aef418b665ced6a7489aef74908b6e376e85
Diffstat (limited to 'main/http.c')
-rw-r--r--main/http.c105
1 files changed, 37 insertions, 68 deletions
diff --git a/main/http.c b/main/http.c
index 77feb397b..9aff4d167 100644
--- a/main/http.c
+++ b/main/http.c
@@ -449,11 +449,13 @@ void ast_http_send(struct ast_tcptls_session_instance *ser,
struct timeval now = ast_tvnow();
struct ast_tm tm;
char timebuf[80];
+ char buf[256];
+ int len;
int content_length = 0;
int close_connection;
struct ast_str *server_header_field = ast_str_create(MAX_SERVER_NAME_LENGTH);
- if (!ser || !ser->f || !server_header_field) {
+ if (!ser || !server_header_field) {
/* The connection is not open. */
ast_free(http_header);
ast_free(out);
@@ -503,7 +505,7 @@ void ast_http_send(struct ast_tcptls_session_instance *ser,
}
/* send http header */
- fprintf(ser->f,
+ ast_iostream_printf(ser->stream,
"HTTP/1.1 %d %s\r\n"
"%s"
"Date: %s\r\n"
@@ -524,18 +526,16 @@ void ast_http_send(struct ast_tcptls_session_instance *ser,
/* send content */
if (method != AST_HTTP_HEAD || status_code >= 400) {
if (out && ast_str_strlen(out)) {
- if (fwrite(ast_str_buffer(out), ast_str_strlen(out), 1, ser->f) != 1) {
+ len = ast_str_strlen(out);
+ if (ast_iostream_write(ser->stream, ast_str_buffer(out), len) != len) {
ast_log(LOG_ERROR, "fwrite() failed: %s\n", strerror(errno));
close_connection = 1;
}
}
if (fd) {
- char buf[256];
- int len;
-
while ((len = read(fd, buf, sizeof(buf))) > 0) {
- if (fwrite(buf, len, 1, ser->f) != 1) {
+ if (ast_iostream_write(ser->stream, buf, len) != len) {
ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
close_connection = 1;
break;
@@ -567,7 +567,7 @@ void ast_http_create_response(struct ast_tcptls_session_instance *ser, int statu
ast_free(http_header_data);
ast_free(server_address);
ast_free(out);
- if (ser && ser->f) {
+ if (ser) {
ast_debug(1, "HTTP closing session. OOM.\n");
ast_tcptls_close_session_file(ser);
}
@@ -921,9 +921,9 @@ static int http_body_read_contents(struct ast_tcptls_session_instance *ser, char
{
int res;
- /* Stay in fread until get all the expected data or timeout. */
- res = fread(buf, length, 1, ser->f);
- if (res < 1) {
+ /* Stream is in exclusive mode so we get it all if possible. */
+ res = ast_iostream_read(ser->stream, buf, length);
+ if (res < length) {
ast_log(LOG_WARNING, "Short HTTP request %s (Wanted %d)\n",
what_getting, length);
return -1;
@@ -945,23 +945,12 @@ static int http_body_read_contents(struct ast_tcptls_session_instance *ser, char
*/
static int http_body_discard_contents(struct ast_tcptls_session_instance *ser, int length, const char *what_getting)
{
- int res;
- char buf[MAX_HTTP_LINE_LENGTH];/* Discard buffer */
-
- /* Stay in fread until get all the expected data or timeout. */
- while (sizeof(buf) < length) {
- res = fread(buf, sizeof(buf), 1, ser->f);
- if (res < 1) {
- ast_log(LOG_WARNING, "Short HTTP request %s (Wanted %zu of remaining %d)\n",
- what_getting, sizeof(buf), length);
- return -1;
- }
- length -= sizeof(buf);
- }
- res = fread(buf, length, 1, ser->f);
- if (res < 1) {
- ast_log(LOG_WARNING, "Short HTTP request %s (Wanted %d of remaining %d)\n",
- what_getting, length, length);
+ ssize_t res;
+
+ res = ast_iostream_discard(ser->stream, length);
+ if (res < length) {
+ ast_log(LOG_WARNING, "Short HTTP request %s (Wanted %d but got %zd)\n",
+ what_getting, length, res);
return -1;
}
return 0;
@@ -1037,7 +1026,7 @@ static int http_body_get_chunk_length(struct ast_tcptls_session_instance *ser)
char header_line[MAX_HTTP_LINE_LENGTH];
/* get the line of hexadecimal giving chunk-size w/ optional chunk-extension */
- if (!fgets(header_line, sizeof(header_line), ser->f)) {
+ if (ast_iostream_gets(ser->stream, header_line, sizeof(header_line)) <= 0) {
ast_log(LOG_WARNING, "Short HTTP read of chunked header\n");
return -1;
}
@@ -1065,8 +1054,8 @@ static int http_body_check_chunk_sync(struct ast_tcptls_session_instance *ser)
char chunk_sync[2];
/* Stay in fread until get the expected CRLF or timeout. */
- res = fread(chunk_sync, sizeof(chunk_sync), 1, ser->f);
- if (res < 1) {
+ res = ast_iostream_read(ser->stream, chunk_sync, sizeof(chunk_sync));
+ if (res < sizeof(chunk_sync)) {
ast_log(LOG_WARNING, "Short HTTP chunk sync read (Wanted %zu)\n",
sizeof(chunk_sync));
return -1;
@@ -1095,7 +1084,7 @@ static int http_body_discard_chunk_trailer_headers(struct ast_tcptls_session_ins
char header_line[MAX_HTTP_LINE_LENGTH];
for (;;) {
- if (!fgets(header_line, sizeof(header_line), ser->f)) {
+ if (ast_iostream_gets(ser->stream, header_line, sizeof(header_line)) <= 0) {
ast_log(LOG_WARNING, "Short HTTP read of chunked trailer header\n");
return -1;
}
@@ -1758,7 +1747,7 @@ static int http_request_headers_get(struct ast_tcptls_session_instance *ser, str
char *name;
char *value;
- if (!fgets(header_line, sizeof(header_line), ser->f)) {
+ if (ast_iostream_gets(ser->stream, header_line, sizeof(header_line)) <= 0) {
ast_http_error(ser, 400, "Bad Request", "Timeout");
return -1;
}
@@ -1832,7 +1821,7 @@ static int httpd_process_request(struct ast_tcptls_session_instance *ser)
int res;
char request_line[MAX_HTTP_LINE_LENGTH];
- if (!fgets(request_line, sizeof(request_line), ser->f)) {
+ if (ast_iostream_gets(ser->stream, request_line, sizeof(request_line)) <= 0) {
return -1;
}
@@ -1913,11 +1902,10 @@ static int httpd_process_request(struct ast_tcptls_session_instance *ser)
static void *httpd_helper_thread(void *data)
{
struct ast_tcptls_session_instance *ser = data;
- struct protoent *p;
- int flags;
int timeout;
+ int arg = 1;
- if (!ser || !ser->f) {
+ if (!ser) {
ao2_cleanup(ser);
return NULL;
}
@@ -1934,23 +1922,11 @@ static void *httpd_helper_thread(void *data)
* This is necessary to prevent delays (caused by buffering) as we
* write to the socket in bits and pieces.
*/
- p = getprotobyname("tcp");
- if (p) {
- int arg = 1;
-
- if (setsockopt(ser->fd, p->p_proto, TCP_NODELAY, (char *) &arg, sizeof(arg) ) < 0) {
- ast_log(LOG_WARNING, "Failed to set TCP_NODELAY on HTTP connection: %s\n", strerror(errno));
- ast_log(LOG_WARNING, "Some HTTP requests may be slow to respond.\n");
- }
- } else {
- ast_log(LOG_WARNING, "Failed to set TCP_NODELAY on HTTP connection, getprotobyname(\"tcp\") failed\n");
+ if (setsockopt(ast_iostream_get_fd(ser->stream), IPPROTO_TCP, TCP_NODELAY, (char *) &arg, sizeof(arg) ) < 0) {
+ ast_log(LOG_WARNING, "Failed to set TCP_NODELAY on HTTP connection: %s\n", strerror(errno));
ast_log(LOG_WARNING, "Some HTTP requests may be slow to respond.\n");
}
-
- /* make sure socket is non-blocking */
- flags = fcntl(ser->fd, F_GETFL);
- flags |= O_NONBLOCK;
- fcntl(ser->fd, F_SETFL, flags);
+ ast_iostream_nonblock(ser->stream);
/* Setup HTTP worker private data to keep track of request body reading. */
ao2_cleanup(ser->private_data);
@@ -1973,23 +1949,17 @@ static void *httpd_helper_thread(void *data)
}
/* We can let the stream wait for data to arrive. */
- ast_tcptls_stream_set_exclusive_input(ser->stream_cookie, 1);
+ ast_iostream_set_exclusive_input(ser->stream, 1);
for (;;) {
- int ch;
-
/* Wait for next potential HTTP request message. */
- ast_tcptls_stream_set_timeout_inactivity(ser->stream_cookie, timeout);
- ch = fgetc(ser->f);
- if (ch == EOF || ungetc(ch, ser->f) == EOF) {
- /* Between request idle timeout */
- ast_debug(1, "HTTP idle timeout or peer closed connection.\n");
+ ast_iostream_set_timeout_idle_inactivity(ser->stream, timeout, session_inactivity);
+ if (httpd_process_request(ser)) {
+ /* Break the connection or the connection closed */
break;
}
-
- ast_tcptls_stream_set_timeout_inactivity(ser->stream_cookie, session_inactivity);
- if (httpd_process_request(ser) || !ser->f || feof(ser->f)) {
- /* Break the connection or the connection closed */
+ if (!ser->stream) {
+ /* Web-socket or similar that took the connection */
break;
}
@@ -2003,10 +1973,9 @@ static void *httpd_helper_thread(void *data)
done:
ast_atomic_fetchadd_int(&session_count, -1);
- if (ser->f) {
- ast_debug(1, "HTTP closing session. Top level\n");
- ast_tcptls_close_session_file(ser);
- }
+ ast_debug(1, "HTTP closing session. Top level\n");
+ ast_tcptls_close_session_file(ser);
+
ao2_ref(ser, -1);
return NULL;
}