[PATCH] httpd: request file range support

Chris Paulson-Ellis chris at edesix.com
Fri Oct 12 01:41:45 PDT 2007


This patch adds a new FEATURE_HTTPD_RANGE supporting partial file downloads, requested with the HTTP Range header.

I have tested all 4 combinations of FEATURE_HTTPD_RANGE & FEATURE_HTTPD_USE_SENDFILE.

Regards,
Chris.

---

--- busybox-1.7.2/networking/Config.in.orig	2007-09-03 12:48:27.000000000 +0100
+++ busybox-1.7.2/networking/Config.in	2007-10-11 17:54:08.000000000 +0100
@@ -183,6 +183,14 @@
 	  '/path/e404.html' file instead of the terse '404 NOT FOUND'
 	  message.
 
+config FEATURE_HTTPD_RANGE
+	bool "Enable support for partial file requests"
+	default n
+	depends on HTTPD
+	help
+	  This option adds support for the HTTP Range header. Only
+	  a single range per request in byte units is supported.
+
 config IFCONFIG
 	bool "ifconfig"
 	default n
--- busybox-1.7.2/networking/httpd.c.orig	2007-09-30 00:54:12.000000000 +0100
+++ busybox-1.7.2/networking/httpd.c	2007-10-11 17:52:02.000000000 +0100
@@ -253,6 +253,10 @@
 #if ENABLE_FEATURE_HTTPD_ERROR_PAGES
 	const char *http_error_page[ARRAY_SIZE(http_response_type)];
 #endif
+#if ENABLE_FEATURE_HTTPD_RANGE
+	off_t range_start;      /* -1 - unspecified */
+	off_t range_end;        /* -1 - unspecified */
+#endif
 };
 #define G (*ptr_to_globals)
 #define verbose           (G.verbose          )
@@ -279,11 +283,15 @@
 #define hdr_ptr           (G.hdr_ptr          )
 #define hdr_cnt           (G.hdr_cnt          )
 #define http_error_page   (G.http_error_page  )
+#define range_start       (G.range_start      )
+#define range_end         (G.range_end        )
 #define INIT_G() do { \
 	PTR_TO_GLOBALS = xzalloc(sizeof(G)); \
 	USE_FEATURE_HTTPD_BASIC_AUTH(g_realm = "Web Server Authentication";) \
 	bind_addr_or_port = "80"; \
 	ContentLength = -1; \
+	USE_FEATURE_HTTPD_RANGE(range_start = -1;) \
+	USE_FEATURE_HTTPD_RANGE(range_end = -1;) \
 } while (0)
 
 
@@ -921,6 +929,30 @@
 	}
 #endif
 
+#if ENABLE_FEATURE_HTTPD_RANGE
+	if( ContentLength == -1 ) {
+		range_start = -1;
+		range_end = -1;
+	} else if (range_start == -1 && range_end != -1) {
+		/* range_end is suffix length */
+		range_start = ContentLength - range_end;
+		range_end = ContentLength - 1;
+	} else if (range_start != -1 && range_end == -1) {
+		range_end = ContentLength - 1;
+	}
+
+	if (range_start != -1 && range_end != -1) {
+		if (range_end >= ContentLength || range_end < range_start) {
+			range_start = -1;
+			range_end = -1;
+		} else {
+			len += sprintf(iobuf + len, "Content-Range: bytes %"OFF_FMT"d-%"OFF_FMT"d/%"OFF_FMT"d\r\n",
+				range_start, range_end, ContentLength);
+			ContentLength = range_end - range_start + 1;
+		}
+	}
+#endif
+
 	if (ContentLength != -1) {    /* file */
 		strftime(tmp_str, sizeof(tmp_str), RFC1123FMT, gmtime(&last_mod));
 		len += sprintf(iobuf + len, "Last-Modified: %s\r\n%s %"OFF_FMT"d\r\n",
@@ -1372,6 +1404,7 @@
 	const char *const *table;
 	const char *try_suffix;
 	ssize_t count;
+	size_t remaining;
 #if ENABLE_FEATURE_HTTPD_USE_SENDFILE
 	off_t offset = 0;
 #endif
@@ -1424,24 +1457,45 @@
 	signal(SIGPIPE, SIG_IGN);
 
 #if ENABLE_FEATURE_HTTPD_USE_SENDFILE
+#if ENABLE_FEATURE_HTTPD_RANGE
+	if (range_start != -1) {
+		offset = range_start;
+		remaining = ContentLength;
+	} else
+#endif
+		remaining = MAXINT(ssize_t) - 0xffff;
 	do {
 		/* byte count (3rd arg) is rounded down to 64k */
-		count = sendfile(1, f, &offset, MAXINT(ssize_t) - 0xffff);
+		count = sendfile(1, f, &offset, remaining);
 		if (count < 0) {
 			if (offset == 0)
 				goto fallback;
 			goto fin;
 		}
+#if ENABLE_FEATURE_HTTPD_RANGE
+		if (range_start != -1)
+			remaining -= count;
+#endif
 	} while (count > 0);
 	log_and_exit();
 
  fallback:
 #endif
-	while ((count = safe_read(f, iobuf, IOBUF_SIZE)) > 0) {
+#if ENABLE_FEATURE_HTTPD_RANGE
+	if (range_start != -1) {
+		remaining = ContentLength;
+	} else
+#endif
+		remaining = IOBUF_SIZE;
+	while ((count = safe_read(f, iobuf, MIN(remaining, IOBUF_SIZE))) > 0) {
 		ssize_t n = count;
 		count = full_write(1, iobuf, count);
 		if (count != n)
 			break;
+#if ENABLE_FEATURE_HTTPD_RANGE
+		if (range_start != -1)
+			remaining -= count;
+#endif
 	}
 #if ENABLE_FEATURE_HTTPD_USE_SENDFILE
  fin:
@@ -1585,7 +1639,6 @@
 	int ip_allowed;
 #if ENABLE_FEATURE_HTTPD_CGI
 	const char *prequest;
-	unsigned long length = 0;
 	char *cookie = 0;
 	char *content_type = 0;
 #endif
@@ -1593,6 +1646,12 @@
 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH
 	int credentials = -1;  /* if not required this is Ok */
 #endif
+#if ENABLE_FEATURE_HTTPD_CGI || ENABLE_FEATURE_HTTPD_RANGE
+	unsigned long length = 0;
+#endif
+#if ENABLE_FEATURE_HTTPD_RANGE
+	char *eptr;
+#endif
 
 	/* Allocation of iobuf is postponed until now
 	 * (IOW, server process doesn't need to waste 8k) */
@@ -1788,6 +1847,48 @@
 				credentials = checkPerm(urlcopy, tptr);
 			}
 #endif          /* FEATURE_HTTPD_BASIC_AUTH */
+#if ENABLE_FEATURE_HTTPD_RANGE
+			if (STRNCASECMP(iobuf, "Range:") == 0) {
+				/*
+				 * Don't support multiple ranges, only one of
+				 *		Range: bytes=first-last
+				 *		Range: bytes=first-
+				 *		Range: bytes=-suffixLen
+				 *
+				 * Ignore unparsable Range headers (whole file sent).
+				 */
+				tptr = skip_whitespace(iobuf + sizeof("Range:")-1);
+				if (STRNCASECMP(tptr, "bytes=") != 0)
+					continue;
+				tptr += sizeof("bytes=")-1;
+				if (tptr[0] == '-') {
+					++tptr;
+				} else if (!isdigit(tptr[0])) {
+					continue;
+				} else {
+					errno = 0;
+					length = strtoul(tptr, &eptr, 10);
+					if (errno || eptr == tptr || length > INT_MAX || eptr[0] != '-' )
+						continue;
+					range_start = length;
+					tptr = eptr + 1;
+				}
+				if (!tptr[0]) {
+					continue;
+				} else if (!isdigit(tptr[0])) {
+					range_start = -1;
+					continue;
+				} else {
+					errno = 0;
+					length = strtoul(tptr, &eptr, 10);
+					if (errno || eptr == tptr || length > INT_MAX || eptr[0] ) {
+						range_start = -1;
+						continue;
+					}
+					range_end = length;
+				}
+			}
+#endif          /* FEATURE_HTTPD_RANGE */
 		} /* while extra header reading */
 	}
 



More information about the busybox mailing list