更新至1.9
自uWSGI 1.9起,提供了一个可编程的内部路由子系统。 (1.1版本之后的旧版本有一个不大起眼的版本)。你可以使用内部路由子系统来动态改变处理请求的方式。例如,你可以用它来触发对于指定URL的301重定向,或者在指定条件下提供来自于缓存的内容。这个内部路由子系统的灵感来源于Apache的 mod_rewrite 和Linux的 iptables 命令。
在喷它是凌乱、不优雅或者不图灵完备之前,请记住,它必须快并且只是快。如果你需要优雅以及更多的复杂度,那么你可以在自己的代码中实现。
在请求周期内,会经过不同的“链”。每个链包含一个路由表 (见下)。
链可以是“递归的”,一个“递归”链在一个请求周期内可以被多次调用。
这是链的顺序:
request 在请求被传递给插件之前应用
error 一个生成了一个HTTP状态码就会被立即应用(递归链)
response 在生成了最后一个响应头后被应用 (仅在发送响应体之前)
final 在已经发送响应到客户端之后被应用
request 链是 (为了方便起见) 是“默认的”链,因此它的选项没有前缀,而其他的链需要前缀。
例如:
route-user-agent -> 用于request链
而
response-route-uri -> 用于response链
内部路由表是一个接一个(也允许向前跳跃)执行的“规则”序列。每个规则由一个''subject(标题)'',一个''condition(条件)''和一个''action(动作)''组成。''条件''通常是一个应用到标题的PCRE正则表达式:如果匹配上了,那么就会触发相应的动作。标题是请求的变量。目前支持以下标题:
host(检查HTTP_HOST)uri(检查REQUEST_URI)qs(检查QUERY_STRING)remote-addr(检查REMOTE_ADDR)remote-user(检查REMOTE_USER)referer(检查HTTP_REFERER)user-agent(检查HTTP_USER_AGENT)status(检查HTTP响应状态码,在request链中不可用)default(默认标题,映射到PATH_INFO)
除此之外,有一个可用的低层次条件可插拔系统。你可以使用 --route-if 选项来访问此系统。目前,支持以下检查:
exists(检查标题是否存在于文件系统)isfile(检查标题是否是一个文件)isdir(检查标题是否是一个目录)isexec(检查标题是否是一个可执行文件)equal/isequal/eq/==(检查标题是否等于指定模式)ishigherequal/>=ishigher/>islower/<islowerequal/<=startswith(检查标题是否以指定模式开头)endswith(检查标题是否以指定模式结尾)regexp/re (检查标题是否匹配指定的正则表达式)empty(检查标题是否为空)contains
当一次检查需要一个模式(例如使用'equal'或者'regexp')时,你可以使用分号将它从标题分隔开:
; never matches
route-if = equal:FOO;BAR log:never here
; matches
route-if = regexp:FOO;^F log:starts with F动作是当一个规则匹配的时候要运行的汗水。这些动作由插件导出,并有一个返回值。
每个动作都有一个返回值,这个返回值告诉路由引擎接下来要做什么。支持以下返回码:
NEXT(继续下一个规则)CONTINUE(停止扫描内部路由表并运行请求)BREAK(停止扫描内部路由表并关闭请求)GOTO x(跳到规则x)
当一个规则不匹配的时候,则假设 NEXT 。
[uwsgi]
route-user-agent = .*curl.* redirect:http://uwsgi.it
route-remote-addr = ^127\.0\.0\.1$ break:403 Forbidden
route = ^/test log:someone called /test
route = \.php$ rewrite:/index.php
route = .* addheader:Server: my uWSGI server
route-host = ^localhost$ logvar:local=1
route-uri = ^/foo/(.*)\.jpg$ cache:key=$1.jpg
route-if = equal:${PATH_INFO};/bad break:500 Internal Server Error前面的规则构建以下表:
- 如果
HTTP_USER_AGENT变量包含'curl',那么重定向请求到 http://uwsgi.it (状态码为302,动作返回BREAK) - 如果
REMOTE_ADDR是'127.0.0.1',则返回403 Forbidden (动作返回 BREAK) - 如果
PATH_INFO以/test开头,则在日志中打印字符串'someone called /test' (动作返回NEXT) - 如果
PATH_INFO以'.php'结尾,那么将其重写至/index.php (动作返回 NEXT) - 对于所有的
PATH_INFO,添加HTTP头'Server: my uWSGI server'到响应中 (动作返回NEXT) - 如果
HTTP_HOST是localhost,那么添加日志变量'local',并将其设置为'1' - 如果
REQUEST_URI以/foo开头,并且以.jpg结尾,那么使用附带的键(通过正则表达式分组构建)从uWSGI缓存中获取它 (动作返回 BREAK) - 如果
PATH_INFO等于/bad,那么抛出500错误
除了PCRE占位符/组 (使用$1到$9),你可以使用${VAR}语法来访问请求变量 (PATH_INFO, SCRIPT_NAME, REQUEST_METHOD...)。
[uwsgi]
route-user-agent = .*curl.* redirect:http://uwsgi.it${REQUEST_URI}你可以使用${cookie[name]}语法来访问一个cookie值:
[uwsgi]
route = ^/foo log:${cookie[foobar]}这将会在日志中记录当前请求的'foobar' cookie内容
你可以使用${qs[name]}语法访问HTTP请求字符串的值:
[uwsgi]
route = ^/foo log:${qs[foobar]}这将会在日志中记录当前请求的查询字符串的'foobar'项的内容
cookie和qs变量都是所谓的“路由变量”。它们是可插拔的,因此外部插件可以添加新的变量来添加新特性到你的应用上。 (看看 :doc:`GeoIP` 插件,以获取关于这个的一个例子。) 还有一些可用的嵌入式路由变量。
mime-- 返回指定变量的mime类型:${mime[REQUEST_URI]}[uwsgi] route = ^/images/(.+) addvar:MYFILE=$1.jpg route = ^/images/ addheader:Content-Type: ${mime[MYFILE]}
time-- 以多种形式返回时间/日期。唯一支持(目前)的是time[unix],返回时代httptime-- 返回http日期,添加数值参数 (如果指定) 来获得当前时间 (使用空参数来获取当前的服务器时间)
[uwsgi]
; add Date header
route-run = addheader:Date ${httptime[]}math-- 需要matheval支持。例如:math[CONTENT_LENGTH+1]base64-- 将指定的变量用base64编码hex-- 将指定的变量用hex编码upper-- 将指定变量转换成大写lower-- 将指定变量转换成小写uwsgi-- 返回内部的uWSGI信息,目前支持uwsgi[wid], uwsgi[pid], uwsgi[uuid]和uwsgi[status]
这是个好问题。你只需要一直记住,uWSGI是关于通用性和 性能 的。可以循环利用总是好的。
--route-if 选项,虽然通用,但是不能够被优化,因为在处理每一个请求的时候都必须重新计算它所有的部分。这显然非常快,但是
--route-uri 选项 (和其他小伙伴)可以被预先优化 (在启动期间) 来直接映射到请求内存区域,因此,如果你可以使用它们,那么你绝对应该用它们。 :)
是哒,整个信息技术产业(和历史)最具争议的结构就是这里。你可以向前 (只能向前!)跳到内部路由表的特定点。你可以设置标签来标记路由表的特定点,或者如果你勇敢 (或者蠢),直接跳到一个规则变化。规则编号会在服务器启动的时候打印处理,但请用标签。
[uwsgi]
route-host = ^localhost$ goto:localhost
route-host = ^sid\.local$ goto:sid.local
route = .* last:
route-label = sid.local
route-user-agent = .*curl.* redirect:http://uwsgi.it
route-remote-addr = ^192\.168\..* break:403 Forbidden
route = ^/test log:someone called /test
route = \.php$ rewrite:/index.php
route = .* addheader:Server: my sid.local server
route = .* logvar:local=0
route-uri = ^/foo/(.*)\.jpg$ cache:key=$1.jpg
route = .* last:
route-label = localhost
route-user-agent = .*curl.* redirect:http://uwsgi.it
route-remote-addr = ^127\.0\.0\.1$ break:403 Forbidden
route = ^/test log:someone called /test
route = \.php$ rewrite:/index.php
route = .* addheader:Server: my uWSGI server
route = .* logvar:local=1
route-uri = ^/foo/(.*)\.jpg$ cache:key=$1.jpg
route = .* last:这个例子像前面那个,但域之间存在些不同。看看"last:"的时候,来终端路由表扫描。你可以将前2个规则写成一个:
[uwsgi]
route-host = (.*) goto:$1正如我们已经看到的,每个uWSGI请求都有一组相关的变量。它们一般是由web服务器传递的CGI变量,但是你也可以用其他变量来扩展它们 (看看'addvar'动作)。
uWSGI 1.9.16添加了一个新特性,允许你存储一个响应头的内容到一个请求变量中。这让编写更高级的规则变得更简单。
例如,你可能想要gzip所有的text/html响应:
[uwsgi]
; store Content-Type response header in MY_CONTENT_TYPE var
collect-header = Content-Type MY_CONTENT_TYPE
; if response is text/html, and client supports it, gzip it
response-route-if = equal:${MY_CONTENT_TYPE};text/html goto:gzipme
response-route-run = last:
response-route-label = gzipme
; gzip only if the client support it
response-route-if = contains:${HTTP_ACCEPT_ENCODING};gzip gzip:返回值: CONTINUE
停止扫描内部路由表,并继续到选定的请求处理器。
返回值: BREAK
停止扫描内部路由表,并关闭该请求。可以选择返回指定的HTTP状态码:
[uwsgi]
route = ^/notfound break:404 Not Found
route = ^/bad break:
route = ^/error break:500注意: break 并不支持请求变量,因为它旨在通知浏览器有关错误,而不是通知终端用户。也就是说,我们可以判定以下代码将会发送它读取的内容给浏览器 (即 ${REMOTE_ADDR} 并不会被转换成远程IP地址)。
[uwsgi]
route-remote-addr = ^127\.0\.0\.1$ break:403 Forbidden for ip ${REMOTE_ADDR}如果你真的想要做些古古怪怪的东西,那么可以看看 clearheaders 。
返回值: BREAK
return 使用uWSGI内置的状态码并同时返回状态码和消息体。它类似于 break ,但正如上面提到的, break
并没有错误消息体。 return:403 等价于:
[uwsgi]
route-run = clearheaders:403 Forbidden
route-run = addheader:Content-Type: text/plain
route-run = addheader:Content-Length: 9
route-run = send:Forbidden
route-run = break:返回值: NEXT
在日志中打印指定消息。
[uwsgi]
route = ^/logme/(.) log:hey i am printing $1返回值: NEXT
添加指定的日志变量。
[uwsgi]
route = ^/logme/(.) logvar:item=$1返回值: NEXT
向前跳到指定的标签或者规则位置。
返回值: NEXT
添加指定的CGI (环境) 变量到请求中。
[uwsgi]
route = ^/foo/(.) addvar:FOOVAR=prefix$1suffix返回值: NEXT
添加指定的HTTP头到响应中。
[uwsgi]
route = ^/foo/(.) addheader:Foo: Bar返回值: NEXT
把指定的HTTP头从响应中移除。
[uwsgi]
route = ^/foo/(.) delheader:Foo返回值: NEXT
引发指定的uwsgi信号。
返回值: NEXT
非常高级 (且危险) 的函数,允许你添加原始数据到响应中。
[uwsgi]
route = ^/foo/(.) send:destroy the world返回值: NEXT
非常高级 (且危险) 的函数,允许你添加原始数据到响应中,以rn作为后缀。
[uwsgi]
route = ^/foo/(.) send-crnl:HTTP/1.0 100 Continue返回值: BREAK
插件: router_redirect
返回一个HTTP 302 Redirect给指定的URL。
返回值: BREAK
插件: router_redirect
返回一个HTTP 3301 Permanent Redirect给指定的URL。
返回值: NEXT
插件: router_rewrite
一个重写引擎,灵感来自于Apache mod_rewrite。在请求被派发给请求处理函数之前,根据指定的规则重建PATH_INFO和 QUERY_STRING。
[uwsgi]
route-uri = ^/foo/(.*) rewrite:/index.php?page=$1.phprewrite的别名,但是返回值是 CONTINUE, 直接传递请求到请求处理器next。
返回值: BREAK
插件: router_uwsgi
重写一个请求的modifier1, modifier2和可选的 UWSGI_APPID 值,或者路由请求到一个外部的uwsgi服务器。
[uwsgi]
route = ^/psgi uwsgi:127.0.0.1:3031,5,0这个配置路由所有以 /psgi 开始的请求到在127.0.0.1:3031上运行的uwsgi服务器,设置modifier1为5,设置modifier2为0.如果你只想修改modifier1/modifier2,而不想将请求路由到一个外部服务器,那么使用以下语法。
[uwsgi]
route = ^/psgi uwsgi:,5,0要设置一个指定的 UWSGI_APPID 值,在其后附加。
[uwsgi]
route = ^/psgi uwsgi:127.0.0.1:3031,5,0,fooapp子请求是异步型的 (支持诸如gevent或者ugreen这样的引擎),如果offload线程可用,那么将会使用它们。
返回值: BREAK
插件: router_http
将请求路由到外部HTTP服务器。
[uwsgi]
route = ^/zope http:127.0.0.1:8181你可以使用以下语法来替换一个替代的Host头:
[uwsgi]
route = ^/zope http:127.0.0.1:8181,myzope.uwsgi.it返回值: BREAK
插件: router_static
提供来自指定物理路径的静态文件。
[uwsgi]
route = ^/logo static:/var/www/logo.png返回值: NEXT or BREAK 401 on failed authentication
插件: router_basicauth
支持四种语法。
basicauth:realm,user:password– 一个简单的用户名:密码映射basicauth:realm,user:– 只鉴权用户名basicauth:realm,htpasswd– 使用一个类htpasswd文件。支持所有的POSIX crypt()算法。这与Apache传统的htpasswd文件 _不_ 同,因此,使用htpasswd工具的-d标志来创建兼容文件。basicauth:realm,– 用来立即引起一个HTTP 401响应。 由于路由是从上至下解析的,因此,你也许想要引发这个来避免绕过规则。
例如:
[uwsgi]
route = ^/foo basicauth-next:My Realm,foo:bar
route = ^/foo basicauth:My Realm,foo2:bar2
route = ^/bar basicauth:Another Realm,kratos:例如: 对Trac使用basicauth
[uwsgi]
; load plugins (if required)
plugins = python,router_basicauth
; bind to port 9090 using http protocol
http-socket = :9090
; set trac instance path
env = TRAC_ENV=myinstance
; load trac
module = trac.web.main:dispatch_request
; trigger authentication on /login
route = ^/login basicauth-next:Trac Realm,pippo:pluto
route = ^/login basicauth:Trac Realm,foo:bar
;high performance file serving
static-map = /chrome/common=/usr/local/lib/python2.7/dist-packages/trac/htdocs与 basicauth 相同,但在鉴权失败的时候返回 NEXT 。
返回值:鉴权失败的时候返回 NEXT 或者 BREAK 401
插件: ldap
这个鉴权路由器是LDAP插件的一部分,因此为了让这个能用,必须加载它。它就像basicauth路由器,但是使用一个LDAP服务器来进行鉴权,语法: ldapauth:realm,options
可用选项:
url- LDAP服务器URI (必填)binddn- 用于绑定的DN。如果LDAP服务器不允许匿名搜索,那么该选项必填。bindpw-binddn用户的密码basedn- 搜索用户时使用的基本DN (必填)filter- 搜索用户时使用的过滤器 (默认是 "(objectClass=*)")attr- LDAP属性,保存用户登录 (默认是"uid")loglevel- 0 - 不记录任何绑定,1 - 记录鉴权错误,2 - 同时记录成功和失败的绑定
例如:
route = ^/protected ldapauth:LDAP auth realm,url=ldap://ldap.domain.com;basedn=ou=users,dc=domain;binddn=uid=proxy,ou=users,dc=domain;bindpw=password;loglevel=1;filter=(objectClass=posixAccount)与ldapauth相同,但是鉴权失败的时候返回 NEXT 。
返回值: BREAK
插件: router_cache
"rpc"路由指令让你可以直接调用来自路由子系统的uWSGI RPC函数,并且将它们的输出转发到客户端。
[uwsgi]
http-socket = :9090
route = ^/foo addheader:Content-Type: text/html
route = ^/foo rpc:hello ${REQUEST_URI} ${HTTP_USER_AGENT}
route = ^/bar/(.+)$ rpc:test $1 ${REMOTE_ADDR} uWSGI %V
route = ^/pippo/(.+)$ rpc:test@127.0.0.1:4141 $1 ${REMOTE_ADDR} uWSGI %V
import = funcs.py插件: rpc
插件: rpc
rpcret 调用指定的rpc函数,并将其返回值当成动作返回码 (next, continue, goto, 等等)
插件: rpc
rpcnext/rpcblob 调用指定的RPC函数,发送响应到客户端,并继续下一条规则。
插件: rpc
插件: rpc
调用指定的rpc函数,并将其返回值赋给指定的CGI环境变量。
开发中……
开发中……
.. seealso:: :doc:`XSLT`
.. seealso:: :doc:`SSI`
.. seealso:: :doc:`GridFS`
更新 REQUEST_URI
插件: cgi
插件: cgi
插件: router_access
插件: router_cache
插件: router_cache
插件: router_cache
插件: router_cache
插件: router_cache
插件: router_cache
插件: router_http
插件: router_memcached
插件: router_memcached
插件: router_memcached
插件: router_memcached
插件: router_redis
插件: router_redis
插件: router_redis
插件: router_redis
插件: router_uwsgi
为当前请求设置harakiri。
在 不 使用加速(sendfile, offloading等等)的情况下直接转移指定的文件名。
[uwsgi]
http-socket = :9090
route-run = file:filename=/var/www/${PATH_INFO}清除响应头,设置一个新的HTTP状态码,可用于重置响应
[uwsgi]
http-socket = :9090
response-route = ^/foo goto:foobar
response-route-run = last:
response-route-label = foobar
response-route-run = clearheaders:404 Not Found
response-route-run = addheader:Content-Type: text/htmlclearheaders的别名
插件: xattr
xattr插件允许你在内部路由子系统中引用文件扩展属性:
[uwsgi]
...
route-run = addvar:MYATTR=user.uwsgi.foo.bar
route-run = log:The attribute is ${xattr[/tmp/foo:MYATTR]}或者 (带2个变量的变体)
[uwsgi]
...
route-run = addvar:MYFILE=/tmp/foo
route-run = addvar:MYATTR=user.uwsgi.foo.bar
route-run = log:The attribute is ${xattr2[MYFILE:MYATTR]}仅在Linux平台上可用。