Search code examples
pythonhttptwistedwebdav

How can I use "Expect: 100-continue" header in Twisted Web?


I've been working with AkaDAV, a Twisted based WebDAV server, and I'm trying to support the full litmus test suite. I'm currently stuck on the http sub-suite.

Specifically, I can run:

$ TESTS=http litmus http://localhost:8080/steder/
-> running `http':
 0. init.................. pass
 1. begin................. pass
 2. expect100............. FAIL (timeout waiting for interim response)
 3. finish................ pass

This test basically does the following:

  1. Open a socket to the WebDAV server
  2. Issue the following PUT:

    PUT /steder/litmus/expect100 HTTP/1.1 Host: localhost:8080 Content-Length: 100 Expect: 100-continue

  3. waits for a response HTTP/1.1 100 Continue response.

  4. uploads 100 byte content payload

The confusing thing here is that it looks like this PUT request never makes it to Twisted. As a sanity check I've confirmed that PUT requests issued through curl -X PUT ... work so it seems like there's something special about this testcase.

Any ideas what I may be doing wrong? I'm happy to share sourcecode if that helps.

EDIT:

After a little more looking around it appears that this is a known twisted.web issue: http://twistedmatrix.com/trac/ticket/4673

Does anyone know of a workaround?


Solution

  • After some more investigation it's pretty clear how to modify the HTTP protocol implementation to support this use case. It looks like the official fix will be in Twisted soon but in the meantime I'm using this as a workaround.

    Just include this code before you instantiate your Site (or t.w.http.HTTPFactory):

    from twisted.web import http
    
    
    class HTTPChannelWithExpectContinue(http.HTTPChannel):
        def headerReceived(self, line):
            """Just extract the header and handle Expect 100-continue:
            """
            header, data = line.split(':', 1)
            header = header.lower()
            data = data.strip()
            if (self._version=="HTTP/1.1" and
                header == 'expect' and data.lower() == '100-continue'):
                self.transport.write("HTTP/1.1 100 Continue\r\n\r\n")
            return http.HTTPChannel.headerReceived(self, line)
    
    
    http.HTTPFactory.protocol = HTTPChannelWithExpectContinue
    

    I imagine if you needed other modifications at the protocol level you could use this same method to patch them in as well. It ain't necessarily pretty but it works for me.