diff --git a/Lib/http/server.py b/Lib/http/server.py index fa204fbc15..19616093f3 100644 --- a/Lib/http/server.py +++ b/Lib/http/server.py @@ -90,6 +90,7 @@ __all__ = [ import copy import datetime import email.utils +import cgi import html import http.client import io @@ -636,9 +637,13 @@ class SimpleHTTPRequestHandler(BaseHTTPRequestHandler): The GET and HEAD requests are identical except that the HEAD request omits the actual contents of the file. + If allow_upload is True, this handler will add a form with a + file input to the directory listing and it will accept POST + requests with file data. """ server_version = "SimpleHTTP/" + __version__ + allow_upload = False extensions_map = _encodings_map_default = { '.gz': 'application/gzip', '.Z': 'application/octet-stream', @@ -646,12 +651,59 @@ class SimpleHTTPRequestHandler(BaseHTTPRequestHandler): '.xz': 'application/x-xz', } - def __init__(self, *args, directory=None, **kwargs): + def __init__(self, *args, directory=None, allow_upload=False, **kwargs): if directory is None: directory = os.getcwd() self.directory = os.fspath(directory) + self.allow_upload = allow_upload super().__init__(*args, **kwargs) + def do_POST(self): + if not self.allow_upload: + self.send_error( + HTTPStatus.METHOD_NOT_ALLOWED, + "{} was not initialized with POST request " + "support".format(self.__class__.__name__)) + return + + try: + form = cgi.FieldStorage( + fp=self.rfile, + headers=self.headers, + environ={ + 'REQUEST_METHOD': 'POST', + 'CONTENT_TYPE': self.headers['Content-Type'] + } + ) + uploaded_file = form['file'] + assert uploaded_file.filename + path = self.translate_path(self.path) + with open(os.path.join(path, uploaded_file.filename), + 'wb') as wfile, uploaded_file.file as rfile: + batch = rfile.read(2048) + while batch: + wfile.write(batch) + batch = rfile.read(2048) + except (KeyError, AssertionError): + self.send_error(HTTPStatus.BAD_REQUEST, 'Invalid file') + except Exception: + self.send_error(HTTPStatus.INTERNAL_SERVER_ERROR, 'Internal error') + raise + else: + self.send_response(HTTPStatus.CREATED) + self.end_headers() + self.wfile.write('

Successfully uploaded {}

'.format( + uploaded_file.filename).encode()) + try: + displaypath = urllib.parse.unquote(self.path, + errors='surrogatepass') + except UnicodeDecodeError: + displaypath = urllib.parse.unquote(self.path) + self.wfile.write('Back to {}'.format( + html.escape(displaypath), + html.escape(displaypath, quote=False), + ).encode()) + def do_GET(self): """Serve a GET request.""" f = self.send_head() @@ -786,6 +838,14 @@ class SimpleHTTPRequestHandler(BaseHTTPRequestHandler): 'content="text/html; charset=%s">' % enc) r.append('%s\n' % title) r.append('\n

%s

' % title) + if self.allow_upload: + r.append('
') + r.append('
'.format(self.path)) + r.append('') + r.append('
') + r.append('\n') + r.append('
\n') r.append('
\n