-- -- Licensed to the Apache Software Foundation (ASF) under one -- or more contributor license agreements. See the NOTICE file -- distributed with this work for additional information -- regarding copyright ownership. The ASF licenses this file -- to you under the Apache License, Version 2.0 (the -- "License"); you may not use this file except in compliance -- with the License. You may obtain a copy of the License at -- -- http://www.apache.org/licenses/LICENSE-2.0 -- -- Unless required by applicable law or agreed to in writing, -- software distributed under the License is distributed on an -- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -- KIND, either express or implied. See the License for the -- specific language governing permissions and limitations -- under the License. -- require 'TTransport' THttpTransport = TTransportBase:new{ __type = 'THttpTransport', path = '/', wBuf = '', rBuf = '', CRLF = '\r\n', VERSION = version, isServer = true } function THttpTransport:new(obj) if ttype(obj) ~= 'table' then error(ttype(self) .. 'must be initialized with a table') end -- Ensure a transport is provided if not obj.trans then error('You must provide ' .. ttype(self) .. ' with a trans') end return TTransportBase.new(self, obj) end function THttpTransport:isOpen() return self.trans:isOpen() end function THttpTransport:open() return self.trans:open() end function THttpTransport:close() return self.trans:close() end function THttpTransport:readAll(len) return self:read(len) end function THttpTransport:read(len) if string.len(self.rBuf) == 0 then self:_readMsg() end if len > string.len(self.rBuf) then local val = self.rBuf self.rBuf = '' return val end local val = string.sub(self.rBuf, 0, len) self.rBuf = string.sub(self.rBuf, len+1) return val end function THttpTransport:_readMsg() while true do self.rBuf = self.rBuf .. self.trans:read(4) if string.find(self.rBuf, self.CRLF .. self.CRLF) then break end end if not self.rBuf then self.rBuf = "" return end self:getLine() local headers = self:_parseHeaders() if not headers then self.rBuf = "" return end local length = tonumber(headers["Content-Length"]) if length then length = length - string.len(self.rBuf) self.rBuf = self.rBuf .. self.trans:readAll(length) end if self.rBuf == nil then self.rBuf = "" end end function THttpTransport:getLine() local a,b = string.find(self.rBuf, self.CRLF) local line = "" if a and b then line = string.sub(self.rBuf, 0, a-1) self.rBuf = string.sub(self.rBuf, b+1) end return line end function THttpTransport:_parseHeaders() local headers = {} repeat local line = self:getLine() for key, val in string.gmatch(line, "([%w%-]+)%s*:%s*(.+)") do if headers[key] then local delimiter = ", " if key == "Set-Cookie" then delimiter = "; " end headers[key] = headers[key] .. delimiter .. tostring(val) else headers[key] = tostring(val) end end until string.find(line, "^%s*$") return headers end function THttpTransport:write(buf, len) if len and len < string.len(buf) then buf = string.sub(buf, 0, len) end self.wBuf = self.wBuf .. buf end function THttpTransport:writeHttpHeader(content_len) if self.isServer then local header = "HTTP/1.1 200 OK" .. self.CRLF .. "Server: Thrift/" .. self.VERSION .. self.CRLF .. "Access-Control-Allow-Origin: *" .. self.CRLF .. "Content-Type: application/x-thrift" .. self.CRLF .. "Content-Length: " .. content_len .. self.CRLF .. "Connection: Keep-Alive" .. self.CRLF .. self.CRLF self.trans:write(header) else local header = "POST " .. self.path .. " HTTP/1.1" .. self.CRLF .. "Host: " .. self.trans.host .. self.CRLF .. "Content-Type: application/x-thrift" .. self.CRLF .. "Content-Length: " .. content_len .. self.CRLF .. "Accept: application/x-thrift " .. self.CRLF .. "User-Agent: Thrift/" .. self.VERSION .. " (Lua/THttpClient)" .. self.CRLF .. self.CRLF self.trans:write(header) end end function THttpTransport:flush() -- If the write fails we still want wBuf to be clear local tmp = self.wBuf self.wBuf = '' self:writeHttpHeader(string.len(tmp)) self.trans:write(tmp) self.trans:flush() end THttpTransportFactory = TTransportFactoryBase:new{ __type = 'THttpTransportFactory' } function THttpTransportFactory:getTransport(trans) if not trans then terror(TProtocolException:new{ message = 'Must supply a transport to ' .. ttype(self) }) end return THttpTransport:new{trans = trans} end