写了一个Web Gateway做Proxy

写了一个Web Gateway做Proxy « Xiaoxia[PG]

写了一个Web Gateway做Proxy

因为EMSVPS的服务器实在有太多的问题,故现在改回比较稳定的burst.net机器。现在Paypal支持Unionpay的卡,5.95USD/mo大概人民币38元。burst.net的机器提供512M内存和2个IP地址,内存充足的时候跑起Wordpress的确轻松很多了。现在一个IP用作博客服务,另外一个IP用作提供一些Web服务。

因为不同的Web服务都需要监听一个服务端口,为了能让他们共用一个IP上的80端口,需要一个代理分发请求的程序。例如
访问http://lab.xiaoxia.org/server/*的请求分发到localhost:10000的服务,
访问http://lab.xiaoxia.org/whois/*的请求分发到localhost:10001的服务,
而访问http://lab.xiaoxia.org/*的请求,直接获取www目录下的资源文件,例如index.html。

因为使用的都是同一个域名,不同的只是路径,要根据不同的路径选择不同的服务端口,我使用了正则表达式来解决这个问题。

效果见 http://lab.xiaoxia.org/

我现在的分发规则如下:

# Host            Request Path           Handle Method   Forward Host or Root    Forward Path
lab.xiaoxia.org   /server/(.*)           proxy           localhost:10000         /$1
lab.xiaoxia.org   /mail/(.*).action      proxy           localhost:10005         /$1.action
lab.xiaoxia.org   /mail/(.*)             resource        /var/mail               /mail/$1
lab.xiaoxia.org   /hashlib/(.*).action   proxy           localhost:10002         /
lab.xiaoxia.org   /whois/request/(.*)    proxy           localhost:10003         /$1
lab.xiaoxia.org   /iplookup/request/(.*) proxy           localhost:10004         /$1
lab.xiaoxia.org   /(.*)                  resource        www                     /$1

今晚写的WebGateway.py的代码如下。可以优化效率的地方很多,但对目前来说,已经足够。本来写了一个epoll版本的,但是代码太复杂太多了,就抛弃了,不利于阅读和维护。对于Python代码来说,应该坚持KISS (Keep It Simple & Stupid) 原则。

规则文件可以经常修改,而不需要重启WebGateway。

  1. #!/usr/bin/python  
  2.   
  3. from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer  
  4. from httplib import HTTPResponse  
  5. from SocketServer import ThreadingMixIn  
  6. import socket, threading  
  7. import posixpath, shutil, mimetypes, urlparse  
  8. import time, sys, re, traceback, os  
  9.   
  10. threading.stack_size(128*1024)  
  11.   
  12. ConnectionTimeout = 30.0  
  13. ServiceConfigFile = "services.list"  
  14.   
  15. class Handler(BaseHTTPRequestHandler):  
  16.     def proxy(self, proxy_host, proxy_port, url):  
  17.         try:  
  18.             self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  
  19.             self.s.settimeout(ConnectionTimeout)  
  20.             self.s.connect((proxy_host, proxy_port))  
  21.             header = " ".join((self.command, url, "HTTP/1.1")) + "\r\n"  
  22.             header += str(self.headers) + "\r\n"  
  23.             self.s.send(header)  
  24.             # Send Post data  
  25.             if(self.command=='POST'):  
  26.                 self.s.send(self.rfile.read(int(self.headers['Content-Length'])))  
  27.             response = HTTPResponse(self.s, method=self.command)  
  28.             response.begin()  
  29.   
  30.             # Reply to the browser  
  31.             status = "HTTP/1.1 " + str(response.status) + " " + response.reason  
  32.             header = status + '\r\n'  
  33.             for hh, vv in response.getheaders():  
  34.                 if hh.upper()!='TRANSFER-ENCODING':  
  35.                     header += hh + ': ' + vv + '\r\n'  
  36.             self.wfile.write(header + '\r\n')  
  37.             while True:  
  38.                 response_data = response.read(8192)  
  39.                 if not response_data: break  
  40.                 self.wfile.write(response_data)  
  41.             self.s.close()  
  42.         except:  
  43.             print('error in ' + self.requestline + '\n' + traceback.format_exc())  
  44.   
  45.     def getResource(self, www_root, path):  
  46.         path = path.split("?")[0].split("#")[0]  
  47.         path = posixpath.normpath(path).strip("/")  
  48.         fullpath = os.path.join(www_root, path)  
  49.         # Default page  
  50.         if os.path.isdir(fullpath):  
  51.             fullpath = os.path.join(fullpath, "index.html")  
  52.         mtype = mimetypes.guess_type(fullpath)[0]  
  53.         if mtype is None: mtype = "text/plain"  
  54.         if os.path.isfile(fullpath):  
  55.             f = open(fullpath, "rb")  
  56.             self.send_response(200)  
  57.             self.send_header("Content-Type", mtype + "; charset=\"utf-8\"")  
  58.             fs = os.fstat(f.fileno())  
  59.             self.send_header("Content-Length", str(fs[6]))  
  60.             self.send_header("Last-Modified"self.date_time_string(fs.st_mtime))  
  61.             self.end_headers()  
  62.             shutil.copyfileobj(f, self.wfile)  
  63.         else:  
  64.             self.send_error(404"File not found %s" % path)  
  65.   
  66.     def getService(self):  
  67.         hostname = self.headers["host"].split(":")[0].lower()  
  68.         self.headers["X-Forwarded-For"] = self.connection.getpeername()[0]  
  69.         for line in file(ServiceConfigFile).readlines():  
  70.             var = line.split()  
  71.             if var[0].lower() == hostname:  
  72.                 r = re.match(var[1], self.path)  
  73.                 if r:  
  74.                     i = 1  
  75.                     for k in r.groups():  
  76.                         var[4] = var[4].replace("$" + str(i), k)  
  77.                         i += 1  
  78.                     if var[2] == "proxy":  
  79.                         ip, port = var[3].split(":")  
  80.                         self.proxy(ip, 80 if port == "" else int(port), var[4])  
  81.                     elif var[2] == "resource":  
  82.                         self.getResource(var[3], var[4])  
  83.                     else:  
  84.                         self.send_error(500"Unknown method")  
  85.                     return  
  86.         self.send_error(400"Bad Request")  
  87.   
  88.     do_POST = getService  
  89.     do_GET = getService  
  90.   
  91. class ThreadingHTTPServer(ThreadingMixIn, HTTPServer): pass  
  92.   
  93. try:  
  94.     server = ThreadingHTTPServer(("", 8000), Handler)  
  95.     server.serve_forever()  
  96. except KeyboardInterrupt:  
  97.     exit()  
posted on 2012-03-04 11:37  lexus 阅读( ...) 评论( ...) 编辑 收藏

转载于:https://www.cnblogs.com/lexus/archive/2012/03/04/2379102.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值