138 lines
3.7 KiB
Python
138 lines
3.7 KiB
Python
#!/usr/bin/env python3
|
|
|
|
import sys
|
|
import http.server
|
|
import socketserver
|
|
import threading
|
|
import os
|
|
import time
|
|
import select
|
|
|
|
import urllib.parse
|
|
|
|
import inotify.adapters
|
|
|
|
PORT = int(os.getenv('PORT', 8000))
|
|
|
|
THIS_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
|
|
MONITORED_EVENT_TYPES = (
|
|
'IN_CREATE',
|
|
# 'IN_MODIFY',
|
|
'IN_CLOSE_WRITE',
|
|
'IN_DELETE',
|
|
'IN_MOVED_FROM',
|
|
'IN_MOVED_TO',
|
|
'IN_DELETE_SELF',
|
|
'IN_MOVE_SELF',
|
|
)
|
|
|
|
WAITING_RESPONSES = []
|
|
SLEEP_TIME = 0.5
|
|
COUNTER = 0
|
|
MAX_WAITS = 100
|
|
|
|
class Server(http.server.SimpleHTTPRequestHandler):
|
|
def do_GET(self):
|
|
if self.path.strip('/') == '__wait_for_changes':
|
|
WAITING_RESPONSES.append(self)
|
|
print(len(WAITING_RESPONSES), "waiting responses")
|
|
global COUNTER
|
|
ticket, COUNTER = COUNTER, COUNTER + 1
|
|
|
|
while self in WAITING_RESPONSES:
|
|
# This is an horribe way to wait! ... but it may work for quick tests 🤷
|
|
|
|
if COUNTER - ticket > MAX_WAITS:
|
|
# Connection closed by the other side
|
|
print("Manually closed for cleanup")
|
|
WAITING_RESPONSES.remove(self)
|
|
# send 502 response, timeout
|
|
self.send_response(502)
|
|
# send response headers
|
|
self.end_headers()
|
|
|
|
return
|
|
|
|
time.sleep(SLEEP_TIME)
|
|
return
|
|
|
|
path = urllib.parse.unquote(self.path)
|
|
if path.strip('/') == '':
|
|
path = '/index.html'
|
|
if os.path.isdir(path.strip('/')):
|
|
if path.endswith('/'):
|
|
path = path.strip('/') + '/index.html'
|
|
else:
|
|
# Redirect to + /
|
|
self.send_response(301)
|
|
self.send_header('Location', path + '/')
|
|
self.end_headers()
|
|
return
|
|
|
|
# send 200 response
|
|
self.send_response(200)
|
|
# send response headers
|
|
self.end_headers()
|
|
|
|
with open(path.strip('/'), 'rb') as f:
|
|
# send the body of the response
|
|
self.wfile.write(f.read())
|
|
|
|
if not path.endswith('.html'):
|
|
return
|
|
else:
|
|
# Append update waiter
|
|
with open(os.path.join(THIS_DIR, 'wait_for_update.js'), 'rb') as f:
|
|
new_data = b'<script>' + f.read() + b'</script>'
|
|
self.wfile.write(new_data)
|
|
new_data_len = len(new_data)
|
|
|
|
return
|
|
|
|
def notify_reloads():
|
|
while len(WAITING_RESPONSES) > 0:
|
|
# Close opened connections
|
|
res = WAITING_RESPONSES.pop(0)
|
|
|
|
try:
|
|
# send 200 response
|
|
res.send_response(200)
|
|
# send response headers
|
|
res.end_headers()
|
|
except Exception as e:
|
|
print("ERROR:", e)
|
|
|
|
global COUNTER
|
|
COUNTER = 0
|
|
|
|
|
|
def start_notifier():
|
|
notifier = inotify.adapters.InotifyTree(os.getcwd())
|
|
should_reload = False
|
|
for event in notifier.event_gen(yield_nones=True):
|
|
if event is None:
|
|
if should_reload:
|
|
print("Reloading!")
|
|
should_reload = False
|
|
notify_reloads()
|
|
continue
|
|
|
|
(ev, types, directory, file) = event
|
|
if any([type in MONITORED_EVENT_TYPES for type in types]):
|
|
print("Detected change!", types, directory, file)
|
|
should_reload = True
|
|
|
|
def serve():
|
|
Handler = Server
|
|
notifier_thread = threading.Thread(target=start_notifier)
|
|
|
|
with http.server.ThreadingHTTPServer(("127.0.0.1", PORT), Handler) as httpd:
|
|
print("serving at port", PORT)
|
|
notifier_thread.start()
|
|
httpd.serve_forever()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
serve()
|