On Sun, 9 Jan 2011, Ted Unangst wrote:

> Downloading things can go a lot faster if the server and client support 
> http compression.  This is easily added to the ftp program's http support.
> 
> It consists of two parts.  Support for deflating the data we receive and 
> support for the chunked transfer the server will use to send data to us.  
> The chunked supported is probably useful in its own right as well.

Upon further reflection, I realize it's possible for a server to send 
gzipped data that's not chunked.  The diff below is a better version that 
should be able to handle all combinations of chunked, gzipped, both, or 
none.

Index: Makefile
===================================================================
RCS file: /home/tedu/cvs/src/usr.bin/ftp/Makefile,v
retrieving revision 1.25
diff -u -r1.25 Makefile
--- Makefile    5 May 2009 19:35:30 -0000       1.25
+++ Makefile    9 Jan 2011 21:14:51 -0000
@@ -17,8 +17,8 @@
 
 CPPFLAGS+= -DINET6
 
-LDADD+=        -ledit -lcurses -lutil -lssl -lcrypto
-DPADD+=        ${LIBEDIT} ${LIBCURSES} ${LIBUTIL}
+LDADD+=        -ledit -lcurses -lutil -lssl -lcrypto -lz
+DPADD+=        ${LIBEDIT} ${LIBCURSES} ${LIBUTIL} ${LIBZ}
 LDSTATIC= ${STATIC}
 
 #COPTS+= -Wall -Wconversion -Wstrict-prototypes -Wmissing-prototypes
Index: fetch.c
===================================================================
RCS file: /home/tedu/cvs/src/usr.bin/ftp/fetch.c,v
retrieving revision 1.103
diff -u -r1.103 fetch.c
--- fetch.c     25 Aug 2010 20:32:37 -0000      1.103
+++ fetch.c     9 Jan 2011 21:38:27 -0000
@@ -63,6 +63,7 @@
 #ifndef SMALL
 #include <openssl/ssl.h>
 #include <openssl/err.h>
+#include <zlib.h>
 #else /* !SMALL */
 #define SSL void
 #endif /* !SMALL */
@@ -167,6 +168,80 @@
        return (epath);
 }
 
+#ifndef SMALL
+static size_t
+chunked_read(FILE *fin, SSL *ssl, unsigned char *buf, size_t amt,
+    int chunked, int gzipped)
+{
+       static int chunksize;
+       static int zinit;
+       static z_stream zctx;
+       static unsigned char zbuf[4096];
+
+       size_t chunkbuflen;
+       char *chunkbuf;
+       size_t len, zlen, zamt;
+       int rv;
+
+       if (chunked && !chunksize) {
+               chunkbuf = ftp_readline(fin, ssl, &chunkbuflen);
+               if (!chunkbuf) {
+                       warnx("no chunk size");
+                       return 0;
+               }
+               chunksize = strtol(chunkbuf, NULL, 16);
+       }
+       if (gzipped) {
+               if (zinit == -1) {
+                       zinit = 0;
+                       return 0;
+               }
+               if (zctx.avail_in == 0) {
+                       zamt = sizeof(zbuf);
+                       if (chunked && zamt > chunksize)
+                               zamt = chunksize;
+                       zlen = ftp_read(fin, ssl, zbuf, zamt);
+                       if (chunked) {
+                               chunksize -= zlen;
+                               /* eat empty line after chunk */
+                               if (chunksize == 0)
+                                       ftp_readline(fin, ssl, &chunkbuflen);
+                       }
+                       zctx.avail_in = zlen;
+                       zctx.next_in = zbuf;
+               }
+               if (!zinit) {
+                       rv = inflateInit2(&zctx, 15 + 32);
+                       zinit = 1;
+               }
+               zctx.next_out = buf;
+               zctx.avail_out = amt;
+               if ((rv = inflate(&zctx, Z_NO_FLUSH)) != Z_OK) {
+                       if (rv == Z_STREAM_END) {
+                               inflateEnd(&zctx);
+                               zinit = -1;
+                       } else {
+                               warnx("inflate failed %d", rv);
+                               return 0;
+                       }
+               }
+               len = zctx.next_out - buf;
+       } else if (chunked) {
+               if (amt > chunksize)
+                       amt = chunksize;
+               len = ftp_read(fin, ssl, buf, amt);
+               chunksize -= len;
+               if (chunksize == 0) /* eat empty line after chunk */
+                       ftp_readline(fin, ssl, &chunkbuflen);
+       } else {
+               len = ftp_read(fin, ssl, buf, amt);
+       }
+
+       return len;
+}
+#endif /* SMALL */
+
+
 /*
  * Retrieve URL, via the proxy in $proxyvar if necessary.
  * Modifies the string argument given.
@@ -195,6 +270,8 @@
        const char *scheme;
        int ishttpsurl = 0;
        SSL_CTX *ssl_ctx = NULL;
+       int chunked = 0;
+       int gzipped = 1;
 #endif /* !SMALL */
        SSL *ssl = NULL;
        int status;
@@ -607,9 +684,11 @@
 #endif /* !SMALL */
                ftp_printf(fin, ssl, "GET /%s %s\r\nHost: ", epath,
 #ifndef SMALL
-                       restart_point ? "HTTP/1.1\r\nConnection: close" :
+                       "HTTP/1.1\r\nConnection: close"
+#else
+                       "HTTP/1.0"
 #endif /* !SMALL */
-                       "HTTP/1.0");
+                       );
                if (strchr(host, ':')) {
                        char *h, *p;
 
@@ -638,6 +717,7 @@
                if (restart_point)
                        ftp_printf(fin, ssl, "\r\nRange: bytes=%lld-",
                                (long long)restart_point);
+               ftp_printf(fin, ssl, "\r\nAccept-Encoding: gzip");
 #else /* !SMALL */
                if (port && strcmp(port, "80") != 0)
                        ftp_printf(fin, ssl, ":%s", port);
@@ -753,6 +833,17 @@
 #ifndef SMALL
                        if (restart_point)
                                filesize += restart_point;
+#define TRANSENC "Transfer-Encoding: "
+               } else if (strncasecmp(cp, TRANSENC, sizeof(TRANSENC)-1) == 0) {
+                       cp += sizeof(TRANSENC) - 1;
+                       if (strcasecmp(cp, "chunked") == 0)
+                               chunked = 1;
+#define CONTENTENC "Content-Encoding: "
+               } else if (strncasecmp(cp, CONTENTENC, sizeof(CONTENTENC) -1 )
+                   == 0) {
+                       cp += sizeof(CONTENTENC) - 1;
+                       if (strcasecmp(cp, "gzip") == 0)
+                               gzipped = 1;
 #endif /* !SMALL */
 #define LOCATION "Location: "
                } else if (isredirect &&
@@ -853,7 +944,13 @@
        len = 1;
        oldinti = signal(SIGINFO, psummary);
        while (len > 0) {
-               len = ftp_read(fin, ssl, buf, 4096);
+#ifndef SMALL
+               if (chunked || gzipped)
+                       len = chunked_read(fin, ssl, buf, 4096,
+                           chunked, gzipped);
+               else
+#endif
+                       len = ftp_read(fin, ssl, buf, 4096);
                bytes += len;
                for (cp = buf, wlen = len; wlen > 0; wlen -= i, cp += i) {
                        if ((i = write(out, cp, wlen)) == -1) {

Reply via email to