[prev in list] [next in list] [prev in thread] [next in thread] 

List:       busybox
Subject:    [PATCH 1/2] wget: replace set_alarm with non blocking functions to support retries
From:       Martin Lewis <martin.lewis.x84 () gmail ! com>
Date:       2018-12-12 15:26:18
Message-ID: 1544628379-99856-1-git-send-email-martin.lewis.x84 () gmail ! com
[Download RAW message or body]

Signed-off-by: Martin Lewis <martin.lewis.x84@gmail.com>
---
 networking/wget.c | 162 +++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 153 insertions(+), 9 deletions(-)

diff --git a/networking/wget.c b/networking/wget.c
index 58a51d9..e9fdd9f 100644
--- a/networking/wget.c
+++ b/networking/wget.c
@@ -355,6 +355,8 @@ static char *base64enc(const char *str)
 }
 #endif
 
+/* We need to find another way to handle timeouts in order to support retries */
+#if 0
 #if ENABLE_FEATURE_WGET_TIMEOUT
 static void alarm_handler(int sig UNUSED_PARAM)
 {
@@ -374,6 +376,7 @@ static void set_alarm(void)
 # define set_alarm()   ((void)0)
 # define clear_alarm() ((void)0)
 #endif
+#endif
 
 #if ENABLE_FEATURE_WGET_OPENSSL
 /*
@@ -403,10 +406,16 @@ static FILE *open_socket(len_and_sockaddr *lsa)
 {
 	int fd;
 	FILE *fp;
-
-	set_alarm();
-	fd = xconnect_stream(lsa);
-	clear_alarm();
+#if ENABLE_FEATURE_WGET_TIMEOUT
+	struct timeval timeout = {G.timeout_seconds, 0};
+#endif
+	fd = xsocket(lsa->u.sa.sa_family, SOCK_STREAM, 0);
+#if ENABLE_FEATURE_WGET_TIMEOUT
+	if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof (timeout)) < 0) {
+		bb_perror_msg_and_die("setsockopt failed\n");
+	}
+#endif
+	xconnect(fd, &lsa->u.sa, lsa->len);
 
 	/* glibc 2.4 seems to try seeking on it - ??! */
 	/* hopefully it understands what ESPIPE means... */
@@ -439,16 +448,150 @@ static char* sanitize_string(char *s)
 	return s;
 }
 
+/*  fgets() is blocking, so we need a non blocking version.
+    We need to do some buffering so we can cut at \n */
+static char fgets_buffer[4096];
+size_t fgets_buffer_len = 0;
+
+/* Returns buffer if successfull, otherswise return NULL. Supports timeout. */
+static char *fgets_read_to_newline(char *buffer, size_t len, FILE *fp)
+{
+	struct pollfd polldata;
+	size_t bytes_read = 0;
+	char *newline = NULL;
+	char *buffer_start = buffer;
+
+	polldata.fd = fileno(fp);
+	polldata.events = POLLIN | POLLPRI;
+
+#if ENABLE_FEATURE_WGET_TIMEOUT
+	ndelay_on(polldata.fd);
+#endif
+
+	if (len < 1) {
+		/* Should not happen */
+		return NULL;
+	}
+
+	/* Clear buffer */
+	if (fgets_buffer_len > 0) {
+		if ((newline = memchr(fgets_buffer, '\n', sizeof (fgets_buffer)))) {
+			size_t to_copy = 0;
+			/* In the buffer there is a complete line, if we can copy and return */
+			if ((newline + 1 - fgets_buffer) + 1 <= len)
+				to_copy = newline + 1 - fgets_buffer;
+			else
+				/* Otherwise we copy only part of it */
+				to_copy = len - 1;
+
+			memcpy(buffer, fgets_buffer, to_copy);
+			/* Comply fgets we are replacing */
+			buffer[to_copy] = '\0';
+			fgets_buffer_len -= to_copy;
+			memmove(fgets_buffer, fgets_buffer + to_copy, fgets_buffer_len);
+
+			return buffer_start;
+		}
+
+		/* We don't have \n yet, empty the buffer */
+		if (fgets_buffer_len <= len - 1) {
+			memcpy(buffer, fgets_buffer, fgets_buffer_len);
+			len -= fgets_buffer_len;
+			buffer += fgets_buffer_len;
+			fgets_buffer_len = 0;
+		} else {
+			/* Or if it is not big enough */
+			memcpy(buffer, fgets_buffer, len - 1);
+			/* Comply fgets we are replacing */
+			buffer[len - 1] = '\0';
+			fgets_buffer_len -= len - 1;
+			memmove(fgets_buffer, fgets_buffer + len - 1, fgets_buffer_len);
+
+			return buffer_start;
+		}
+	}
+
+	while (1) {
+		/* Check if data available */
+#if ENABLE_FEATURE_WGET_TIMEOUT
+		if (safe_poll(&polldata, 1, G.timeout_seconds * 1000) == 0) {
+#else
+		/* Infinite time */
+		if (safe_poll(&polldata, 1, -1) == 0) {
+#endif
+			/* Timed out */
+			return NULL;
+		}
+
+		bytes_read = fread(fgets_buffer + fgets_buffer_len, 1, sizeof (fgets_buffer) - fgets_buffer_len, fp);
+		if (bytes_read <= 0) {
+			return NULL;
+		}
+
+		fgets_buffer_len += bytes_read;
+		newline = memchr(fgets_buffer, '\n', fgets_buffer_len);
+		if (newline != NULL) {
+			size_t to_copy = 0;
+			/* In the buffer there is a complete line, if we can copy and return */
+			if ((newline + 1 - fgets_buffer) + 1 <= len)
+				to_copy = newline + 1 - fgets_buffer;
+			else
+				/* Otherwise we copy only part of it */
+				to_copy = len - 1;
+
+			memcpy(buffer, fgets_buffer, to_copy);
+			/* Comply fgets we are replacing */
+			buffer[to_copy] = '\0';
+			fgets_buffer_len -= to_copy;
+			memmove(fgets_buffer, fgets_buffer + to_copy, fgets_buffer_len);
+
+			return buffer_start;
+		}
+
+		if (fgets_buffer_len >= len - 1) {
+			/* Buffer is full, return it */
+			memcpy(buffer, fgets_buffer, len - 1);
+			/* Comply fgets we are replacing */
+			buffer[len - 1] = '\0';
+			fgets_buffer_len -= len - 1;
+			memmove(fgets_buffer, fgets_buffer + len - 1, fgets_buffer_len);
+
+			return buffer_start;
+		}
+	}
+}
+
+/* Empty our buffer then call fread */
+static int fread_buffered(char *buffer, size_t len, FILE *fp)
+{
+	int read = 0;
+	if (fgets_buffer_len > 0) {
+		if (fgets_buffer_len <= len) {
+			memcpy(buffer, fgets_buffer, fgets_buffer_len);
+			read = fgets_buffer_len;
+			fgets_buffer_len = 0;
+			return read;
+		} else {
+			/* Or if it is not big enough */
+			memcpy(buffer, fgets_buffer, len);
+			fgets_buffer_len -= len;
+			memmove(fgets_buffer, fgets_buffer + len, fgets_buffer_len);
+			return len;
+		}
+	}
+
+	/* If no buffer then just call fread */
+	return fread(buffer, 1, len, fp);
+}
+
 /* Returns '\n' if it was seen, else '\0'. Trims at first '\r' or '\n' */
 static char fgets_trim_sanitize(FILE *fp, const char *fmt)
 {
 	char c;
 	char *buf_ptr;
 
-	set_alarm();
-	if (fgets(G.wget_buf, sizeof(G.wget_buf), fp) == NULL)
+	if (fgets_read_to_newline(G.wget_buf, sizeof(G.wget_buf), fp) == NULL)
 		bb_perror_msg_and_die("error getting response");
-	clear_alarm();
 
 	buf_ptr = strchrnul(G.wget_buf, '\n');
 	c = *buf_ptr;
@@ -910,7 +1053,7 @@ static void NOINLINE retrieve_file_data(FILE *dfp)
 					rdsz = (unsigned)G.content_len;
 				}
 			}
-			n = fread(G.wget_buf, 1, rdsz, dfp);
+			n = fread_buffered(G.wget_buf, rdsz, dfp);
 
 			if (n > 0) {
 				xwrite(G.output_fd, G.wget_buf, n);
@@ -1099,6 +1242,8 @@ static void download_one_url(const char *url)
 	/*G.content_len = 0; - redundant, got_clen = 0 is enough */
 	G.got_clen = 0;
 	G.chunked = 0;
+	/* If we get redirection, flush the current buffer */
+	fgets_buffer_len = 0;
 	if (use_proxy || target.protocol[0] != 'f' /*not ftp[s]*/) {
 		/*
 		 *  HTTP session
@@ -1436,7 +1581,6 @@ IF_DESKTOP(	"no-parent\0"        No_argument       "\xf0")
 
 #if ENABLE_FEATURE_WGET_TIMEOUT
 	G.timeout_seconds = 900;
-	signal(SIGALRM, alarm_handler);
 #endif
 	G.proxy_flag = "on";   /* use proxies if env vars are set */
 	G.user_agent = "Wget"; /* "User-Agent" header field */
-- 
1.9.1

_______________________________________________
busybox mailing list
busybox@busybox.net
http://lists.busybox.net/mailman/listinfo/busybox
[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic