1

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?

stderr
  • 8,567
  • 1
  • 34
  • 50
  • Because this is Twisted issue you'll have much better response rate to this issue on a Twisted specific forum - I believe it is unlikely a "general programmer", which stackoverflow.com is populated with, comes across this problem. – Mikko Ohtamaa Aug 27 '11 at 11:06

1 Answers1

1

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.

stderr
  • 8,567
  • 1
  • 34
  • 50
  • 1
    This is a pretty crude way of solving this issue. The purpose of the `expect continue` is to give the server a chance to refuse a possibly large request before the client has started sending it, saving bandwidth for both parties. An even better solution might be to call a method on the protocol that could return true or false to indicate that the request should be accepted (with a default that always returns true) – SingleNegationElimination Aug 29 '11 at 14:52