Actually, I added a mime-type so that doesn't entirely work.

My temporary and possibly incorrect fix is to modify url.tcl.  Here is a new
version of Url_ReadPost.  Note the change in the while loop.  I consider
this a temporary fix.

There are at least two problems here.  First, it should be true that timing
out a transaction should return a 408 error rather than 500.  I don't see
how to do this minimally since Url_ReadPost is called from all over the
place.  Secondly, and possibly more seriously, I don't know what other state
I need to save before doing the vwait.

A much less serious worry is that under overload conditions, the stack can
start getting deeper than I like since entering the event loop in
Url_ReadPost can ultimately call Url_ReadPost to be called again and so on.
This means that if the data from the body shows up, the return could be
delayed pretty much infinitely.  This would only happen if the server is
going down the tubes anyway, though.

proc Url_ReadPost {sock varname} {
    upvar 1 $varname query
    global Url Httpd Url_ContinueFlag

    append query ""
    if {[info exist Url(postlength)] && ($Url(postlength) > 0)} {
        
        # For compatibility with older versions of the Httpd module
        # that used to read all the post data for us, we read it now
        # if it hasn't already been read

        set result $Url(postlength)
        if {[string length $query]} {
            # This merges query data from the GET/POST URL
            append query &
        }

        set postlength $Url(postlength)
        set t0 [clock clicks -milliseconds]
        unset Url(postlength)
        while {$postlength > 0} {
            set postlength [Httpd_GetPostData $sock query]
            after 100 {set Url_ContinueFlag 1}
            vwait Url_ContinueFlag
            if {[clock clicks -milliseconds]-$t0 > $Httpd(timeout2)} break
        }
        if {$postlength > 0} {
            error "Transaction timeout"
        }
        return $result
    } else {
        return 0
    }
}

-----Original Message-----
From: Brent Welch [mailto:[EMAIL PROTECTED]]
Sent: Thursday, October 12, 2000 11:46 AM
To: Ted Dunning
Cc: 'TCL-HTTPD '; Amir Doron; Brad Kindig
Subject: Re: Serious bug in Url_DecodeQuery/HttpReadPost -- client can
cause n ear infinite hang


There is an asynchronous interface to read the post data in
Httpd_GetPostDataAsync - 
What should happen is that Url_ReadPost should be changed to use
that interface.  Urk - it ought to happen sooner.  

I think the best solution
is to make it a domain-level property as to whether post data is read
before the domain implementation is called.  I'll add a switch to
3.2 Url_PrefixInstall that implements this.  Then it'll all be
asyncrhonous and transparent to the domain.  I've got a bit of spare
time at the moment and will try to get 3.2 out quickly.

If you are implementing a custom domain handler, then it can do it
itself for the moment.

>>>Ted Dunning said:
 >       
 > 
 > I have found that a poorly behaving client can cause a hang in the
 > tcl-httpd.  This hang prevents any other connections and results from
 > tcl-httpd getting stuck in a very tight loop.  This hang should resolve
if
 > the client closes the connection, but tcl-httpd will not be able to
resolve
 > the problem itself.
 > 
 > This problem occurs when a post is done with something like the following
 > sequence:
 > 
 >     set f [socket $server $port]
 >     puts $f "POST $url HTTP/1.0"
 >     puts $f "Content-length: [string length $body]"
 >     puts $f ""
 >     flush $f
 >     after 200
 >     puts $f [string range $body 0 100]
 >     flush $f
 >     after $hellFreezesOver
 >     puts $f [string range $body 101 end]
 >     flush $f
 > 
 > What happens is that the body is not read using the event loop because
the
 > data is not ready after the headers are read.  Instead, reading the body
is
 > deferred.  Then when Url_DecodeQuery is called, it attempts to read the
rest
 > of the body by spinning in a loop that looks like this:
 > 
 >     while {$Url(postlength) > 0} {
 >      set Url(postlength) [Httpd_GetPostData $sock query]
 >     }
 > 
 > Httpd_GetPostData ultimately causes read on a non-blocking socket.  After
 > reading the first bit of body, this read returns nothing since the client
 > doesn't send anything more.  Thus, tcl-httpd will spin infinitely by
calling
 > read repeatedly with no effect.  If the client finally sends the data or
 > closes the socket, the server will be released.  The socket will not,
 > however, time out since the after cancel for that socket will never be
 > activated.
 > 
 > Older versions of tcl-httpd didn't have this problem since they read the
 > body of a transaction greedily in the event loop.  If the client never
sent
 > the data, then there would be no read events on that socket and the
cancel
 > would eventually be called, clearing out the mess.  This design was
deemed
 > problematic since a page could never send results until they entire body
was
 > received.
 > 
 > Does anyone have any suggestions for the best remedy here?
 > 

--      Brent Welch     <[EMAIL PROTECTED]>
        http://www.ajubasolutions.com
        Scriptics changes to Ajuba Solutions
        scriptics.com => ajubasolutions.com

Reply via email to