Skip to content

Instantly share code, notes, and snippets.

@suzumura-ss
Last active December 22, 2015 16:47
Show Gist options
  • Save suzumura-ss/2490ecc7a5edffd596fd to your computer and use it in GitHub Desktop.
Save suzumura-ss/2490ecc7a5edffd596fd to your computer and use it in GitHub Desktop.
nginx-lua: upload to aws-s3
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