Project

General

Profile

« Previous | Next » 

Revision e9b7f4d7

Added by jahoti about 2 years ago

Enable the hijacking proxy in the test suite to serve responses

View differences:

copyright
85 85
 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
86 86
 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
87 87

  
88
Files: test/server.py
89
Copyright: 2021 jahoti <jahoti@tilde.team>
90
License: AGPL-3+
91

  
88 92
Files: licenses/*
89 93
Copyright: 2001, 2002, 2011-2013 Creative Commons
90 94
License: CC-BY-4.0
test/proxy_core.py
1
#!/usr/bin/env python3
2
#
3 1
# Copyright (c) 2015, inaz2
4 2
# Copyright (C) 2021 jahoti <jahoti@tilde.team>
5 3
# Licensing information is collated in the `copyright` file
......
38 36
				p1 = Popen((gen_cert_req % hostname).split(' '), stdout=PIPE).stdout
39 37
				Popen((sign_cert_req % (time.time() * 1000, certpath)).split(' '), stdin=p1, stderr=PIPE).communicate()
40 38

  
41
		self.wfile.write('HTTP/1.1 200 Connection Established\r\n')
39
		self.send_response(200)
42 40
		self.end_headers()
43 41

  
44 42
		self.connection = ssl.wrap_socket(self.connection, keyfile='cert.key', certfile=certpath, server_side=True)
......
48 46
		self.close_connection = int(self.headers.get('Proxy-Connection', '').lower() == 'close')
49 47

  
50 48
	def proxy(self):
51
		if self.path == 'http://hachette.test/':
52
			with open('ca.crt', 'rb') as f:
53
				data = f.read()
54

  
55
			self.wfile.write('HTTP/1.1 200 OK\r\n')
56
			self.send_header('Content-Type', 'application/x-x509-ca-cert')
57
			self.send_header('Content-Length', len(data))
58
			self.send_header('Connection', 'close')
59
			self.end_headers()
60
			self.wfile.write(data)
61
			return
62

  
63 49
		content_length = int(self.headers.get('Content-Length', 0))
64 50
		req_body = self.rfile.read(content_length) if content_length else None
65 51

  
......
79 65

  
80 66
class ThreadingHTTPServer(ThreadingMixIn, HTTPServer):
81 67
	"""The actual proxy server"""
82
	address_family, daemon_threads, handler = socket.AF_INET6, True, ProxyRequestHandler
68
	address_family, daemon_threads = socket.AF_INET6, True
83 69

  
84 70
	def handle_error(self, request, client_address):
85 71
		# suppress socket/ssl related errors
86 72
		cls, e = sys.exc_info()[:2]
87 73
		if not (cls is socket.error or cls is ssl.SSLError):
88 74
			return HTTPServer.handle_error(self, request, client_address)
89

  
90

  
91
def start(server_class, port=1337):
92
	"""Start up the proxy/server"""
93
	
94
	print('Serving HTTP Proxy')
95
	httpd = server_class(('::1', port), ProxyRequestHandler)
96
	httpd.serve_forever()
test/server.py
1
# Copyright (C) 2021 jahoti <jahoti@tilde.team>
2
# Licensing information is collated in the `copyright` file
3

  
4
"""
5
A modular "virtual network" proxy,
6
wrapping the classes in proxy_core.py
7
"""
8

  
9
from proxy_core import *
10
from urllib.parse import parse_qs
11

  
12
internet = {} # Add info here later
13
mime_types = {
14
	"7z": "application/x-7z-compressed",	"oga": "audio/ogg",
15
	"abw": "application/x-abiword",		"ogv": "video/ogg",
16
	"arc": "application/x-freearc",		"ogx": "application/ogg",
17
	"bin": "application/octet-stream",	"opus": "audio/opus",
18
	"bz": "application/x-bzip",		"otf": "font/otf",
19
	"bz2": "application/x-bzip2",		"pdf": "application/pdf",
20
	"css": "text/css",			"png": "image/png",
21
	"csv": "text/csv",			"sh": "application/x-sh",
22
	"gif": "image/gif",			"svg": "image/svg+xml",
23
	"gz": "application/gzip",		"tar": "application/x-tar",
24
	"htm": "text/html",			"ts": "video/mp2t",
25
	"html": "text/html",			"ttf": "font/ttf",
26
	"ico": "image/vnd.microsoft.icon",	"txt": "text/plain",
27
	"js": "text/javascript",		"wav": "audio/wav",
28
	"jpeg": "image/jpeg",			"weba": "audio/webm",
29
	"jpg": "image/jpeg",			"webm": "video/webm",
30
	"json": "application/json",		"woff": "font/woff",	
31
	"mjs": "text/javascript",		"woff2": "font/woff2",
32
	"mp3": "audio/mpeg",			"xhtml": "application/xhtml+xml",
33
	"mp4": "video/mp4",			"zip": "application/zip",
34
	"mpeg": "video/mpeg",
35
	"odp": "application/vnd.oasis.opendocument.presentation",
36
	"ods": "application/vnd.oasis.opendocument.spreadsheet",
37
	"odt": "application/vnd.oasis.opendocument.text",
38
	"xml": "application/xml" # text/xml if readable from casual users
39
}
40

  
41
class RequestHijacker(ProxyRequestHandler):
42
	def handle_request(self, req_body):
43
		path_components = self.path.split('?', maxsplit=1)
44
		path = path_components[0]
45
		try:
46
			# Response format: (status_code, headers (dict. of strings),
47
			#       body as bytes or filename containing body as string)
48
			if path in internet:
49
				info = internet[path]
50
				if type(info) == tuple:
51
					status_code, headers, body_file = info
52
					if type(body_file) == str:
53
						if 'Content-Type' not in headers and '.' in body_file:
54
							ext = body_file.rsplit('.', maxsplit=1)[-1]
55
							if ext in mime_types:
56
								headers['Content-Type'] = mime_types[ext]
57
							
58
						with open(body_file, mode='rb') as f:
59
							body_file = f.read()
60
						
61
				else:
62
					# A function to evaluate to get the response
63
					get_params, post_params = {}, {}
64
					if len(path_components) == 2:
65
						get_params = parse_qs(path_components[1])
66
					
67
					# Parse POST parameters; currently only supports
68
					# application/x-www-form-urlencoded
69
					if req_body:
70
						post_params = parse_qs(req_body.encode())
71

  
72
					status_code, headers, body_file = info(self.command, get_params, post_params)
73
					if type(body_file) == str:
74
						body_file = body_file.encode()
75
			
76
				if type(status_code) != int or status_code <= 0:
77
					raise Exception('Invalid status code %r' % status_code)
78
				
79
				for header, header_value in headers.items():
80
					if type(header) != str:
81
						raise Exception('Invalid header key %r' % header)
82
					
83
					elif type(header_value) != str:
84
						raise Exception('Invalid header value %r' % header_value)
85
			else:
86
				status_code, headers = 404, {'Content-Type': 'text/plain'}
87
				body_file = b'Handler for this URL not found.'
88
		
89
		except Exception as e:
90
			status_code, headers, body_file = 500, {'Content-Type': 'text/plain'}, b'Internal Error:\n' + repr(e).encode()
91
		
92
		headers['Content-Length'] = str(len(body_file))
93
		self.send_response(status_code)
94
		for header, header_value in headers.items():
95
			self.send_header(header, header_value)
96
		
97
		self.end_headers()
98
		self.wfile.write(body_file)
99
		
100

  
101

  
102
def do_an_internet(port=1337):
103
	"""Start up the proxy/server"""
104
	
105
	httpd = ThreadingHTTPServer(('', port), RequestHijacker)
106
	httpd.serve_forever()

Also available in: Unified diff