diff --git a/src/http.c b/src/http.c
index 3420d5e00f1c075b2be3434c35950e6424e91f46..e32ecf93eeaa18d1cd1473501385398d9eb04aa4 100644
--- a/src/http.c
+++ b/src/http.c
@@ -22,6 +22,7 @@
 #include "userinput.h"
 #include "log.h"
 
+#include <assert.h>
 #include <ctype.h>
 #include <string.h>
 #include <stdarg.h>
@@ -30,7 +31,8 @@
 #include <unistd.h>
 #include <arpa/inet.h>
 
-#define BUFSZ 0x10000
+static const uint32_t HTTP_BUFFER_SIZE = 0x8000;
+
 
 /*
  * URL-encodes a string for HTTP requests.
@@ -58,6 +60,7 @@ static void url_encode(char *dest, const char *str)
 	*dest = '\0';
 }
 
+
 /*
  * Sends data to the HTTP server.
  *
@@ -68,13 +71,13 @@ static void url_encode(char *dest, const char *str)
 int http_send(struct tunnel *tunnel, const char *request, ...)
 {
 	va_list args;
-	char buffer[BUFSZ];
-	char logbuffer[BUFSZ];
+	char buffer[HTTP_BUFFER_SIZE];
+	char logbuffer[HTTP_BUFFER_SIZE];
 	int length;
 	int n = 0;
 
 	va_start(args, request);
-	length = vsnprintf(buffer, BUFSZ, request, args);
+	length = vsnprintf(buffer, HTTP_BUFFER_SIZE, request, args);
 	va_end(args);
 	strcpy(logbuffer, buffer);
 	if (loglevel <= OFV_LOG_DEBUG_DETAILS && tunnel->config->password[0] != '\0') {
@@ -96,7 +99,7 @@ int http_send(struct tunnel *tunnel, const char *request, ...)
 
 	if (length < 0)
 		return ERR_HTTP_INVALID;
-	else if (length >= BUFSZ)
+	else if (length >= HTTP_BUFFER_SIZE)
 		return ERR_HTTP_TOO_LONG;
 
 	log_debug_details("%s:\n%s\n", __func__, logbuffer);
@@ -113,6 +116,7 @@ int http_send(struct tunnel *tunnel, const char *request, ...)
 	return 1;
 }
 
+
 static const char *find_header(
         const char *res,
         const char *header,
@@ -135,6 +139,7 @@ static const char *find_header(
 	return NULL;
 }
 
+
 /*
  * Receives data from the HTTP server.
  *
@@ -150,75 +155,87 @@ int http_receive(
         uint32_t *response_size
 )
 {
-	uint32_t res_size = BUFSZ;
-	char *buffer, *res;
-	int n = 0;
-	int bytes_read = 0;
-	int header_size = 0;
-	int content_size = 0;
+	uint32_t capacity = HTTP_BUFFER_SIZE;
+	char *buffer;
+	uint32_t bytes_read = 0;
+	uint32_t header_size = 0;
+	uint32_t content_size = 0;
 	int chunked = 0;
 
-	buffer = malloc(res_size);
+	buffer = malloc(capacity);
 	if (buffer == NULL)
 		return ERR_HTTP_NO_MEM;
 
-	do {
-		n = safe_ssl_read(tunnel->ssl_handle,
-		                  (uint8_t *) buffer + bytes_read,
-		                  BUFSZ - 1 - bytes_read);
-		if (n > 0) {
-			log_debug_details("%s:\n%s\n", __func__, buffer);
-			const char *eoh;
-
-			bytes_read += n;
-
-			if (!header_size) {
-				/* Did we see the header end? Then get the body size. */
-				eoh = memmem(buffer, bytes_read, "\r\n\r\n", 4);
-				if (eoh) {
-					const char *header;
-
-					header = find_header(
-					                 buffer,
-					                 "Content-Length: ", BUFSZ);
-					header_size = eoh - buffer + 4;
-					if (header)
-						content_size = atoi(header);
-
-					if (find_header(buffer,
-					                "Transfer-Encoding: chunked",
-					                BUFSZ))
-						chunked = 1;
-				}
+	while (1) {
+		int n;
+
+		while ((n = safe_ssl_read(tunnel->ssl_handle,
+		                          (uint8_t *) buffer + bytes_read,
+		                          capacity - bytes_read)) == ERR_SSL_AGAIN)
+			;
+		if (n < 0) {
+			log_error("Error reading from SSL connection (%s).\n",
+			          err_ssl_str(n));
+			free(buffer);
+			return ERR_HTTP_SSL;
+		}
+		bytes_read += n;
+
+		log_debug_details("%s:\n%s\n", __func__, buffer);
+
+		if (!header_size) {
+			/* Have we reached the end of the HTTP header? */
+			static const char EOH[4] = "\r\n\r\n";
+			const char *eoh = memmem(buffer, bytes_read,
+			                         EOH, sizeof(EOH));
+
+			if (eoh) {
+				header_size = eoh - buffer + sizeof(EOH);
+
+				/* Get the body size. */
+				const char *header = find_header(buffer,
+				                                 "Content-Length: ",
+				                                 header_size);
+
+				if (header)
+					content_size = atoi(header);
+
+				if (find_header(buffer,
+				                "Transfer-Encoding: chunked",
+				                header_size))
+					chunked = 1;
 			}
+		}
 
-			if (header_size) {
-				/* We saw the whole header, is the body done as well? */
-				if (chunked) {
-					/* Last chunk terminator. Done naively. */
-					if (bytes_read >= 7 &&
-					    !memcmp(&buffer[bytes_read - 7],
-					            "\r\n0\r\n\r\n", 7))
-						break;
-				} else {
-					if (bytes_read >= header_size + content_size)
-						break;
-				}
+		if (header_size) {
+			/* Have we reached the end of the HTTP body? */
+			if (chunked) {
+				static const char EOB[7] = "\r\n0\r\n\r\n";
+
+				/* Last chunk terminator. Done naively. */
+				if (bytes_read >= sizeof(EOB) &&
+				    !memcmp(&buffer[bytes_read - sizeof(EOB)],
+				            EOB, sizeof(EOB)))
+					break;
+			} else {
+				if (bytes_read >= header_size + content_size)
+					break;
 			}
+		}
 
-			if (bytes_read == BUFSZ - 1) {
-				log_warn("Response too big\n");
+		/* expand the buffer if necessary */
+		if (bytes_read == capacity) {
+			char *new_buffer;
+
+			assert(UINT32_MAX / capacity >= 2);
+			capacity *= 2;
+			new_buffer = realloc(buffer, capacity);
+			if (new_buffer == NULL) {
 				free(buffer);
-				return ERR_HTTP_SSL;
+				return ERR_HTTP_NO_MEM;
 			}
+			buffer = new_buffer;
 		}
-	} while (n >= 0);
-
-	if (!header_size) {
-		log_debug("Error reading from SSL connection (%s).\n",
-		          err_ssl_str(n));
-		free(buffer);
-		return ERR_HTTP_SSL;
 	}
 
 	if (memmem(&buffer[header_size], bytes_read - header_size,
@@ -231,23 +248,26 @@ int http_receive(
 
 	if (response == NULL) {
 		free(buffer);
-		return 1;
-	}
+	} else {
+		char *tmp;
+
+		assert(capacity < UINT32_MAX);
+		capacity = bytes_read + 1;
+		tmp = realloc(buffer, capacity);
+		if (tmp == NULL) {
+			free(buffer);
+			return ERR_HTTP_NO_MEM;
+		}
+		tmp[bytes_read] = '\0';
+		*response = tmp;
 
-	res_size = bytes_read + 1;
-	res = realloc(buffer, res_size);
-	if (res == NULL) {
-		free(buffer);
-		return ERR_HTTP_NO_MEM;
+		if (response_size != NULL)
+			*response_size = capacity;
 	}
-	res[bytes_read] = '\0';
-
-	*response = res;
-	if (response_size != NULL)
-		*response_size = res_size;
 	return 1;
 }
 
+
 static int do_http_request(struct tunnel *tunnel,
                            const char *method,
                            const char *uri,
@@ -276,6 +296,8 @@ static int do_http_request(struct tunnel *tunnel,
 
 	return http_receive(tunnel, response, response_size);
 }
+
+
 /*
  * Sends and receives data from the HTTP server.
  *
@@ -307,6 +329,7 @@ static int http_request(struct tunnel *tunnel, const char *method,
 	return ret;
 }
 
+
 /*
  * Read value for key from a string like "key1=value1&key2=value2".
  * The `key` arg is supposed to contains the final "=".
@@ -351,6 +374,7 @@ end:
 	return ret;
 }
 
+
 static int get_auth_cookie(
         struct tunnel *tunnel,
         char *buf,
@@ -390,6 +414,7 @@ static int get_auth_cookie(
 	return ret;
 }
 
+
 static void delay_otp(struct tunnel *tunnel)
 {
 	if (tunnel->config->otp_delay > 0) {
@@ -398,8 +423,8 @@ static void delay_otp(struct tunnel *tunnel)
 	}
 }
 
-static
-int try_otp_auth(
+
+static int try_otp_auth(
         struct tunnel *tunnel,
         const char *buffer,
         char **res,
@@ -555,6 +580,7 @@ int try_otp_auth(
 #undef SPACE_AVAILABLE
 }
 
+
 /*
  * Authenticates to gateway by sending username and password.
  *
@@ -684,11 +710,13 @@ end:
 	return ret;
 }
 
+
 int auth_log_out(struct tunnel *tunnel)
 {
 	return http_request(tunnel, "GET", "/remote/logout", "", NULL, NULL);
 }
 
+
 int auth_request_vpn_allocation(struct tunnel *tunnel)
 {
 	int ret = http_request(tunnel, "GET", "/remote/index", "", NULL, NULL);
@@ -699,6 +727,7 @@ int auth_request_vpn_allocation(struct tunnel *tunnel)
 	return http_request(tunnel, "GET", "/remote/fortisslvpn", "", NULL, NULL);
 }
 
+
 static int parse_xml_config(struct tunnel *tunnel, const char *buffer)
 {
 	const char *val;
@@ -778,8 +807,8 @@ static int parse_xml_config(struct tunnel *tunnel, const char *buffer)
 	return 1;
 }
 
-static
-int parse_config(struct tunnel *tunnel, const char *buffer)
+
+static int parse_config(struct tunnel *tunnel, const char *buffer)
 {
 	const char *c, *end;
 
@@ -828,6 +857,7 @@ int parse_config(struct tunnel *tunnel, const char *buffer)
 	return 1;
 }
 
+
 int auth_get_config(struct tunnel *tunnel)
 {
 	char *buffer;
diff --git a/src/http.h b/src/http.h
index e1e171011ed2fda8d992979a04909b8d38d7b2ee..ec124e99e7ef43200275a9dc8c6caf8aa2b22ca3 100644
--- a/src/http.h
+++ b/src/http.h
@@ -20,6 +20,8 @@
 
 #include "tunnel.h"
 
+#include <stdint.h>
+
 #define ERR_HTTP_INVALID	-1
 #define ERR_HTTP_TOO_LONG	-2
 #define ERR_HTTP_NO_MEM		-3
diff --git a/src/ssl.h b/src/ssl.h
index 879b9d49a82a0203b9a0257c092d01374e4e7292..c0fe623b92e1808c7f0993e1e5de79b39f7ee8a5 100644
--- a/src/ssl.h
+++ b/src/ssl.h
@@ -30,6 +30,7 @@
 #define OPENFORTIVPN_SSL_H
 
 #include <errno.h>
+#include <stdint.h>
 #include <string.h>
 #include <openssl/err.h>
 #include <openssl/ssl.h>
@@ -76,7 +77,7 @@ static inline const char *err_ssl_str(int code)
 	else if (code == ERR_SSL_SEE_ERRNO)
 		return strerror(errno);
 	else if (code == ERR_SSL_SEE_SSLERR)
-		return ERR_error_string(ERR_peek_last_error(), NULL);
+		return ERR_reason_error_string(ERR_peek_last_error());
 	return "unknown";
 }