This repository has been archived by the owner on Sep 17, 2019. It is now read-only.
/
webresource.py
151 lines (131 loc) · 5.42 KB
/
webresource.py
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
""" wrapid: Micro framework built on Python WSGI for RESTful server APIs
Interface to a wrapid-style web resource, using JSON as default representation.
"""
import urlparse
import httplib
import urllib
import base64
import wsgiref.headers
import json
import copy
class Webresource(object):
"Interface to a web resource."
def __init__(self, url, account=None, password=None):
assert url
self.set_url(url)
self.account = account
self.password = password
self.accept = 'application/json'
def __str__(self):
return self.url
def set_url(self, url):
parts = urlparse.urlparse(url)
self.scheme = parts.scheme
if not self.scheme in ['http', 'https']:
raise ValueError('url scheme must be http or https')
self.host = parts.netloc.split(':')[0]
self.port = parts.port
self.root = parts.path.rstrip('/') + '/'
def get_url(self):
if self.port:
netloc = "%s:%s" % (self.host, self.port)
else:
netloc = self.host
return urlparse.urlunsplit((self.scheme, netloc, self.root, '', ''))
url = property(get_url, set_url)
def request(self, method, path, body=None, headers=dict()):
"""Send the request using the specified HTTP method.
The response is an instance of httplib.HTTPResponse,
which has the following attributes:
status HTTP status code
reason HTTP status phrase
msg a mimetools.Message instance containing headers
read() returns the response body
getheader(name, [default]) returns the specified header
getheaders() returns the headers as tuples
"""
cnx = httplib.HTTPConnection(self.host, self.port)
cnx.request(method, path, body=body, headers=headers)
return cnx.getresponse()
def get_path(self, rpath):
"Get the full URL path from the resource path relative to root."
return self.root + rpath.lstrip('/')
def get_rpath(self, url):
"""Return the resource path relative to root from the full URL.
Raises ValueError if the URL does not refer to this web resource.
"""
parts = urlparse.urlparse(url)
if not parts.scheme in ['http', 'https']:
raise ValueError('url scheme must be http or https')
if self.host != parts.netloc.split(':')[0]:
raise ValueError('netloc of URL does not match')
if self.port != parts.port:
raise ValueError('port of URL does not match')
if not parts.path.startswith(self.root):
raise ValueError('path root of URL does not match')
return '/' + parts.path[len(self.root):]
def get_headers(self, **headers):
headers = wsgiref.headers.Headers([(k.replace('_', '-'), v)
for k,v in headers.items()
if v])
if not headers['accept']:
headers.add_header('Accept', self.accept)
if self.account and self.password:
encoded = base64.b64encode("%s:%s" % (self.account, self.password))
headers.add_header('Authorization', "Basic %s" % encoded)
return dict(headers.items())
def copy(self):
return copy.copy(self)
def GET(self, rpath, **query):
"""Retrieve the representation of the given resource,
which is specified relative to the root URL.
"""
path = self.get_path(rpath)
if query:
path += '?' + urllib.urlencode(query)
return self.request('GET', path, headers=self.get_headers())
def POST(self, rpath, data=None, content_type='application/json'):
"""Post the data, if any.
If the content type is 'application/json', then encode it as such.
Else assume that it has already been encoded, and send as is.
"""
if data:
if content_type == 'application/json':
body = json.dumps(data)
else:
body = data
headers = self.get_headers(content_type=content_type)
else:
body = None
headers = self.get_headers()
path = self.get_path(rpath)
return self.request('POST', path, body, headers)
def PUT(self, rpath, data=None, content_type='application/json'):
"""Put the data, if any.
If the content type is 'application/json', then encode it as such.
Else assume that it has already been encoded, and send as is.
"""
if data:
if content_type == 'application/json':
body = json.dumps(data)
else:
body = data
headers = self.get_headers(content_type=content_type)
else:
body = None
headers = self.get_headers()
path = self.get_path(rpath)
return self.request('PUT', path, body, headers)
def DELETE(self, rpath):
"Delete the specified resource."
path = self.get_path(rpath)
return self.request('DELETE', path, headers=self.get_headers())
if __name__ == '__main__':
import sys
for filename in sys.argv[1:]:
wr = Webresource('http://localhost/webtables')
response = wr.GET("/static/%s" % filename)
print response.status, response.reason
print 'Response data length', len(response.read())
for item in response.getheaders():
print item