Eurasia/标准web服务器

来自站长百科
跳转至: 导航、​ 搜索

模板:Eurasia top 框架的关键应用之一便是web 。首先通过 httpserver(addr, handler) 创建标准 http 服务器

地址格式[ ]

接口 httpserver 允许多种形式的 addr 参数。

httpserver('127.0.0.1:8080', handler)  # IPv4 地址,端口 8080
httpserver('[::1]:8080', handler)  # IPv6 地址
httpserver(':8080', handler)  # 本机 8080 端口
httpserver(('127.0.0.1', 8080), handler)  # IPv4
httpserver(('::1', 8080), handler)  # IPv6

# 同时指定地址及 family:
from socket import AF_INET6, AF_UNIX
from eurasia.web import httpserver
httpserver((('::1', 8080), AF_INET6), handler)
httpserver(('/var/httpd.sock', AF_UNIX), handler)  # unix socket

# 绑定到 fileno 为 0 的资源描述符,默认 family 是 AF_INET:
httpserver(0, handler)

# 绑定到 fileno 为 0 的描述符,指定 family 为 AF_INET6:
from socket import AF_INET6
from eurasia.web import httpserver
httpserver((AF_INET6, 0), handler)

启动和暂停服务器[ ]

通过 httpserver.start() 和 httpserver.stop() 启动和暂停服务器,暂停的服务器可以通过 httpserver.start() 重新启动。

from eurasia.web import httpserver, mianloop

# 工作服务器,绑定到 8080
def handler(httpfile):
    httpfile.write('hello world!')
    httpfile.close()

httpd = httpserver(':8080', handler)
httpd.start()

# 管理服务器,绑定到 8090
def manager(httpfile):
    # 如果请求地址是 /start 则启动工作服务器
    if httpfile.path_info == '/start':
        httpd.start()
   # 如果请求地址是 /pause 则停止工作服务器
    elif httpfile.path_info == '/pause':
        httpd.stop()

man = httpserver('8090', manager)
man.start()
mainloop() # 开始调度

注意区分 httpserver.start() 和 mainloop():

  • mainloop() 是整个程序的调度器
  • httpserver.start() 是启动服务器(开始监听)
  • httpserver 可以有多个而 mainloop() 只有一个

CGI规范适配[ ]

httpfile 对象是服务器关键接口,其设计在很大程度上与 CGI/1.1 规范适配,以下是一些对应关系:

Eursia CGI 1.jpg

可以通过 httpfile.environ[envname] 获取的环境变量:

Eursia CGI 2.jpg

也可以使用 httpfile.request_uri、httpfile.path_info、httpfile.query_string 操作环境变量

# 操作 httpfile.request_uri 会同时影响到 PATH_INFO 和 QUERY_STRING
httpfile.request_uri = '/login?username=tom&passwd=***'
print httpfile.path_info, httpfile.query_string  # 分别是 "/login" 和 "username=tom&passwd=***"
print httpfile.environ['PATH_INFO'], httpfile.environ['QUERY_STRING']  # 同上

# 操作 httpfile.path_info 会影响到 REQUEST_URI
httpfile.path_info = '/check'
print httpfile.request_uri  # uri 变成 "/check?username=tom&passwd=***",query_string 不变
print httpfile.environ['REQUEST_URI']  # 同上

# 操作 httpfile.query_string 会影响到 REQUEST_URI
httpfile.query_string = 'username=jerry&passwd=***'
print httpfile.request_uri  # uri 变成 "/check?username=jerry&passwd=***",path_info 不变
  • 修改 httpfile.request_uri、httpfile.path_info、httpfile.query_string 时,这三个值会联动
  • 设置 httpfile.environ 这三个值不会发生联动,一般避免通过 httpfile.environ 直接操作这三个变量

解读请求[ ]

通过 httpfile.read(size=-1, timeout=-1) 和 httpfile.readline(size=-1, timeout=-1) 读取报文体。

eurasia.cgietc 模块提供了解析 POST 形式表单的工具 form(httpfile, max_size=1048576, timeout=-1):

  • 需要指定一个 httpfile 对象
  • 使用 max_size 限定允许用户提交表单的最大字节数,超过限制会抛出 ValueError 并立即杀死客户端,默认最多传输 1M 数据
  • 使用 timeout 指定读取表单的超时时间
  • 使用 dict 返回解析的表单
    • 正常情况下每个表单项的取值都是 str
    • 如果提交了多个同名的表单项,那么该表单项取值就是一个 list,在其中保存多个 str 值
# -*- conding: utf-8 -*-
from eurasia.cgietc import form
from eurasia.web import httpserver, mainloop

# 返回表单内容
def handler(httpfile):
    form1 = form(httpfile)
    # 假定提交的表单:a=hello&b=world&c=1&c=2&c=3
    # 则输出 {'a': 'hello', 'b': 'world', 'c': ['1', '2', '3']}
    httpfile.write(repr(form1))
    httpfile.close()
  • form() 会同时处理 QUERY_STRING 和 POST 报文体
  • 注意,不能处理 multipart 报文,比如文件上传
  • 注意,httpfile.read() / httpfile.readline() 不能与 form() 混用,会导内容混乱
  • form() 处理完请求以后将结果以新的 dict 对象返回,不增加 httpfile 的引用

完成响应[ ]

我们提供了接口 httpfile.start_response(status, response_headers, timeout=-1) 用来简化 httpfile[...]和 httpfile.status 的操作:

def handler(httpfile):
    response_status = '200 OK'
    response_headers = [('Content-Type', 'text/html')]
    httpfile.start_response(response_status, response_headers)
    httpfile.sendall('hello world!')
    httpfile.close()
  • httpfile.start_response() 发送指定的 status 和 headers 报文头,这和 wsgi 规范(pep333)中 start_response() 接口的定义比较接近。
  • 通过 httpfile.start_response() 完成报文头部以后,就可以通过 httpfile.sendall(data, timeout=-1) 发送报文体了。
  • httpfile.write() 与 httpfile.sendall() 的功能相当接近,都用于发送报文体,他们的区别是:
  • httpfile.write() 会判断头部如果没有发送,会首先发送 httpfile.status 和 httpfile[...] 的头部设置,再发送报文
  • httpfile.sendall() 不会理会头部是否已发送,而直接发送报文,在没有调用 httpfile.start_response() 的情况下这会导致严重问题
  • 一般情况下,httpfile.status、httpfile[...] 与 httpfile.write() 配合使用;httpfile.start_response() 与 httpfile.sendall() 是一对
  • 需要注意 httpfile.start_response() 会覆盖 httpfile[...] 和 htttpfile.status 的设定
  • 严格说 httpfile.start_response() 的头部内容会追加在 httpfile[...] 的设定之后

wsgiserver[ ]

使用 wsgiserver(addr, app) 创建标准的 wsgi 服务器。

from eurasia.web import wsgiserver, mainloop
def app(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/plain')])
    return ['hello world!']

httpd = wsgiserver(':8080', app)
httpd.start()
mainloop()
  • wsgiserver 使用和 httpserver 相同的地址描述格式
  • wsgiserver 中应使用框架自带的文件 IO 接口

或者使用 wsgi(app) 将 app 转换为普通的 http handler。

from eurasia.web import httpserver, wsgi, mainloop
def app(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/plain')])
    return ['hello world!']

def handler(httpfile):
    wsgihandler = wsgi(app)
    wsgihandler(httpfile)

httpd = httpserver(':8080', handler)
httpd.start()
mainloop()

参考来源[ ]

http://code.google.com/p/eurasia/wiki/eurasia_3_1_userguide

模板:Eurasia