Last active
December 22, 2015 05:17
-
-
Save suzumura-ss/1dc0860820c975e859a2 to your computer and use it in GitHub Desktop.
nginx-lua: upload request-body to other service without LuaSocket
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
local function header_join_with_crlf(header) | |
local str = "", k, v | |
for k,v in pairs(header) do | |
str = str .. v .. "\r\n" | |
end | |
return str | |
end | |
-- @return client, err | |
local function connect(ngxctx, host, port, path) | |
local ok, err | |
local obj = {} | |
obj.clientsock, err = ngxctx.req.socket() | |
if err then | |
return nil, 'failed to get sock - ' .. err | |
end | |
obj.serversock = ngxctx.socket.tcp() | |
ok, err = obj.serversock:connect(host, port) | |
if not ok then | |
return nil, 'failed to connect: ' .. host .. ':' .. port .. ' - ' .. err | |
end | |
obj.path = path | |
obj.send_header = function(self, m) | |
local method = m or ngxctx.req.get_method() | |
local headers = ngxctx.req.get_headers() | |
local reqtype = headers['content-type'] or 'application/json' | |
local reqsize = headers['content-length'] or '0' | |
local header = {method.." "..path.." HTTP/1.0", "Content-Type: "..reqtype, "Content-Length: "..reqsize, ""} | |
local bytes, err = self.serversock:send(header_join_with_crlf(header)) | |
if err then | |
self.err = 'failed to send header - ' .. err | |
return nil | |
end | |
self.reqsize = reqsize | |
return true | |
end | |
obj.send_body = function(self) | |
local size = tonumber(self.reqsize) | |
while size>0 do | |
local rsize = 2048 | |
if size<rsize then rsize=size end | |
local data, err, partial = self.clientsock:receive(rsize) | |
if err then | |
self.err = 'failed to receive request body - ' .. err | |
return nil | |
end | |
if not chunk then chunk = partial end | |
local bytes, err = self.serversock:send(data) | |
if err then | |
self.err = 'failed to send request body - ' .. err | |
return nil | |
end | |
size = size - rsize | |
end | |
return true | |
end | |
obj.recv_header = function(self) | |
self.content_length = nil | |
while true do | |
local data, err, partial = self.serversock:receive() | |
if err then | |
self.err = 'failed to receive response - ' .. err | |
return nil | |
end | |
if not data then data = partial end | |
local m, err = ngxctx.re.match(data, "HTTP/1.[01] ([0-9]+)", "i") | |
if m then ngxctx.status = tonumber(m[1]) end | |
m, err = ngxctx.re.match(data, "content-type: (.+)", "i") | |
if m then ngxctx.header.content_type = m[1] end | |
m, err = ngxctx.re.match(data, "content-length: ([0-9]+)", "i") | |
if m then self.content_length = tonumber(m[1]) end | |
if data=="" then break end | |
end | |
return true | |
end | |
obj.recv_body = function(self) | |
if self.content_length then | |
local data, err, partial = self.serversock:receive(self.content_length) | |
if err then | |
self.err = 'failed to receive response - ' .. err | |
return nil | |
end | |
if not data then data = partial end | |
ngxctx.header.content_length = #data | |
ngxctx.say(data) | |
end | |
return true | |
end | |
obj.request = function(self, m) | |
local ok = self:send_header(m) and self:send_body() and self:recv_header() and self:recv_body() | |
self.serversock:close() | |
return ok | |
end | |
return obj | |
end | |
-- upload | |
local client, err = connect(ngx, '127.0.0.1', 80, '/teapot') | |
if err then | |
ngx.log(ngx.ERR, err) | |
return ngx.exit(500) | |
end | |
if not client:request('PUT') then | |
ngx.log(ngx.ERR, client.err) | |
return ngx.exit(500) | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment