#!/usr/bin/python3 # # Skeleton proxy server (in Python 3) for CS 125 # """Web proxy server. Intercepts Web requests, caches them, and optionally modifies them. Usage: proxyserver.py [-d] SERVER-IP PORT Options: -d Enable debugging messages SERVER-IP is the DNS name or IP address to listen on; usually "localhost" will do just fine. PORT is the port to listen on; usually the UID printed by the "id" command will be appropriate.""" import docopt import hashlib from socket import * import sys args = None debug = False port = None def main(): global args global debug args = docopt.docopt(__doc__, version = 'proxyserver version 0.1') # # args['SERVER-IP'] contains the DNS name or IP address to bind to. # args['PORT'] contains the port number (as a string). # if args['-d']: debug = True # # Invoke the proxy # proxy() def proxy(): # # Convert the port number to an integer. # port = int(args['PORT']) # Create a server socket, bind it to a port and start listening tcpServerSocket = socket(AF_INET, SOCK_STREAM) # The following line allows you to rerun the server right away after killing it. # That’s useful for testing; otherwise you may have to wait 15-30 seconds each time. tcpServerSocket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) # FILL IN HERE while True: # Start receiving data from the client if debug: print('Ready to serve...') tcpClientSocket, clientAddress = tcpServerSocket.accept() print('Received a connection from:', clientAddress) clientMessage = ''# FILL IN HERE if debug: print('Request:', clientMessage) # # Extract the filename from the given message. Note that we # are very sloppy about error checking; this should be # improved. However, this will work for syntactically correct # requests and thus is handy for initial development. Error # handling can be added later. # # FILL IN HERE url = clientMessage.split()[1] if debug: print('Parsed URL:', url) # # Again, we are sloppy about parsing the requested URL, and # that should be improved. # # FILL IN HERE head, slash, filename = url.partition('/') if debug: print('Parsed filename:', filename) fileExists = False # # Figure out a name to use in the cache. Note that it is both # dangerous and incorrect to simply use the name requested by # the client. The name provided might include strange # characters that could break your file system, and if the # client requests a file with the same name from two different # Web servers, the proxy will serve up the cached file from # the first server without noticing the problem. (This is # especially troublesome for common names like "index.html".) # Correcting this flaw is left as an exercise for the student # (it can be useful to calculate a SHA-1 hash [see # hashlib.sha1] of the entire URL and use that as the basis # for a file name). # # FILL IN HERE cachedName = "/" + filename if debug: print('Cached name:', cachedName) try: # Check whether the file exists in the cache cachedFile = open(cachedName[1:], "rb") # # It is incorrect to use read() here; if the file is # enormous the proxy server will die trying to swallow it. # However, this code will work for small files. # # FILL IN HERE outputdata = cachedFile.read() fileExists = True # # We found a cache hit, so we can generate a response message. # However, the code below is incorrect because it always # assumes that the result is text/html. A correct # implementation will record the content-type of the # cached data and pass that on to the client. # # FILL IN HERE tcpClientSocket.send("HTTP/1.0 200 OK\r\n.encode()") tcpClientSocket.send("Content-Type: text/html\r\n".encode()) # FILL IN HERE if debug: print('Successfully served data from cache') cachedFile.close() # Error handling for file not found in cache except IOError: if not fileExists: # Create a socket to connect to the main Web server socketToServer = ''# FILL IN HERE try: # Connect to the server on port 80 # FILL IN HERE socketToServer.send( "GET http://" + filename + " HTTP/1.0\n\n".encode()) # Read the server's response response = ''# FILL IN HERE # Create a new file in the cache for the requested file. # See above regarding file naming. # FILL IN HERE tmpFile = open("./" + filename, "wb") # Also send the response the client socket. # FILL IN HERE except: # We couldn't connect to the server. Generate an HTTP 500. # FILL IN HERE pass # # All done! Close the server socket # socketToServer.close() else: # # We failed to read from the cache; generate an HTTP 500 # (internal server error). # # FILL IN HERE pass # Close the client socket tcpClientSocket.close() if __name__ == '__main__': main()