Last active
December 22, 2015 16:47
-
-
Save suzumura-ss/2490ecc7a5edffd596fd to your computer and use it in GitHub Desktop.
nginx-lua: upload to aws-s3
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 signature(ngxctx, method, bucket, objectkey) | |
local access_key = os.getenv('AWS_ACCESS_KEY_ID') | |
local secret_key = os.getenv('AWS_SECRET_ACCESS_KEY') | |
local date = ngxctx.http_time(ngxctx.time()) | |
local headers = ngxctx.req.get_headers() | |
local s2s = method .. "\n" | |
s2s = s2s .. (headers['content-md5'] or "") .. "\n" | |
s2s = s2s .. (headers['content-type'] or "") .. "\n" | |
s2s = s2s .. date .. "\n" | |
s2s = s2s .. "/" .. bucket .. "/" .. objectkey | |
local auth = 'AWS '..access_key..':'..ngxctx.encode_base64(ngxctx.hmac_sha1(secret_key, s2s)) | |
return auth, date | |
end | |
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, bucket, objectkey) | |
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, 80) | |
if not ok then | |
return nil, 'failed to connect: ' .. host..' - ' .. err | |
end | |
local path = bucket.."/"..objectkey | |
obj.send_header = function(self) | |
local headers = ngxctx.req.get_headers() | |
local reqtype = headers['content-type'] or 'application/json' | |
local reqsize = headers['content-length'] or '0' | |
local header = {'PUT /'..path..' HTTP/1.0', 'Content-Type: '..reqtype, 'Content-Length: '..reqsize} | |
local auth , date = signature(ngxctx, 'PUT', bucket, objectkey) | |
table.insert(header, 'Authorization: '..auth) | |
table.insert(header, 'Date: '..date) | |
local bytes, err = self.serversock:send(header_join_with_crlf(header).."\r\n") | |
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) | |
local ok = self:send_header() and self:send_body() and self:recv_header() and self:recv_body() | |
self.serversock:close() | |
return ok | |
end | |
return obj | |
end | |
-- upload | |
local bucket = ngx.var.bucket -- s3 bucket name' | |
local objectkey = ngx.var.objectkey -- s3 object key | |
local endpoint = ngx.var.s3_endpoint -- s3 endpoint. ex: 's3-ap-northeast-1.amazonaws.com' | |
-- access_key = os.getenv('AWS_ACCESS_KEY_ID') | |
-- secret_key = os.getenv('AWS_SECRET_ACCESS_KEY') | |
local client, err = connect(ngx, endpoint, bucket, objectkey) | |
if err then | |
ngx.log(ngx.ERR, err) | |
return ngx.exit(500) | |
end | |
if not client:request() 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