Created
June 6, 2022 08:14
-
-
Save xchellx/a5251600263c32904ae5b8c07dee0e6e to your computer and use it in GitHub Desktop.
CORS-supported alternative to `py -3 -m http.server 8000`
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# encoding: utf-8 | |
# Absolute TRUE process exit handling (atexit is psuedo-atexit, this is TRUE atexit!) | |
__all__ = ['register','unregister'] | |
import sys | |
import os | |
from pathlib import Path | |
# Ensure current path is on the system path in case local relative import of libraries fail | |
CWD: str | |
CWD = None | |
try: | |
CWD = __file__ | |
except NameError: | |
CWD = sys.argv[0] | |
CWD = os.fspath(Path(CWD).resolve().parent) | |
if (CWD not in sys.path): | |
sys.path.append(CWD) | |
import atexit | |
from signal import signal, getsignal, SIGINT, SIGTERM | |
if sys.platform.startswith('win'): | |
import win32api | |
import win32con | |
else: | |
from signal import SIGTSTP, SIGHUP | |
global __winsigs__checklist__ | |
__winsigs__checklist__ = [] | |
global __og_sig_handlers__ | |
__og_sig_handlers__ = { | |
'SIGINT': getsignal(SIGINT), | |
'SIGTERM': getsignal(SIGTERM), | |
'SIGTSTP': getsignal(SIGTSTP) if not sys.platform.startswith('win') else None, | |
'SIGHUP': getsignal(SIGHUP) if not sys.platform.startswith('win') else None | |
} | |
def handle_signal(sig, frame): | |
sys.exit(sig) | |
def handle_signal_windows(sig): | |
global __winsigs__checklist__ | |
__winsigs__checklist__ = [] if __winsigs__checklist__ is None else __winsigs__checklist__ | |
if sig in __winsigs__checklist__: | |
sys.exit(sig) | |
def signal_windows(handler, handle_interrupt, remove): | |
if (handler is None): | |
raise ValueError('Parameter "handler" is required') | |
handle_interrupt = False if handle_interrupt is None else handle_interrupt | |
remove = False if remove is None else remove | |
global __winsigs__checklist__ | |
__winsigs__checklist__ = [] if __winsigs__checklist__ is None else __winsigs__checklist__ | |
if sys.platform.startswith('win'): | |
__winsigs__checklist__ = [win32con.CTRL_LOGOFF_EVENT, win32con.CTRL_SHUTDOWN_EVENT, win32con.CTRL_CLOSE_EVENT] | |
if handle_interrupt: | |
__winsigs__checklist__.append(win32con.CTRL_BREAK_EVENT) | |
__winsigs__checklist__.append(win32con.CTRL_C_EVENT) | |
if remove: | |
__winsigs__checklist__.clear() | |
win32api.SetConsoleCtrlHandler(handler, 0 if remove else 1) | |
else: | |
raise NotImplementedError('SetConsoleCtrlHandler is not supported on platforms other than windows') | |
def register(handler, handle_interrupt=None): | |
if (handler is None): | |
raise ValueError('Parameter "handler" is required') | |
handle_interrupt = False if handle_interrupt is None else handle_interrupt | |
global __og_sig_handlers__ | |
atexit.register(handler) | |
if handle_interrupt: | |
signal(SIGINT, handle_signal) | |
signal(SIGTERM, handle_signal) | |
if sys.platform.startswith('win'): | |
signal_windows(handle_signal_windows, handle_interrupt, False) | |
else: | |
signal(SIGTSTP, handle_signal) | |
signal(SIGHUP, handle_signal) | |
def unregister(handler): | |
if (handler is None): | |
raise ValueError('Parameter "handler" is required') | |
global __og_sig_handlers__ | |
atexit.unregister(handler) | |
signal(SIGINT, __og_sig_handlers__['SIGINT']) | |
signal(SIGTERM, __og_sig_handlers__['SIGTERM']) | |
if sys.platform.startswith('win'): | |
signal_windows(handle_signal_windows, True, True) | |
else: | |
signal(SIGTSTP, __og_sig_handlers__['SIGTSTP']) | |
signal(SIGHUP, __og_sig_handlers__['SIGHUP']) | |
if __name__ == '__main__': | |
print('Absolute TRUE process exit handling (atexit is psuedo-atexit, this is TRUE atexit!)') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@ECHO OFF | |
py -3 serve.py . -n localhost -p 8000 -d .__localservelogs__ -l | |
REM py -3 -m http.server 8000 | |
PAUSE |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# encoding: utf-8 | |
# Use instead of 'python3 -http.server 8000' when you need CORS | |
import sys | |
import os | |
from pathlib import Path | |
# Ensure current path is on the system path in case local relative import of libraries fail | |
CWD: str | |
CWD = None | |
try: | |
CWD = __file__ | |
except NameError: | |
CWD = sys.argv[0] | |
CWD = os.fspath(Path(CWD).resolve().parent) | |
if (CWD not in sys.path): | |
sys.path.append(CWD) | |
import argparse | |
import exitlistener | |
from datetime import datetime, timezone | |
from functools import partial | |
from http.server import HTTPServer, SimpleHTTPRequestHandler | |
def log_is_valid(): | |
global log | |
if log is None: | |
return False | |
else: | |
return not log.closed | |
def print_write(*args, **kwargs): | |
if log_is_valid(): | |
global log | |
for arg in args: | |
log.write(arg) | |
log.write('\n') | |
log.flush() | |
print(*args, **kwargs) | |
def stdout_write(str): | |
if log_is_valid(): | |
global log | |
log.write(str) | |
log.flush() | |
sys.stdout.write(str) | |
def close_log(): | |
if log_is_valid(): | |
global log | |
log.close() | |
def close_server(): | |
global httpd | |
if not httpd is None: | |
httpd._BaseServer__is_shut_down.set() | |
httpd.__shutdown_request = True | |
httpd.shutdown() | |
httpd.server_close() | |
def onexit(): | |
print_write('Shutting down server...') | |
try: | |
close_server() | |
except: | |
pass | |
try: | |
close_log() | |
except: | |
pass | |
try: | |
exitlistener.unregister(onexit) | |
except: | |
pass | |
# For HTTPServer | |
class CORSRequestHandler(SimpleHTTPRequestHandler): | |
def __init__(self, *args, **kwargs): | |
super().__init__(*args, **kwargs) | |
# Set according to: https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types | |
self.extensions_map.update({ | |
'.js': 'text/javascript', # Required or ES6 JavaScript modules will be interpreted incorrectly | |
'.html': 'text/html', | |
'.htm': 'text/html', | |
'.xml': 'application/xml', | |
'.css': 'text/css', | |
'.txt': 'text/plain', | |
'.apng': 'image/apng', | |
'.avif': 'image/avif', | |
'.gif': 'image/gif', | |
'.jpg': 'image/jpeg', | |
'.jpeg': 'image/jpeg', | |
'.jfif': 'image/jpeg', | |
'.pjpeg': 'image/jpeg', | |
'.pjp': 'image/jpeg', | |
'.png': 'image/png', | |
'.svg': 'image/svg+xml', | |
'.webp': 'image/webp', | |
'.wav': 'audio/wav', | |
'.webm': 'video/webm', | |
'.ogg': 'audio/ogg', | |
'.zip': 'application/zip', | |
'.7z': 'application/x-7z-compressed', | |
'.gz': 'application/gzip', | |
'.tar': 'application/x-tar', | |
'.tgz': 'application/x-gtar-compressed', | |
'.rar': 'application/rar', | |
'.vmo': 'application/x-virtools' | |
}); | |
def end_headers(self): | |
self.send_response(200, 'ok') | |
# CORS headers (permissive) | |
self.send_header('Access-Control-Allow-Credentials', 'true') | |
self.send_header('Access-Control-Allow-Origin', '*') | |
self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS') | |
self.send_header('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type') | |
self.send_header('Cache-Control', 'no-store, no-cache, must-revalidate') | |
return super(CORSRequestHandler, self).end_headers() | |
def log_request(self, code='-', size='-'): | |
self.log_message('"%s" %s %s', | |
self.requestline, str(code), str(size)) | |
def log_error(self, format, *args): | |
self.log_message(format, *args) | |
def log_message(self, format, *args): | |
msg = ('%s - - [%s] %s\n' % | |
(self.address_string(), | |
self.log_date_time_string(), | |
format%args)) | |
stdout_write(msg) | |
def serve(directory, hostname, portnumber, enablelog, logdir): | |
# For logging to file | |
if not log_is_valid(): | |
global log | |
log = open( | |
logdir + os.sep + 'serve' + str( | |
datetime.strptime( | |
datetime.now().strftime('%Y%m%d%H%M%S'), '%Y%m%d%H%M%S' | |
).replace( | |
tzinfo=timezone.utc | |
).timestamp() | |
) + '.log', 'w+', encoding='utf-8' | |
) | |
print_write('HTTPServer started on ' + str(hostname) + ' at port ' + str(portnumber)) | |
print_write('To stop the server, close the window or send a cancel command via Ctrl+C.') | |
# Start server | |
global httpd | |
if httpd is None: | |
httpd = HTTPServer((hostname, portnumber), partial(CORSRequestHandler, directory=directory)) | |
httpd.serve_forever() | |
if __name__ == '__main__': | |
STATUS_SUCCESS=0 | |
STATUS_ERROR=1 | |
try: | |
global log | |
log = None | |
global httpd | |
httpd = None | |
exitlistener.register(onexit, True) | |
parser = argparse.ArgumentParser(prog=sys.argv[0], formatter_class=lambda prog: argparse.ArgumentDefaultsHelpFormatter( | |
prog=sys.argv[0], max_help_position=os.get_terminal_size().columns, width=os.get_terminal_size().columns | |
)) | |
parser.add_argument('-n', '--hostname', default='localhost', help='name of the host address (localhost, IP, etc.') | |
parser.add_argument('-p', '--portnumber', default='8000', type=int, help='port to host at') | |
parser.add_argument('-l', '--enablelog', action='store_true', help='enable log to file') | |
parser.add_argument('-d', '--logdir', default='logs', help='path to write log files to') | |
parser.add_argument('directory') | |
if len(sys.argv) == 1: | |
raise argparse.ArgumentError(None, 'Missing one or more arguments') | |
else: | |
args = parser.parse_args() | |
serve(args.directory, args.hostname, args.portnumber, args.enablelog, args.logdir) | |
sys.exit(STATUS_SUCCESS) | |
except (argparse.ArgumentError, argparse.ArgumentTypeError) as e: | |
print(e) | |
parser.print_help(sys.stderr) | |
sys.exit(STATUS_ERROR) | |
except (ValueError, ConnectionError) as e: | |
print_error(e) | |
sys.exit(STATUS_ERROR) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment