Skip to content

Commit e2ba3ec

Browse files
committed
initial commit
0 parents  commit e2ba3ec

File tree

5 files changed

+305
-0
lines changed

5 files changed

+305
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.vscode
2+
__pycache__
3+
debug.log

README.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Python HTTP Server for web development!
2+
3+
**Disclaimer**:
4+
Once you start the server, an instance of Chrome will open up. Due to limitations of the Selenium package, you will have to manually type in the address of the server. But after that you shouldn't encounter any issues!
5+
6+
## Description
7+
An easy-to-use Local HTTP Server written in Python using the Socket and Selenium packages!
8+
9+
## Features
10+
* Easy to use and fast to set up
11+
* It detects file changes and conveniently refreshes your Chrome tab for you (Support for other browsers coming soon!)
12+
* Supports GET requests and POST requests are coming soon as well
13+
14+
## Installation & Requirements
15+
#### Requirements:
16+
* Python 3.x, you can install by visiting http://www.python.org
17+
* Selenium python package, install using pip (Python's package manager): `pip install selenium`
18+
* Watchdog python package (it helps with watching for file changes in the `web` directory), install with: `pip install selenium`
19+
* Socket python package, install with: `pip install socket`
20+
* Chrome webdriver (Selenium is based on the chromedriver for refreshing your tab.), visit https://chromedriver.chromium.org/home
21+
22+
23+
#### Chrome Webdriver installation
24+
1. Visit https://chromedriver.chromium.org/home and download the latest stable release of the driver
25+
2. Once you downloaded it, go to your C:/ drive and create a folder called `webdrivers` and copy the chrome webdriver to that folder.
26+
3. Open the Windows menu by pressing the Windows key and type 'Environment Variables', a settings result should appear. Open it, then go to Environment Variables, then scroll down to where it says 'Path', select it then click 'Edit', create a new entry and enter the path to the folder you created earlier. If you followed the steps it should be `C:\webdrivers`.
27+
4. You're done! You can now run the server with no problem.
28+
29+
## Usage
30+
Open a command prompt in the directory in which you have the files and type `python server.py`. You're done! You can now start editing your files for your website inside of the `web` directory and the server will display them at `http://127.0.0.1:8000`!

requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
selenium
2+
socket

server.py

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
import socket
2+
import sys
3+
import threading
4+
from selenium import webdriver
5+
from watchdog.observers import Observer
6+
from watchdog.events import PatternMatchingEventHandler
7+
8+
9+
10+
class Server:
11+
def __init__(self, host='127.0.0.1', port=8000):
12+
self.host = host
13+
self.port = port
14+
self.content_dir = 'web'
15+
self.status_codes = {
16+
100: 'Continue',
17+
101: 'Switching Protocols',
18+
102: 'Processing',
19+
103: 'Early Hints',
20+
21+
200: 'OK',
22+
201: 'Created',
23+
202: 'Accepted',
24+
203: 'Non-Authoritative Information',
25+
204: 'No Content',
26+
205: 'Reset Content',
27+
206: 'Partial Content',
28+
207: 'Multi-Status',
29+
208: 'Already Reported',
30+
226: 'IM Used',
31+
32+
300: 'Multiple Choices',
33+
301: 'Moved Permanently',
34+
302: 'Found',
35+
303: 'See Other',
36+
304: 'Not Modified',
37+
305: 'Use Proxy',
38+
306: 'Switch Proxy',
39+
307: 'Temporary Redirect',
40+
308: 'Permanent Redirect',
41+
42+
400: 'Bad Request',
43+
401: 'Unauthorized',
44+
402: 'Payment Required',
45+
403: 'Forbidded',
46+
404: 'Not Found',
47+
405: 'Method Not Allowed',
48+
406: 'Not Acceptable',
49+
407: 'Proxy Authentication Required',
50+
408: 'Request Timeout',
51+
409: 'Conflict',
52+
410: 'Gone',
53+
411: 'Length Required',
54+
412: 'Precondition Failed',
55+
413: 'Payload Too Large',
56+
414: 'URI Too Long',
57+
415: 'Unsupported Media Type',
58+
416: 'Range Not Satisfiable',
59+
417: 'Expectation Failed',
60+
418: "I'm a cup of coffee :3",
61+
421: 'Misdirected Request',
62+
422: 'Unprocessable Entity',
63+
423: 'Locked',
64+
424: 'Failed Dependency',
65+
425: 'Too Early',
66+
426: 'Upgrade Required',
67+
428: 'Precondition Required',
68+
429: 'Too Many Requests',
69+
431: 'Request Header Fields Too Large',
70+
451: 'Unavailable For Legal Reasons',
71+
72+
500: 'Internal Server Error',
73+
501: 'Not Implemented',
74+
502: 'Bad Gateway',
75+
503: 'Service Unavailable',
76+
504: 'Gateway Timeout',
77+
505: 'HTTP Version Not Supported',
78+
506: 'Variant Also Negotiates',
79+
507: 'Insufficient Storage',
80+
508: 'Loop Detected',
81+
510: 'Not Extended',
82+
511: 'Network Authentication Required'
83+
}
84+
self.headers = {
85+
'Server': 'Sai',
86+
'Content-Type': 'text/html',
87+
'Connection': 'keep-alive'
88+
}
89+
self.driver = webdriver.Chrome()
90+
91+
event_handler = PatternMatchingEventHandler(
92+
patterns="*",
93+
ignore_patterns="",
94+
ignore_directories=False,
95+
case_sensitive=True
96+
)
97+
98+
event_handler.on_any_event = self.on_file_event
99+
100+
self.observer = Observer()
101+
102+
self.observer.schedule(
103+
event_handler=event_handler,
104+
path="./web",
105+
recursive=True
106+
)
107+
108+
109+
def on_file_event(self, event):
110+
print(f"Detected a change at {event.src_path}. Refreshing web page.")
111+
112+
self.driver.refresh()
113+
114+
115+
def start(self):
116+
self.sv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
117+
118+
try:
119+
print(f"Starting server on {self.host}:{self.port}")
120+
121+
self.sv.bind((self.host, self.port))
122+
123+
print(f"Successfully started server on {self.host}:{self.port}")
124+
125+
print("\nDISCLAIMER: Due to limitations of the Selenium package and the way the code is set up, you will have to manually type in the server address. We're looking into solving the problem!\n")
126+
127+
self.listen()
128+
129+
except Exception as e:
130+
print("Failed to start server")
131+
132+
self.shutdown()
133+
134+
def listen(self):
135+
self.sv.listen(5)
136+
137+
self.observer.start()
138+
139+
while True:
140+
conn, addr = self.sv.accept()
141+
142+
threading.Thread(target=self.handle_client, args=(conn, addr)).start()
143+
144+
145+
def handle_client(self, conn, addr):
146+
BUFSIZ = 8192
147+
148+
while True:
149+
try:
150+
data = conn.recv(BUFSIZ).decode()
151+
152+
if data:
153+
lines = data.split('\r\n')
154+
155+
req_line = lines[0]
156+
157+
req_method = req_line.split(' ')[0]
158+
159+
if req_method == 'GET':
160+
response_line = b""
161+
response_header = b""
162+
blank_line = b"\r\n"
163+
response_body = b""
164+
165+
166+
requested_file = req_line.split(' ')[1]
167+
168+
if requested_file == '/':
169+
requested_file = '/index.html'
170+
else:
171+
pass
172+
173+
filepath = self.content_dir + requested_file
174+
175+
print(f'Initiating webpage {filepath}')
176+
177+
try:
178+
with open(filepath, 'rb') as f:
179+
response_body = f.read()
180+
181+
f.close()
182+
183+
response_line = self.create_response_line(status_code=200)
184+
response_header = self.create_headers()
185+
186+
187+
except Exception as e:
188+
print(f'Something went wrong while trying to serve {filepath}')
189+
190+
response_line = self.create_response_line(status_code=404)
191+
response_header = self.create_headers()
192+
response_body = "<h1>{} {}</h1>".format(
193+
404,
194+
self.status_codes[404]
195+
).encode('utf8')
196+
197+
response = b""
198+
199+
response += response_line
200+
response += response_header
201+
response += blank_line
202+
response += response_body
203+
204+
conn.send(response)
205+
206+
conn.close()
207+
208+
break
209+
210+
else:
211+
print(f'Unsupported request method: {req_method}')
212+
213+
else: break
214+
215+
except KeyboardInterrupt:
216+
self.sv.shutdown()
217+
218+
219+
220+
def shutdown(self):
221+
try:
222+
print("Shutting down...")
223+
224+
sys.exit(0)
225+
except: pass
226+
227+
228+
def create_response_line(self, status_code):
229+
name = self.status_codes[status_code]
230+
231+
return f"HTTP/1.1 {status_code} {name}\r\n".encode('utf8')
232+
233+
234+
def create_headers(self, extra_headers=None):
235+
headers = ""
236+
237+
headers_copy = self.headers.copy()
238+
239+
if extra_headers:
240+
headers_copy.update(extra_headers)
241+
242+
for h in self.headers:
243+
headers += f"{h}: {self.headers[h]}\r\n"
244+
245+
return headers.encode('utf8')
246+
247+
248+
if __name__ == "__main__":
249+
server = Server()
250+
251+
server.start()

web/index.html

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<!DOCTYPE html>
2+
3+
<html>
4+
<head>
5+
<title>
6+
Success!
7+
</title>
8+
9+
<style>
10+
#text {
11+
word-wrap: break-word;
12+
}
13+
</style>
14+
</head>
15+
16+
<body>
17+
<h3 id="text">If you're seeing this, it means that the server started up successfully and you're ready to use it in your web design endeavours! You don't have to restart the server if you want to change a file. All you have to do is save your desired file and it will automatically refresh your browser for you! If you like using my server, consider starring the Github repository <a href="https://github.com/dsnk24/sai-http-server">here!</a></h3>
18+
</body>
19+
</html>

0 commit comments

Comments
 (0)