-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Http
云风 edited this page Aug 4, 2014
·
13 revisions
skynet 从 v0.5.0 开始提供了简单的 http 服务器的支持。skynet.httpd
是一个独立于 skynet 的,用于 http 协议解析的库,它本身依赖 socket api 的注入。使用它,你需要把读写 socket 的 API 封装好,注入到里面就可以工作。
skynet.sockethelper
模块将 skynet 的 Socket API 封装成 skynet.httpd
可以接受的形式:阻塞读写指定的字节数、网络错误以异常形式抛出。
下面是一个简单的范例:
-- examples/simpleweb.lua
local skynet = require "skynet"
local socket = require "socket"
local httpd = require "http.httpd"
local sockethelper = require "http.sockethelper"
local urllib = require "http.url"
local table = table
local string = string
local mode = ...
if mode == "agent" then
local function response(id, ...)
local ok, err = httpd.write_response(sockethelper.writefunc(id), ...)
if not ok then
-- if err == sockethelper.socket_error , that means socket closed.
skynet.error(string.format("fd = %d, %s", id, err))
end
end
skynet.start(function()
skynet.dispatch("lua", function (_,_,id)
socket.start(id) -- 开始接收一个 socket
-- limit request body size to 8192 (you can pass nil to unlimit)
-- 一般的业务不需要处理大量上行数据,为了防止攻击,做了一个 8K 限制。这个限制可以去掉。
local code, url, method, header, body = httpd.read_request(sockethelper.readfunc(id), 8192)
if code then
if code ~= 200 then -- 如果协议解析有问题,就回应一个错误码 code 。
response(id, code)
else
-- 这是一个示范的回应过程,你可以根据你的实际需要,解析 url, method 和 header 做出回应。
local tmp = {}
if header.host then
table.insert(tmp, string.format("host: %s", header.host))
end
local path, query = urllib.parse(url)
table.insert(tmp, string.format("path: %s", path))
if query then
local q = urllib.parse_query(query)
for k, v in pairs(q) do
table.insert(tmp, string.format("query: %s= %s", k,v))
end
end
response(id, code, table.concat(tmp,"\n"))
end
else
-- 如果抛出的异常是 sockethelper.socket_error 表示是客户端网络断开了。
if url == sockethelper.socket_error then
skynet.error("socket closed")
else
skynet.error(url)
end
end
socket.close(id)
end)
end)
else
skynet.start(function()
local agent = {}
for i= 1, 20 do
-- 启动 20 个代理服务用于处理 http 请求
agent[i] = skynet.newservice(SERVICE_NAME, "agent")
end
local balance = 1
-- 监听一个 web 端口
local id = socket.listen("0.0.0.0", 8001)
socket.start(id , function(id, addr)
-- 当一个 http 请求到达的时候, 把 socket id 分发到事先准备好的代理中去处理。
skynet.error(string.format("%s connected, pass it to agent :%08x", addr, agent[balance]))
skynet.send(agent[balance], "lua", id)
balance = balance + 1
if balance > #agent then
balance = 1
end
end)
end)
end