diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..28d227e --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,16 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Python: Current File", + "type": "python", + "request": "launch", + "program": "${file}", + "args": ["10000"], + "console": "integratedTerminal" + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..c355c47 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,13 @@ +{ + "python.testing.unittestArgs": [ + "-v", + "-s", + ".", + "-p", + "*test*.py" + ], + "python.testing.pytestEnabled": false, + "python.testing.nosetestsEnabled": false, + "python.testing.unittestEnabled": true, + "python.pythonPath": "C:\\Users\\erica\\AppData\\Local\\Programs\\Python\\Python37\\python.exe" +} \ No newline at end of file diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/http_server.py b/http_server.py index 58d7386..5457c11 100644 --- a/http_server.py +++ b/http_server.py @@ -1,6 +1,9 @@ import socket import sys +import os import traceback +import mimetypes +import subprocess def response_ok(body=b"This is a minimal response", mimetype=b"text/plain"): """ @@ -16,24 +19,45 @@ def response_ok(body=b"This is a minimal response", mimetype=b"text/plain"): Content-Type: text/html\r\n \r\n

Welcome:

\r\n - ''' - """ - - # TODO: Implement response_ok - return b"" - + # """ + # response = f"""HTTP/1.1 200 OK\r + # Content-Type:{mimetype}\r\n\r + # {body} + # """ + if mimetype == "text/plain": + response = "HTTP/1.1 200 OK\r\n" + response += "Content-Type:{}\r\n".format(mimetype) + response += "\r\n" + response += "{}".format(body) + + return response.encode() + + else: + response = "HTTP/1.1 200 OK\r\n" + response += "Content-Type: {}\r\n".format(mimetype) + response += "\r\n" + response = response.encode() + response += body + + return response + + def response_method_not_allowed(): """Returns a 405 Method Not Allowed response""" - # TODO: Implement response_method_not_allowed - return b"" + response = f"""HTTP/1.1 405 Method Not Allowed\r +Content-Type: text/plain\r\n\r +Method Not Allowed\r""" + return response.encode() def response_not_found(): """Returns a 404 Not Found response""" - # TODO: Implement response_not_found - return b"" + response = f"""HTTP/1.1 404 Not Found\r + Content-Type: text/plain\r\n\r + Not Found\r""" + return response.encode() def parse_request(request): @@ -44,8 +68,13 @@ def parse_request(request): NotImplementedError if the method of the request is not GET. """ - # 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): """ @@ -74,10 +103,37 @@ def response_path(path): response_path('/a_page_that_doesnt_exist.html') -> Raises a NameError """ - + content = "" + mime_type = "" + path = f"webroot{path}" + if not os.path.exists(path): + raise NameError # TODO: Raise a NameError if the requested content is not present # under webroot. + + if os.path.isdir(path): + dir_list = os.listdir(path) + content = '\r\n'.join(dir_list) + mime_type = "text/plain" + + # print(content) + + if os.path.isfile(path): + types = {".jpg": "image/jpeg", ".png": "image/png", ".html": "text/html", ".ico": "image/vnd.microsoft.icon", ".txt" : "text/plain", ".py" : "text/plain", ".bmp" : "image/bmp" } + name, extension = os.path.splitext(path) + mime_type = types.get(extension) + # print(mime_type) + + if "text/plain" in mime_type: + with open(path, 'r', newline="\r\n") as file: + content = file.read() + + else: + with open(path, 'rb') as file: + content = file.read() + + # 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 @@ -86,8 +142,8 @@ def response_path(path): # 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 @@ -114,25 +170,29 @@ def server(log_buffer=sys.stderr): if '\r\n\r\n' in request: break - - print("Request received:\n{}\n\n".format(request)) # TODO: Use parse_request to retrieve the path from the request. - + 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=b"Welcome to my web server", - mimetype=b"text/plain" + body=content, + mimetype=mime_type ) - + # print(response) + conn.sendall(response) + except NotImplementedError: + response = response_method_not_allowed() + conn.sendall(response) + except NameError: + response = response_not_found() conn.sendall(response) except: traceback.print_exc() @@ -146,7 +206,13 @@ def server(log_buffer=sys.stderr): traceback.print_exc() + if __name__ == '__main__': + # response = response_ok( + # body=b"Welcome to my web server", + # mimetype=b"text/plain" + # ) + # print(response) server() sys.exit(0) diff --git a/tests.py b/tests.py index 21da57e..ac32132 100644 --- a/tests.py +++ b/tests.py @@ -11,7 +11,9 @@ def setUp(self): self.server_process = subprocess.Popen( [ "python", - "http_server.py" + "-u", + "http_server.py", + "10000" ], stdout=subprocess.PIPE, stderr=subprocess.PIPE, @@ -25,15 +27,18 @@ def get_response(self, url): """ Helper function to get a response from a given url, using http.client """ + try: + conn = http.client.HTTPConnection('localhost:10000') + conn.request('GET', url) - conn = http.client.HTTPConnection('localhost:10000') - conn.request('GET', url) + response = conn.getresponse() - response = conn.getresponse() + conn.close() - conn.close() - - return response + return response + except Exception as e: + print(e) + raise def test_post_yields_method_not_allowed(self): """ @@ -54,7 +59,7 @@ def test_get_sample_text_content(self): """ A call to /sample.txt returns the correct body """ - file = 'sample.txt' + file = 'make_time.py' local_path = os.path.join('webroot', *file.split('/')) web_path = '/' + file @@ -66,6 +71,13 @@ def test_get_sample_text_content(self): with open(local_path, 'rb') as f: self.assertEqual(f.read(), response.read(), error_comment) + # given = f.read() + # actual = response.read() + # print() + # print(given) + # print(actual) + + def test_get_sample_text_mime_type(self): """ @@ -93,11 +105,13 @@ def test_get_sample_scene_balls_jpeg(self): response = self.get_response(web_path) - self.assertEqual(response.getcode(), 200, error_comment) + # self.assertEqual(response.getcode(), 200, error_comment) with open(local_path, 'rb') as f: - self.assertEqual(f.read(), response.read(), error_comment) - + + given = f.read() + actual = response.read() + self.assertEqual(given, actual, error_comment) def test_get_sample_scene_balls_jpeg_mime_type(self): """ A call to /images/Sample_Scene_Balls.jpg returns the correct mimetype diff --git a/unit-tests.py b/unit_tests.py similarity index 97% rename from unit-tests.py rename to unit_tests.py index a0c657a..499b321 100644 --- a/unit-tests.py +++ b/unit_tests.py @@ -52,7 +52,7 @@ def test_response_path_file(self): content, mime_type = http_server.response_path(path) - self.assertEqual(b"text/html", mime_type) + self.assertEqual("text/html", mime_type) with open(os.path.join("webroot", "a_web_page.html"), "rb") as f: self.assertEqual(f.read(), content) diff --git a/webroot/favicon.ico b/webroot/favicon.ico index 97a68ab..c9efc58 100644 Binary files a/webroot/favicon.ico and b/webroot/favicon.ico differ