Package fcp :: Module fproxyproxy
[hide private]
[frames] | no frames]

Source Code for Module fcp.fproxyproxy

  1  #@+leo-ver=4 
  2  #@+node:@file fproxyproxy.py 
  3  """ 
  4  fproxyproxy 
  5   
  6  An http proxy atop fproxy which uses pyFreenet's 'name services' 
  7  """ 
  8   
  9  #@+others 
 10  #@+node:imports 
 11  import sys, os, getopt, traceback, mimetypes, time 
 12  from BaseHTTPServer import HTTPServer 
 13  from SimpleHTTPServer import SimpleHTTPRequestHandler 
 14  from SocketServer import ThreadingMixIn 
 15  from httplib import HTTPConnection 
 16  import socket 
 17   
 18  import node 
 19  from node import ERROR, INFO, DETAIL, DEBUG 
 20   
 21  #@-node:imports 
 22  #@+node:globals 
 23  progname = sys.argv[0] 
 24   
 25  #@-node:globals 
 26  #@+node:class Handler 
27 -class Handler(SimpleHTTPRequestHandler):
28 """ 29 Handles each FProxyProxy client request 30 """ 31 #@ @+others 32 #@+node:__init__
33 - def __init__(self, request, client_address, server):
34 35 self.server = server 36 SimpleHTTPRequestHandler.__init__(self, request, client_address, server)
37 38 #@-node:__init__ 39 #@+node:do_GET
40 - def do_GET(self):
41 42 print "GET: client=%s path=%s" % (self.client_address, self.path) 43 44 #SimpleHTTPRequestHandler.do_GET(self) 45 46 try: 47 #mimetype, data = self.server.node.get(self.path[1:]) 48 #self.send_response(200) 49 #self.send_header("Content-type", mimetype) 50 #self.send_header("Content-Length", str(len(data))) 51 #self.end_headers() 52 #self.wfile.write(data) 53 #self.wfile.flush() 54 55 self.fproxyGet(self.path) 56 57 except: 58 traceback.print_exc() 59 self.send_error(404, "File not found") 60 return None
61 62 #@-node:do_GET 63 #@+node:fproxyGet
64 - def fproxyGet(self, path):
65 """ 66 Fetches from fproxy, returns (status, mimetype, data) 67 """ 68 server = self.server 69 headers = self.headers 70 71 print "--------------------------------------------" 72 print "** path=%s" % path 73 74 # first scenario - user is pointing their browser directly at 75 # fproxyfproxy, barf! 76 if not path.startswith("http://"): 77 self.send_response(400) 78 self.send_header("Content-type", "text/html") 79 data = "\n".join([ 80 "<html><head>", 81 "<title>Access Denied</title>", 82 "</head><body>", 83 "<h1>Access Denied</h1>", 84 "Sorry, but FProxyProxy is an http proxy server.<br>", 85 "Please don't try to access it like a web server.", 86 "</body></html>", 87 "", 88 ]) 89 self.send_header("Content-Length", str(len(data))) 90 self.send_header("Location", location) 91 self.end_headers() 92 self.wfile.write(data) 93 self.wfile.flush() 94 return 95 96 # convert path to relative 97 path = "/" + path[7:].split("/", 1)[-1] 98 #print "** path=%s" % repr(path) 99 100 101 try: 102 # check host header 103 hostname = headers.get("Host", 'fproxy') 104 pathbits = path.split("/") 105 106 print "** hostname = %s" % hostname 107 108 # second scenario, user has just given a domain name without trailing / 109 if len(pathbits) == 1: 110 # redirect to force trailing slash 111 location = path + "/" 112 print "** redirecting to: %s" % location 113 114 self.send_response(301) 115 self.send_header("Content-type", "text/html") 116 data = "\n".join([ 117 "<html><head>", 118 "<title>Permanent redirect: new URI</title>", 119 "</head><body>", 120 "<h1>Permanent redirect: new URI</h1>", 121 "<a href=\"%s\">Click here</a>", 122 "</body></html>", 123 "", 124 ]) % location 125 self.send_header("Content-Length", str(len(data))) 126 self.send_header("Location", location) 127 self.end_headers() 128 self.wfile.write(data) 129 self.wfile.flush() 130 return 131 132 tail = "/".join(pathbits[1:]) 133 134 # third scenario - request into fproxy 135 if hostname == 'fproxy': 136 137 # tis an fproxy request, go straight through 138 conn = HTTPConnection(server.fproxyHost, server.fproxyPort) 139 conn.request("GET", path) 140 resp = conn.getresponse() 141 self.send_response(resp.status) 142 self.send_header("Content-type", 143 resp.getheader("Content-Type", "text/plain")) 144 data = resp.read() 145 self.send_header("Content-Length", str(len(data))) 146 self.end_headers() 147 self.wfile.write(data) 148 self.wfile.flush() 149 conn.close() 150 return 151 152 else: 153 # final scenario - some other domain, try lookup 154 uri = server.node.namesiteLookup(hostname) 155 156 if not uri: 157 # lookup failed, do the usual 404 thang 158 print "** lookup of domain %s failed" % hostname 159 self.send_response(404) 160 self.send_header("Content-type", "text/html") 161 data = "\n".join([ 162 "<html><head>", 163 "<title>404 - Freenet name not found</title>", 164 "</head><body", 165 "<h1>404 - Freenet name not found</title>", 166 "The pyFreenet name service was unable to resolve ", 167 "the name %s" % hostname, 168 "<br><br>", 169 "You might like to find its freenet uri and try that ", 170 "within <a href=\"/fproxy/\">FProxy</a>", 171 "</body></html>", 172 "", 173 ]) 174 self.send_header("Content-Length", str(len(data))) 175 self.end_headers() 176 self.wfile.write(data) 177 self.wfile.flush() 178 179 # lookup succeeded - ok to go now via fproxy 180 conn = HTTPConnection(server.fproxyHost, server.fproxyPort) 181 newpath = "/" + uri 182 if tail: 183 if not newpath.endswith("/"): 184 newpath += "/" 185 newpath += tail 186 print "** newpath=%s" % newpath 187 conn.request("GET", newpath) 188 resp = conn.getresponse() 189 print "** status=%s" % resp.status 190 self.send_response(resp.status) 191 self.send_header("Content-type", 192 resp.getheader("Content-Type", "text/plain")) 193 194 # has fproxy sent us a redirect? 195 if resp.status == 301: 196 # yuck, fproxy is telling us to redirect, which 197 # sadly means we have to lose the domain name 198 # from our browser address bar 199 location = resp.getheader("location") 200 newLocation = "http://fproxy" + location 201 print "*** redirected!!!" 202 print "*** old location = %s" % location 203 print "*** --> %s" % newLocation 204 self.send_header("Location", newLocation) 205 206 # get the data from fproxy and send it up to the client 207 data = resp.read() 208 self.send_header("Content-Length", str(len(data))) 209 self.end_headers() 210 self.wfile.write(data) 211 self.wfile.flush() 212 conn.close() 213 return 214 215 return 216 217 except socket.error: 218 raise
219 220 #@-node:fproxyGet 221 #@-others 222 223 #@-node:class Handler 224 #@+node:class FProxyProxy
225 -class FProxyProxy(ThreadingMixIn, HTTPServer):
226 """ 227 an http proxy that runs atop fproxy, and uses the pyFreenet name service 228 """ 229 #@ @+others 230 #@+node:__init__
231 - def __init__(self, **kw):
232 """ 233 runs the FProxyProxy service 234 235 Keywords: 236 - node - a live FCPNode object 237 - fproxyHost - hostname of fproxy 238 - fproxyPort - port of fproxy 239 - listenHost - hostname to listen on for client HTTP connections 240 - listenPort - port to listen on for client HTTP connections 241 """ 242 for k in ['node', 'fproxyHost', 'fproxyPort', 'listenHost', 'listenPort']: 243 setattr(self, k, kw[k]) 244 245 self.log = self.node._log 246 247 HTTPServer.__init__(self, (self.listenHost, self.listenPort), Handler)
248 249 #@-node:__init__ 250 #@+node:run
251 - def run(self):
252 """ 253 Starts the proxy, runs forever till interrupted 254 """ 255 log = self.log 256 log(ERROR, "FproxyProxy listening on %s:%s" % (self.listenHost, self.listenPort)) 257 log(ERROR, " -> forwarding requests to fproxy at %s:%s" % ( 258 self.fproxyHost, self.fproxyPort)) 259 260 self.serve_forever()
261 262 #@-node:run 263 #@-others 264 265 #@-node:class FProxyProxy 266 #@+node:usage
267 -def usage(msg=None, ret=1):
268 """ 269 Prints usage message then exits 270 """ 271 if msg: 272 sys.stderr.write(msg+"\n") 273 sys.stderr.write("Usage: %s [options] src-uri target-uri\n" % progname) 274 sys.stderr.write("Type '%s -h' for help\n" % progname) 275 sys.exit(ret)
276 277 #@-node:usage 278 #@+node:help
279 -def help():
280 """ 281 print help options, then exit 282 """ 283 print "%s: runs an http proxy atop fproxy," % progname 284 print "which uses pyFreenet 'name services'" 285 print 286 print "Note - you should configure fproxyproxy as an http proxy" 287 print "in your browser (best done via Firefox's 'switchproxy' extension" 288 print 289 print "Usage: %s [options] src-uri target-uri" % progname 290 print 291 print "Options:" 292 print " -h, -?, --help" 293 print " Print this help message" 294 print " -v, --verbose" 295 print " Print verbose progress messages to stderr" 296 print " -H, --fcpHost=<hostname>" 297 print " Connect to FCP service at host <hostname>" 298 print " -P, --fcpPort=<portnum>" 299 print " Connect to FCP service at port <portnum>" 300 print " -p, --fproxyAddress=[<hostname>][:<portnum>]" 301 print " Use fproxy service at <hostname>:<portnum>," 302 print " default 127.0.0.1:8888" 303 print " -L, --listenAddress=[<hostname>][:<portnum>]" 304 print " Listen for http connections on <hostname>:<portnum>," 305 print " default is 127.0.0.1:8889" 306 print " -V, --version" 307 print " Print version number and exit" 308 print 309 print "Environment:" 310 print " Instead of specifying -H and/or -P, you can define the environment" 311 print " variables FCP_HOST and/or FCP_PORT respectively" 312 313 sys.exit(0)
314 315 #@-node:help 316 #@+node:main
317 -def main():
318 """ 319 Front end for fproxyproxy utility 320 """ 321 # default job options 322 verbosity = node.ERROR 323 verbose = False 324 fcpHost = node.defaultFCPHost 325 fcpPort = node.defaultFCPPort 326 fproxyHost = os.environ.get("FPROXY_HOST", "127.0.0.1") 327 fproxyPort = int(os.environ.get("FPROXY_PORT", 8888)) 328 listenHost = os.environ.get("FPROXYPROXY_HOST", "127.0.0.1") 329 listenPort = int(os.environ.get("FPROXYPROXY_PORT", 8889)) 330 331 opts = { 332 "Verbosity" : 0, 333 } 334 335 # process command line switches 336 try: 337 cmdopts, args = getopt.getopt( 338 sys.argv[1:], 339 "?hvH:P:Vp:L:", 340 ["help", "verbose", "fcpHost=", "fcpPort=", "version", 341 "listenAddress=", "fproxyAddress=", 342 ] 343 ) 344 except getopt.GetoptError: 345 # print help information and exit: 346 usage() 347 sys.exit(2) 348 output = None 349 verbose = False 350 #print cmdopts 351 for o, a in cmdopts: 352 353 if o in ("-?", "-h", "--help"): 354 help() 355 356 if o in ("-V", "--version"): 357 print "This is %s, version %s" % (progname, node.fcpVersion) 358 sys.exit(0) 359 360 if o in ("-v", "--verbosity"): 361 verbosity = node.DETAIL 362 opts['Verbosity'] = 1023 363 verbose = True 364 365 if o in ("-H", "--fcpHost"): 366 fcpHost = a 367 368 if o in ("-P", "--fcpPort"): 369 try: 370 fcpPort = int(a) 371 except: 372 usage("Invalid fcpPort argument %s" % repr(a)) 373 374 if o in ("-L", "--listenAddress"): 375 parts = a.split(":") 376 if len(parts) == 1: 377 listenHost = parts[0] 378 elif len(parts) == 2: 379 if parts[0]: 380 listenHost = parts[0] 381 if parts[1]: 382 listenPort = int(parts[1]) 383 else: 384 usage("Invalid listen address '%s'" % a) 385 386 if o in ("-p", "--fproxyAddress"): 387 parts = a.split(":") 388 if len(parts) == 1: 389 fproxyHost = parts[0] 390 elif len(parts) == 2: 391 if parts[0]: 392 fproxyHost = parts[0] 393 if parts[1]: 394 fproxyPort = int(parts[1]) 395 396 # try to create an FCP node, needed for name lookups 397 try: 398 n = node.FCPNode(host=fcpHost, port=fcpPort, verbosity=verbosity, 399 logfile=sys.stderr) 400 log = n._log 401 except: 402 if verbose: 403 traceback.print_exc(file=sys.stderr) 404 usage("Failed to connect to FCP service at %s:%s" % (fcpHost, fcpPort)) 405 406 try: 407 proxy = FProxyProxy( 408 node=n, 409 fproxyHost=fproxyHost, fproxyPort=fproxyPort, 410 listenHost=listenHost, listenPort=listenPort) 411 proxy.run() 412 sys.exit(0) 413 except KeyboardInterrupt: 414 print "fproxyproxy terminated by user" 415 n.shutdown() 416 sys.exit(0) 417 except: 418 traceback.print_exc() 419 print "fproxyproxy terminated" 420 n.shutdown() 421 sys.exit(1)
422 423 #@-node:main 424 #@-others 425 426 #@-node:@file fproxyproxy.py 427 #@-leo 428