?? http_match.py
字號:
from proxy4_base import *from connection import *class ServerPool: def __init__(self): self.map = {} # {(ipaddr, port) -> {server -> ('available'|'busy')}} self.http_versions = {} # {(ipaddr, port) -> http_version} self.callbacks = {} # {(ipaddr, port) -> [functions to call]} make_timer(60, self.expire_servers) # Usage: # reserve_server to receive a server or None # unreserve_server to put a server back on the available set # # register_server to add a server (busy) # unregister_server to remove a server (busy or not) # # register_callback to express an interest in a server def count_servers(self, addr): "How many server objects connect to this address?" return len(self.map.get(addr, {})) def reserve_server(self, addr): for server,status in self.map.get(addr, {}).items(): if status[0] == 'available': # Let's reuse this one self.map[addr][server] = ('busy', ) print color(6, 'reserve_server'), addr, server return server return None def unreserve_server(self, addr, server): #print color(6, 'unreserve_server'), addr, server assert self.map.has_key(addr), '%s missing %s' % (self.map, addr) assert self.map[addr].has_key(server), '%s missing %s' % (self.map[addr], server) assert self.map[addr][server][0] == 'busy' self.map[addr][server] = ('available', time.time()) self.invoke_callbacks(addr) def register_server(self, addr, server): "Register the server as being used" #print color(6, 'register_server'), addr, server if not self.map.has_key(addr): self.map[addr] = {} self.map[addr][server] = ('busy',) def unregister_server(self, addr, server): "Unregister the server" #print color(6, 'unregister_server'), addr, server assert self.map.has_key(addr), '%s missing %s' % (self.map, addr) assert self.map[addr].has_key(server), '%s missing %s' % (self.map[addr], server) del self.map[addr][server] if not self.map[addr]: del self.map[addr] self.invoke_callbacks(addr) def register_callback(self, addr, callback): # Callbacks are called whenever a server may be available # for (addr). It's the callback's responsibility to re-register # if someone else has stolen the server already. if not self.callbacks.has_key(addr): self.callbacks[addr] = [] self.callbacks[addr].append(callback) def connection_limit(self, addr): if self.http_versions.get(addr, 1.1) <= 1.0: # For older versions of HTTP, we open lots of connections return 6 else: return 2 def set_http_version(self, addr, http_version): self.http_versions[addr] = http_version self.invoke_callbacks(addr) def expire_servers(self): expire_time = time.time() - 300 # Unused for five minutes to_expire = [] for addr,set in self.map.items(): for server,status in set.items(): if status[0] == 'available' and status[1] < expire_time: # It's old .. let's get rid of it to_expire.append(server) for server in to_expire: server.close() make_timer(60, self.expire_servers) def invoke_callbacks(self, addr): # Notify whoever wants to know about a server becoming available if self.callbacks.has_key(addr): callbacks = self.callbacks[addr] del self.callbacks[addr] for callback in callbacks: callback() serverpool = ServerPool() class ClientServerMatchmaker: # States: # # dns: Client has sent all of the browser information # We have asked for a DNS lookup # We are waiting for an IP address # => Either we get an IP address, we redirect, or we fail # # server: We have an IP address # We are looking for a suitable server # => Either we find a server from ServerPool and use it # (go into 'response'), or we create a new server # (go into 'connect'), or we wait a bit (stay in 'server') # # connect: We have a brand new server object # We are waiting for it to connect # => Either it connects and we send the request (go into # 'response'), or it fails and we notify the client # # response: We have sent a request to the server # We are waiting for it to respond # => Either it responds and we have a client/server match # (go into 'done'), it doesn't and we retry (go into # 'server'), or it doesn't and we give up (go into 'done') # # done: We are done matching up the client and server def __init__(self, client, request, headers, content): words = split(request) hostname, document = stripsite(join(words[1:-1], ' ')) port = 80 m = re.match(r'^(.*):(\d+)$', hostname) if m: hostname = m.group(1) port = int(m.group(2)) self.client = client self.request = request self.hostname = hostname self.port = port self.document = document self.headers = headers self.content = content # Temporary HACK if hostname == '_proxy' and document == '/xul': local_server.ServerHandleDirectly( self.client, 'HTTP/1.0 200 OK\r\n', 'Content-type: text/xul\r\n' '\r\n', open('/home/amitp/xul/google.xul', 'r').read()) return if hostname == '_proxy' and document == '/sidegoogle': local_server.ServerHandleDirectly( self.client, 'HTTP/1.0 200 OK\r\n', 'Content-type: text/html\r\n' '\r\n', '<html><body><script>sidebar.addPanel("Google", "http://_proxy/xul", "");</script></body></html>') return self.state = 'dns' dns_lookups.background_lookup(self.hostname, self.handle_dns) def handle_dns(self, hostname, answer): assert self.state == 'dns' if not self.client.connected: # The browser has already closed this connection, so abort return if answer.isFound(): self.ipaddr = answer.data[0] self.state = 'server' self.find_server() elif answer.isRedirect(): # Let's use a different hostname new_url = answer.data if self.port != 80: new_url = new_url + ':%s' % self.port new_url = new_url + self.document self.state = 'done' local_server.ServerHandleDirectly( self.client, 'HTTP/1.0 301 Use different host\r\n', 'Content-type: text/html\r\n' 'Location: http://%s\r\n' '\r\n' % new_url, 'Host %s is an abbreviation for %s\r\n' % (hostname, answer.data)) else: # Couldn't look up the host, so close this connection self.state = 'done' local_server.ServerHandleDirectly( self.client, 'HTTP/1.0 504 Host not found\r\n', 'Content-type: text/html\r\n' '\r\n', 'Host %s not found .. %s\r\n' % (hostname, answer.data)) def find_server(self): assert self.state == 'server' if not self.client.connected: return addr = (self.ipaddr, self.port) server = serverpool.reserve_server(addr) if server: # Let's reuse it message(6, 'resurrecting', None, None, server) self.state = 'connect' self.server_connected(server) elif serverpool.count_servers(addr)>=serverpool.connection_limit(addr): # There are too many connections right now, so register us # as an interested party for getting a connection later serverpool.register_callback(addr, self.find_server) else: # Let's make a new one self.state = 'connect' server = http_server.HttpServer(self.ipaddr, self.port, self) serverpool.register_server(addr, server) def server_connected(self, server): if not self.client.connected: # The client has aborted, so let's return this server # connection to the pool server.reuse() return self.server = server self.state = 'response' # At this point, we tell the server that we are the client. # Once we get a response, we transfer to the real client. self.server.client_send_request(split(self.request)[0], self.hostname, self.document, self.headers, self.content, self) def server_abort(self): # The server had an error, so we need to tell the client # that we couldn't connect if self.client.connected: self.client.server_no_response() def server_close(self): message(6, 'resurrection failed', None, self.server.sequence_number, self.server) # Look for a server again if self.server.sequence_number > 0: # It has already handled a request, so the server is allowed # to kill the connection. Let's find another server object. self.state = 'server' self.find_server() else: # The server didn't handle the original request, so we just # tell the client, sorry. if self.client.connected: self.client.server_no_response() def server_response(self, response, headers): # Okay, transfer control over to the real client if self.client.connected: self.server.client = self.client self.client.server_response(self.server, response, headers) else: self.server.client_abort() import http_server, local_serverimport dns_lookups
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -