updated on Thu Jan 12 08:01:00 UTC 2012
[aur-mirror.git] / groovepwn / proxy.py
blob24d40e74f4c2e54e0caba19a1c1c7b5284e60e74
1 #!/usr/bin/python2
3 __doc__ = """groovePwn -- a grooveshark caching proxy."""
4 __version__ = "0.1"
6 # The timeout (secs) to wait for a socket to become available
7 WAIT_TIMEOUT = 1
9 # Number of 1kb blocks to transfer per cycle
10 BLOCK_SIZE = 1
12 import BaseHTTPServer, socket, select, urlparse, SocketServer, grooveMod
14 def addressToTuple(addressString):
15 """
16 Convert a computer address with/without a port number into a tuple containing
17 the address and port.
18 """
19 address, _ , port = addressString.partition(":")
20 if port != "":
21 port = int(port)
22 else:
23 port = 80
24 return (address, port)
26 def otherItem(tuple, element):
27 """Return the other element from a two-element tuple given one element"""
28 if tuple[0] == element:
29 return tuple[1]
30 else:
31 return tuple[0]
33 class GroovePwnProxy(BaseHTTPServer.BaseHTTPRequestHandler):
34 server_version = "groovePwnProxy/" + __version__
35 rbufsize = 0
36 def _canConnectToTarget(self, targetAddressString, soc):
37 """
38 Opens a connection to a remote system via the provided socket and
39 responds with the sucess of the operation.
40 """
41 target = addressToTuple(targetAddressString)
42 try:
43 # The provided socket is now propperly connected
44 soc.connect(target)
45 return True
46 except socket.error, messageList:
47 # Couldn't connect: inform the client
48 try: message = messageList[1]
49 except: message = messageList
50 self.send_error(404, message)
51 return False
53 def _streamData(self, remoteSiteSocket, maxIdleTimeout=5):
54 """
55 Attempt to stream data from the remote socket to the client in blocks of
56 1Kbyte. maxIdleTimeout is the maximum number of idle responses to tolerate
57 before ending the stream.
58 """
60 # Load the correct modifier for this file
61 gsFilter = grooveMod.getFilter(self.path)
63 # A list of sockets which are to be connected
64 streamSockets = (self.connection, remoteSiteSocket)
66 # The number of consecutive attempts to read the data which have resulted in
67 # an idle response.
68 idleResponseCount = 0
70 while idleResponseCount < maxIdleTimeout:
71 idleResponseCount += 1
73 # Wait until a socket is ready to read
74 readySockets, _ , errSockets = select.select(streamSockets, [],
75 streamSockets, WAIT_TIMEOUT)
76 if errSockets:
77 # An exceptional state has occurred on one of the sockets
78 break
79 if readySockets:
80 # For each socket which is ready, read from it
81 for readSocket in readySockets:
82 writeSocket = otherItem(streamSockets, readSocket)
84 # Read 1Kbyte from the socket
85 data = readSocket.recv(BLOCK_SIZE * 1024 * 8)
86 if data:
87 # Data was recieved, forward it on
88 writeSocket.send(gsFilter.process(data))
89 idleResponseCount = 0
91 finalData = gsFilter.done()
92 if finalData != None:
93 self.connection.send(finalData)
95 def do_GET(self):
96 url = urlparse.urlparse(self.path, "http")
98 # If the url is not for a HTTP protocal address, has a fragment or has no
99 # location specified then this script cannot handle it.
100 if url.scheme != "http" or url.fragment or not url.netloc:
101 self.send_error(400, "Bad URL: %s"%(self.path,))
102 return False
104 # Create a socket for the remote server
105 remoteSiteSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
106 try:
107 if self._canConnectToTarget(url.netloc, remoteSiteSocket):
108 # Send top of the header indicating the connection details
109 remoteSiteSocket.send("%s %s %s\r\n"%(
110 self.command, # e.g. GET, HEAD, etc.
111 urlparse.urlunparse(("", "", url.path, url.params, url.query, "")),
112 self.request_version
115 # Inform the remote site that the connection to it will cease
116 self.headers['Connection'] = 'close'
117 del self.headers['Proxy-Connection']
119 # Send headers to the client
120 for key_val in self.headers.items():
121 remoteSiteSocket.send("%s: %s\r\n"%key_val)
123 # End the header data
124 remoteSiteSocket.send("\r\n")
126 # Stream data between the client and remote site
127 self._streamData(remoteSiteSocket)
128 finally:
129 remoteSiteSocket.close()
130 self.connection.close()
132 def log_message(self,*args,**kwargs):
133 # Disable debug info
134 pass
136 do_DELETE = do_GET
137 do_HEAD = do_GET
138 do_POST = do_GET
139 do_PUT = do_GET
142 # Add threadding support to the minimal HTTP server
143 class ThreadingHTTPServer (SocketServer.ThreadingMixIn,
144 BaseHTTPServer.HTTPServer):
145 pass
147 def startServer(port=8080):
148 """Start a proxy server to pwn grooveshark through"""
149 hostname = ""
150 serverAddress = (hostname, port)
152 server = ThreadingHTTPServer(serverAddress, GroovePwnProxy)
154 print "Starting a groovePwn proxy server on localhost:%i"%(port,)
155 print "Instruct your browser to use this as the proxy --",
156 print "groovePwn will save all MP3s you listen to in your /tmp directory"
158 server.serve_forever()
160 if __name__ == "__main__":
161 print "groovePwn v%s"%(__version__,),
162 print "(c) 2010 Jonathan Heathcote -- GPLv3\n"
163 startServer()