123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224 |
- #!/usr/bin/python3
- import socket
- import codecs
- ## define Request and Responce classes to simplify the actual code
- class Request:
- def __init__(self, request_data):
- try:
- decoded_request = request_data.decode("UTF-8")
- self.successful = True
- except Exception as e:
- print("Failed to decode request ({})".format(e))
- self.successful = False
- if self.successful:
- self.method = decoded_request.split(" ")[0]
- self.path = decoded_request.split(" ")[1]
- class Responce:
- def __init__(self):
- self.code = ""
- self.content_type = ""
- self.body = ""
- def generate(self):
- responce_data = "HTTP/1.1 {}\r\nContent-Type: {}\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}".format(self.code, self.content_type, len(self.body.encode("UTF-8")), self.body)
- return responce_data.encode("UTF-8")
- ## defining intify() function to make code reading easier
- ## and to create a failsafe wrapper around the int() built-in function
- def intify(x):
- try:
- return int(x)
- except Exception as e:
- print("Failed to parse integer from {} ({})".format(x, e))
- return False
- ## defining readfile() function to make code easier to read
- def readfile(path):
- try:
- return codecs.open(path, "r", encoding = "UTF-8").read()
- except FileNotFoundError:
- print("No such file: {}".format(path))
- return False
- except Exception as e:
- print("Failed to read file {} ({})".format(path, e))
- return False
- s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- s.bind( ("", 8080) )
- s.listen()
- print("[INFO] Server started")
- while True:
- try:
- conn, addr = s.accept()
- ## allowing very high time intervals as they
- ## are possible on slow networks during
- ## high loads (or during war)
- conn.settimeout(1800)
-
- connection_alive = True
- request_buffer = bytes()
- ## receiving request
-
- while True:
- new_data = conn.recv(1024)
- if len(new_data) == 0:
- ## connection closed, cleaning up
- conn.close()
- connection_alive = False
- break
- request_buffer += new_data
- if request_buffer[-4:] == "\r\n\r\n".encode("UTF-8"):
- ## request finished, stopping the loop
- break
- ## check if client is still there
- if not connection_alive:
- print("[LOG] Lost connection from {}".format(addr[0] + ":" + str(addr[1])))
- conn.close()
- continue
- ## processing the request
- req = Request(request_buffer)
- ## if it is not a proper request, ignore it
- if not req.successful:
- conn.close()
- continue
- ## logging requests to terminal
- print("[LOG] {} - {} {}".format( addr[0] + ":" + str(addr[1]), req.method, req.path ))
- if req.method == "GET":
- ## giving index page at /
- if req.path == "/":
- responce_body = readfile("www/index.html")
- if responce_body == False:
- conn.close()
- continue
- res = Responce()
- res.code = "200"
- res.content_type = "text/html"
- res.body = responce_body
- responce_data = res.generate()
- conn.send(responce_data)
- conn.close()
- ## giving requested amount of lines in the end of the file at /last/*
- elif req.path[:6] == "/last/":
- raw_request_amount = req.path.split("/")[2]
- x = intify(raw_request_amount)
- if x == False:
- conn.close()
- continue
- file_content = readfile("files/links.txt")
- if file_content == False:
- conn.close()
- continue
- file_lines = file_content.split("\n")
- res = Responce()
- res.code = "200"
- res.content_type = "text/plain"
- res.body = "\r\n".join(file_lines[-x-1:]) # selecting requested amount of lines
- responce_data = res.generate()
- conn.send(responce_data)
- conn.close()
- ## giving the whole file at /all
- elif req.path == "/all":
- responce_body = readfile("files/links.txt")
- if responce_body == False:
- conn.close()
- continue
- res = Responce()
- res.code = "200"
- res.content_type = "text/plain"
- res.body = responce_body
- responce_data = res.generate()
- conn.send(responce_data)
- conn.close()
- ## at everything else return 404 Not Found error
- else:
- res = Responce()
- res.code = "404"
- res.content_type = "text/plain"
- res.body = "{} is not a supported request path".format(req.path)
- responce_data = res.generate()
- conn.send(responce_data)
- conn.close()
- ## HEAD is not supported yet to reduce debug time and possible error surface
- elif req.method == "HEAD":
- res = Responce()
- res.code = "501"
- res.content_type = "text/plain"
- res.body = "{} is not a supported request method - yes, it IS intentional (and yes, it WILL be added in upcoming releases; it's not there yet to make a working release faster by reducing the amount of code to debug and polish)".format(req.method)
- responce_data = res.generate()
- conn.send(responce_data)
- conn.close()
- ## other methods are not supported yet
- else:
- res = Responce()
- res.code = "501"
- res.content_type = "text/plain"
- res.body = "{} is not a supported request method".format(req.method)
- responce_data = res.generate()
- conn.send(responce_data)
- conn.close()
- ## handling Ctrl+C signal
- except KeyboardInterrupt:
- print("\r[INFO] Exiting properly")
- break
- ## show thread exception as non-critical error
- except Exception as e:
- print("[ERROR] Got exception {}".format(e))
- conn.close()
- continue
|