mod_python官方手册翻译-全文

第一章简介

1.1性能

使用mod_python的主要优势在于比传统CGI更高的性能。一个测试,使用在Pentium 1.2GHz的机器上运行Red Hat Linux 7.3。使用4种类型的脚本,基于标准的CGI导入模块(以典型的Python CGI脚本开始),然后输出'Hello!',测试10000次请求作为基准。

标准CGI 23 次请求/

mod_python CGI处理器: 385 次请求/

mod_python 发布处理器: 476 次请求/

mod_python 处理器: 1203 次请求/


1.2可移植性

apache是按照阶段处理请求的(比如:读取请求、处理请求头、检查存取等)。这些阶段可以分别用处理器调用的函数来实现。传统上,使用C语言编写处理器模块。mod_python提供了使用Python拜你些apache处理器扩展apache功能的方法。更多的apache请求处理过程,参考"Apache API Notes",或者参考"Mod_python-Integrating Python with Apache"文件。

为了方便的从CGI移植,标准的mod_python处理器提供了CGI模拟环境,允许用户不修改遗留下来的脚本,而在大多数时候无需进行任何修改。

参考:

http://dev.apache.org

apache开发者资源

http://www.modpython.org/python10/

mod_python-整合PythonApache,出现在Python 10(?)


1.3历史

mod_python起源于Httpdapy项目(1997)。很久以后Httpdapy才被叫做mod_python,因为Httpdapy并不是特定用于apache服务器的。Httpdapy设计成跨平台的,而实际上主要从Netscape服务器开始(追溯到1997年的Nsapy)

摘录自Httpdapy项目的README文件的描述将Python嵌入到apache服务器的挑战与解决方法。

""" #小字体

在我多年前开发第一个WEB应用程序时就发现CGI访问数据库非常慢,因为每次点击请求都会装入解释器并执行数MB的数据。数据库往往都很大,并将大量时间浪费在数据库的连接/认证等"意义重大"的事情上,像DNS查询、编码与解码、内存分配等等。在应用提速的压力下,我尝试过使用Python和多种工具的综合使用。我并不迷信微软的ASP,又被Netscape的性能和满是臭虫而郁闷。"Cold Fusion"(冷聚变)看起来有希望,但我稍后学会了HTML类标签的书写方式,可读性跟汇编有一拼。比如PHP。但是我真的很想用Python来写东西。

就在同时有关Python互联网编程的书籍出版了,其中将Python嵌入到Netscape服务器的方式立即引起了我的注意。我在我的项目中使用了这个例子,并在稍后开发了我自己的改进版,叫做Nsapy,并在WindowsNTSolaris上编译。

尽管Nsapy仅能工作在Netscape服务器上,但是出于灵活通用的面向对象设计,在Python精灵中,很容易移植到其他的WEB服务器。

后来,曾经流行的Netscape服务器挂了,我就将Nsapy移植到最流行的服务器apache上。所以从Nsapy衍生出了Httpdapy

"""

继续这个传奇,你会想到将Httpdapy移植到其他服务器上,实际上他们比预想要复杂而且枯燥。

反之,提供一个类似于Perl语言的apache扩展mod_perl一样或更好性能的部件会更有意思。

俱往矣,mod_python的第一个版本发布于20005月。


完成...




第二章安装

注意:获得关于安装的帮助和其他问题的最好地方是mod_python的邮件列表。可以发送标题为"subscribe"的邮件到[email protected]


2.1先决条件

  • Python 2.2.1或更新的版本,早起版本将无法使用。

  • apache 2.0.40或更新的版本,如果要使用apache 1.3.x,请使用mod_python的版本2.7.x

编译mod_python需要有apachePython的包含文件,并且确保安装了Python标准库。如果你从源代码安装了Pythonapache,那么你已经拥有了这些了。如果你使用了预打包软件,如RPM或其他方式则只有二进制版本,而没有源码。通常apachePython的包含文件和库文件在各自的"development"包中。如果你不确定拥有这些重要文件,请从源码安装Pythonapache,或者阅读你系统的文档以了解如何获得开发包。


2.2编译

有两种链接apache模块的方式:静态的和DSO方式。

DSO方式现在非常流行,并推荐用于mod_python的安装。模块被编译成共享库,并在运行时动态装入。

DSO方式的优势在于安装模块时无需重新编译apache。关于DSO机制的详细信息参见http://httpd.apache.org/docs-2.0/dso.html

当前版本的mod_python仅支持DSO方式。

静态链接是一种较老的方式,比动态连接的方式支持更多的平台,但是较少使用。主要的缺陷是必须要重新编译apache,在很多情况下并不是一个好的选择。

2.2.1运行./configure

./configure脚本将会分析你的运行环境,并创建自定义的Make文件用于你的系统。使用autoconf生成的./configure包含如下内容:

  • 查看apxs程序是否存在。这个程序作为apache标准发行版的一部分提供DSO支持。如果找不到则无法实现DSO功能。使用--with-apxs指定选项:

$ ./configure --with-apxs=/usr/local/apache/bin/apxs

强烈建议使用这个选项。

  • 检查Python版本,并尝试找到libpython参数变量的二进制发行版。缺省时可以在Path变量中找到python。如果找到的首个python二进制版本不适合mod_python,则可以手动指定python的路径使用--with-python,如:

$ ./configure --with-python=/usr/local/bin/python2.3

  • 设置mod_python保留的互斥锁数量。在某些系统中,互斥锁是一种有限的资源。增加最大互斥锁数量将会增加使用session锁定的性能。缺省值是8,一个性能不错的数值是32。使用-with-max-locks选项:

$ ./configure --with-max-locks=32

这个选项是在3.2.0版本开始提供的。

  • 尝试查找flex并确定版本。如果PATH中找不到flexconfigure会出错。如果版本不对则会给出警告。一般可以忽略这个警告,除非需要重新创建src/psp_parser.c。剖析器(parser)会被PSP调用,使用flexC语言书写。要求的版本是2.5.31,在大多数平台上2.5.4并不合适。所以已经源码里已经包含了一个psp_parser.c的副本。如果需要编译src/psp_parser.c则需要获取正确版本的flex。如果PATH中的flex版本有误,则可用-with-flex选项:

$ ./configure --with-flex=/usr/local/bin/flex

这是3.2.0版本中的新特性

  • 构建mod_python文档时需要用到python的源代码。可以如果不需要文档,可以忽略这个选项。如果需要则用--with-python-src选项:

$ ./configure --with-python-src=/usr/src/python2.3

这个是3.2.0版本中的新特性

2.2.2运行make

开始构建过程,简单的运行如下即可:

$ make


2.3安装

2.3.1运行make install

这个安装过程必须在root用户下完成:

$ su

# make install

这将会简单的将二进制版本复制到apachelibexec目录。

并且会安装python-librariessite-packages并编译他们。

如果你想只安装python库或者只安装DSO,可以用如下make目标:

# make install_py_lib

# make install_dso

2.3.2配置apache

按照DSO模式安装的apache,需要在httpd.conf中加入启动mod_python的句子:

LoadModule python_module libexec/mod_python.so

实际的mod_python.so路径可能有所变化,但是make install将会显示这个文件的确切路径。


2.4测试

注意次部分内容仅适用于3.x版本,如果使用2.7.x版本,你需要找到更适合的文档。

  • 创建一个在WEB站点可见的目录,如htdocs/test

  • httpd.conf或者.htaccess文件中添加如下内容。如果是在.htaccess文件中,则无需标签。并且确保httpd.conf文件中的AllowOverride目标至少包含FileInfo,缺省是None,将会无法工作。

AddHandler mod_python .py

PythonHandler mptest

PythonDebug On

  • 重定向所有以.py结尾的URLmod_python处理器。mod_python收到请求之后查找合适的处理器来处理请求。这里有个简单的自定义处理器叫mptest。我们看看如何定义这个处理器。

  • 此时如果修改了主配置文件httpd.conf则需要重启apache来使之生效。

  • 编辑mptest.py文件在htdocs/test目录。

from mod_python import apache

def handler(req):

req.content_type='text/plain'

req.write("Hello, world!")

return apache.OK

  • 指向URL路径到mptest.py。将会看到"Hello, world!"。如果看不到则参考故障处理。

  • 依照配置文件的规则,指定任何.py文件都会最终指向mptest.py做处理。因为已经明确的指定了mptest做处理器。如果需要用不同的处理器处理不同的文件则需要更高层的处理器,比如publisher发布器、mpservletsvampire。这些都是动态装入的处理器。

  • 如果都运行无误则转入第三章,入门。

参考:

http://home.comcast.net/d.popowich/mpservlets

http://www.dscpl.com.au/projects/vampire


2.5故障处理

如果出现问题,可以按照如下的处理:

  • 检查错误输出

  • 检查服务器错误日志

  • 单处理执行apache

$ ./httpd -X

这样将会显示更多有用信息

  • mod_python3.2.0开始,可以使用mod_python.testhandler来诊断你的配置。添加到httpd.conf文件:

SetHandler mod_python

PythonHandler mod_python.testhandler

这时指向你的/mpinfoURL则会显示一些信息帮助处理mod_python的问题。

  • mod_python邮件列表提问,并确保提供如下信息:

mod_python版本

操作系统类型、名字、版本号

Python版本、非常规的编辑选项

apache服务器版本

相关的apache配置,.htaccess

相关的Python代码


完成...




第三章指南

3.1使用发布器(Publisher)的快速例子

这一节提供无需太多细节的发布器指南。更多如何使用mod_python处理器的细节将在以后解释。

发布器(publisher)提供了发布mod_python标准模块的方式。需要在配置文件中加入如下配置:

AddHandler mod_python .py

PythonHandler mod_python.publisher

PythonDebug On

下例会返回简单的表单数据。询问姓名、电子邮件、注释之后将会给管理员发邮件。这个简单的应用程序由两个文件组成:form.html用于提交数据,form.py用于处理表单动作。

如下是表单:

Please provide feedback below:

Name:

Email:

Comment:

注意表单的action属性指向form.py/email调用,如下创建form.py,如下:

import smtplib

WEBMASTER="webmaster" #管理员电邮

SMTP_SERVER="localhost" #本机SMTP服务器

def email(req,name,email,comment):

#一定要确保用户提供了所有的参数

if not(name and email and comment):

return "缺少必要的参数"

#创建消息文本

msg=""" /

From: %s

Subject: feedback

To: %s

I have the following comment:

%s

Thank You,

%s

""" % (email,WEBMASTER,comment,name)

#发送信息

conn=smtplib.SMTP(SMTP_SERVER)

conn.sendmail(email,[WEBMASTER],msg)

conn.quit()

#提供返回页面

s="""/

Dear %s,

Thank you for your kind comments, we

will get back to you shortly.

""" % name

return name

当用户点击提交按钮时,发布器将会装入email函数到form模块,传递表单字段作为关键字参数。也会传递请求对象到req

注意逆必须要有req参数。发布器已经可以很聪明的传递必要的参数。函数的返回值将被发送到客户端浏览器。

即使使用发布器可以非常简单的处理,但是程序仍然可以利用mod_python的强大功能,比如调用请求对象。当然也可以调用本地的mod_python处理器来完成大量的工作。比如设置HTML头路由req.headers_out;返回错误抛出apache.SERVER_ERROR异常;从客户端的重定向读写文件req.write()req.read(),等等。

阅读6.1节了解更多关于发布器的详细信息。


3.2apache处理器概览

节标题翻译有误,应为"快速学习apache如何处理请求"

如果你想要深入钻研mod_python的功能,需要理解什么是处理器。

apache分阶段(phase)处理请求。比如第一个阶段是用户认证,之后是检验用户是否有权访问特定的文件,然后是读取文件并发送到客户端。一个典型的静态文件请求包含三个阶段:1.翻译URI到文件位置2.读取文件并发送到客户端3.记录请求日志。更复杂的处理依赖于配置文件。

一个处理器就是初始处理某个阶段的函数。同一个阶段可以有多于一个处理器进行处理,被叫做apache序列。对应每个阶段有个缺省的apache处理器(大多数只做缺省动作或者什么都不作)。然后由其他的apache模块提供处理器,比如mod_python

mod_python提供了apache每一个重要的处理器。mod_python处理器缺省时不会做任何事情,除非用特定的配置文件标志。这些标志以"Python"开始并以"Handler"结尾(如:PythonAuthenHandler),指定Python函数来处理指定的阶段。所以mod_python的主函数只是作为发报机的角色连接apache处理器和Python函数。

最常用的处理器是PythonHandler。它处理含有上下文的请求。因为它没有名字,所以有时也成为通用处理器。这个处理器的apache缺省行为是读取文件并发送到客户端。大多数应用应该重写这个处理器。


3.3mod_python做了什么

我们假设有如下配置:

AddHandler mod_python .py

PythonHandler myscript

PythonDebug On

所以还需要一个如下文件,'/mywebdir/myscript.py'如下:

from mod_python import apache

def handler(req):

req.content_type="text/plain"

req.write("Hello World!")

return apache.OK

如下的事情将会发生:AddHandler标志告诉apache所有的以.py结尾的'/mywebdir'下或其子目录下的文件请求都由mod_python负责处理。'PythonHandler myscript'标志告诉mod_python通用处理器在myscript脚本。'PythonDebug On'标志告诉mod_python如果发生错误,则把错误信息输出到客户端,便于开发。

当请求到来时,apache开始分步骤处理请求并讲请求发送到mod_pythonmod_python处理器检查配置获取处理器。在本例,我们除了通用处理器之外没有调用其他东西。当读取配置到'PythonHandler myscript'时,会发生下面的事情:

1.如果没有做过,假设预设目录已经存在于sys.path中。

2.尝试按名字导入模块myscript。注意子目录是不在sys.path中的,如果需要只能按照包名的方式调用。

3.myscript中查找函数handler

4.调用这个函数并传递请求对象(关于请求对象的更多东西在后面)

5.在这里我们细致研究一下脚本:

from mod_python import apache

导入apache模块来获取apache的接口。大多数mod_python程序需要有这一行。

def handler(req):

这是处理器函数的声明。叫做'handler'是因为mod_python按照配置标志寻找函数。将配置标志转换成小写,然后去掉'python'即可。这样'PythonHandler'就成了'handler'。当然也可以用其他的名字,但是需要用'::'来分隔函数名,比如一个处理器函数叫'spam',那么配置标志就是'PythonHandler myscript::spam'

注意,处理器必须包含一个请求对象参数。请求对象提供了关于请求的大量信息,比如客户端的IP,请求头,URL等。向客户端发送信息也需要用到请求对象,这里是没有'response'对象的。

req.content_type="text/plain"

设置文档MIME类型为'text/plain'。缺省的是'text/html',但是如果处理器没有打算返回html,那么'text/plain'更恰当(appropriate)一些。特别注意:需要在req.write方法之前调用如上语句,因为一旦调用req.writeHTTP响应头就被发送出去了,其后的HTTP响应头的改变都会忽略。

req.write("Hello World!")

将会把字符串写入到客户端。

return apache.OK

告知apache,请求处理完成并成功。如果没有返回OK,则apache会自动返回为apache.HTTP_INTERNAL_SERVER_ERRORreturn apache.HTTP_FORBIDDEN。当处理失败时会在错误日志中记录,并告知客户端一点错误信息。

小技巧:如果你注意到,会发现如果请求的URL不是指向myscript.py,而只是一个.py结尾的文件,哪怕是一个不存在的文件,也可以得到相同的结果。因为服务器只是将特定结尾的处理直接交给相关模块的,而并不要求那一定是一个存在的文件。


3.4更复杂的例子-认证

现在,你知道写一个处理器多么的简单,下面我们来显示一个更复杂的例子。

如果需要用密码来保护一个目录。比如登录名为'spam',密码为'eggs'

首先需要告诉apache在需要认证时调用我们的认证处理器。加入PythonAuthenHandler标志到配置文件:

AddHandler mod_python .py

PythonHandler myscript

PythonAuthenHandler myscript

PythonDebug On

因为一个模块可以包含多个函数,所以也可以在一个模块中使用多个处理器。

然后是告知apache,使用Basic HTTP认证。仅限有效用户进入。配置如下:

AddHandler mod_python .py

PythonHandler myscript

PythonAuthenHandler myscript

PythonDebug On

AuthType Basic

AuthName "Restricted Area"

require valid-user

然后在myscript.py中写认证函数,一个简单认证函数如下:

from mod_python import apache

def authenhandler(req):

pw=req.get_basic_auth_pw()

user=req.user

if user=="spam" and pw=="eggs":

return apache.OK

else:

return apache.HTTP_UNAUTHORIZED

如下是逐行解释:

def authenhandler(req):

认证函数的声明,至于为什么叫这个名字,自己可以参考命名规则。

pw=req.get_basic_auth_pw()

这里获取密码。basic HTTP认证采用base64编码发送到服务器。这个函数返回的直接就是密码了。注意必须在获取用户名之前先获取密码。

user=req.user

获取用户名

if user=="spam" and pw=="eggs":

return apache.OK

对比用户名和密码的值,如果正确返回告知验证成功,并进入下一个阶段的处理。在本例里下一个阶段是handler()

else:

return apache.HTTP_UNAUTHORIZED

告知apache验证失败。这通常会导致客户端浏览器弹出对话框要求用户名和密码。

实测:

发现我使用的httpd-2.0.55提示验证类型无效,一共两种验证类型,一种是Basic一种是Digest,都是不允许的。就是按照如上例子。

终于实验成功了,最上面的配置必须写在httpd.conf中才会有效,写在.htaccess中无效。无论是否指定了AllowOverride FileInfo都是。这样进入一个目录之后会弹出系统的对话框提示输入用户名密码。验证成功后只要浏览器不关闭就可以继续进入这个目录,但是关闭后就需要再次验证。


3.5你自己的404处理器

在某些时候,如果希望返回404(HTTP_NOT_FOUND)或者其他非200的结果到客户端,这里有个窍门。如果从你的处理器返回HTTP_NOT_FOUNDapache将会生成错误页。但是却未必是你喜欢的错误页。

这时,你只要设置req.status=apache.HTTP_NOT_FOUND,提交你的页面,然后返回apache.OK就可以了:

from mod_python import apache

def handler(req):

if req.filename[-17:]=='apache-error.html':

#放行apache的错误页

if req.filename[-18:]=='handler-error.html':

#使用自己的错误页

req.status=apache.HTTP_NOT_FOUND

pagebuffer='页面不存在'

else:

#使用文件内容

pagebuffer=open(req.filename,'r').read()

#输出错误页

req.write(pagebuffer)

return(apache.OK)


完成...




第四章 Python API

4.1多解释器

使用mod_python工作的时候应该理解,并不是使用Python在命令行下写脚本,而是用Python调用C语言API

PythonC语言API提供了创建子解释器的能力。关于子解释器的更多信息详见Py_NewInterpreter()函数。在这里,每个子解释器拥有独立的命名空间,并与其他子解释器不可访问。子解释器非常适合于理解同一个服务器中处理各个请求的进程。

服务器启动mod_python的时候会启动一个主解释器。主解释器包含子解释器词典。刚开始词典是空的。对应每一个请求就会创建一个子解释器,并将引用存储在词典里。这个词典用解释器名作为键名,主解释器叫做'main_interpreter'。其他的解释器受到PythonInterp*的控制。缺省的行为是使用apache虚拟服务器名来命名(ServerName标志)。这意味着同一个虚拟服务器中的脚本在同一个子解释器中执行,但不再同一个虚拟服务器中的脚本因为不同的子解释器而无法互相访问。

PythonInterpPerDirectoryPythonInterpPerDirective标志改变(alter)命名惯例(convention)到要存取的绝对路径,或者在Python*Handler可以访问到的地方,分别的(respectively)PythonInterpreter能够强制使用解释器名重载所有的命名惯例。

一旦创建,一个子解释器将会被接踵而来的请求重用,在apache停止之前不会被销毁。从调用req.interpreter可以获取当前的解释器名。

参考PythonC语言API

http://www.python.org/doc/current/api/api.html


4.2请求(request)处理器

一个请求处理器是处理请求阶段细节的处理器。apache在处理请求的每一个阶段都对调用相应的处理器。对每一个阶段都会调用请求处理器,由apache的核心或者模块提供,比如mod_python由用户的Python函数提供。用Python书写的处理器只要遵守如下规则就与C写的没有区别:

处理器函数总是会传递请求对象的引用,一般是req变量。

每个处理器可以返回如下:

  • apache.OK:这个阶段处理无误

  • apache.DECLINED:这个阶段处理过后还需要队列中的其他模块处理

  • apache.HTTP_ERROR:发生了HTTP错误,如下值:

HTTP_CONTINUE 100

HTTP_SWITCHING_PROTOCOLS 101

HTTP_PROCESSING 102

HTTP_OK 200

HTTP_CREATED 201

HTTP_ACCEPTED 202

HTTP_NON_AUTHORITATIVE 203

HTTP_NO_CONTENT 205

HTTP_PARTIAL_CONTENT 206

HTTP_MULTI_STATUS 207

HTTP_MULTIPLE_CHOICES 300

HTTP_MOVED_PERMANENTLY 301

HTTP_MOVEDTEMPORARILY 302

HTTP_SEE_OTHER 303

HTTP_NOT_MODIFIED 304

HTTP_USE_PROXY 305

HTTP_TEMPORARY_REDIRECT 307

HTTP_BAD_REQUEST 400

HTTP_UNAUTHORIZED 401

HTTP_PAYMENT_REQUIRED 402

HTTP_FORBIDDEN 403

HTTP_NOT_FOUND 404

HTTP_METHOD_NOT_ALLOWED 405

HTTP_NOT_ACCEPTABLE 406

HTTP_PROXY_AUTHENTICATION_REQUIRED 407

HTTP_REQUEST_TIME_OUT 408

HTTP_CONFLICT 409

HTTP_GONE 410

HTTP_LENGTH_REQUIRED 411

HTTP_PRECONDITION_FAILED 412

HTTP_REQUEST_ENTITY_TOO_LARGE 413

HTTP_REQUEST_URI_TOO_LARGE 414

HTTP_UNSUPPORTED_MEDIA_TYPE 415

HTTP_RANGE_NOT_SATISFIABLE 416

HTTP_EXPECTATION_FAILED 417

HTTP_UNPROCESSABLE_ENTITY 422

HTTP_LOCKED 423

HTTP_FAILED_DEPENDENCY 424

HTTP_INTERNAL_SERVER_ERROR 500

HTTP_NOT_IMPLEMENTED 501

HTTP_BAD_GATEWAY 502

HTTP_SERVICE_UNAVAILABLE 503

HTTP_GATEWAY_TIME_OUT 504

HTTP_VERSION_NOT_SUPPORTED 505

HTTP_VARIANT_ALSO_VARIES 506

HTTP_INSUFFICIENT_STORAGE 507

HTTP_NOT_EXTENDED 510

可以通过抛出apache.SERVER_RETURN异常来返回错误码,并将HTTP错误码作为异常的参数。例如:

raise apache.SERVER_RETURN, apache.HTTP_FORBIDDEN

处理器可以通过req.write()方法将文档发送到客户端。

客户端提交的数据可以通过req.read()读取。

注意:Python*Handler所在的目录必须已经在sys.path中,如果没有在配置段指定则不会在。

一个请求处理器的例子:

from mod_python import apache

def requesthandler(req):

req.content_type='text/plain'

req.write('Hello World!')

return apache.OK


4.3过滤(filter)处理器

过滤处理器可以改变服务器的输入和输出。分两种:输入和输出的过滤。现在mod_python仅提供请求级别(request-level)的过滤器,就是HTTP的请求和回应体可以被过滤。未来的apache将会提供连接级别(connection-level)的过滤器。

过滤处理器接收一个filter对象做参数,请求对象可经由此得出filter.req,但是所有的读写动作必须经由filter的方法。

过滤器在读到None时需要被关闭,代表流的结束。过滤器的返回值将会被忽略,但可用filter.pass_on()方法达到同样的效果。

过滤器必须一开始就注册为PythonInputFilterPythonOutputFilter,然后添加到apache的配置标志Add/SetInputFilterAdd/SetOutputFilter

如下例子把所有.py结尾文件用CAPITALIZE过滤器处理,如下是配置:

PythonOutputFilter capitalize CAPITALIZE

AddOutputFilter CAPTITALIZE .py

如下是文件capitalize.py的内容:

from mod_python import apache

def outputfilter(filter):

s=filter.read()

while s:

filter.write(s.upper())

s=filter.read()

if s is None:

filter.close()

书写过滤器时应该注意到过滤器随时都会被调用。过滤器无法控制数据的总量,也无法确定请求何时调用。比如对于一个请求,过滤器可能会执行一次或五次,并无法知晓调用何时结束和先后顺序。从一个read操作返回的EOS代表请求的结束。

过滤器也可能在子请求的递归调用中。为避免数据多次被改变,经常的确保并不在子请求中,依靠req.main变量值。


4.4连接(connection)处理器

连接处理器处理连接到服务器的TCP连接。不同于HTTP处理器,连接处理器接收connection对象做参数。连接处理器可以用于实现协议。如下例配置:

PythonConnectionHandler echo

实现文件echo.py

from mod_python import apache

def connectionhandler(conn):

while 1:

conn.write(conn.readline())

return apache.OK


4.5apache-访问Apache内部

apache内部对Python的接口也恰好叫做apache,在mod_python包。提供了连接apache内部的重要功能,比如有用的函数,文档等(request对象也提供访问Apache内部的接口,但不在本节)

apache模块仅可作为mod_python下的一个脚本来导入,因为它依赖于mod_python内建的_apache模块。最好按照如下导入:

from mod_python import apache

mod_python.apache模块定义了如下函数和对象,更多深入的信息查看Apache文档。

4.5.1函数

log_error(message[,level,server])

Apache函数ap_log_error()的接口,message是错误信息,level是如下值:

APLOG_EMERG

APLOG_ALERT

APLOG_CRIT

APLOG_ERR

APLOG_WARNING

APLOG_NOTICE

APLOG_INFO

APLOG_DEBUG

APLOG_NOERRNO

serverreq.server对象的引用。如果没有指定则写入到缺省的日志,否则写入到对应虚拟服务器的日志中。当server没有指定时,日志级别也无效,日志级别是httpd在编译时指定的,经常为warn

如果拥有请求对象的引用,最好用req.log_error,这样将会存入请求相关信息,如源IP等。

import_module(module_name[,autoreload=1,log=0,path=None])

mod_python的内部高级特性,用于在模块改变时自动重新载入模块。

module_name是模块名,可以包含点的包名。

autoreload标志查看包是否有所改变,如果改变则自动重新载入。

如果logTrue则这个事件会记入日志。

path严格指定模块的位置。

例子:

from mod_python import apache

mymodule=apache.import_module('mymodule',log=1)

allow_methods([*args])

用于设置req.allowedreq.allowed是多个标志位的叠加,对应'Allow:'头。可以在返回HTTP_NOT_IMPLEMENTED错误之前设置。参数如下:

M_GET

M_PUT

M_POST

M_DELETE

M_CONNECT

M_OPTIONS

M_TRACE

M_PATCH

M_PROPFIND

M_PROPPATCH

M_MKCOL

M_COPY

M_MOVE

M_LOCK

M_UNLOCK

M_VERSION_CONTROL

M_CHECKOUT

M_UNCHECKOUT

M_CHECKIN

M_UPDATE

M_LABEL

M_REPORT

M_MKWORKSPACE

M_MKACTIVITY

M_BASELINE_CONTROL

M_MERGE

M_INVALID

exists_config_define(name)

确定Apache是否存在一个name配置。比如Apache定义了-DFOOBAR则如下测试为真:

apache.exists_config_define('FOOBAR')

register_cleanup(handler[,data])

注册一个清除行为,等同于req.register_cleanup()reg.server.register_cleanup(),除非服务器或请求对象不需要。

config_tree()

返回服务器级(server-level)配置树。这些配置不包含.htaccess文件的标志。返回的是副本,修改对配置没有效果。

server_root()

返回配置标志'ServerRoot'的值。

make_table()

一个作废的函数,可用table代替。

mpm_query(code)

允许查询MPM参数变量来处理线程。返回值是如下三种常量之一:

AP_MPMQ_NOT_SUPPORTED=0

MPM支持线程或子进程

AP_MPMQ_STATIC=1

MPM正在使用静态守护线程

AP_MPMQ_DYNAMIC=2

MPM正在使用动态守护线程

code参数是如下值:

AP_MPMQ_MAX_DAEMON_USED=1 最大允许使用的守护线程数量

AP_MPMQ_IS_THREADED=2 MPM是否允许使用线程

AP_MPMQ_IS_FORKED=3 MPM是否可用fork生成子进程

AP_MPMQ_HARD_LIMIT_DAEMONS=4 编译时允许的最大守护进程数量

AP_MPMQ_HARD_LIMIT_THREADS=5 编译时允许最大线程数量

AP_MPMQ_MAX_THREADS=6 threads/child配置允许的最大线程数量

AP_MPMQ_MIN_SPARE_DAEMONS=7 最小剩余守护数

AP_MPMQ_MIN_SPARE_THREADS=8 最小剩余线程数

AP_MPMQ_MAX_SPARE_DAEMONS=9 最大剩余守护数

AP_MPMQ_MAX_SPARE_THREADS=10 最大剩余线程数

AP_MPMQ_MAX_REQUESTS_DAEMON=11每个守护最大允许请求数量

AP_MPMQ_MAX_DAEMONS=12 配置允许的最大守护数量

例如:

if apache.mpm_query(apache.AP_MPMQ_IS_THREADED):

# do something

else:

# do something else

4.5.2表格对象(mp_table)

class table([mapping-or-sequence])

返回一个新的mp_table表格对象。mapping-or-sequence提供了表格的初始化数据。表格对象是对APR表格对象的包装(wrapper)。表格对象的行为(behave)非常像词典(还支持Python2.2以后的in操作符),但是有以下不同:

  • 键和值都必须是字符串

  • 键的查询不区分大小写

  • 允许相同的键,即一个键对应一个列表的值

很多apache的信息存储在表格中,如req.headers_inreq.headers_outmod_python提供的所有表格对象都是真正(actual)映射(mapping)apache结构的,所以改变表格的值也会改变apache的表格。

除了(in addition to)像词典一样的行为(behavior)之外,表格对象还支持如下操作:

add(key,val)

这个方法允许使用相同的键,这对重复的头很有用,比如"Set-Cookie:"就需要。这个功能从3.0开始提供。

4.5.3请求对象

请求对象是映射到apacherequest_rec结构的对象。当处理器被调用时,会传递唯一个的一个参数就是请求对象。可以为请求对象指定属性,用于在处理器之间通信。如下是方法:

add_common_vars()

间接调用ap_add_common_vars()函数。调用后,req.subprocess_env会包含大量的CGI信息。

add_handler(htype,handler[,dir])

允许动态注册处理器。htype是要添加的处理器的标志,如'PythonHandler',但不可以是过滤器(filter)或连接(connection)handler是包含模块名或函数名的处理器函数。可选参数dir是需要添加到pythonpath环境变量的路径。如果未指定目录,而且有同类型的处理器,则继承路径,否则用前一路径。如果有PythonPath标志存在,则sys.path将会设置为它。

通过这种方式添加的处理器生存期为这个请求。为同一个类型添加多个处理器是很有用的,不过要注意避免死循环(infinite loop)

动态注册处理器是一种很有用的技术,比如用于PythonAuthenHandler,可以为不同的认证级别指定不同的权限。如:

if manager:

req.add_handler("PythonHandler","menu::admin")

else:

req.add_handler("PythonHandler","menu::basic")

注意,如果传递了一个无效处理器,则会在寻找处理器时发生异常。

allow_methods(methods[,reset])

添加方法到req.allowed_methods列表。这个列表将会允许传递,在头部指定HTTP_METHOD_NOT_ALLOWEDHTTP_NOT_IMPLEMENTED返回到客户端。注意apache并不会对这些方法起作用。这个列表仅仅用于构造头部。实际的方法处理逻辑在处理器代码中提供。

methods是一个字符串列表,如果reset设置为1,则列表会被清空。

document_root()

返回DocumentRoot设置

get_basic_auth_pw()

返回简单认证时的密码字符串

get_config()

返回包含mod_python配置的表格对象的引用,用于当前请求,除了Python*HandlerPythonOption(可以通过req.get_options()获得)。表格包含键,和值列表。

get_remote_host([type,str_is_ip])

用于查询客户端的DNS名和IP地址,首次调用时查询DNS,其后的查询返回缓存数据。

可选参数type可为如下值:

apache.REMOTE_HOST:查询DNS名,当apache标志HostNameLookupsoff时返回None,无法查询。

apache.REMOTE_NAME:缺省值,尽可能返回DNS名,否则返回IP地址字符串。

apache.REMOTE_NOLOOKUP:不执行DNS查询,直接返回IP地址。如果曾经执行过查询,则返回缓存的机器名。

apache.REMOTE_DOUBLE_REV:强制使用double-reverse查询,失败返回None

如果str_is_ip设为None或未指定,则返回值为DNS名或IP地址的字符串。如果str_is_ip不为None,则返回一个(address,str_is_ip)元组。当str_is_ip为非零则addressIP地址字符串。失败返回None

get_options()

返回PythonOptions标志的选项表格的引用。

internal_redirect(new_uri)

对请求进行内部重定向。new_uri是字符串。内部重定向将会创建一个新的请求对象,并执行所有的阶段。req.prev包含重定向之前的地址。

log_error(message[,level])

apache函数ap_log_error()函数的接口。message是日志信息,level是日志级别标志,可为如下值:

APLOG_EMERG

APLOG_ALERT

APLOG_CRIT

APLOG_ERR

APLOG_WARNNING

APLOG_NOTICE

APLOG_INFO

APLOG_DEBUG

APLOG_NOERRNO

如果需要写入日志而不是通过请求对象,可用apache.log_error函数。

meets_conditions()

调用apache函数ap_meets_conditions()返回状态码。如果statusapache.OK,则响应正确。如果不是则简单的返回状态码status。注意req.headers_out将会被优先(prior)设置,并也会影响req.status的值,假如不是返回apache.OK

例如:

...

r.headers_out['ETag']="1130794f-3774-4584-a4ea-0ab19e684268"

r.headers_out['Last-Modified']='Wed, 23 Feb 2005 00:00:00 GMT'

r.headers_out['Expires']='Mon, 18 Apr 2005 17:30:00 GMT'

status=r.meets_conditons()

if status!=apache.OK:

return status

... 处理响应内容 ...

requires()

返回包含require标志参数的元组。例如如下apache配置:

AuthType Basic

require user joe

require valid-user

函数将会返回('user joe','valid-user')

read([len])

从客户端读取最多len字节的数据,返回字符串数据。如果len为负数(negative)或者缺省(omitted),则读取所有的数据。这个函数也会受到apache配置标志Timeout的影响,如果读取时超时则会终端,并抛出IOError异常。

这个函数依赖于(rely的复数relies)客户端提供的Content-length头。如果未提供这个头,则缺省为0。不正确的Content-length将会导致函数试图读取更多的数据,并且在超时到达之前一直出于阻塞状态。

readline([len])

read()一样读取到行尾。注意,同HTTP定义一样,大多数客户端的行结束是'/r/n',而不是简单的'/n'

readlines([sizehint])

读取sizehint字节以内的所有行,并返回行列表。

register_cleanup(callable[,data])

注册一个清理行为。参数callable可以为任何可调用对象,可选参数data可以是任何对象,缺省为None。在每个请求结束的时候,而在请求对象被销毁之前,callable将会被调用,并且传递一个唯一的参数data

推荐传递请求对象作为data,但是注意,在执行清理行为时,请求对象的处理已经完成,这时再对客户端进行写操作是无意义的(pointless)

如果在执行清理时遇到(encounter)错误,将会被日志记录。除非直接影响请求处理,否则执行清理中的bug很难辨认(spot)

如果在清理行为之前,服务器关闭了,则很可能不会被执行。

sendfile(path[,offset,len])

发送path文件到客户端,开始为offset,并发送len字节。offset缺省为0,而len缺省为-1,发送整个(entire)文件。

返回成功发送的字节数,或者在出错时抛出IOError异常。这个函数提供了向客户端发送文件的最有效(most efficient)的方式。

write(string[,flush=1])

将字符串立即(directly)写入客户端,然活清空(flush)缓存,除非flush0

flush()

将缓存数据写入客户端,并清空缓存。

set_content_length(len)

设置req.length"Content-length"头。注意必须在任何发送数据之前设置,比如req.write()之前,否则是无意义的(meaningless)

  • 请求对象的成员:

connection

这个请求的连接对象。查看后面的详细介绍。只读。

server

请求所属的服务器对象。查看后面的详细介绍。只读。

next

如果是内部重定向,重定向到的请求对象。只读。

prev

如果是内部重定向,来自重定向的请求对象。只读。

main

如果这是一个子请求,指向主请求。只读。

the_request

包含请求的第一行的字符串。只读。

assbackwards

指定HTTP/0.9的简单请求。这意味着响应不包含任何头,仅包含信息主体。用于兼容古老的浏览器。也可以指定这个选项用于在重定向中避免(avoid)发送头。

proxyreq

代理请求。一个apache.PROXYREQ_*值。只读。

header_only

指定HEAD请求的布尔变量,反对GET。只读。

protocol

客户端提供的协议,或者是'HTTP/0.9'。同CGI SERVER_PROTOCOL。只读。

proto_num

整数,协议版本号,比如1.1对应1001。只读。

hostname

字符串,由URIHost:设置,头。只读。

request_time

长整数,请求发生的时间。只读。

status_line

状态行,如'200 OK'。只读。

status

状态,apache.HTTP_*中的一个值。

method

包含方法的字符串,如'GET''HEAD''POST'。同CGI REQUEST_METHOD。只读。

method_number

方法号,整数。只读。

allowed

整数,允许方法的位逻辑。使用允许结构:头响应HTTP_METHOD_NOT_ALLOWEDHTTP_NOT_IMPLEMENTED。这个字段是apache内部使用的。需要设置则使用req.allow_methods()方法。只读。

allowed_xmethods

元组,允许的扩展(extension)方法。只读。

allowed_methods

元组,允许的方法,使用METHOD_NOT_ALLOWED的关系。这个成员无法被req.allow_methods()方法修改。只读。

sent_bodyct

整数,正文的字节数。只读。

bytes_sent

长整数,已经发送的字节数。只读。

mtime

长整数,资源的修改时间。只读。

chunked

传送大块数据时的编码,布尔值。只读。

range

字符串'Range:'头。只读。

clength

长整数,真正的正文长度。只读。

remaining

长整数,等待读取的数据字节数,只在一个读取操作中有效,只读。

read_length

长整数,已经读取的字节数,只读。

read_body

整数,请求体怎样被读取,只读。

read_chunked

布尔值,读取大块数据的编码。只读。

expecting_100

布尔值,是否客户端需要等待100(HTTP_CONTINUE)响应,只读。

headers_in

客户端发来的头的表格对象。

headers_out

需要发送给客户端的头的表格对象。

err_headers_out

当出错时需要发送的头的表格对象,用于出错时替换headers_out

subprocess_env

包含CGI信息的表格对象。在需要信息之前需要先调用req.add_common_vars()方法来填充信息。

notes

与请求拥有共同生存期的对象,用于存储各种信息,可以在一个请求的各个处理器之间传递信息。如果确实需要在各个处理器之间传递信息,最好的方式是给请求对象添加成员。

phase

正在处理的阶段,比如'PythonHandler',只读。

interpreter

正在运行的子解释器名。

content_type

字符串,正文类型。mod_python维护一个内部标志(req._content_type_set)来保持content_type的设置。发布器按照如下方式使用这个标志:当content_type没有明确(explicitly)的设置时,它依靠输出的前几个字节来猜测(attempt)

content_languages

元组。用字符串列表表示正文的语言。

handler

当前处理器的名字。由mod_mime设置,而不是mod_python处理器。大多数情况还是'mod_python',只读。

content_encoding

字符串,正文编码,只读。

vlist_validator

整数,有效变量的列表(如果为负数),只读。

user

获取验证的用户名。同CGI REMOTE_USER,只读。注意,req.get_basic_auth_pw()必须在此之前调用(be called prior)

ap_auth_type

验证类型,同CGI AUTH_TYPE,只读。

no_cache

布尔值,如果为true则没有缓存,只读。

no_local_copy

布尔值,如果没有本地副本,只读。

unparsed_uri

URI没有经过任何转换,只读。

uri

URI的一部分路径,只读。

filename

请求的文件名字符串。

canonical_filename

字符串,真实的文件名(req.filename是有效的,如果她们不同)。只读。

path_info

字符串,在文件名之后,查询参数之前的部分。同CGI PATH_INFO

args

字符串,同CGI QUERY_ARGS,只读。

finfo

元组,文件信息结构,类似于POSIX状态,URI指向的文件的信息(mode,ino,dev,nlink,uid,gid,size,atime,mtime,ctime,fname,name)。模块apache定义了FINFO_*变量的常量,可用于存取其中的元素,如:

fname=req.finfo[apache.FINFO_FNAME]

只读。

parsed_uri

元组,被分解开的URI(scheme,hostinfo,user, password, hostname, port,path,query,fragment)。模块apache定义了URI*常量用于存取元组的元素,如:

fname=req.parsed_uri[apache.URI_PATH]

只读。

used_path_info

当前请求中被拒绝(reject)path_info,只读。

eos_sent

布尔值,是否发送过EOS,只读。(EOS=end of stream)

4.5.4连接对象(mp_conn)

连接对象是apacheconn_rec结构的映射。

连接对象的方法:

read([length])

从客户端读取最多length字节的数据。在读到任何数据之前将会阻塞。如果length-1,则读取直到套接字关闭,就是HTTP服务器代码的EXHAUSTIVE模式。仅可在连接处理器中使用。

注意从3.0.3版本开始,这个方法的行为有所改变,在此前是阻塞读取直到length个字节读取完成。

readline([length])

读取一行,最多到length个字节。

仅可在连接处理器中使用。

write(string)

向客户端写入字符串。仅可在连接处理器中使用。

连接对象的成员:

base_server

连接通过的虚拟主机,只读。

local_addr

服务器的地址元组(address,port),只读。

remote_addr

客户端地址元组(address,port),只读。

remote_ip

客户端的IP地址,同CGI REMOTE_ADDR,只读。

remote_host

字符串,客户端的DNS名称,如果没有检查DNS则返回None,如果没有名字则返回""。同CGI REMOTE_HOST,只读。

remote_logname

远程的名字,如果使用了RFC1413识别信号(ident)。同CGI REMOTE_IDENT,只读。

aborted

布尔值,如果连接被中断则为真,只读。

keepalive

整数,1代表连接保持到下一个请求,0代表未定,-1代表严重错误,只读。

double_reverse

整数,1代表执行反向DNS查询,0代表未启用,-1代表查询失败,只读。

keepalives

连接被使用过的次数,只读。

local_ip

本机的服务器IP地址,只读。

local_host

服务器的DNS名称,只读。

id

长整数,一个唯一的连接ID,只读。

notes

表格对象,包含各种与连接具有相同生命周期的信息。

4.5.5过滤器对象(mp_filter)

过滤器对象控制mod_python的输入输出,通常用于提取信息,获取信息并存入过滤器栈。

过滤器的方法:

pass_on()

不处理任何数据,全部放行。

read([length])

从临近过滤器至多读取length个字节的数据,返回字符串数据,如果读取流结束则返回None。过滤器在遇到EOS之后必须关闭。如果未指定length或为负数,则读取当前所有有效数据。

readline([length])

读取一行至多length个字节。

write(string)

将字符串写入临近的过滤器。

flush()

将缓存输出到FLUSH缓存桶。

close()

关闭过滤器,并发送EOS标志。之后关于这个过滤器的IO操作会抛出异常。

disable()

告知mod_python忽略处理器并让数据放行。在mod_python内部使用打印栈跟踪,防止进入死循环。

过滤器成员:

closed

布尔值,指示过滤器是否关闭,只读。

name

字符串,过滤器的注册名,只读。

req

请求对象的引用,只读。

is_input

布尔值,如果是一个输入过滤器则为True,只读。

handler

字符串,配置中的处理器名称,只读。

4.5.6服务器对象(mp_server)

请求对象映射到apacherequest_rec结构。服务器结构描述了服务器(也可能是虚拟服务器)如何处理请求。

服务器方法:

get_config()

类似于req.get_config(),但返回指向server->module_config的配置向量。

register_cleanup(request,callable[,data])

注册一个清除行为。类似于req.register_cleanup(),除了在子结束时间以外(字句不通)。这个函数需要一个额外的参数-请求对象。

服务器成员:

defn_name

字符串,配置文件中服务器定义名,只读。

defn_line_number

整数,服务器定义所在配置文件的行号,只读。

server_admin

ServerAdmin标志的值,只读。

server_hostname

ServerName标志的值,同CGI SERVER_NAME,只读。

names

元组,ServerAlias标志的列表,不包含通配符,用wild_names分隔,只读。

wild_names

元组,ServerAlias标志的通配服务器名,只读。

port

整数,TCP/IP端口号,同CGI SERVER_ROOT。在apache2.0开始只是显示为0,可以查看req.connection.local_addr。只读。

error_fname

错误日志文件的文件名,只读。

loglevel

整数,日志级别,只读。

is_virtual

布尔值,如果是一个虚拟服务器则为True,只读。

timeout

整数,Timeout标志的值,只读。

keep_alive_timeout

整数,保持连接的超时时间,只读。

keep_alive_max

每个连接最大允许的请求数,只读。

keep_alive

保持连接?只读。

path

字符串,ServerPath标志的值,服务器路径,只读。

pathlen

整数,路径长度,只读。

limit_req_line

整数,HTTP请求行的长度限制,只读。

limit_req_fieldsize

整数,请求头的长度限制,只读。

limit_req_fields

整数,请求头的字段数量限制,只读。


4.6util-工具箱

util模块提供了类似于cgi模块的多种实用工具。util模块的实现也是非常有效率的,直接调用ApacheAPI,并且通过环境变量传递信息。

推荐的使用这个模块的方式:

from mod_python import util

参考CGI文档:

http://CGI-spec.golux.com

4.6.1FieldStorage

通过FieldStorage类传递数据,这个类类似于标准模块cgi FieldStorage

class FieldStorage(req[,keep_blank_values,strict_parsing])

这个类提供了处理从客户端提交的HTMLreqmod_python请求对象的实例。

可选参数keep_blank_values是一个标志选项,判定是否把从数据编码而得的URL中的空值作为空字符串处理。缺省值为False,代表忽略空值,好像他们没有出现过一样。

可选参数strict_parsing还没有实现。

初始化时,FieldStorage类读取所有从客户端提交的信息。当所有客户端信息处理完成时,只剩下一个对应一个请求的FieldStorage类的实例。你也可以尝试在FieldStorage实例化之前和之后访问客户端数据。

从客户端读取的数据将会被转换成Field对象,每项数据一个字段。从HTML提交的输入类型file,将会被以临时文件的形式稍后提交成Field对象的file属性。

FieldStorage类有一个映射对象接口,可以作为词典来处理。当使用映射时,键名是输入字段名,返回的词典值可能是如下的:

  • StringField的实例,包含输入值。仅限输入一个值的时候。StringFieldstr的子类提供了value属性来兼容cgi模块。

  • Field类的实例,如果输入是一个上传文件。

  • StringField或者/Field对象的列表。当输入多个值时,比如HTML标签