首先介紹下目前市面上比較知名的2種C實(shí)現(xiàn)的http parser :
在cf框架未成形之初! 作者為了測試簡單與方便, 自己用string庫封裝了基礎(chǔ)的http server. 這可以在最初的提交代碼找到.
當(dāng)開發(fā)進(jìn)展到底層代碼封裝完畢后, 所以就想到去開發(fā)者交友網(wǎng)站進(jìn)行咨詢了解.
目前需求為對線性字符串匹配與header頭部start與end poosition定位, 然后創(chuàng)建指定大小table組裝http content.
然后針對性的進(jìn)行數(shù)據(jù)解析, 判斷方法類型、打上數(shù)據(jù)類型標(biāo)簽, 最后返回給使用者.
在明確上述需求后, 經(jīng)過一些簡單調(diào)研與測試便選擇了h2o的http parser實(shí)現(xiàn).
原因:
當(dāng)初簡單對pico與http_parser進(jìn)行過對比, 在不開啟sse4的情況下至少快2倍左右。有興趣可以自行測試.
pico的代碼我放在luaclib內(nèi), 封裝后使用makefile編譯lua可用動態(tài)庫. 默認(rèn)沒開啟sse4, 為了照顧一些同學(xué)機(jī)器不支持sse4指令集的情況, 省卻了改makefile的麻煩 :)
眾所周知, 中間件設(shè)計的目的是為了授權(quán)驗(yàn)證、請求準(zhǔn)入等需求設(shè)計而來的. 在大多數(shù)http server框架中都會增加類似設(shè)計.
cf為此分離除了before函數(shù)專門用來處理httpd service使用, 避免冗余無用的參數(shù)驗(yàn)證等代碼與業(yè)務(wù)邏輯耦合在一起.
同時, 提供了http庫來體現(xiàn)before回調(diào)函數(shù)應(yīng)該如何工作. 在使用者熟練之后可以使用cf很輕松的寫出高性能api service.
下面著重為大家介紹httpd的使用與相關(guān)函數(shù)原型, 有能力的同學(xué)也可以直接閱讀源碼進(jìn)行學(xué)習(xí):
使用之前先導(dǎo)入httpd庫
使用new方法創(chuàng)建一個httpd對象.(這沒什么好說的)
此方法返回一個httpd對象;
timeout方法設(shè)置每個http請求的connection最大保持時間, 默認(rèn)為15秒;
此方法返回值為nil;
server_name方法的參數(shù)是一個字符串類型的參數(shù), 這個方法設(shè)置cf的http response頭部Server字段的值;
此方法返回值為nil;
max_path_size方法設(shè)置最大path路徑長度, 超過number大小的path都會被禁止; 默認(rèn)長度為1024;
此方法返回值為nil;
max_body_size方法設(shè)置最大body長度, 超過number大小的body都會被禁止; 默認(rèn)長度為1024 * 1024;
此方法返回值為nil;
max_header_size方法設(shè)置最大body長度, 超過number大小的header都會被禁止; 默認(rèn)長度為65535;
此方法返回值為nil;
before方法設(shè)置每個請求在被業(yè)務(wù)函數(shù)處理之前, 優(yōu)先被function處理.
此方法需要配合http庫進(jìn)行使用, http可以為此設(shè)計了專門的API來語義化中間件設(shè)計. 提升代碼可讀性.
這個方法一般可用于設(shè)計業(yè)務(wù)中間件、準(zhǔn)入認(rèn)證、行為收集等等; 具體使用方法參見main.lua示例.
local http = require "http"
app:before(function(content)
if content.METHOD == 'GET' then
return http.ok()
end
return http.throw(401, '<h1>未授權(quán)</h1>')
end)
需要注意的是: 只要你注入了funtion, 即使function內(nèi)部什么都不做也會返回401.
此方法返回值為nil;
ws方法為使用者注入http -> websocket類型的路由升級.
注冊此方法后, httpd在檢測到當(dāng)前路由類型為ws類型后將會為檢查請求頭部(header), 錯誤的頭部將會被判斷并返回錯誤碼.
首先被回調(diào)的是注冊ws路由的class ctor方法; 這個方法將會有一個opt參數(shù)(table類型). ws對象將會被注入其中.
具體使用示例, 請參考script/ws.lua
.
use/api方法為route注入路由處理函數(shù), handle即可以是一個函數(shù), 也可以是一個lua table(class).
注意:
如果注入的handle為table:
① 必須使用cf框架內(nèi)置的lua class語法進(jìn)行設(shè)計;
② new方法會傳入http request content(注意: 構(gòu)造函數(shù)異常將會引起路由對應(yīng)的方法無法被正確調(diào)用);
③ get/post/put方法會根據(jù)用戶請求類型(method)被回調(diào)(無參數(shù)), 如未實(shí)現(xiàn)對應(yīng)方法將會有框架返回對應(yīng)狀態(tài)碼;
如果注入的handle為function:
cf會為此function注入當(dāng)前的http request content, 內(nèi)部包含所有請求上下文與參數(shù);
handle返回值必須為一個string類型的值, 否則將可能出現(xiàn)警告或返回空值;
方法返回值為nil
group方法提供了一種批量注冊路由的方式, 為一組同一組路由提供簡單便方便在注冊方法.
第一個參數(shù)type為需要批量注冊的路由類型; 初始化httpd對象后, 使用app.USE
或app.API
進(jìn)行傳值;
第二個參數(shù)prefix為string類型的頭部; 例如:/api
、/admin
;
第三個參數(shù)為一組路由處理函數(shù)或處理類數(shù)組; 類型為: {route = '/login', class = class};
注意: 此方法主要為批量注冊api類型或use類型路由對象準(zhǔn)備, 不可同時注冊不同類型路由, 不可同時注冊websocket路由;
為了模塊化view類型路由與api類型路由(區(qū)分admin/api類型), 提供方便項(xiàng)目管理與使用, 減少潛在口頭的約定;
此方法返回值為nil
listen方法用于告訴httpd對象監(jiān)聽指定端口; 第一個參數(shù)ip暫未被httpd使用, 默認(rèn)監(jiān)聽所有網(wǎng)卡的ip地址與port端口號;
此方法需要在run方法之前被調(diào)用.
此方法返回值為nil
run方法用于開始監(jiān)聽模式.
注意: run方法被調(diào)用后, 寫在run方法之后的代碼將直到程序結(jié)束后都可能(maybe)不會被執(zhí)行.
此方法返回值為nil
一個最簡單的cf web service使用示例:
local httpd = require "httpd"
local app = httpd:new('httpd')
app:use('/view', function(content)
return '<h1>CF Web Service</h1>'
end)
app:api('/api', function(content)
return '{"code":200,"message":"ok"}' -- json string
end)
app:listen("0.0.0.0", 8080)
app:run()
[年/月/日 時:分:秒] - [ip] - [x-real-ip] - [path] - [http code] - [request handle timeline]
args : 支持標(biāo)準(zhǔn)get或者post的參數(shù), 對a[1]=1&a[2]=2將會不會解析為數(shù)組類型; 支持multipart-form傳參;
body : body支持三中類型, multipart-form file/post json/post xml/;
header: 原始header數(shù)組, 不會進(jìn)行header解析;
file : 進(jìn)行multipart-form上傳一個或多個文件時, 才會有這個屬性;
cf會將后兩種數(shù)據(jù)交換格式根據(jù)post content-type類型在content內(nèi)注入類型標(biāo)識; 如:
app:use('/admin', function(content)
if conten.json then
return 'json', print('json')
elseif conten.xml then
return 'xml', print('xml')
end
return '未知類型'
end)
body為```xml```或```json```類型時, 框架僅會判斷類型做好標(biāo)識, 為了不依賴相關(guān)庫所以不會主動解析.
更多建議: