r1 - 11 Mar 2005 - 18:04:25 - HeikkiToivonenYou are here: OSAF >  Journal Web  >  ContributorNotes > HeikkiToivonenNotes > HeikkiToivonen20050311

WebDAV Client a la Twisted

This is a proof-of-concept effort at the moment, and SSL redirects (for example) will fail because we fall back to Twisted's default SSL handling which we do not want.

We probably are also not reusing the connection (i.e. if you do two get's in succession we create two objects and do it with two connections). One of the goals of possibly switching to twisted-based solution is so that we could be more efficient.

import twisted.internet.reactor as reactor
import twisted.web.client as client
import twisted.internet.protocol as protocol
import twisted.protocols.policies as policies
import M2Crypto.SSL.TwistedProtocolWrapper as wrapper
import M2Crypto.SSL as SSL
import urlparse
import base64

XML_CONTENT_TYPE = 'text/xml; charset="utf-8"'
XML_DOC_HEADER = '<?xml version="1.0" encoding="utf-8"?>'
DEFAULT_RETRIES = 3

class WebDAVPageGetter(client.HTTPPageGetter):
    handleStatus_204 = lambda self: self.handleStatus_200()
    handleStatus_207 = lambda self: self.handleStatus_200()

class WebDAVClientFactory(client.HTTPClientFactory):
    protocol = WebDAVPageGetter

    
# Lifted from twisted
def _parse(url, defaultPort=None):
    parsed = urlparse.urlparse(url)
    scheme = parsed[0]
    path = urlparse.urlunparse(('','')+parsed[2:])
    if defaultPort is None:
        if scheme == 'https':
            defaultPort = 443
        else:
            defaultPort = 80
    host, port = parsed[1], defaultPort
    if ':' in host:
        host, port = host.split(':')
        port = int(port)
    return scheme, host, port, path

# Lifted from twisted, slightly modified
def getPage(url, contextFactory=None, *args, **kwargs):
    """Download a web page as a string.

    Download a page. Return a deferred, which will callback with a
    page (as a string) or errback with a description of the error.

    See HTTPClientFactory to see what extra args can be passed.
    """
    scheme, host, port, path = _parse(url)
    factory = WebDAVClientFactory(url, *args, **kwargs)
    wrappingFactory = policies.WrappingFactory(factory)
    wrappingFactory.protocol = wrapper.TLSProtocolWrapper
    if scheme == 'https':
        factory.startTLS = 1
    #factory.getContext = lambda : self.ctx or Globals.crypto.getSSLContext()
    factory.sslChecker = SSL.Checker.Checker()
    reactor.connectTCP(host, port, wrappingFactory)
    return factory.deferred


def success(value):
    print value
    reactor.stop()

def failure(error):
    print error
    reactor.stop()


class Client(object):
    def __init__(self, host, port=80, username=None, password=None,
                 useSSL=False, ctx=None, retries=DEFAULT_RETRIES):
        self.host = host
        self.port = port
        self.username = username
        self.password = password
        self.useSSL = useSSL
        if self.useSSL:
            self.scheme = 'https'
        else:
            self.scheme = 'http'
        self.ctx = ctx # TODO
        self.retries = retries # TODO

    def get(self, path, extraHeaders={ }):
        self._request('GET', path, body=None, extraHeaders=extraHeaders)
    
    def put(self, path, body, contentType=None, contentEncoding=None, extraHeaders={ }):
        # contentType, contentEncoding TODO
        self._request('PUT', path, body, extraHeaders)

    def propfind(self, path, body=None, depth=None, extraHeaders={ }):
        extraHeaders = extraHeaders.copy()
        extraHeaders['Content-Type'] = XML_CONTENT_TYPE
        if body is None: # by default, ask for etags
            body = "%s\n<D:propfind xmlns:D=\"DAV:\"><D:prop><D:getetag/></D:prop></D:propfind>" % XML_DOC_HEADER
        if depth is not None:
            extraHeaders['Depth'] = str(depth)

        self._request('PROPFIND', path, body, extraHeaders)

    def _request(self, method, path, body=None, extraHeaders={ }):
        if self.username:
            extraHeaders = extraHeaders.copy()
            extraHeaders['Authorization'] = 'Basic ' + \
                base64.encodestring(self.username + ':' + self.password).strip()

        url = '%s://%s:%d/%s' %(self.scheme, self.host, self.port, path)
        getPage(url,
                method=method,
                postdata=body,
                headers=extraHeaders).addCallbacks(callback=success,
                                                   errback=failure)


c = Client('www.sharemation.com',
           port=443,
           username='', # Fill in your username and password here
           password='',
           useSSL=True)
c.get('heikki2/hello.txt')

reactor.run()
Edit | WYSIWYG | Attach | Printable | Raw View | Backlinks: Web, All Webs | History: r1 | More topic actions
 
Open Source Applications Foundation
Except where otherwise noted, this site and its content are licensed by OSAF under an Creative Commons License, Attribution Only 3.0.
See list of page contributors for attributions.