From 50008213f1b105dfb9153b20b1c2f4643f81a399 Mon Sep 17 00:00:00 2001 From: Ian Mayther Date: Mon, 3 May 2021 15:09:50 -0700 Subject: [PATCH 1/2] Completed unit-tests.py --- http_server.py | 124 ++++++++++++++++++++++++++++++++++++++----------- tests.py | 4 +- unit-tests.py | 1 - 3 files changed, 99 insertions(+), 30 deletions(-) diff --git a/http_server.py b/http_server.py index 58d7386..df2a03e 100644 --- a/http_server.py +++ b/http_server.py @@ -1,6 +1,7 @@ import socket import sys import traceback +import os def response_ok(body=b"This is a minimal response", mimetype=b"text/plain"): """ @@ -19,22 +20,29 @@ 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"\r\n", + 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 can't do that on this server!" + ]) 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"Error encountered while visiting " + ]) def parse_request(request): """ @@ -45,7 +53,33 @@ 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 read_file(file_name): + ''' + Read the provided file as bytes and return content + return byte string + ''' + + with open(os.path.join(os.getcwd(), "webroot", file_name), "rb") as f: + content = f.read() + + return content + +def contents_to_bytes(path): + ''' + Converts the directory list to byte string + ''' + file_path = os.path.join(os.getcwd(), "webroot", path) + files = os.listdir(file_path) + contents = ''.join(files).encode() + return contents def response_path(path): """ @@ -77,17 +111,46 @@ def response_path(path): # TODO: Raise a NameError if the requested content is not present # under webroot. + ''' + Get path + Determine if path is valid + Determine content + Determine mime_type + ''' + + content = b"not implemented" + mime_type = b"not implemented" + path_1 = path.strip("/") + + if path_1 == '': + content=contents_to_bytes(path_1) + mime_type=b"text/plain" + return content, mime_type + + try: + contents_to_bytes(path_1) + except NotADirectoryError: + file_type = path_1.split(".") + except FileNotFoundError: + raise NameError + + type_dict = { + "txt" : [read_file(path_1), b"text/plain"], + "html" : [read_file(path_1), b"text/html"], + "ico" : [read_file(path_1), b"image/ico"], + "jpg" : [read_file(path_1), b"image/jpg"], + } + + content = type_dict[file_type[-1]][0] + mime_type = type_dict[file_type[-1]][1] # 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" return content, mime_type @@ -118,21 +181,26 @@ def server(log_buffer=sys.stderr): print("Request received:\n{}\n\n".format(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" - ) - + try: + path = parse_request(request) + # TODO: Use response_path to retrieve the content and the mimetype, + # based on the request path. + content, mime_type = response_path(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=content, + mimetype=mime_type + ) + except NotImplementedError: + response = response_method_not_allowed() + except NameError: + response = response_not_found() + + print(response) conn.sendall(response) except: traceback.print_exc() diff --git a/tests.py b/tests.py index 21da57e..41788d4 100644 --- a/tests.py +++ b/tests.py @@ -49,7 +49,6 @@ def test_post_yields_method_not_allowed(self): self.assertEqual(response.getcode(), 405) - def test_get_sample_text_content(self): """ A call to /sample.txt returns the correct body @@ -76,6 +75,9 @@ def test_get_sample_text_mime_type(self): web_path = '/' + file error_comment = "Error encountered while visiting " + web_path + print(web_path) + print(os.getcwd()) + response = self.get_response(web_path) self.assertEqual(response.getcode(), 200, error_comment) diff --git a/unit-tests.py b/unit-tests.py index a0c657a..4f1846d 100644 --- a/unit-tests.py +++ b/unit-tests.py @@ -73,6 +73,5 @@ def test_response_path_not_found(self): with self.assertRaises(NameError): http_server.response_path(path) - if __name__ == '__main__': unittest.main() From a89b0ac23a4e21a4552d99abbada611c14426104 Mon Sep 17 00:00:00 2001 From: Ian Mayther Date: Sun, 9 May 2021 18:02:05 -0700 Subject: [PATCH 2/2] Completed the testing and programming --- http_server.py | 78 +++++++++++++++++--------------------------------- tests.py | 5 +--- 2 files changed, 27 insertions(+), 56 deletions(-) diff --git a/http_server.py b/http_server.py index df2a03e..7635615 100644 --- a/http_server.py +++ b/http_server.py @@ -2,6 +2,7 @@ import sys import traceback import os +import mimetypes def response_ok(body=b"This is a minimal response", mimetype=b"text/plain"): """ @@ -51,11 +52,8 @@ def parse_request(request): This server only handles GET requests, so this method shall raise a 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": raise NotImplementedError @@ -66,19 +64,25 @@ def read_file(file_name): Read the provided file as bytes and return content return byte string ''' - - with open(os.path.join(os.getcwd(), "webroot", file_name), "rb") as f: - content = f.read() + try: + with open(file_name, "rb") as f: + content = f.read() + except PermissionError: + content = 'directory' return content -def contents_to_bytes(path): +def contents_to_bytes(path, file_name): ''' Converts the directory list to byte string ''' - file_path = os.path.join(os.getcwd(), "webroot", path) - files = os.listdir(file_path) - contents = ''.join(files).encode() + try: + file_path = os.path.join(path, file_name) + files = os.listdir(file_path) + contents = ''.join(files).encode('utf-8') + except NotADirectoryError: + raise NotADirectoryError + return contents def response_path(path): @@ -109,49 +113,26 @@ def response_path(path): """ - # TODO: Raise a NameError if the requested content is not present - # under webroot. - ''' - Get path - Determine if path is valid - Determine content - Determine mime_type - ''' - content = b"not implemented" mime_type = b"not implemented" path_1 = path.strip("/") - - if path_1 == '': - content=contents_to_bytes(path_1) - mime_type=b"text/plain" - return content, mime_type + path_2 = os.path.join( + os.getcwd(), + 'socket-http-server', + 'webroot' + ) try: - contents_to_bytes(path_1) + content = contents_to_bytes(path_2, path_1) + mime_type = b"text/plain" except NotADirectoryError: - file_type = path_1.split(".") + guess_type = mimetypes.guess_type(path_1)[0] + content = read_file(os.path.join(path_2, path_1)) + mime_type = guess_type.encode('utf-8') except FileNotFoundError: raise NameError - type_dict = { - "txt" : [read_file(path_1), b"text/plain"], - "html" : [read_file(path_1), b"text/html"], - "ico" : [read_file(path_1), b"image/ico"], - "jpg" : [read_file(path_1), b"image/jpg"], - } - - content = type_dict[file_type[-1]][0] - mime_type = type_dict[file_type[-1]][1] - # 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`. - return content, mime_type @@ -183,14 +164,9 @@ def server(log_buffer=sys.stderr): try: path = parse_request(request) - # TODO: Use response_path to retrieve the content and the mimetype, - # based on the request path. + content, mime_type = response_path(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=content, mimetype=mime_type @@ -217,5 +193,3 @@ def server(log_buffer=sys.stderr): if __name__ == '__main__': server() sys.exit(0) - - diff --git a/tests.py b/tests.py index 41788d4..0983988 100644 --- a/tests.py +++ b/tests.py @@ -75,9 +75,6 @@ def test_get_sample_text_mime_type(self): web_path = '/' + file error_comment = "Error encountered while visiting " + web_path - print(web_path) - print(os.getcwd()) - response = self.get_response(web_path) self.assertEqual(response.getcode(), 200, error_comment) @@ -201,7 +198,7 @@ def test_ok_response_at_root_index(self): response = self.get_response(web_path) self.assertEqual(response.getcode(), 200) - + if __name__ == '__main__': unittest.main()