3 __doc__
= """groovePwn -- a grooveshark caching proxy."""
6 # The timeout (secs) to wait for a socket to become available
9 # Number of 1kb blocks to transfer per cycle
12 import BaseHTTPServer
, socket
, select
, urlparse
, SocketServer
, grooveMod
14 def addressToTuple(addressString
):
16 Convert a computer address with/without a port number into a tuple containing
19 address
, _
, port
= addressString
.partition(":")
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
:
33 class GroovePwnProxy(BaseHTTPServer
.BaseHTTPRequestHandler
):
34 server_version
= "groovePwnProxy/" + __version__
36 def _canConnectToTarget(self
, targetAddressString
, soc
):
38 Opens a connection to a remote system via the provided socket and
39 responds with the sucess of the operation.
41 target
= addressToTuple(targetAddressString
)
43 # The provided socket is now propperly connected
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
)
53 def _streamData(self
, remoteSiteSocket
, maxIdleTimeout
=5):
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.
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
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
)
77 # An exceptional state has occurred on one of the sockets
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)
87 # Data was recieved, forward it on
88 writeSocket
.send(gsFilter
.process(data
))
91 finalData
= gsFilter
.done()
93 self
.connection
.send(finalData
)
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
,))
104 # Create a socket for the remote server
105 remoteSiteSocket
= socket
.socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
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
, "")),
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
)
129 remoteSiteSocket
.close()
130 self
.connection
.close()
132 def log_message(self
,*args
,**kwargs
):
142 # Add threadding support to the minimal HTTP server
143 class ThreadingHTTPServer (SocketServer
.ThreadingMixIn
,
144 BaseHTTPServer
.HTTPServer
):
147 def startServer(port
=8080):
148 """Start a proxy server to pwn grooveshark through"""
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"