From 668f828a6455ad110dc1890cd433571078b991fd Mon Sep 17 00:00:00 2001 From: stellie Date: Wed, 9 Dec 2020 12:42:24 -0800 Subject: [PATCH 1/4] setup assignment, update response ok, response method not allowed, parse request functions --- http_server.py | 72 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 47 insertions(+), 25 deletions(-) diff --git a/http_server.py b/http_server.py index 58d7386..54241ef 100644 --- a/http_server.py +++ b/http_server.py @@ -1,8 +1,12 @@ +# Stella Kim +# Practice Activity 3: Web Protocols + import socket import sys import traceback -def response_ok(body=b"This is a minimal response", mimetype=b"text/plain"): + +def response_ok(body=b'This is a minimal response', mimetype=b'text/plain'): """ returns a basic HTTP response Ex: @@ -20,20 +24,30 @@ def response_ok(body=b"This is a minimal response", mimetype=b"text/plain"): """ # TODO: Implement response_ok - return b"" + return b'\r\n'.join([ + b'HTTP/1.1 200 OK', + b'Content-Type: ' + mimetype, + b'', + body, + ]) + def response_method_not_allowed(): """Returns a 405 Method Not Allowed response""" # TODO: Implement response_method_not_allowed - return b"" + return b'\r\n'.join([ + b'HTTP/1.1 405 Method Not Allowed', + b'', + b'You cannot do that on this server!' + ]) def response_not_found(): """Returns a 404 Not Found response""" # TODO: Implement response_not_found - return b"" + return b'' def parse_request(request): @@ -45,7 +59,13 @@ def parse_request(request): """ # TODO: implement parse_request - return "" + method, path, version = request.split('\r\n')[0].split(' ') + + if method != 'GET': + raise NotImplementedError + + return path + def response_path(path): """ @@ -85,9 +105,9 @@ def response_path(path): # If the path is "make_time.py", then you may OPTIONALLY return the # result of executing `make_time.py`. But you need only return the # CONTENTS of `make_time.py`. - - content = b"not implemented" - mime_type = b"not implemented" + + content = b'not implemented' + mime_type = b'not implemented' return content, mime_type @@ -96,7 +116,7 @@ def server(log_buffer=sys.stderr): address = ('127.0.0.1', 10000) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - print("making a server on {0}:{1}".format(*address), file=log_buffer) + print('making a server on {0}:{1}'.format(*address), file=log_buffer) sock.bind(address) sock.listen(1) @@ -114,24 +134,28 @@ def server(log_buffer=sys.stderr): if '\r\n\r\n' in request: break - - print("Request received:\n{}\n\n".format(request)) + print('Request received:\n{}\n\n'.format(request)) + + try: + path = parse_request(request) - # TODO: Use parse_request to retrieve the path from the request. + # TODO: Use parse_request to retrieve the path from the request. - # TODO: Use response_path to retrieve the content and the mimetype, - # based on the request path. + # TODO: Use response_path to retrieve the content and the mimetype, + # based on the request path. - # TODO; If parse_request raised a NotImplementedError, then let - # response be a method_not_allowed response. If response_path raised - # a NameError, then let response be a not_found response. Else, - # use the content and mimetype from response_path to build a - # response_ok. - response = response_ok( - body=b"Welcome to my web server", - mimetype=b"text/plain" - ) + # TODO; If parse_request raised a NotImplementedError, then let + # response be a method_not_allowed response. If response_path raised + # a NameError, then let response be a not_found response. Else, + # use the content and mimetype from response_path to build a + # response_ok. + response = response_ok( + body=b'Welcome to my web server', + mimetype=b'text/plain' + ) + except NotImplementedError: + response = response_method_not_allowed() conn.sendall(response) except: @@ -149,5 +173,3 @@ def server(log_buffer=sys.stderr): if __name__ == '__main__': server() sys.exit(0) - - From 45f270266619cbb5aa4a4fb9cbde01717b432a16 Mon Sep 17 00:00:00 2001 From: stellie Date: Fri, 11 Dec 2020 12:13:03 -0800 Subject: [PATCH 2/4] update response path function to determine if given path is a directory or file --- http_server.py | 48 ++++++++++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/http_server.py b/http_server.py index 54241ef..6b7e262 100644 --- a/http_server.py +++ b/http_server.py @@ -4,11 +4,13 @@ import socket import sys import traceback +import os +import mimetypes def response_ok(body=b'This is a minimal response', mimetype=b'text/plain'): """ - returns a basic HTTP response + Returns a basic HTTP response Ex: response_ok( b"

Welcome:

", @@ -23,11 +25,10 @@ def response_ok(body=b'This is a minimal response', mimetype=b'text/plain'): ''' """ - # TODO: Implement response_ok return b'\r\n'.join([ b'HTTP/1.1 200 OK', b'Content-Type: ' + mimetype, - b'', + b'', # CRLF to denote end of header body, ]) @@ -35,7 +36,6 @@ def response_ok(body=b'This is a minimal response', mimetype=b'text/plain'): def response_method_not_allowed(): """Returns a 405 Method Not Allowed response""" - # TODO: Implement response_method_not_allowed return b'\r\n'.join([ b'HTTP/1.1 405 Method Not Allowed', b'', @@ -46,8 +46,11 @@ def response_method_not_allowed(): def response_not_found(): """Returns a 404 Not Found response""" - # TODO: Implement response_not_found - return b'' + return b'\r\n'.join([ + b'HTTP/1.1 404 Not Found', + b'', + b'File or directory not found', + ]) def parse_request(request): @@ -58,7 +61,6 @@ def parse_request(request): NotImplementedError if the method of the request is not GET. """ - # TODO: implement parse_request method, path, version = request.split('\r\n')[0].split(' ') if method != 'GET': @@ -95,19 +97,25 @@ def response_path(path): """ - # TODO: Raise a NameError if the requested content is not present - # under webroot. - - # TODO: Fill in the appropriate content and mime_type give the path. - # See the assignment guidelines for help on "mapping mime-types", though - # you might need to create a special case for handling make_time.py - # - # If the path is "make_time.py", then you may OPTIONALLY return the - # result of executing `make_time.py`. But you need only return the - # CONTENTS of `make_time.py`. - - content = b'not implemented' - mime_type = b'not implemented' + content = b'' + mime_type = b'' + + requested_path = os.path.join(os.getcwd(), 'webroot' + path) + + if os.path.exists(requested_path): + if os.path.isfile(requested_path): # path is a file + # print('this is a file') + mime_type = mimetypes.guess_type(requested_path)[0].encode() + with open(requested_path, 'rb') as f: + content = f.read() + + elif os.path.isdir(requested_path): # path is a directory + # print('this is a directory') + mime_type = b'text/plain' + for item in os.listdir(requested_path): + content += item.encode() + b'\r\n' + else: + raise NameError # requested content is not present under webroot return content, mime_type From f7a4424956a80db0f549ceaeb628d43bb143ff77 Mon Sep 17 00:00:00 2001 From: stellie Date: Sun, 13 Dec 2020 11:18:06 -0800 Subject: [PATCH 3/4] update directory content output for path --- http_server.py | 31 ++++++++----------------------- 1 file changed, 8 insertions(+), 23 deletions(-) diff --git a/http_server.py b/http_server.py index 6b7e262..a9d675b 100644 --- a/http_server.py +++ b/http_server.py @@ -97,23 +97,19 @@ def response_path(path): """ - content = b'' - mime_type = b'' - requested_path = os.path.join(os.getcwd(), 'webroot' + path) + print(requested_path) if os.path.exists(requested_path): if os.path.isfile(requested_path): # path is a file - # print('this is a file') mime_type = mimetypes.guess_type(requested_path)[0].encode() with open(requested_path, 'rb') as f: content = f.read() elif os.path.isdir(requested_path): # path is a directory - # print('this is a directory') mime_type = b'text/plain' - for item in os.listdir(requested_path): - content += item.encode() + b'\r\n' + dir_contents = '\r\n'.join(os.listdir(requested_path)) + content = dir_contents.encode() else: raise NameError # requested content is not present under webroot @@ -147,29 +143,18 @@ def server(log_buffer=sys.stderr): try: path = parse_request(request) - - # TODO: Use parse_request to retrieve the path from the request. - - # TODO: Use response_path to retrieve the content and the mimetype, - # based on the request path. - - # TODO; If parse_request raised a NotImplementedError, then let - # response be a method_not_allowed response. If response_path raised - # a NameError, then let response be a not_found response. Else, - # use the content and mimetype from response_path to build a - # response_ok. - response = response_ok( - body=b'Welcome to my web server', - mimetype=b'text/plain' - ) + content, mimetype = response_path(path) + response = response_ok(body=content, mimetype=mimetype) except NotImplementedError: response = response_method_not_allowed() + except NameError: + response = response_not_found() conn.sendall(response) except: traceback.print_exc() finally: - conn.close() + conn.close() except KeyboardInterrupt: sock.close() From 48c31fb57de0fa166500ae9f00571ce0e8a5ee72 Mon Sep 17 00:00:00 2001 From: stellie Date: Sun, 13 Dec 2020 12:12:49 -0800 Subject: [PATCH 4/4] add break in server function if no data is received, as Firefox will continue to hang if no data is present --- http_server.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/http_server.py b/http_server.py index a9d675b..4e75107 100644 --- a/http_server.py +++ b/http_server.py @@ -98,7 +98,6 @@ def response_path(path): """ requested_path = os.path.join(os.getcwd(), 'webroot' + path) - print(requested_path) if os.path.exists(requested_path): if os.path.isfile(requested_path): # path is a file @@ -134,6 +133,8 @@ def server(log_buffer=sys.stderr): request = '' while True: data = conn.recv(1024) + if not data: # break loop if no data received + break request += data.decode('utf8') if '\r\n\r\n' in request: