This repository was archived by the owner on Apr 15, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdevil_ray.py
More file actions
executable file
·176 lines (154 loc) · 4.53 KB
/
Copy pathdevil_ray.py
File metadata and controls
executable file
·176 lines (154 loc) · 4.53 KB
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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
#!/usr/bin/env python2.7
import base64
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
import cStringIO
import hashlib
import hmac
from itertools import chain, ifilter, imap
import sys
from time import gmtime, strftime
import urllib2
from urlparse import urlparse
import yaml
DEBUG = False
CONFIG = None
CHUNK_SIZE = 4096
# From http://docs.python.org/2/library/time.html
def rfc_2822_now():
return strftime("%a, %d %b %Y %H:%M:%S +0000", gmtime())
def is_amz_key(key):
return key.startswith("x-amz-")
def prepare_headers(headers):
if not headers:
return
for key in headers.keys():
key = key.lower()
if is_amz_key(key):
yield (key, headers[key])
def Status(Error):
def __init__(code):
self.code = code
class Handler(BaseHTTPRequestHandler):
def _debug(self):
self.send_response(200)
self.end_headers()
self.wfile.close()
print("BEGIN")
print(self._s3_url())
headers = self._s3_headers()
for key in headers.keys():
print("%s: %s" % (key, headers[key]))
print("END\n")
def do_GET(self):
if DEBUG:
self._debug()
return
try:
req = urllib2.Request(self._s3_url(), None, self._s3_headers())
except Status as status:
self.send_response(status.code)
self.end_headers()
self.wfile.close()
return
try:
resp = urllib2.urlopen(req)
except urllib2.HTTPError as err:
resp = err
self.send_response(resp.getcode())
resp_headers = resp.info()
for key in resp_headers.keys():
# TODO support transfer-encoding: chunked
if key.lower() != "transfer-encoding":
self.send_header(key, resp_headers.get(key))
self.end_headers()
# TODO read and write in parallel?
chunk = None
while chunk != "":
chunk = resp.read(CHUNK_SIZE)
self.wfile.write(chunk)
self.wfile.close()
resp.close()
def _s3_url(self):
if not self.path.startswith("/"):
raise Status(400)
parts = self.path[1:].split("/", 1)
if len(parts) == 0:
domain = "s3.amazonaws.com"
else:
domain = parts[0] + ".s3.amazonaws.com"
if len(parts) < 2:
path = ""
else:
path = parts[1]
return "http://" + domain + "/" + path
def _s3_headers(self):
date = rfc_2822_now()
headers = {}
# Date header
headers["date"] = date
# Headers from the request
for key in self.headers:
if key.lower() != "host":
headers[key] = self.headers[key]
# Headers from the config file
c_headers = CONFIG["headers"]
if c_headers:
for key in c_headers:
headers[key] = c_headers[key]
# Authorization header
# TODO pass the existing headers down into self._hmac so it doesn't
# have to iterate over all the separate header sources again
auth_value = "AWS %s:%s" % (CONFIG["key"]["id"], self._hmac(date))
headers["Authorization"] = auth_value
return headers
def _hmac(self, date):
with open(CONFIG["key"]["secret_file"], "r") as secret:
h = hmac.new(secret.read().strip(), self._canonical(date), hashlib.sha1)
secret.close()
return base64.b64encode(h.digest())
def _canonical(self, date):
buffer = cStringIO.StringIO()
buffer.write(self.command)
buffer.write("\n")
buffer.write(self.headers.get("Content-MD5", ""))
buffer.write("\n")
buffer.write(self.headers.get("Content-Type", ""))
buffer.write("\n")
buffer.write(self.headers.get("Date", date))
buffer.write("\n")
self._canonical_amz_headers(buffer)
self._canonical_resource(buffer)
value = buffer.getvalue()
buffer.close()
return value
def _canonical_resource(self, buffer):
buffer.write(self.path)
# TODO
# Handle sub-resources
def _canonical_amz_headers(self, buffer):
# TODO
# Handle continuation lines
# Handle multiple headers with the same key
config_headers = prepare_headers(CONFIG["headers"])
request_headers = prepare_headers(self.headers)
headers = list(chain(config_headers, request_headers))
headers.sort()
for (key, value) in headers:
buffer.write(key)
buffer.write(":")
buffer.write(value)
buffer.write("\n")
def load_yaml(path):
with open(path, "r") as handle:
return yaml.safe_load(handle)
def main():
global CONFIG
CONFIG = load_yaml("s3.yaml")
listen = (CONFIG["listen"]["address"], CONFIG["listen"]["port"])
server = HTTPServer(listen, Handler)
# TODO handle simultaneous requests?
while True:
print("Ready for a request...")
server.handle_request()
if __name__ == "__main__":
main()