1 Star 0 Fork 216

张工/iRTU

forked from 稀饭放姜/iRTU 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
httpv2.lua 7.48 KB
一键复制 编辑 原始数据 按行查看 历史
--- 模块功能:HTTP客户端
-- @module httpv2
-- @author 稀饭放姜
-- @license MIT
-- @copyright OpenLuat.com
-- @release 2017.10.23
require 'socket'
require 'utils'
module(..., package.seeall)
local Content_type = {'application/x-www-form-urlencoded', 'application/json', 'application/octet-stream'}
-- 处理表的url编码
function urlencodeTab(params)
local msg = {}
for k, v in pairs(params) do
table.insert(msg, string.urlEncode(k) .. '=' .. string.urlEncode(v))
table.insert(msg, '&')
end
table.remove(msg)
return table.concat(msg)
end
--- HTTP客户端
-- @string method,提交方式"GET" or "POST"
-- @string url,HTTP请求超链接
-- @number timeout,超时时间
-- @param params,table类型,请求发送的查询字符串,通常为键值对表
-- @param data,table类型,正文提交的body,通常为键值对、json或文件对象类似的表
-- @number ctype,Content-Type的类型(可选1,2,3),默认1:"urlencode",2:"json",3:"octet-stream"
-- @string basic,HTTP客户端的authorization basic验证的"username:password"
-- @param headers,table类型,HTTP headers部分
-- @param cert,table类型,此参数可选,默认值为: nil,ssl连接需要的证书配置,只有ssl参数为true时,才参数才有意义,cert格式如下:
-- {
-- caCert = "ca.crt", --CA证书文件(Base64编码 X.509格式),如果存在此参数,则表示客户端会对服务器的证书进行校验;不存在则不校验
-- clientCert = "client.crt", --客户端证书文件(Base64编码 X.509格式),服务器对客户端的证书进行校验时会用到此参数
-- clientKey = "client.key", --客户端私钥文件(Base64编码 X.509格式) clientPassword = "123456", --客户端证书文件密码[可选]
-- }
-- @return string,table,string,正常返回response_code, response_header, response_body
-- @return string,string,错误返回 response_code, error_message
-- @usage local c, h, b = httpv2.request(url, method, headers, body)
-- @usage local r, e = httpv2.request("http://wrong.url/ ")
function request(method, url, timeout, params, data, ctype, basic, head, cert, fnc)
local response_header, response_code, response_body = {}
local _, idx, offset, ssl, auth, https, host, port, path
local headers = {
['User-Agent'] = 'Mozilla/4.0',
['Accept'] = '*/*',
['Accept-Language'] = 'zh-CN,zh,cn',
['Content-Type'] = 'application/x-www-form-urlencoded',
['Connection'] = 'close',
}
if type(head) == "string" then
log.info("user header:", basic, head)
local tmp = {}
for k, v in string.gmatch(head, "(.-):%s*(.-)\r\n") do tmp[k] = v end
-- headers = tmp
table.merge(headers, tmp)
elseif type(head) == "table" then
table.merge(headers, head)
end
-- 处理url的协议头和鉴权
_, offset, https = url:find("^(%a+)://")
_, idx, auth = url:find("(.-:.-)@", (offset or 0) + 1)
offset = idx or offset
-- 对host:port整形
if url:match("^[^/]+:(%d+)", (offset or 0) + 1) then
_, offset, host, port = url:find("^([^/]+):(%d+)", (offset or 0) + 1)
elseif url:find("(.-)/", (offset or 0) + 1) then
_, offset, host = url:find("(.-)/", (offset or 0) + 1)
offset = offset - 1
else
offset, host = #url, url:sub((offset or 0) + 1, -1)
end
if not host then return '105', 'ERR_NAME_NOT_RESOLVED' end
if not headers.Host then headers["Host"] = host end
port = port or (https == "https" and 443 or 80)
path = url:sub(offset + 1, -1)
path = path == "" and "/" or path
-- 处理查询字符串
if params then path = path .. '?' .. (type(params) == 'table' and urlencodeTab(params) or params) end
-- 处理HTTP协议body部分的数据
ctype = ctype or 2
headers['Content-Type'] = Content_type[ctype]
if ctype == 1 and type(data) == 'table' then
data = urlencodeTab(data)
headers['Content-Length'] = #data or 0
elseif ctype == 2 and data ~= nil then
data = type(data) == 'string' and data or (type(data) == 'table' and json.encode(data)) or ""
headers['Content-Length'] = #data or 0
elseif ctype == 3 and type(data) == 'string' then
headers['Content-Length'] = io.fileSize(data) or 0
elseif data and type(data) == "string" then
headers['Content-Length'] = #data or 0
end
-- 处理HTTP Basic Authorization 验证
if auth then
headers['Authorization'] = 'Basic ' .. crypto.base64_encode(auth, #auth)
elseif type(basic) == 'string' and basic ~= "" then
headers['Authorization'] = 'Basic ' .. crypto.base64_encode(basic, #basic)
end
-- 处理headers部分
local str = ""
for k, v in pairs(headers) do str = str .. k .. ": " .. v .. "\r\n" end
-- 发送请求报文
while not socket.isReady() do sys.wait(1000) end
local c = socket.tcp(https == "https", cert)
if not c:connect(host, port) then
c:close()
return '502', 'SOCKET_CONN_ERROR'
end
if ctype ~= 3 then
str = method .. ' ' .. path .. ' HTTP/1.0\r\n' .. str .. '\r\n' .. (data and data .. "\r\n" or "")
log.info("发送的http报文:", str)
if not c:send(str) then
c:close()
return '426', 'SOCKET_SEND_ERROR'
end
else
str = method .. ' ' .. path .. ' HTTP/1.0\r\n' .. str .. '\r\n'
log.info("发送的http报文:", str)
if not c:send(str) then
c:close()
return '426', 'SOCKET_SEND_ERROR'
end
local file = io.open(data, 'r')
if file then
while true do
local dat = file:read(8192)
if dat == nil then
io.close(file)
break
end
if not c:send(dat) then
io.close(file)
c:close()
return '426', 'SOCKET_SEND_ERROR'
end
end
end
if not c:send('\r\n') then
c:close()
return '426', 'SOCKET_SEND_ERROR'
end
end
------------------------------------ 接收服务器返回消息部分 ------------------------------------
local msg, str = {}, ""
local r, s = c:recv(timeout)
if not r then
c:close()
return '503', 'SOCKET_RECV_TIMOUT'
end
-- 处理状态代码
_, idx, response_code = s:find("%s(%d+)%s.-\r\n")
_, offset = s:find('\r\n\r\n')
if not idx or not offset then return '501', 'SERVER_NOT_RESPONSE' end
log.info('httpv2.response code:', response_code)
-- 处理headers代码
for k, v in string.gmatch(s:sub(idx + 1, offset), "(.-):%s*(.-)\r\n") do response_header[k] = v end
local len = response_header["Content-Range"] and tonumber(response_header["Content-Range"]:match("/(%d+)")) or tonumber(response_header["Content-Length"]) or 2147483648
s = s:sub((offset or 0) + 1, -1)
local cnt = #s
if tonumber(response_code) == 200 or tonumber(response_code) == 206 then
-- 处理body
while true do
if type(fnc) == "function" then
fnc(s, len)
else
table.insert(msg, s)
end
if cnt >= len then break end
r, s = c:recv(timeout)
if not r then break end
cnt = cnt + #s
end
s = table.concat(msg) or ""
end
c:close()
local gzip = response_header["Content-Encoding"] == "gzip"
return response_code, response_header, gzip and ((zlib.inflate(s)):read()) or s
end
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Lua
1
https://gitee.com/qiang3698_admin/iRTU.git
[email protected]:qiang3698_admin/iRTU.git
qiang3698_admin
iRTU
iRTU
master

搜索帮助