Eggenstein, Heinz-Bernd | 3 Feb 2003 17:19
Picon
Favicon

Sporadic core dumps (dangling timer??)


Hi!

I'm experiencing a mysterious problem running a relatively simple application using libwww 5.4.0 under
Digital Unix. The application is doing HTTP POST requests sequentially to a fixed host (running an Apache
Server) in a LAN. 

From time to time, I get core dumps with the same kind of symptoms: The backtrace sems to indicate that the
libwww code calls a callback function which is set to a data pointer resulting in an illegal instruction error.

The error happens when in HTEvtLst.c , 

PRIVATE int EventListTimerHandler (HTTimer * timer, void * param, HTEventType type)

is executed (with type=HTEvent_TIMEOUT ) and calls a callback function in line 229: 

...

if (sockp->timeouts[HTEvent_INDEX(HTEvent_OOB)] == timer) {

event = sockp->events[HTEvent_INDEX(HTEvent_OOB)];

HTTRACE(THD_TRACE, "Event....... OOB timed out on %d.\n" _ sockp->s);

// line 229:

return (*event->cbf) (sockp->s, event->param, HTEvent_TIMEOUT);

// =======

(Continue reading)

Timothee Besset | 3 Feb 2003 17:27
Picon

Re: Sporadic core dumps (dangling timer??)


I had a similar crash on win32. I had to add a call to 
HTEventTerminate(); before deleting my HTRequest object. Otherwise the 
timeout would trigger and try to call into old HTRequest handlers (and 
die). Not sure that applies to the select-based HTEvent stuff though.

TTimo

Eggenstein, Heinz-Bernd wrote:

>Hi!
>
>I'm experiencing a mysterious problem running a relatively simple application using libwww 5.4.0 under
Digital Unix. The application is doing HTTP POST requests sequentially to a fixed host (running an Apache
Server) in a LAN. 
>
>>From time to time, I get core dumps with the same kind of symptoms: The backtrace sems to indicate that the
libwww code calls a callback function which is set to a data pointer resulting in an illegal instruction error.
>
>The error happens when in HTEvtLst.c , 
>
>PRIVATE int EventListTimerHandler (HTTimer * timer, void * param, HTEventType type)
>
>is executed (with type=HTEvent_TIMEOUT ) and calls a callback function in line 229: 
>
>...
>
>if (sockp->timeouts[HTEvent_INDEX(HTEvent_OOB)] == timer) {
>
>event = sockp->events[HTEvent_INDEX(HTEvent_OOB)];
(Continue reading)

Arthur P. Smith | 7 Feb 2003 01:15
Favicon

Patch for HTProfil.c


We have been using the www-lib with xmlrpc in several C programs compiled
and run on Solaris 7 systems, and were receiving occasional bus errors
and core dumps that we localized to Library/src/HTEvtLst.c, in
the subroutine EventOrder_executeAndDelete().

Turning on WWW_TraceFlag we found the problem to be the re-use of a socket
that had previously been closed by HTChannel_deleteAll() in HTLibTerminate().
In between the first set of calls to the www libs and the later calls that had
the bus error, we were opening and closing various files on the system, so
when HTEventList_register was called again later, it was using a different
socket file descriptor; hence the problem.

The resolution was to make sure that HTEventList_unregisterAll() is
also called when the channels are deleted. Since xmlrpc-c was cleaning
up with a call to HTProfile_delete(), we just added that line
to that subroutine, and things work for us. However, there may be
a better location for this call. It's definitely a bug needing a patch
somewhere in lib-www. I hope this makes it into the next release!

			Arthur Smith (apsmith <at> aps.org)

cvs diff HTProfil.c
Index: HTProfil.c
===================================================================
RCS file: /sources/public/libwww/Library/src/HTProfil.c,v
retrieving revision 2.30
diff -r2.30 HTProfil.c
34a35,36
>       HTEventList_unregisterAll();
(Continue reading)

Mildred Frisco | 11 Feb 2003 02:19
Picon

error installing libwww


-----Original Message-----
From: ViewML development list [mailto:viewml-devel <at> viewml.com]On Behalf
Of Mildred Frisco
Sent: Monday, February 10, 2003 5:48 PM
To: ViewML development list
Subject: error installing libwww

Hi!  I would like to ask help in building libwww for stroongarm.  My project
is to build the viewml binary for strongarm.  I've already built jpeglib,
microwindows and flnx.  I had problems cross compiling libwww but i think
they're solved now but I have errors with make install in order to install
them to the correct directories.  Here are the commands i used in compiling
libwww.  I created the directory /arm-linux-projects/w3c-libwww-5.2.8/arm,
and under this directory i cross-compiled libwww .....

[mildred <at> arwen arm]$ ../configure --prefix=.
[mildred <at> arwen arm]$ CC=arm-linux-gcc CXX=arm-linux-g++ NM='arm-linux-nm -B'
RANLIB=arm-linux-ranlib make CPPFLAGS+=/armdir/usr/local/arm/include
CFLAGS+=/armdir/usr/local/arm/include  -e
	(note: I also set CFLAGS and CPPFLAGS so the program will look to the
arm-linux directory for header files.  what other way can I do this ?)
[mildred <at> arwen arm]$ make install
Making install in config
make[1]: Entering directory
`/arm-linux-projects/w3c-libwww-5.2.8/arm/config'
make[2]: Entering directory
`/arm-linux-projects/w3c-libwww-5.2.8/arm/config'
make[2]: Nothing to be done for `install-exec-am'.
make[2]: Nothing to be done for `install-data-am'.
(Continue reading)

Pim Tijmensen | 11 Feb 2003 10:31
Picon

Problems reading and writing to server - Mac OS 9.2

I am trying to make a connection between a Macintosh computer (Mac OS 9.2) using libwww to a Microsoft Exchange server. At the server side you can see that a connection is made but there is never any data send to the server. This means that the log-in request does not arrive...
 
The log-file of the program seems to indicate the same thing. I see a write error and a read error.
Here is a copy of a part of the logfile:
 
~~~~~~~
 
HTHost 0x0fd41f60 going to state TCP_NEED_CONNECT.
HTHost 0x0fd41f60 going to state TCP_CONNECTED.
HTHost 0x0fd41f60 connected.
Host connect Unlocking Host 0x0fd41f60
StreamStack. Constructing stream stack for text/x-http to text/xml
Tee......... Created stream 0x0fd310e0 with resolver 0x0fcc7f2c
HTTP........ Dumping response to `w3chttp.out'
Tee......... Created stream 0x0fd31100 with resolver 0x0fcc7f2c
HTTP........ Dumping request to `w3chttp.out'
Memory Free. 0x0fd28fd0
Memory Free. 0x0fd2b2c0
Memory Free. 0x0fd2dad0
Memory Free. 0x0fd28fd0
Memory Free. 0x0fd2ab70
Memory Free. 0x0fd2dad0
Memory Free. 0x0fd2b2c0
Memory Free. 0x0fd31f70
HTTP........ Generating HTTP/1.x Request Headers
HTTP........ Generating General Headers
MIME........ Generating Entity Headers
Buffer...... Flushing 0x0fd2b320
Error....... Add  73    Severity: 1 Parameter: `Mac OS error  (-1404)'  Where: `NETWRITE'
Memory Free. 0x0fd312c0
Timer....... Created one shot timer 0x0fd312c0 with callback 0x0fcc7fdc, context 0x0fd31060, and relative timeout 25
Uploading... Holding 0x0fd31060 for 25 ms using time 0x0fd312c0
Event....... Register socket 3, request 0x0fd3e9e0 handler 0x0fcc795c type HTEvent_READ at priority 20
Event....... Registering socket for HTEvent_READ
Event....... New value for MaxSock is 3
Error....... Add  73    Severity: 1 Parameter: `Mac OS error  (-1404)'  Where: `NETREAD'
Memory Free. 0x0fd310a0
Host kill... Pipeline due to HTEvent_CLOSE event
Host kill... Terminating net object 0x0fd3f720 from pipe line
HTTP Clean.. Called with status -1, net 0x0fd3f720
Memory Free. 0x0fd31bb0
Memory Free. 0x0fd31100
Memory Free. 0x0fd31120
Memory Free. 0x0fd2b2f0
Memory Free. 0x0fd31440
Memory Free. 0x0fd31180
Memory Free. 0x0fd31fa0
Timer....... Deleted active timer 0x0fd312c0
Memory Free. 0x0fd312c0
Net Object.. Delete 0x0fd3f720 and call AFTER filters
Host info... Remove 0x0fd3f720 from pipe
Host Object. closing socket 3
Channel..... Semaphore set to 0 for channel 0x0fd2b200
Event....... No more events registered for socket 3
Event....... Reset MaxSock from 3 to 1
Memory Free. 0x0fd31620
Memory Free. 0x0fd31fc0
Event....... Socket 3 unregistered for HTEvent_READ
Event....... Couldn't find socket 3. Can't unregister type HTEvent_WRITE
Channel..... Delete 0x0fd2b200 with semaphore 0, status -1
Channel..... Delete input stream 0x0fd488e0 from channel 0x0fd2b200
Memory Free. 0x0fd50d10
Memory Free. 0x0fd31b50
Memory Free. 0x0fd310e0
Channel..... Delete input stream 0x0fd488e0 from channel 0x0fd2b200
Memory Free. 0x0fd31cf0
Socket read. FREEING....
 
~~~~~~~`
 
Looks like there is a problem writing and reading so possibly the connection is not made correctly.
Could it be that I am missing some software on my macintosh or that I miss something else ?
Does anyone have an idea where I should look at to find a solution ?
 
thanks in advance...
 
 
Greetings,
 
 
Pim
Richard Atterer | 12 Feb 2003 18:34

Pausing, stopping and resuming downloads


Hello,

sometimes this mailing list is quite depressing to read, because there are
always so many people with problems, and so few people with solutions. So I
thought that since ATM I'm making some progress with my own use of libwww, 
I should share it to save others some work.

So: If an HTTP or FTP download is running, how do you pause/stop/resume it?

The bad news first: AFAICT libwww doesn't support FTP resumes (REST
command) at all. :-( Maybe I'll come up with a patch.

Pausing a download while leaving the connection open

  libwww doesn't actually make provisions for this, but it's relatively 
  easy to do (read: took me all of two days ;-/ ). You prevent the socket 
  of the download from being polled by select(), which means that the 
  connection stays open, but no data is transmitted over it. This is nice 
  because continuing is faster and because some servers (and some proxies 
  like WWWOFFLE) don't support HTTP ranges, so resuming the download in a 
  new connection isn't possible.

  For me, the following works with the glibwww event loop:

    /* The HTNet object whose socket we'll unregister from the event loop. This
       will prevent more data from being delivered to it, effectively 
       pausing the request. */
    HTNet* net = HTRequest_net(request);

    unsigned protocol = HTProtocol_id(HTNet_protocol(net));
    if (protocol == 21) {
      /* Protocol is FTP, which uses a control connection (which 
         corresponds to the main HTNet object) and a data connection. We 
         need the HTNet object for the latter. */
      ftp_ctrl* ctrl = static_cast<ftp_ctrl*>(HTNet_context(net));
      net = ctrl->dnet;
    }

    // Unregister socket
    HTEvent_setTimeout(HTNet_event(net), -1); // No timeout for the socket
    SOCKET socket = HTNet_socket(net);
    HTEvent_unregister(socket, HTEvent_READ);

  Unfortunately, this is a bit ugly: To make the FTP stuff work, you need 
  to copy the definitions of enum _HTFTPState and struct _ftp_ctrl from 
  HTFTP.c to your application's code.

Continuing a download whose connection is still open

  Analogous to the above, except we register the socket:

    // Register socket again
    /* For some weird reason the timeout gets reset to 0 somewhere, which
       causes *immediate* timeouts with glibwww - fix that. */
    HTEvent* event = HTNet_event(net);
    HTEvent_setTimeout(event, HTHost_eventTimeout());
    HTEvent_register(HTNet_socket(net), HTEvent_READ, event);

  Obviously, all requests in a HTTP pipeline get paused with this, not just
  the current one. In particular, the paused request may only be pending;
  in this case, the transmission of an earlier request in the same pipeline
  may actually get paused - not what we want. I partially solve this by
  pausing only the moment my request actually receives data via the write()
  method of the HTStream object I registered as the request's output
  stream.

  Immediately after continuing, the connection may be dropped, e.g. if the
  user paused, disconnected his modem, dialed in again, and then told the
  app to continue. Consequently, when an app uses this "soft pause", it 
  should also be able to do a full resume with a new connection.

Aborting a download

  There are a few minor pitfalls here. HTHost_killPipe closes all sockets 
  for that host instead of just the one of the connection, so use

    HTNet_killPipe(HTRequest_net(request))

  If your app crashes when you call this, it is because you're calling it
  from within the write() method of your stream - libwww doesn't like it if
  you delete the request object etc. from "right underneath its feet" while
  it's still processing the data that has just arrived on the request's
  socket.

  Instead, wait until the main event loop is reached again, and *then* kill 
  the pipe. With glibwww, this is easy to do because you can use 
  g(tk)_idle_add() to register a function which will be called back once 
  the main loop becomes idle.

  Obviously, this kills all requests in the pipeline, so you should
  re-schedule all the ones which you do want, or resume them if they were
  already downloaded in part. (AFAICT, the HTTP 1.1 standard doesn't allow
  you to selectively cancel just some pending or active requests, so the
  only thing libwww can do is to close the connection.)

How do I tell whether the download has succeeded/failed?

  HTAlert_setInteractive(YES);
  HTAlert_add(myAlertCallback, HT_A_PROGRESS);
  and pay attention to the HTAlertOpcode passed to myAlertCallback()

How do I distinguish between the connection being dropped due to an error
and the end of the transmission?

  With FTP, AFAIK you can't tell, unless you scan the directory listing 
  first to find the file size. (Haven't explored how difficult this would 
  be.)

  With HTTP downloads, the server will /usually/ have sent a Content-Length
  header, so you can check whether the promised number of bytes has already
  been received. Do *not* use HTAnchor_length(HTRequest_anchor(request)) to
  read the number of bytes, because for some reason this is not set up
  correctly for "206 Partial Content" responses. Instead, use

    HTResponse_length(HTRequest_response(request))

  which, for a 206, returns the number of bytes in the partial request, 
  i.e. total length - requested start offset.

Resuming a download starting with a certain byte offset.

  Requires HTTP ranges (i.e. HTTP 1.1) . Before starting the download with
  HTLoad(request, NO), use

    HTRequest_addRange(request, "bytes", "333-999")

  to fetch bytes 333-999 (both inclusive, starting from 0), you can also
  use just "333-" to fetch from byte 333 onwards.

  Beware that a non-HTTP 1.1 aware server may just ignore the range request
  and send the data starting with offset 0 - to detect this case, you need
  to check whether the server sent a Content-Range header like
  "Content-Range: bytes 303104-17242732/17242733". The header is present if

    HTResponse_range(HTRequest_response(request))

  returns non-null. This only works for HTTP, i.e. if
  HTProtocol_id(HTNet_protocol(HTRequest_net(request))) == 80.

Further problem with FTP: "REIN"

  libwww doesn't behave correctly with my test FTP server (running
  OpenBSD's ftpd): When libwww wants to reuse an existing control
  connection, it first issues REIN, which the server doesn't understand
  ("502 REIN command not implemented."). Next, it thinks it has to send
  "USER anonymous" again, which doesn't work either ("530 Can't change user
  from guest login.") At this point libwww gives up, when it could actually
  just proceed with the RETR. Grr... patch forthcoming.

  
All of the above is based on work on my program "jigdo" - see the
"download.cc" file in its sources. (But wait until the next release, the
current 0.6.9 code doesn't yet have all the pause/resume code.)

Cheers,

  Richard

--

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

Ofir Gefen | 12 Feb 2003 18:00

Error compiling libwww on Sun Solaris 2.5.6


Hi,

I have downloaded the libwww Unix tarball and extracted the data. I followed the
instructions and ran "configure" then GNU Make (version 3.79.1) from the root
directory of the library.

I'm getting the following error:
gmake[4]: Entering directory `/w3c-libwww-5.4.0/modules/expat/xmltok'
/bin/sh ../../../libtool --mode=compile
-DHAVE_CONFIG_H -I. -I. -I../../..     -g -O2 -Wall -DXML_NS -c xmlrole.c
.../../../libtool: test: argument expected
gmake[4]: *** [xmlrole.lo] Error 1
gmake[4]: Leaving directory `/w3c-libwww-5.4.0/modules/expat/xmltok'
gmake[3]: *** [all-recursive] Error 1
gmake[3]: Leaving directory `/w3c-libwww-5.4.0/modules/expat'
gmake[2]: *** [all-recursive] Error 1
gmake[2]: Leaving directory `/w3c-libwww-5.4.0/modules'
gmake[1]: *** [all-recursive] Error 1
gmake[1]: Leaving directory `/w3c-libwww-5.4.0'
gmake: *** [all-recursive-am] Error 2

Can someone help me figure out what is the problem and how to fix it?

Thanks
   Ofir

Richard Atterer | 13 Feb 2003 13:12

Re: Error compiling libwww on Sun Solaris 2.5.6


On Wed, Feb 12, 2003 at 12:00:42PM -0500, Ofir Gefen wrote:
> Can someone help me figure out what is the problem and how to fix it?

Hm, the Solaris /bin/sh is quite limited. Try configuring with bash, by 
using

  CONFIG_SHELL=/path/to/bin/bash ./configure ...

Cheers,

  Richard

--

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

Richard Atterer | 13 Feb 2003 16:25

HTFTP: Don't fail if server doesn't understand REIN [patch]


On Wed, Feb 12, 2003 at 06:34:29PM +0100, Richard Atterer wrote:
> Further problem with FTP: "REIN"
> 
>   libwww doesn't behave correctly with my test FTP server (running
>   OpenBSD's ftpd): When libwww wants to reuse an existing control
>   connection, it first issues REIN, which the server doesn't understand
>   ("502 REIN command not implemented."). Next, it thinks it has to send
>   "USER anonymous" again, which doesn't work either ("530 Can't change user
>   from guest login.") At this point libwww gives up, when it could actually
>   just proceed with the RETR. Grr... patch forthcoming.

...and here is the patch. To apply it, pipe this mail into
patch -d somepath/w3c-libwww-5.4.0/Library/src

I have taken care not to change the behaviour: libwww still tries REIN, but 
if that fails *and* a subsequent USER also fails, it just assumes that 
everything is OK, and does the RETR.

This way, if a server doesn't understand REIN, but it does need the
subsequent USER, everything will still work. (Do some FTP servers really
behave this way?)

This patch does not fix the problem if a server doesn't like REIN *and* it
also doesn't support "RETR /path/to/file", because during the first
retrieval, libwww will CWD to the correct subdirectory to do just "RETR
file" - but later, it will have forgotten that the current dir is still 
/path/to.

All the best,

  Richard

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

--- HTFTP.c.orig	2003-01-13 18:12:44.000000000 +0100
+++ HTFTP.c	2003-02-12 17:17:47.000000000 +0100
 <at>  <at>  -542,6 +542,7  <at>  <at> 
 	NEED_GREETING,
 	NEED_REIN,
 	NEED_UID,
+	NEED_UID_NO_REIN,
 	NEED_PASSWD,
 	NEED_ACCOUNT,
 	PROMPT_USER
 <at>  <at>  -596,7 +597,7  <at>  <at> 
 		    */
 		    if ((ctrl->repcode/100 == 2) || (ctrl->repcode == 502)) {
 			ctrl->substate = (ctrl->uid && *ctrl->uid) ?
-			    NEED_UID : PROMPT_USER;
+			    NEED_UID_NO_REIN : PROMPT_USER;/*_NO_REIN?*/
 		    } else {
 			ctrl->substate = SUB_SUCCESS;	    /* hope the best */
 		    }
 <at>  <at>  -609,6 +610,11  <at>  <at> 

 	  case NEED_UID:
 	    HTTRACE(PROT_TRACE, "FTP Login... now in state NEED_UID\n");
+	  case NEED_UID_NO_REIN:
+	    if (ctrl->substate == NEED_UID_NO_REIN) {
+		HTTRACE(PROT_TRACE, "FTP Login... now in state "
+			"NEED_UID_NO_REIN\n");
+	    }
 	    if (!ctrl->sent) {
 		status = SendCommand(request, ctrl, "USER", ctrl->uid);
 		if (status == HT_WOULD_BLOCK)
 <at>  <at>  -632,6 +638,10  <at>  <at> 
 		    /* } else if (ctrl->repcode == 530) */
 			/* ctrl->substate = PROMPT_USER;*/        /* User unknown */
 		    } else if (ctrl->repcode == 530) {
+			/* If REIN failed and a subsequent USER also failed,
+			   just assume that we're still logged in. */
+			if (ctrl->substate == NEED_UID_NO_REIN)
+			    ctrl->alreadyLoggedIn = YES;
 			if (ctrl->alreadyLoggedIn == YES) {
 			    ctrl->substate = SUB_SUCCESS;
 			    HTTRACE(PROT_TRACE, "FTP Login... Already logged in\n");

Richard Atterer | 13 Feb 2003 16:47

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
 <at>  <at>  -24,6 +24,13  <at>  <at> 
 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
 <at>  <at>  -55,6 +55,7  <at>  <at> 
 **      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
 <at>  <at>  -1093,6 +1094,7  <at>  <at> 
 	NEED_SELECT = 0,
 	NEED_CONNECT,
 	NEED_ACCEPT,
+	NEED_REST,
 	NEED_ACTION,
         NEED_CWD,
 	NEED_SEGMENT,
 <at>  <at>  -1105,7 +1107,7  <at>  <at> 
 	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:
 <at>  <at>  -1116,7 +1118,7  <at>  <at> 
 	    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);
 <at>  <at>  -1140,6 +1142,67  <at>  <at> 
 		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) {
 <at>  <at>  -1202,7 +1265,7  <at>  <at> 
 			HTCleanTelnetString(segment);
 			ctrl->substate = NEED_CWD;
 		    } else
-			ctrl->substate = NEED_ACTION;
+			ctrl->substate = NEED_REST;
 		}
 	    }
 	    break;


Gmane