Python basehttpserver未正确处理请求

4 投票
2 回答
6320 浏览
提问于 2025-04-17 07:49

我正在尝试写一个简单的本地代理,用于处理网页中的JavaScript。因为我需要从网页中的JavaScript加载一些内容,所以我用Python写了这个简单的后台程序:

import string,cgi,time
from os import curdir, sep
import urllib
import urllib2

from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer


class MyHandler(BaseHTTPRequestHandler):



    def fetchurl(self, url, post, useragent, cookies):
        headers={"User-Agent":useragent, "Cookie":cookies}

        url=urllib.quote_plus(url, ":/?.&-=")
        if post:
            req = urllib2.Request(url,post,headers)
        else:
            req=urllib2.Request(url, None, headers)
        try:
            response=urllib2.urlopen(req)
        except urllib2.URLError, e:
            print "URLERROR: "+str(e)
            return False
        except urllib2.HTTPError, e:
            print "HTTPERROR: "+str(e)
            return False
        else:
            return response.read()


    def do_GET(self):
        if self.path != "/":
            [callback, url, post, useragent, cookies]=self.path[1:].split("%7C")

            print "callback = "+callback
            print "url = "+url
            print "post = "+post
            print "useragent = "+useragent
            print "cookies = "+cookies

            if useragent=="":
                useragent="pyjproxy v. 1.0"

            load=self.fetchurl(url, post, useragent, cookies)

            pack=load.replace("\\", "\\\\").replace("\"", "\\\"").replace("\n",         "\\n").replace("\r", "\\r").replace("\t", "\\t").replace("    </script>", "</scr\"+\"ipt>")
            response=callback+"(\""+pack+"\");"

            if load:
                self.send_response(200)
                self.send_header('Content-type',    'text/javascript')
                self.end_headers()
                self.wfile.write(response)
                self.wfile.close()
                return
            else:
                self.send_error(404,'File Not Found: %s' % self.path)
                return
        else:
            embedscript="function pyjload(datadict){  if(!datadict[\"url\"] ||             !datadict[\"callback\"]){return false;}  if(!datadict[\"post\"])             datadict[\"post\"]=\"\";  if(!datadict[\"useragent\"])     datadict[\"useragent\"]=\"\";  if(!datadict[\"cookies\"])     datadict[\"cookies\"]=\"\";  var oHead =                     document.getElementsByTagName('head').item(0);  var oScript=             document.createElement(\"script\");  oScript.type =         \"text/javascript\";  oScript.src=\"http://localhost:1180/\"+datadict[\"callback\"]+\"%7C\"+datadict[\"url\"]+\"%7C\"+datadict[\"post\"]+\"%7C\"+datadict[\"useragent\"]+\"%7C\"+datadict[\"cookies\"];  oHead.appendChild( oScript);}"
            self.send_response(200)
            self.send_header("Content-type", "text/html")
            self.end_headers()
            self.wfile.write(embedscript)
            self.wfile.close()
            return

def main():
    try:
        server = HTTPServer(('127.0.0.1', 1180), MyHandler)
        print 'started httpserver...'
        server.serve_forever()
    except KeyboardInterrupt:
        print '^C received, shutting down server'
        server.socket.close()

if __name__ == '__main__':
    main()

然后我在网页中这样使用它:

<!DOCTYPE HTML>
<html><head>

<script>
function miocallback(htmlsource)
{
  alert(htmlsource);
}


</script>

<script type="text/javascript" src="http://localhost:1180"></script>


</head><body>


<a onclick="pyjload({'url':'http://www.google.it','callback':'miocallback'});"> Take     the Red Pill</a>

</body></html>

在Firefox和Chrome浏览器中,它似乎总是能正常工作。但是在Opera和Internet Explorer中,我发现有时候它不工作,或者会卡很久……我在想,这是什么情况?我是不是做错了什么?

谢谢大家的帮助!

2 个回答

0

请注意,Python的basehttpserver是一个非常基础的HTTP服务器,远远不够完美,但这并不是你面临的主要问题。

如果你把这两个脚本放在文档的最后,也就是在</body>标签之前,会发生什么呢?这样做有帮助吗?

12

你需要明白,现代浏览器为了提高浏览速度,会使用各种技巧,这就是为什么在不同的浏览器上你会看到不同的结果。

在你的情况中,造成麻烦的技术是并发的HTTP/1.1会话设置:为了更好地利用你的带宽,浏览器可以同时启动多个HTTP/1.1会话。这样就可以同时获取多个资源(比如图片)。

但是,BaseHTTPServer并不是线程安全的:当你的浏览器尝试打开另一个连接时,它会失败,因为BaseHTTPServer已经被第一个仍然打开的会话阻塞了。请求根本无法到达服务器,最终会超时。这也意味着在任何给定时间内,只有一个用户可以访问你的服务。这很不方便?没错,但这里有解决办法:

线程!而且Python让这个变得相对简单:

你可以从HTTPServer派生一个新类,并使用socketserver中的MixIn。

.

示例:

from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
from SocketServer import ThreadingMixIn
import threading

class Handler(BaseHTTPRequestHandler):

    def do_HEAD(self):
        pass

    def do_GET(self):
        pass


class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
    """ This class allows to handle requests in separated threads.
        No further content needed, don't touch this. """

if __name__ == '__main__':
server = ThreadedHTTPServer(('localhost', 80), Handler)
print 'Starting server on port 80...'
server.serve_forever()

从现在开始,BaseHTTPServer就可以支持多线程,能够同时处理多个连接(也就是多个请求),这样就能解决你的问题。

除了使用ThreadingMixIn,你也可以使用ForkingMixIn,这样可以生成另一个进程,而不是另一个线程。

祝一切顺利,

creo

撰写回答