httpd库

httpd库是基于HTTP 1.1协议实现而来, 内置了高性能的http协议解析器与urldecode解析库.

httpd 使用

require("httpd")

使用之前需要先导入httpd库, local httpd = require "httpd"

httpd:new()

使用new方法创建一个httpd对象.(这没什么好说的)

此方法返回一个httpd对象;

httpd:timeout(number)

设置每个连接到最大空闲(idle)连接等待时间, 超过这个数值httpd将主动断开连接. (默认值为:30秒)

此方法返回值为nil;

httpd:server_name(string)

server_name方法的参数是一个字符串类型的参数, 这个方法设置cf的http response头部Server字段的值;

此方法返回值为nil;

httpd:max_path_size(number)

设置Path的最大长度, 超过这个值httpd将会返回414. (默认值为: 1024)

此方法返回值为nil;

httpd:max_body_size(number)

设置Body的最大长度, 超过这个值将会返回413. (默认为 1024 * 1024)

此方法返回值为nil;

httpd:max_header_size(number)

设置headers最大长度, 超过这个值httpd将会返回431. (默认值为: 65535)

此方法返回值为nil;

此方法将会开启use路由的cookie解析, 默认情况下未开启.

此方法返回值为nil;

此方法设置cookie密匙, 设置之前请先开启enable_cookie.(推荐设置一个复杂的值)

此方法返回值为nil;

httpd:before(function)

before方法决定API与USE路由回调在触发之前的行为, 默认情况下允许所有路由通过.

before方法一般用来设置与修改用户验证路由行为(例如头部验证), 这提供了开发者基于before函数设计中间件的机会.

  1. local http = require "httpd.http"
  2. app:before(function(content)
  3. if content.method == 'GET' then
  4. return http.ok()
  5. end
  6. return http.throw(401, '<h1>未授权</h1>')
  7. end)

需要注意的是: 只要你注入了funtion, 即使function内部什么都不做也会返回401.

此方法返回值为nil;

httpd:ws(route, handle)

ws方法为使用者注入http -> websocket类型的路由升级.

注册此方法后, httpd在检测到当前路由类型为ws类型后将会为检查请求头部(header), 错误的头部将会被判断并返回错误码.

首先被回调的是注册ws路由的class ctor方法; 这个方法将会有一个opt参数(table类型). ws对象将会被注入其中.

具体使用示例, 请参考script/ws.lua.

httpd:use(route, handle)

httpd:api(route, handle)

use/api方法为route注入路由处理函数, handle即可以是一个函数, 也可以是一个lua table(class).

注意:

  1. 1. 无关乎handle的类型, handle的返回值必须为string类型作为body返回给客户端;
  2. 2. apicontent-typeApplicaton/json; use则为:text/html;
  3. 3. http库不可在此handle内部使用, 否则会出现异常;

如果注入的handle为table:

  1. 必须使用cf框架内置的lua class语法进行设计;
  2. new方法会传入http request content(注意: 构造函数异常将会引起路由对应的方法无法被正确调用);
  3. get/post/put方法会根据用户请求类型(method)被回调(无参数), 如未实现对应方法将会有框架返回对应状态码;

如果注入的handle为function:

  1. cf会为此function注入当前的http request content, 内部包含所有请求上下文与参数;

handle返回值必须为一个string类型的值, 否则将可能出现警告或返回空值;

方法返回值为nil

httpd:group(type, prefix, handles)

group方法提供了一种批量注册路由的方式, 为一组同一组路由提供简单便方便在注册方法.

第一个参数type为需要批量注册的路由类型; 初始化httpd对象后, 使用app.USEapp.API进行传值;

第二个参数prefix为string类型的头部; 例如:/api/admin;

第三个参数为一组路由处理函数或处理类数组; 类型为: {route = '/login', class = class};

注意: 此方法仅支持批量注册API与USE路由, 不可同时注册不同类型路由;

此方法返回值为nil

httpd:static(folder, ttl)

folder定义了静态文件路由的查找根目录, ttl为静态文件是否有缓存周期.

ttl默认为nil(永不缓存), ttl大于0的时候浏览器再次访问相同的资源时会优先从本地读取.

默认情况下未开启static路由;

httpd:listen(ip, port, backlog)

listen方法用于告诉httpd对象监听指定端口.

第一个参数ip暂未被httpd使用(但是必须设置), 默认监听所有网卡的'0.0.0.0'地址与指定的端口号;

backlog为用户最大连接等待队列, 合理的设置能减少连接被重置的情况(默认值为128).

此方法需要在run方法之前被调用, 且此方法返回值为nil.

httpd:run()

在httpd库所有参数与路由设置完毕之后, 调用run方法开启监听模式.

run方法被调用后, 写在run方法之后的代码直到程序结束后可能(maybe)不会被执行.

此方法返回值为nil

httpd示例:

一个最简单的cf web service使用示例:

  1. local httpd = require "httpd"
  2. local app = httpd:new('httpd')
  3. app:use('/view', function(content)
  4. return '<h1>CF Web Service</h1>'
  5. end)
  6. app:api('/api', function(content)
  7. return '{"code":200,"message":"ok"}' -- json string
  8. end)
  9. app:listen("0.0.0.0", 8080)
  10. app:run()

httpd 日志格式:

日志格式为: [年/月/日 时:分:秒] - [ip] - [x-real-ip] - [path] - [method] - [http code] - [request handle timeline]

httpd中间件设计

中间件是设计模式上的概念, 中间件在本质上定义了软件(框架)层与业务层之间的一系列行为(Access、 Logging等等)

httpd:before就是httpd库为了处理中间件设计模式需要而诞生的方法, 使用者有需要根据实际情况来决定是否使用此方法.

httpd 的content

每个http请求都会在调用before与用户注册的路由时为其传入一个content, 这个Content是客户端请求的所有参数.

args : 支持标准get或者post的参数, 对a[1]=1&a[2]=2将会不会解析为数组类型; 支持multipart/form-data的参数传递方式;

header: 原始header key-value表, 框架层不会进行header进行内容解析. (一般情况下没这个必要);

body : 目前body支持这些类型: multipart/form-dataapplication/x-www-form-urlencodedapplication/jsonapplication/xml;

json/xml: 在body为json类型的时候, content的json属性为true; 在body为xml类型的时候, content的xml属性为true.

file : 当客户端使用multipart/form-data传递数据时将会有这个属性; 这个属性是数组类型;

cf会将后两种数据交换格式根据post content-type类型在content内注入类型标识; 如:

  1. app:use('/admin', function(content)
  2. if conten.json then
  3. return 'json', print('json')
  4. elseif conten.xml then
  5. return 'xml', print('xml')
  6. end
  7. return '未知类型'
  8. end)

注意: body为xmljson类型时, 框架仅会判断类型做好标识, 为了不依赖相关库所以不会主动解析.