forked from nat/natbot
-
Notifications
You must be signed in to change notification settings - Fork 0
/
proxy.py
246 lines (226 loc) · 9.08 KB
/
proxy.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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
import argparse
import http.cookiejar
import http.cookies
import io
import logging
import os
import random
import sys
import urllib.error
import urllib.parse
import urllib.request
import urllib.response
from http.server import BaseHTTPRequestHandler, HTTPServer
logging.basicConfig()
logger = logging.getLogger(__name__)
class PassThroughRedirectHandler(urllib.request.HTTPRedirectHandler):
# alternative handler
def http_error_300(self, req, fp, code, msg, header_list):
data = urllib.response.addinfourl(fp, header_list, req.get_full_url())
data.status = code
data.code = code
return data
# setup aliases
http_error_301 = http_error_300
http_error_302 = http_error_300
http_error_303 = http_error_300
http_error_307 = http_error_300
opener = urllib.request.build_opener(PassThroughRedirectHandler)
urllib.request.install_opener(opener)
class ProxyHTTPRequestHandler(BaseHTTPRequestHandler):
def send_response(self, code, message=None):
# this is a proxy, so I don't want it to add headers. i want to inherit all of these headers.
self.log_request(code)
self.send_response_only(code, message)
def do_HEAD(self):
self.do_GET(body=False)
def do_GET(self, body=True):
sent = False
try:
req = None
resp = None
sio = io.StringIO()
try:
hostname = self.headers.get("Host")
if not hostname:
hostname = "localhost"
url = self.path
req = urllib.request.Request(url=url)
sio.write("====BEGIN REQUEST=====\n")
sio.write(url)
sio.write("\n")
sio.write(self.command)
sio.write(" ")
sio.write(self.path)
sio.write(" ")
sio.write(self.request_version)
sio.write("\n")
for line in self.headers:
key = line
value = self.headers.get(key)
line_parts = [key, value]
if len(line_parts) == 2:
if line_parts[0].startswith("X-"):
pass
elif line_parts[0] in ("Connection", "User-Agent"):
pass
else:
sio.write(line)
req.add_header(key=key, val=value)
sio.write("\n")
sio.write("====END REQUEST=======\n")
logger.error(sio.getvalue() + "\n")
try:
cookie_jar = http.cookiejar.CookieJar()
cookie_processor = urllib.request.HTTPCookieProcessor(cookie_jar)
opener = urllib.request.build_opener(cookie_processor)
resp = opener.open(req)
except urllib.error.HTTPError as e:
if e.getcode():
resp = e
else:
self.send_error(599, "error proxying: {}".format(str(e)))
sent = True
return
self.send_response(resp.getcode())
headers = resp.getheaders()
for header in headers:
name, value = header
self.send_header(keyword=name, value=value)
# cookie?
for cookie in cookie_jar:
try:
c = http.cookies.SimpleCookie()
c[cookie.name] = cookie.value
for attr in ["path", "expires", "secure"]:
value = getattr(cookie, attr, None)
if value is not None:
c[cookie.name][attr] = value
header = c.output(header="").lstrip()
print(header)
self.send_header("Set-Cookie", header)
except AttributeError as e:
print("WE GOT A COOKIE ERROR GET")
print(cookie)
print(e)
self.end_headers()
sent = True
if body:
self.wfile.write(resp.read())
return
finally:
if resp:
resp.close()
sio.close()
except IOError as e:
if not sent:
self.send_error(404, "error trying to proxy: {}".format(str(e)))
def do_POST(self, body=True):
sent = False
try:
req = None
resp = None
sio = io.StringIO()
try:
hostname = self.headers.get("Host")
if not hostname:
hostname = "localhost"
url = "http://{}".format(hostname)
url = self.path
req = urllib.request.Request(url=url, method="POST")
sio.write("====BEGIN REQUEST=====\n")
sio.write(url)
sio.write("\n")
sio.write(self.command)
sio.write(" ")
sio.write(self.path)
sio.write(" ")
sio.write(self.request_version)
sio.write("\n")
for line in self.headers:
key = line
value = self.headers.get(key)
line_parts = [key, value]
if len(line_parts) == 2:
if line_parts[0].startswith("X-"):
pass
elif line_parts[0] in ("Connection", "User-Agent"):
pass
else:
sio.write(f"{key}={value}")
sio.write("\n")
req.add_header(key=key, val=value)
req.add_header(key="pragma", val="no-cache")
content_length = int(self.headers.get("Content-Length", 0))
# Read the request body from the socket
request_body = self.rfile.read(content_length)
sio.write(request_body.decode("utf-8"))
sio.write("\n")
sio.write("====END REQUEST=======\n")
logger.error(sio.getvalue() + "\n")
logger.error("No real error\n")
try:
cookie_jar = http.cookiejar.CookieJar()
cookie_processor = urllib.request.HTTPCookieProcessor(cookie_jar)
opener = urllib.request.build_opener(cookie_processor)
resp = opener.open(req, request_body)
except urllib.error.HTTPError as e:
if e.getcode():
resp = e
else:
self.send_error(599, "error proxying: {}".format(str(e)))
sent = True
return
self.send_response(resp.getcode())
headers = resp.getheaders()
for header in headers:
name, value = header
self.send_header(keyword=name, value=value)
# cookie?
for cookie in cookie_jar:
try:
c = http.cookies.SimpleCookie()
c[cookie.name] = cookie.value
for attr in ["domain", "path", "expires", "secure"]:
value = getattr(cookie, attr, None)
if value is not None:
c[cookie.name][attr] = value
header = c.output(header="").lstrip()
print(header)
self.send_header("Set-Cookie", header)
except AttributeError as e:
print("WE GOT A COOKIE ERROR POST")
print(cookie)
print(e)
self.end_headers()
sent = True
if body:
self.wfile.write(resp.read())
return
finally:
if resp:
resp.close()
sio.close()
except IOError as e:
if not sent:
self.send_error(404, "error trying to proxy: {}".format(str(e)))
def parse_args(argv=sys.argv[1:]):
parser = argparse.ArgumentParser(description="Either Proxy or Echo HTTP requests")
parser.add_argument(
"--port",
dest="port",
type=int,
default=8181,
help="serve HTTP requests on specified port (default: random)",
)
args = parser.parse_args(argv)
return args
def main(argv=sys.argv[1:]):
args = parse_args(argv)
print(("http server is starting on port {}...".format(args.port)))
server_address = ("127.0.0.1", args.port)
httpd = HTTPServer(server_address, ProxyHTTPRequestHandler)
print("http server is running as proxy")
httpd.serve_forever()
if __name__ == "__main__":
main()