-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTCPServer.py
More file actions
143 lines (109 loc) · 3.79 KB
/
TCPServer.py
File metadata and controls
143 lines (109 loc) · 3.79 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
import asyncio
from .enums import RequestType
from .HTTPResponse import HTTPResponse
class HTTPMessage:
reader = None
writer = None
host = None
tcpAddress = None
contentLength = None
userAgent = None
path = None
type = None
version = None
unhandledHeaders = {}
def parseHeader(self, line):
delimLocation = line.find(":")
header = line[:delimLocation]
value = line[delimLocation + 1 :]
if value[0] == " ":
value = value[
1:
] # Checks for and removes the optional trailing space (https://datatracker.ietf.org/doc/html/rfc9112#name-field-syntax)
match header:
case "Host":
self.host = value
case "Content-Length":
self.contentLength = value
case "User-Agent":
self.userAgent = value
case _:
self.unhandledHeaders[header] = value
async def readLine(self):
data = await self.reader.readline()
line = data.decode()
if line[-2] == "\r":
line = line[:-2]
return line
# Parse HTTPMessage into class variables
async def parse(self):
startLine = await self.readLine()
options = startLine.split(" ")
if len(options) != 3:
self.writer.close() # We close the connection because it's not a valid HTTP Message
return
match options[0]:
case "GET":
self.type = RequestType.GET
case "POST":
self.type = RequestType.POST
case "PUT":
self.type = RequestType.PUT
case _:
self.type = RequestType.UNKNOWN
self.path = options[1]
self.verison = options[2]
completed = False
while not completed:
line = await self.readLine()
if line == "":
completed = True
break
self.parseHeader(line)
def GetPath(self):
return self.path
def GetType(self):
return self.requestType
def __init__(self, reader, writer):
self.writer = writer # Take in writer for sending response
self.reader = reader
self.tcpAddress = writer.get_extra_info("peername")
class TCPServer:
port = None
ip = None
numberOfBytes = None # Amount of bytes read by read function
requestHandler = None
def __init__(self, port=7007, ip="127.0.0.1", numberOfBytes=100):
self.port = port
self.ip = ip
self.numberOfBytes = numberOfBytes
async def handler(self, reader, writer):
httpMessage = HTTPMessage(
reader, writer
) # Create HTTPMessage class to parse packet
await httpMessage.parse()
await self.requestHandler(httpMessage)
async def listenInternal(self):
server = await asyncio.start_server(self.handler, self.ip, self.port)
print(",".join(str(sock.getsockname()) for sock in server.sockets))
async with server:
await server.serve_forever()
def Listen(self, requestHandler):
self.requestHandler = requestHandler
asyncio.run(self.listenInternal())
async def Reply(self, response, writer):
version = "HTTP/1.1"
reason = "OK"
crf = "\r\n"
contentHeader = "Content-Type: "
if response.reason != None:
reason = response.reason
packet = version + " " + str(response.code) + " " + reason + crf
packet += contentHeader + response.contentType + crf
for header, value in response.headers:
packet += header + value + crf
packet += crf
packet += response.message
writer.write(packet.encode())
await writer.drain()
writer.close()