1-- 2-- Licensed to the Apache Software Foundation (ASF) under one 3-- or more contributor license agreements. See the NOTICE file 4-- distributed with this work for additional information 5-- regarding copyright ownership. The ASF licenses this file 6-- to you under the Apache License, Version 2.0 (the 7-- "License"); you may not use this file except in compliance 8-- with the License. You may obtain a copy of the License at 9-- 10-- http://www.apache.org/licenses/LICENSE-2.0 11-- 12-- Unless required by applicable law or agreed to in writing, 13-- software distributed under the License is distributed on an 14-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15-- KIND, either express or implied. See the License for the 16-- specific language governing permissions and limitations 17-- under the License. 18-- 19 20require 'TTransport' 21 22THttpTransport = TTransportBase:new{ 23 __type = 'THttpTransport', 24 path = '/', 25 wBuf = '', 26 rBuf = '', 27 CRLF = '\r\n', 28 VERSION = version, 29 isServer = true 30} 31 32function THttpTransport:new(obj) 33 if ttype(obj) ~= 'table' then 34 error(ttype(self) .. 'must be initialized with a table') 35 end 36 37 -- Ensure a transport is provided 38 if not obj.trans then 39 error('You must provide ' .. ttype(self) .. ' with a trans') 40 end 41 42 return TTransportBase.new(self, obj) 43end 44 45function THttpTransport:isOpen() 46 return self.trans:isOpen() 47end 48 49function THttpTransport:open() 50 return self.trans:open() 51end 52 53function THttpTransport:close() 54 return self.trans:close() 55end 56 57function THttpTransport:readAll(len) 58 return self:read(len) 59end 60 61function THttpTransport:read(len) 62 if string.len(self.rBuf) == 0 then 63 self:_readMsg() 64 end 65 if len > string.len(self.rBuf) then 66 local val = self.rBuf 67 self.rBuf = '' 68 return val 69 end 70 71 local val = string.sub(self.rBuf, 0, len) 72 self.rBuf = string.sub(self.rBuf, len+1) 73 return val 74end 75 76function THttpTransport:_readMsg() 77 while true do 78 self.rBuf = self.rBuf .. self.trans:read(4) 79 if string.find(self.rBuf, self.CRLF .. self.CRLF) then 80 break 81 end 82 end 83 if not self.rBuf then 84 self.rBuf = "" 85 return 86 end 87 self:getLine() 88 local headers = self:_parseHeaders() 89 if not headers then 90 self.rBuf = "" 91 return 92 end 93 94 local length = tonumber(headers["Content-Length"]) 95 if length then 96 length = length - string.len(self.rBuf) 97 self.rBuf = self.rBuf .. self.trans:readAll(length) 98 end 99 if self.rBuf == nil then 100 self.rBuf = "" 101 end 102end 103 104function THttpTransport:getLine() 105 local a,b = string.find(self.rBuf, self.CRLF) 106 local line = "" 107 if a and b then 108 line = string.sub(self.rBuf, 0, a-1) 109 self.rBuf = string.sub(self.rBuf, b+1) 110 end 111 return line 112end 113 114function THttpTransport:_parseHeaders() 115 local headers = {} 116 117 repeat 118 local line = self:getLine() 119 for key, val in string.gmatch(line, "([%w%-]+)%s*:%s*(.+)") do 120 if headers[key] then 121 local delimiter = ", " 122 if key == "Set-Cookie" then 123 delimiter = "; " 124 end 125 headers[key] = headers[key] .. delimiter .. tostring(val) 126 else 127 headers[key] = tostring(val) 128 end 129 end 130 until string.find(line, "^%s*$") 131 132 return headers 133end 134 135function THttpTransport:write(buf, len) 136 if len and len < string.len(buf) then 137 buf = string.sub(buf, 0, len) 138 end 139 self.wBuf = self.wBuf .. buf 140end 141 142function THttpTransport:writeHttpHeader(content_len) 143 if self.isServer then 144 local header = "HTTP/1.1 200 OK" .. self.CRLF 145 .. "Server: Thrift/" .. self.VERSION .. self.CRLF 146 .. "Access-Control-Allow-Origin: *" .. self.CRLF 147 .. "Content-Type: application/x-thrift" .. self.CRLF 148 .. "Content-Length: " .. content_len .. self.CRLF 149 .. "Connection: Keep-Alive" .. self.CRLF .. self.CRLF 150 self.trans:write(header) 151 else 152 local header = "POST " .. self.path .. " HTTP/1.1" .. self.CRLF 153 .. "Host: " .. self.trans.host .. self.CRLF 154 .. "Content-Type: application/x-thrift" .. self.CRLF 155 .. "Content-Length: " .. content_len .. self.CRLF 156 .. "Accept: application/x-thrift " .. self.CRLF 157 .. "User-Agent: Thrift/" .. self.VERSION .. " (Lua/THttpClient)" 158 .. self.CRLF .. self.CRLF 159 self.trans:write(header) 160 end 161end 162 163function THttpTransport:flush() 164 -- If the write fails we still want wBuf to be clear 165 local tmp = self.wBuf 166 self.wBuf = '' 167 self:writeHttpHeader(string.len(tmp)) 168 self.trans:write(tmp) 169 self.trans:flush() 170end 171 172THttpTransportFactory = TTransportFactoryBase:new{ 173 __type = 'THttpTransportFactory' 174} 175function THttpTransportFactory:getTransport(trans) 176 if not trans then 177 terror(TProtocolException:new{ 178 message = 'Must supply a transport to ' .. ttype(self) 179 }) 180 end 181 return THttpTransport:new{trans = trans} 182end 183