[PATCH 1/2] wget: replace set_alarm with non blocking functions to support retries
Martin Lewis
martin.lewis.x84 at gmail.com
Wed Dec 12 15:26:18 UTC 2018
Signed-off-by: Martin Lewis <martin.lewis.x84 at 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
More information about the busybox
mailing list