#!/usr/bin/env python3# -*- coding: UTF-8 -*-"""Simple HTTP Server With Upload.
This module builds on http.server by implementing the standard GET
and HEAD requests in a fairly straightforward manner.
"""
__version__ ="1.0.0"
__author__ ="freelamb"
__all__ =["SimpleHTTPRequestHandler"]import os
import sys
import posixpath
import http.server
import urllib.parse
import shutil
import mimetypes
import re
from io import BytesIO
import html # For HTML escapingclassSimpleHTTPRequestHandler(http.server.BaseHTTPRequestHandler):"""Simple HTTP request handler with GET/HEAD/POST commands.
This serves files from the current directory and any of its
subdirectories. The MIME type for files is determined by
calling the .guess_type() method. And can receive file uploaded
by client.
"""
server_version ="simple_http_server/"+ __version__
defdo_GET(self):"""Serve a GET request."""
fd = self.send_head()if fd:
shutil.copyfileobj(fd, self.wfile)
fd.close()defdo_HEAD(self):"""Serve a HEAD request."""
fd = self.send_head()if fd:
fd.close()defdo_POST(self):"""Serve a POST request."""
r, info = self.deal_post_data()print(r, info,"by: ", self.client_address)
f = BytesIO()
f.write(b'<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">')
f.write(b"<html>\n<title>Upload Result Page</title>\n")
f.write(b"<body>\n<h2>Upload Result Page</h2>\n")
f.write(b"<hr>\n")if r:
f.write(b"<strong>Success:</strong>")else:
f.write(b"<strong>Failed:</strong>")
f.write(info.encode())
f.write(b"<br><a href=\"%s\">back</a>"% self.headers['referer'].encode())
f.write(b"<hr><small>Powered By: freelamb, check new version at ")
f.write(b"<a href=\"https://github.com/freelamb/simple_http_server\">")
f.write(b"here</a>.</small></body>\n</html>\n")
length = f.tell()
f.seek(0)
self.send_response(200)
self.send_header("Content-type","text/html")
self.send_header("Content-Length",str(length))
self.end_headers()if f:
shutil.copyfileobj(f, self.wfile)
f.close()defdeal_post_data(self):
boundary = self.headers['Content-Type'].split("=")[1]
remain_bytes =int(self.headers['Content-Length'])
line = self.rfile.readline()
remain_bytes -=len(line)if boundary.encode()notin line:returnFalse,"Content NOT begin with boundary"
line = self.rfile.readline()
remain_bytes -=len(line)
fn = re.findall(r'Content-Disposition.*name="file"; filename="(.*)"', line.decode())ifnot fn:returnFalse,"Can't find out file name..."
path = translate_path(self.path)
fn = os.path.join(path, fn[0])while os.path.exists(fn):
fn +="_"
line = self.rfile.readline()
remain_bytes -=len(line)
line = self.rfile.readline()
remain_bytes -=len(line)try:
out =open(fn,'wb')except IOError:returnFalse,"Can't create file to write, do you have permission to write?"
pre_line = self.rfile.readline()
remain_bytes -=len(pre_line)while remain_bytes >0:
line = self.rfile.readline()
remain_bytes -=len(line)if boundary.encode()in line:
pre_line = pre_line[0:-1]if pre_line.endswith(b'\r'):
pre_line = pre_line[0:-1]
out.write(pre_line)
out.close()returnTrue,"File '%s' upload success!"% fn
else:
out.write(pre_line)
pre_line = line
returnFalse,"Unexpected end of data."defsend_head(self):"""Common code for GET and HEAD commands."""
path = translate_path(self.path)if os.path.isdir(path):ifnot self.path.endswith('/'):
self.send_response(301)
self.send_header("Location", self.path +"/")
self.end_headers()returnNonefor index in"index.html","index.htm":
index = os.path.join(path, index)if os.path.exists(index):
path = index
breakelse:return self.list_directory(path)
content_type = self.guess_type(path)try:
f =open(path,'rb')except IOError:
self.send_error(404,"File not found")returnNone
self.send_response(200)
self.send_header("Content-type", content_type)
fs = os.fstat(f.fileno())
self.send_header("Content-Length",str(fs[6]))
self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
self.end_headers()return f
deflist_directory(self, path):"""Helper to produce a directory listing."""try:
list_dir = os.listdir(path)except os.error:
self.send_error(404,"No permission to list directory")returnNone
list_dir.sort(key=lambda a: a.lower())
f = BytesIO()
display_path = html.escape(urllib.parse.unquote(self.path))
f.write(b'<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">')
f.write(b"<html>\n<title>Directory listing for %s</title>\n"% display_path.encode())
f.write(b"<body>\n<h2>Directory listing for %s</h2>\n"% display_path.encode())
f.write(b"<hr>\n")
f.write(b"<form ENCTYPE=\"multipart/form-data\" method=\"post\">")
f.write(b"<input name=\"file\" type=\"file\"/>")
f.write(b"<input type=\"submit\" value=\"upload\"/></form>\n")
f.write(b"<hr>\n<ul>\n")for name in list_dir:
fullname = os.path.join(path, name)
display_name = linkname = name
if os.path.isdir(fullname):
display_name = name +"/"
linkname = name +"/"if os.path.islink(fullname):
display_name = name +"@"
f.write(b'<li><a href="%s">%s</a>\n'%(urllib.parse.quote(linkname).encode(), html.escape(display_name).encode()))
f.write(b"</ul>\n<hr>\n</body>\n</html>\n")
length = f.tell()
f.seek(0)
self.send_response(200)
self.send_header("Content-type","text/html")
self.send_header("Content-Length",str(length))
self.end_headers()return f
defguess_type(self, path):
base, ext = posixpath.splitext(path)if ext in self.extensions_map:return self.extensions_map[ext]
ext = ext.lower()if ext in self.extensions_map:return self.extensions_map[ext]else:return self.extensions_map['']ifnot mimetypes.inited:
mimetypes.init()
extensions_map = mimetypes.types_map.copy()
extensions_map.update({'':'application/octet-stream','.py':'text/plain','.c':'text/plain','.h':'text/plain',})deftranslate_path(path):
path = path.split('?',1)[0]
path = path.split('#',1)[0]
path = posixpath.normpath(urllib.parse.unquote(path))
words =filter(None, path.split('/'))
path = os.getcwd()for word in words:
drive, word = os.path.splitdrive(word)
head, word = os.path.split(word)if word in(os.curdir, os.pardir):continue
path = os.path.join(path, word)return path
defmain():if sys.argv[1:]:
port =int(sys.argv[1])else:
port =8058
server_address =('', port)
SimpleHTTPRequestHandler.protocol_version ="HTTP/1.0"
httpd = http.server.HTTPServer(server_address, SimpleHTTPRequestHandler)
sa = httpd.socket.getsockname()print("Serving HTTP on", sa[0],"port", sa[1],"...")
httpd.serve_forever()if __name__ =='__main__':
main()