HTFTP: Support for resuming downloads with "REST" [patch]

On Wed, Feb 12, 2003 at 06:34:29PM +0100, Richard Atterer wrote:
> The bad news first: AFAICT libwww doesn't support FTP resumes (REST
> command) at all. :-( Maybe I'll come up with a patch.

Yes, here it is! Again, to apply it, pipe this mail into
  patch -d somepath/w3c-libwww-5.4.0/Library/src
It will apply with or without the previous patch, although there may be a 
few "Hunk succeeded at offset -10 lines" messages.

Jose/anybody, please apply this, I consider it a quite important feature!

Cheers,

  Richard

-- 
  __   _
  |_) /|  Richard Atterer     |  CS student at the Technische  |  GnuPG key:
  | \/¯|  http://atterer.net  |  Universität München, Germany  |  0x888354F7
  ¯ '` ¯

--- HTFTP.h.orig	2003-01-13 18:12:46.000000000 +0100
+++ HTFTP.h	2003-02-13 16:33:53.000000000 +0100
@@ -24,6 +24,13 @@
 a part of the W3C
 Sample Code Library.
 
+The module supports partial downloads by using the FTP "REST" command. To
+initiate such a partial request, proceed the same way as with an HTTP
+request; use something like HTRequest_addRange(request, "bytes", "12345-"). 
+HTFTP only supports ranges without an end offset, a range like "12345-20000"
+will be ignored. If the "REST" command was successful, an appropriate range
+entry is added to the request's response object.
+
 */
 
 #ifndef HTFTP_H
--- HTFTP.c.orig	2003-02-12 17:39:04.000000000 +0100
+++ HTFTP.c	2003-02-13 00:23:46.000000000 +0100
@@ -55,6 +55,7 @@
 **      Jan 2000 JB     Joe Bester - Fixed the protocol bug that appeared after
 **                      after the CERT advisory warning on wu. Added code
 **                      to do an incremental streaming of FTP data.
+**      Feb 2003        Richard Atterer - added REST support
 **                       
 ** Notes:
 **     			Portions Copyright 1994 Trustees of Dartmouth College
@@ -1093,6 +1094,7 @@
 	NEED_SELECT = 0,
 	NEED_CONNECT,
 	NEED_ACCEPT,
+	NEED_REST,
 	NEED_ACTION,
         NEED_CWD,
 	NEED_SEGMENT,
@@ -1105,7 +1107,7 @@
 	switch ((state) ctrl->substate) {
 	  case NEED_SELECT:
 	    HTTRACE(PROT_TRACE, "FTP Get Data now in state NEED_SELECT\n");
-	    ctrl->substate = data->pasv ? NEED_CONNECT : NEED_ACTION;
+	    ctrl->substate = data->pasv ? NEED_CONNECT : NEED_REST;
 	    break;
 
 	  case NEED_CONNECT:
@@ -1116,7 +1118,7 @@
 	    else if (status == HT_OK) {
 		HTTRACE(PROT_TRACE, "FTP Get Data Active data socket %d\n" _ 
 			    HTNet_socket(dnet));
-		ctrl->substate = NEED_ACTION;
+		ctrl->substate = NEED_REST;
 	    } else {			 	  /* Swap to PORT on the fly */
 		NETCLOSE(HTNet_socket(dnet));
 		HTNet_setSocket(dnet, INVSOC);
@@ -1140,6 +1142,67 @@
 		ctrl->substate = SUB_ERROR;
 	    break;
 
+	  case NEED_REST:
+	    HTTRACE(PROT_TRACE, "FTP Get Data now in state NEED_REST\n");
+	    if (!ctrl->sent) {
+                ctrl->substate = NEED_ACTION;
+                /* If the user added a Range header to the request and that
+                   header is a single "from offset x to the end", transform
+                   that into a REST command. */
+                HTAssocList* ranges = HTRequest_range(request);
+                HTAssoc* r1, * r2;
+                const char* rangeVal, * s;
+                int len = 0;
+                char* chunkData;
+                HTStream* input;
+
+                /* Do nothing unless exactly one range item present. */
+                if (ranges == 0) break; 
+                r1 = HTAssocList_nextObject(ranges);
+                if (r1 == 0) break;
+                r2 = HTAssocList_nextObject(ranges);
+                if (r2 != 0) break;
+                /* Do nothing unless assoc reads: "bytes" => "12345-" */
+                if (strcmp(HTAssoc_name(r1), "bytes") != 0) break;
+                rangeVal = HTAssoc_value(r1);
+                s = rangeVal;
+                if (*s < '0' || *s > '9') break;
+                while (*++s >= '0' && *s <= '9') len += *s - '0';
+                if (*s != '-' || s[1] != '\0' || len == 0) break;
+
+                /* send: "REST 12345" */
+                len = 4 + 1 + (s - rangeVal) + 2;
+                HTChunk_setSize(ctrl->cmd, len + 1);
+                chunkData = HTChunk_data(ctrl->cmd);
+                sprintf(chunkData, "REST %s%c", rangeVal, LF);
+                chunkData[len - 2] = CR; /* Overwrite '-' */
+                input = HTRequest_inputStream(request);
+                HTTRACE(PROT_TRACE, "FTP Get Data Tx...... %s" _ chunkData);
+                status = (*input->isa->put_block)(input, chunkData, len);
+		if (status == HT_WOULD_BLOCK)
+		    return HT_WOULD_BLOCK;
+		else if (status == HT_ERROR)
+		    ctrl->substate = SUB_ERROR;
+		ctrl->sent = YES;
+                ctrl->substate = NEED_REST; /* Wait for reply */
+            } else {
+		status = HTHost_read(HTNet_host(cnet), cnet);
+		if (status == HT_WOULD_BLOCK) {
+		    return HT_WOULD_BLOCK;
+                } else if (status == HT_LOADED && ctrl->repcode == 350) {
+                    /* Add appropriate range header to response object */
+                    HTResponse* resp = HTRequest_response(request);
+                    HTAssocList* ranges = HTRequest_range(request);
+                    HTAssoc* r1 = HTAssocList_nextObject(ranges);
+                    HTResponse_addRange(resp, "bytes", HTAssoc_value(r1));
+                    ctrl->substate = NEED_ACTION;
+                } else {
+		    ctrl->substate = SUB_ERROR;
+                }
+		ctrl->sent = NO;
+            }
+	    break;
+
 	  case NEED_ACTION:
 	    HTTRACE(PROT_TRACE, "FTP Get Data now in state NEED_ACTION\n");
 	    if (!ctrl->sent) {
@@ -1202,7 +1265,7 @@
 			HTCleanTelnetString(segment);
 			ctrl->substate = NEED_CWD;
 		    } else
-			ctrl->substate = NEED_ACTION;
+			ctrl->substate = NEED_REST;
 		}
 	    }
 	    break;

Received on Thursday, 13 February 2003 14:46:07 UTC