summaryrefslogtreecommitdiff
path: root/fs/contrib/tahoelafs/connection.py
blob: 448919b9119d903641f712a1735c9d1222cb0a9f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import platform
import logging

import fs.errors as errors
from fs import SEEK_END

python3 = int(platform.python_version_tuple()[0]) > 2

if python3:
    from urllib.parse import urlencode, pathname2url, quote
    from urllib.request import Request, urlopen
else:
    from urllib import urlencode, pathname2url
    from urllib2 import Request, urlopen, quote

class PutRequest(Request):
    def __init__(self, *args, **kwargs):
        self.get_method = lambda: u'PUT'
        Request.__init__(self, *args, **kwargs)

class DeleteRequest(Request):
    def __init__(self, *args, **kwargs):
        self.get_method = lambda: u'DELETE'
        Request.__init__(self, *args, **kwargs)

class Connection:
    def __init__(self, webapi):
        self.webapi = webapi
        self.headers = {'Accept': 'text/plain'}

    def _get_headers(self, f, size=None):
        '''
            Retrieve length of string or file object and prepare HTTP headers.
        '''
        if isinstance(f, basestring):
            # Just set up content length
            size = len(f)
        elif getattr(f, 'read', None):
            if size == None:
                # When size is already known, skip this
                f.seek(0, SEEK_END)
                size = f.tell()
                f.seek(0)
        else:
            raise errors.UnsupportedError("Cannot handle type %s" % type(f))

        headers = {'Content-Length': size}
        headers.update(self.headers)
        return headers

    def _urlencode(self, data):
        _data = {}
        for k, v in data.items():
            _data[k.encode('utf-8')] = v.encode('utf-8')
        return urlencode(_data)

    def _quotepath(self, path, params={}):
        q = quote(path.encode('utf-8'), safe='/')
        if params:
            return u"%s?%s" % (q, self._urlencode(params))
        return q

    def _urlopen(self, req):
        try:
            return urlopen(req)
        except Exception, e:
            if not getattr(e, 'getcode', None):
                raise errors.RemoteConnectionError(str(e))
            code = e.getcode()
            if code == 500:
                # Probably out of space or unhappiness error
                raise errors.StorageSpaceError(e.fp.read())
            elif code in (400, 404, 410):
                # Standard not found
                raise errors.ResourceNotFoundError(e.fp.read())
            raise errors.ResourceInvalidError(e.fp.read())

    def post(self, path, data={}, params={}):
        data = self._urlencode(data)
        path = self._quotepath(path, params)
        req = Request(''.join([self.webapi, path]), data, headers=self.headers)
        return self._urlopen(req)

    def get(self, path, data={}, offset=None, length=None):
        data = self._urlencode(data)
        path = self._quotepath(path)
        if data:
            path = u'?'.join([path, data])

        headers = {}
        headers.update(self.headers)
        if offset:
            if length:
                headers['Range'] = 'bytes=%d-%d' % \
                                    (int(offset), int(offset+length))
            else:
                headers['Range'] = 'bytes=%d-' % int(offset)

        req = Request(''.join([self.webapi, path]), headers=headers)
        return self._urlopen(req)

    def put(self, path, data, size=None, params={}):
        path = self._quotepath(path, params)
        headers = self._get_headers(data, size=size)
        req = PutRequest(''.join([self.webapi, path]), data, headers=headers)
        return self._urlopen(req)

    def delete(self, path, data={}):
        path = self._quotepath(path)
        req = DeleteRequest(''.join([self.webapi, path]), data, headers=self.headers)
        return self._urlopen(req)