http代理的脚本http_proxy.py

#!/usr/bin/env python

# Copyright (C) 2010, Adam Fourney <afourney@cs.uwaterloo.ca>
#
# This file is part of Adaptable GIMP
#  
# Adaptable GIMP is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
# 

import sys, subprocess, socket, os, errno, random, hashlib, time, re

if sys.platform == 'win32':
    from threading import Thread
    import array

    class Value:
        def __init__(self, typecode, arg):
	    self.__arr = array.array(typecode, [arg])
        def getv(self):
	    return self.__arr[0]
	def setv(self, val):
	    self.__arr[0] = val
	
        value = property(getv, setv)
        	    
else:
    from multiprocessing import Process, Value



# CONSTANTS
PROXY_TO         = 'www.uwaterloo.ca'
HOST             = '127.0.0.1'
PREFERRED_PORT   = 8080 
HEARTBEAT_PERIOD = 15 
APPLICATION      = './gimp-2.6.exe' if sys.platform == 'win32' else './gimp-2.6'

#####################################################################


def main():
    thread_q       = []
    shutdown       = Value('i', 0)
    is_online      = Value('i', 1) 

    # Set up the socket
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # Try finding a free port to listen on. 
    # Stop after 10 tries.
    tries = 10
    port = PREFERRED_PORT
    while (tries > 0):
        tries = tries - 1
        try:
            s.bind((HOST, port))
            s.listen(5)
            print "Listening on port {0}".format(port)
            break
        except socket.error, (err, msg):
            if (tries == 0):
                raise
            elif (err == errno.EADDRINUSE):
                port = random.randint(1024, 49151)
                continue
            else:
                raise
    
    # Set socket timeout
    s.settimeout(0.5)

    # Spawn the heartbeat
    heartbeat_thread = spawn_best_thread(target=start_heartbeat, args=(shutdown,is_online))
    heartbeat_thread.start()

    # Spawn our process
    app_thread = spawn_best_thread(target=spawn_app, args=(shutdown,port))
    app_thread.start()

    # Handle incoming connections
    while shutdown.value == 0:

        # Poll the children and reap zombies
        new_q = []
        for p in thread_q:
            if p.is_alive():
                new_q.append(p)
        thread_q = new_q

        # Accept a new connection 
        try:
            conn, addr = s.accept()
        except socket.timeout:
            continue
        except socket.error, err:
            if (err == errno.EINTR):
                continue
            else:
                raise

	      # Service the request in a new thread
        conn.settimeout(None)
        p = spawn_best_thread(target=service_request, args=(conn,is_online))
        thread_q.append(p)
        p.start()

    s.close()


#####################################################################


def spawn_best_thread(target, args):
    if sys.platform == 'win32':
        return Thread(target=target, args=args)
    else:
        return Process(target=target, args=args)


#####################################################################


def start_heartbeat(shutdown, is_online):

    sleep_for = 0

    while shutdown.value == 0:

        # Sleep for half a second at a time to allow for checking of the
        # shutdown condition.
        if (sleep_for > 0):
            time.sleep(0.5)
            sleep_for = sleep_for - 0.5
            continue
        

        # Do actual work
        start_time = time.clock()
        previous_status = is_online.value
        new_status = previous_status

        try:
            client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            client.settimeout(HEARTBEAT_PERIOD)
            client.connect((PROXY_TO, 80))
            client.sendall("HEAD / HTTP/1.1\r\nHost: {0}\r\nConnection: close\r\n\r\n".format(PROXY_TO))

            response = ""
            while 1:
                data = client.recv(1024)
                if not data:
                    break
                response += data
            
            if re.search('^HTTP\/\d\.\d\s+200\s+OK', response):
                new_status = 1
            else:
                new_status = 0

        except socket.error: 
            new_status = 0
        except socket.timeout: 
            new_status = 0

        # Shutdown and close the connection, but report no errors
        try: 
            client.shutdown(socket.SHUT_RDWR);
        except socket.error: 
            pass

        try: 
            client.close()
        except socket.error: 
            pass

        if new_status != previous_status:
            print "Connection status changed. Now {0}".format('Online' if new_status else 'Offline') 
        is_online.value = new_status

        # Arrange to sleep a little
        sleep_for = HEARTBEAT_PERIOD - (time.clock() - start_time)


#####################################################################


def service_request(conn, is_online):

    # Read the request, respond and exit.
    request= ""
    while 1:
        data = conn.recv(1024)
        if not data:
            break

        request += data
            
        # Requests are terminated by the following sequence
        pos = request.find("\r\n\r\n")
        if (pos > -1):
            data = data[0:pos+4]
            break 

    response = make_request(request, is_online)
    conn.sendall(response)

    try:
        conn.shutdown(socket.SHUT_RDWR);
    except socket.error:
        pass

    conn.close()


#####################################################################


def make_request(data, is_online):

    # Split the request into lines
    lines = data.split("\r\n")
    if data.endswith("\r\n"):
        lines.pop()

    # Hash the first line of the request for use as a key
    first_line = lines[0];
    key = hashlib.md5(first_line).hexdigest()

    # Check for special PROXY messages
    if first_line == "PROXY GET STATUS":
        status_str = "Online" if is_online.value > 0 else "Offline"
        return "Status: {0}\r\n\r\n".format(status_str)

    # Exit early if we are offline
    if is_online.value == 0:
        return read_from_cache(key)

    # Modify the request for proxying
    data = "";
    for line in lines:
        if line.startswith('Connection:'):
            data = data + 'Connection: close' + "\r\n"
        elif line.startswith('Host:'):
            data = data + 'Host: {0}'.format(PROXY_TO) + "\r\n"
        else: 
            data = data + line + "\r\n"

    # Try to fetch from the server, but fall back on the cache if we're offline 
    try:
        client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        client.settimeout(HEARTBEAT_PERIOD)
        client.connect((PROXY_TO, 80))
        client.sendall(data)
    except: 
        return read_from_cache(key)
    
    # Read the response
    response = ""
    while 1:
        data = client.recv(1024)
        if not data:
            break
        response += data

    client.close()

    # Cache the response and return
    write_to_cache(key, response)
    return response


#####################################################################


# Read a response from the cache. Return 404 if there is a problem.
def read_from_cache(key):
    try:
        f = open("web_cache/{0}.tmp".format(key), "r")
        f_data = f.read()
        f.close()
    except IOError as (errnum, strerror):
        if (errnum == errno.ENOENT):
            response = """HTTP/1.1 404 Not Found\r
Content-Type: text/html\r
Connection: close\r
Content-Length: 78\r
\r
\r
<HTML><HEAD><TITLE>404</TITLE></HEAD><BODY>Page Not Found: 404</BODY></HTML>"""
            return response
        else:
            raise
    return f_data 


#####################################################################


# Write a response to the cache. Create a web_cache directory if required.
def write_to_cache(key, response):
    
    if not os.path.isdir('web_cache'):
        os.mkdir('web_cache')
    
    f = open('web_cache/{0}.tmp'.format(key), 'w')                    
    f.write(response)                    
    f.close() 


#####################################################################


# Spawn the main process
def spawn_app(shutdown, port):
    os.environ['AGIMP_PROXY_PORT'] = str(port)
    subprocess.call([APPLICATION])
    shutdown.value = 1   


######## CALL MAIN ########

if __name__ == '__main__':
    main()

转载于:https://my.oschina.net/u/1385797/blog/174017

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值