Completed the WebDav library with standard GET and POST HTTP requests. MultiPart not yet implemented.
This commit is contained in:
		
							parent
							
								
									2aedf8c88a
								
							
						
					
					
						commit
						feca97bda3
					
				
					 3 changed files with 118 additions and 10 deletions
				
			
		| 
						 | 
					@ -1,5 +1,7 @@
 | 
				
			||||||
<tal:comment replace="nothing">View macro for a Boolean.</tal:comment>
 | 
					<tal:comment replace="nothing">View macro for a Boolean.</tal:comment>
 | 
				
			||||||
<metal:view define-macro="view"><span tal:replace="value"/></metal:view>
 | 
					<metal:view define-macro="view">
 | 
				
			||||||
 | 
					  <span tal:attributes="class widget/master_css; id rawValue" tal:content="value"></span>
 | 
				
			||||||
 | 
					</metal:view>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<tal:comment replace="nothing">Edit macro for an Boolean.</tal:comment>
 | 
					<tal:comment replace="nothing">Edit macro for an Boolean.</tal:comment>
 | 
				
			||||||
<metal:edit define-macro="edit">
 | 
					<metal:edit define-macro="edit">
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										122
									
								
								shared/dav.py
									
										
									
									
									
								
							
							
						
						
									
										122
									
								
								shared/dav.py
									
										
									
									
									
								
							| 
						 | 
					@ -1,24 +1,89 @@
 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					# ------------------------------------------------------------------------------
 | 
				
			||||||
import os, re, httplib, sys, stat
 | 
					import os, re, httplib, sys, stat, urlparse, time
 | 
				
			||||||
 | 
					from urllib import quote
 | 
				
			||||||
from StringIO import StringIO
 | 
					from StringIO import StringIO
 | 
				
			||||||
from mimetypes import guess_type
 | 
					from mimetypes import guess_type
 | 
				
			||||||
from base64 import encodestring
 | 
					from base64 import encodestring
 | 
				
			||||||
from appy.shared.utils import copyData
 | 
					from appy.shared.utils import copyData
 | 
				
			||||||
 | 
					from appy.gen.utils import sequenceTypes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# ------------------------------------------------------------------------------
 | 
				
			||||||
 | 
					class DataEncoder:
 | 
				
			||||||
 | 
					    '''Allows to encode form data for sending it through a HTTP request.'''
 | 
				
			||||||
 | 
					    def __init__(self, data):
 | 
				
			||||||
 | 
					        self.data = data # The data to encode, as a dict
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def marshalValue(self, name, value):
 | 
				
			||||||
 | 
					        if isinstance(value, basestring):
 | 
				
			||||||
 | 
					            return '%s=%s' % (name, quote(str(value)))
 | 
				
			||||||
 | 
					        elif isinstance(value, float):
 | 
				
			||||||
 | 
					            return '%s:float=%s' % (name, value)
 | 
				
			||||||
 | 
					        elif isinstance(value, int):
 | 
				
			||||||
 | 
					            return '%s:int=%s' % (name, value)
 | 
				
			||||||
 | 
					        elif isinstance(value, long):
 | 
				
			||||||
 | 
					            res = '%s:long=%s' % (name, value)
 | 
				
			||||||
 | 
					            if res[-1] == 'L':
 | 
				
			||||||
 | 
					                res = res[:-1]
 | 
				
			||||||
 | 
					            return res
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            raise 'Cannot encode value %s' % str(value)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def encode(self):
 | 
				
			||||||
 | 
					        res = []
 | 
				
			||||||
 | 
					        for name, value in self.data.iteritems():
 | 
				
			||||||
 | 
					            res.append(self.marshalValue(name, value))
 | 
				
			||||||
 | 
					        return '&'.join(res)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# ------------------------------------------------------------------------------
 | 
				
			||||||
 | 
					class HttpResponse:
 | 
				
			||||||
 | 
					    '''Stores information about a HTTP response.'''
 | 
				
			||||||
 | 
					    def __init__(self, code, text, headers, body, duration=None):
 | 
				
			||||||
 | 
					        self.code = code # The return code, ie 404, 200, ...
 | 
				
			||||||
 | 
					        self.text = text # Textual description of the code
 | 
				
			||||||
 | 
					        self.headers = headers # A dict-like object containing the headers
 | 
				
			||||||
 | 
					        self.body = body # The body of the HTTP response
 | 
				
			||||||
 | 
					        # The following attribute may contain specific data extracted from
 | 
				
			||||||
 | 
					        # the previous fields. For example, when response if 302 (Redirect),
 | 
				
			||||||
 | 
					        # self.data contains the URI where we must redirect the user to.
 | 
				
			||||||
 | 
					        self.data = self.extractData()
 | 
				
			||||||
 | 
					        # p_duration, if given, is the time, in seconds, we have waited, before
 | 
				
			||||||
 | 
					        # getting this response after having sent the request.
 | 
				
			||||||
 | 
					        self.duration = duration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __repr__(self):
 | 
				
			||||||
 | 
					        duration = ''
 | 
				
			||||||
 | 
					        if self.duration: duration = ', got in %.4f seconds' % self.duration
 | 
				
			||||||
 | 
					        return '<HttpResponse %s (%s)%s>' % (self.code, self.text, duration)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def extractData(self):
 | 
				
			||||||
 | 
					        '''This method extracts, from the various parts of the HTTP response,
 | 
				
			||||||
 | 
					           some useful information. For example, it will find the URI where to
 | 
				
			||||||
 | 
					           redirect the user to if self.code is 302.'''
 | 
				
			||||||
 | 
					        if self.code == 302:
 | 
				
			||||||
 | 
					            return urlparse.urlparse(self.headers['location'])[2]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					# ------------------------------------------------------------------------------
 | 
				
			||||||
urlRex = re.compile(r'http://([^:/]+)(:[0-9]+)?(/.+)?', re.I)
 | 
					urlRex = re.compile(r'http://([^:/]+)(:[0-9]+)?(/.+)?', re.I)
 | 
				
			||||||
binaryRex = re.compile(r'[\000-\006\177-\277]')
 | 
					binaryRex = re.compile(r'[\000-\006\177-\277]')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					 | 
				
			||||||
class Resource:
 | 
					class Resource:
 | 
				
			||||||
    '''Every instance of this class represents some web resource accessible
 | 
					    '''Every instance of this class represents some web resource accessible
 | 
				
			||||||
       through WebDAV.'''
 | 
					       through WebDAV.'''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, url, username=None, password=None):
 | 
					    def __init__(self, url, username=None, password=None, measure=False):
 | 
				
			||||||
        self.username = username
 | 
					        self.username = username
 | 
				
			||||||
        self.password = password
 | 
					        self.password = password
 | 
				
			||||||
        self.url = url
 | 
					        self.url = url
 | 
				
			||||||
 | 
					        # If some headers must be sent with any request sent through this
 | 
				
			||||||
 | 
					        # resource (like a cookie), you can store them in the following dict.
 | 
				
			||||||
 | 
					        self.headers = {}
 | 
				
			||||||
 | 
					        # If p_measure is True, we will measure, for every request sent, the
 | 
				
			||||||
 | 
					        # time we wait until we receive the response.
 | 
				
			||||||
 | 
					        self.measure = measure
 | 
				
			||||||
 | 
					        # If measure is True, we will store hereafter, the total time (in
 | 
				
			||||||
 | 
					        # seconds) spent waiting for the server for all requests sent through
 | 
				
			||||||
 | 
					        # this resource object.
 | 
				
			||||||
 | 
					        self.serverTime = 0
 | 
				
			||||||
        # Split the URL into its components
 | 
					        # Split the URL into its components
 | 
				
			||||||
        res = urlRex.match(url)
 | 
					        res = urlRex.match(url)
 | 
				
			||||||
        if res:
 | 
					        if res:
 | 
				
			||||||
| 
						 | 
					@ -50,16 +115,29 @@ class Resource:
 | 
				
			||||||
        '''Sends a HTTP request with p_method, for p_uri.'''
 | 
					        '''Sends a HTTP request with p_method, for p_uri.'''
 | 
				
			||||||
        conn = httplib.HTTP()
 | 
					        conn = httplib.HTTP()
 | 
				
			||||||
        conn.connect(self.host, self.port)
 | 
					        conn.connect(self.host, self.port)
 | 
				
			||||||
 | 
					        # Tell what kind of HTTP request it will be.
 | 
				
			||||||
        conn.putrequest(method, uri)
 | 
					        conn.putrequest(method, uri)
 | 
				
			||||||
        # Add HTTP headers
 | 
					        # Add HTTP headers
 | 
				
			||||||
        self.updateHeaders(headers)
 | 
					        self.updateHeaders(headers)
 | 
				
			||||||
 | 
					        if self.headers: headers.update(self.headers)
 | 
				
			||||||
        for n, v in headers.items(): conn.putheader(n, v)
 | 
					        for n, v in headers.items(): conn.putheader(n, v)
 | 
				
			||||||
        conn.endheaders()
 | 
					        conn.endheaders()
 | 
				
			||||||
        if body: copyData(body, conn, 'send', type=bodyType)
 | 
					        # Add HTTP body
 | 
				
			||||||
        ver, code, msg = conn.getreply()
 | 
					        if body:
 | 
				
			||||||
        data = conn.getfile().read()
 | 
					            if not bodyType: bodyType = 'string'
 | 
				
			||||||
 | 
					            copyData(body, conn, 'send', type=bodyType)
 | 
				
			||||||
 | 
					        # Send the request, get the reply
 | 
				
			||||||
 | 
					        if self.measure: startTime = time.time()
 | 
				
			||||||
 | 
					        code, text, headers = conn.getreply()
 | 
				
			||||||
 | 
					        if self.measure: endTime = time.time()
 | 
				
			||||||
 | 
					        body = conn.getfile().read()
 | 
				
			||||||
        conn.close()
 | 
					        conn.close()
 | 
				
			||||||
        return data
 | 
					        # Return a smart object containing the various parts of the response
 | 
				
			||||||
 | 
					        duration = None
 | 
				
			||||||
 | 
					        if self.measure:
 | 
				
			||||||
 | 
					            duration = endTime - startTime
 | 
				
			||||||
 | 
					            self.serverTime += duration
 | 
				
			||||||
 | 
					        return HttpResponse(code, text, headers, body, duration=duration)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def mkdir(self, name):
 | 
					    def mkdir(self, name):
 | 
				
			||||||
        '''Creates a folder named p_name in this resource.'''
 | 
					        '''Creates a folder named p_name in this resource.'''
 | 
				
			||||||
| 
						 | 
					@ -105,4 +183,32 @@ class Resource:
 | 
				
			||||||
        res = self.sendRequest('PUT', fileUri, body, headers, bodyType=bodyType)
 | 
					        res = self.sendRequest('PUT', fileUri, body, headers, bodyType=bodyType)
 | 
				
			||||||
        # Close the file when relevant
 | 
					        # Close the file when relevant
 | 
				
			||||||
        if type =='fileName': body.close()
 | 
					        if type =='fileName': body.close()
 | 
				
			||||||
 | 
					        return res
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _encodeFormData(self, data):
 | 
				
			||||||
 | 
					        '''Returns the encoded form p_data.'''
 | 
				
			||||||
 | 
					        res = []
 | 
				
			||||||
 | 
					        for name, value in data.items():
 | 
				
			||||||
 | 
					            n = name.rfind( '__')
 | 
				
			||||||
 | 
					            if n > 0:
 | 
				
			||||||
 | 
					                tag = name[n+2:]
 | 
				
			||||||
 | 
					                key = name[:n]
 | 
				
			||||||
 | 
					            else: tag = 'string'
 | 
				
			||||||
 | 
					            func = varfuncs.get(tag, marshal_string)
 | 
				
			||||||
 | 
					            res.append(func(name, value))
 | 
				
			||||||
 | 
					        return '&'.join(res)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get(self, uri=None, headers={}):
 | 
				
			||||||
 | 
					        '''Perform a HTTP GET on the server.'''
 | 
				
			||||||
 | 
					        if not uri: uri = self.uri
 | 
				
			||||||
 | 
					        return self.sendRequest('GET', uri, headers=headers)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def post(self, data, uri=None, headers={}):
 | 
				
			||||||
 | 
					        '''Perform a HTTP POST on the server.'''
 | 
				
			||||||
 | 
					        if not uri: uri = self.uri
 | 
				
			||||||
 | 
					        # Format the form data and prepare headers
 | 
				
			||||||
 | 
					        body = DataEncoder(data).encode()
 | 
				
			||||||
 | 
					        headers['Content-Type'] = 'application/x-www-form-urlencoded'
 | 
				
			||||||
 | 
					        headers['Content-Length'] = str(len(body))
 | 
				
			||||||
 | 
					        return self.sendRequest('POST', uri, headers=headers, body=body)
 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					# ------------------------------------------------------------------------------
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -95,7 +95,7 @@ def copyData(data, target, targetMethod, type='string', encoding=None,
 | 
				
			||||||
       that is compatible with the content of p_data, ie file('myFile.doc','wb')
 | 
					       that is compatible with the content of p_data, ie file('myFile.doc','wb')
 | 
				
			||||||
       if content is binary.'''
 | 
					       if content is binary.'''
 | 
				
			||||||
    dump = getattr(target, targetMethod)
 | 
					    dump = getattr(target, targetMethod)
 | 
				
			||||||
    if type == 'string': dump(encodeData(data, encoding))
 | 
					    if not type or (type == 'string'): dump(encodeData(data, encoding))
 | 
				
			||||||
    elif type == 'file':
 | 
					    elif type == 'file':
 | 
				
			||||||
        while True:
 | 
					        while True:
 | 
				
			||||||
            chunk = data.read(chunkSize)
 | 
					            chunk = data.read(chunkSize)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue