blob: 7e24548c1c505bb189b37dea09a3bed74abbe2b9 [file] [log] [blame]
Wang Yaofue432c6b2016-03-09 16:39:03 +08001--
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',
James E. King IIIc3375d92018-12-30 11:06:00 -050028 VERSION = version,
Wang Yaofue432c6b2016-03-09 16:39:03 +080029 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
nighmared96b7f212026-03-03 16:27:06 +010045local function THttpHeaders()
46 local data = {}
47 return setmetatable({}, {
48 __index = function(_, key) return data[string.lower(key)] end,
49 __newindex = function(_, key, value) data[string.lower(key)] = value end,
50 __pairs = function() return pairs(data) end
51 })
52end
53
Wang Yaofue432c6b2016-03-09 16:39:03 +080054function THttpTransport:isOpen()
55 return self.trans:isOpen()
56end
57
58function THttpTransport:open()
59 return self.trans:open()
60end
61
62function THttpTransport:close()
63 return self.trans:close()
64end
65
66function THttpTransport:readAll(len)
67 return self:read(len)
68end
69
70function THttpTransport:read(len)
71 if string.len(self.rBuf) == 0 then
72 self:_readMsg()
73 end
74 if len > string.len(self.rBuf) then
75 local val = self.rBuf
76 self.rBuf = ''
77 return val
78 end
79
80 local val = string.sub(self.rBuf, 0, len)
81 self.rBuf = string.sub(self.rBuf, len+1)
82 return val
83end
84
85function THttpTransport:_readMsg()
86 while true do
87 self.rBuf = self.rBuf .. self.trans:read(4)
88 if string.find(self.rBuf, self.CRLF .. self.CRLF) then
89 break
90 end
91 end
92 if not self.rBuf then
93 self.rBuf = ""
94 return
95 end
96 self:getLine()
97 local headers = self:_parseHeaders()
98 if not headers then
99 self.rBuf = ""
100 return
101 end
102
103 local length = tonumber(headers["Content-Length"])
104 if length then
105 length = length - string.len(self.rBuf)
106 self.rBuf = self.rBuf .. self.trans:readAll(length)
107 end
108 if self.rBuf == nil then
109 self.rBuf = ""
110 end
111end
112
113function THttpTransport:getLine()
114 local a,b = string.find(self.rBuf, self.CRLF)
115 local line = ""
116 if a and b then
117 line = string.sub(self.rBuf, 0, a-1)
118 self.rBuf = string.sub(self.rBuf, b+1)
119 end
120 return line
121end
122
123function THttpTransport:_parseHeaders()
nighmared96b7f212026-03-03 16:27:06 +0100124 local headers = THttpHeaders()
Wang Yaofue432c6b2016-03-09 16:39:03 +0800125
126 repeat
127 local line = self:getLine()
128 for key, val in string.gmatch(line, "([%w%-]+)%s*:%s*(.+)") do
129 if headers[key] then
130 local delimiter = ", "
131 if key == "Set-Cookie" then
132 delimiter = "; "
133 end
134 headers[key] = headers[key] .. delimiter .. tostring(val)
135 else
136 headers[key] = tostring(val)
137 end
138 end
139 until string.find(line, "^%s*$")
140
141 return headers
142end
143
144function THttpTransport:write(buf, len)
145 if len and len < string.len(buf) then
146 buf = string.sub(buf, 0, len)
147 end
148 self.wBuf = self.wBuf .. buf
149end
150
151function THttpTransport:writeHttpHeader(content_len)
152 if self.isServer then
153 local header = "HTTP/1.1 200 OK" .. self.CRLF
154 .. "Server: Thrift/" .. self.VERSION .. self.CRLF
155 .. "Access-Control-Allow-Origin: *" .. self.CRLF
156 .. "Content-Type: application/x-thrift" .. self.CRLF
157 .. "Content-Length: " .. content_len .. self.CRLF
158 .. "Connection: Keep-Alive" .. self.CRLF .. self.CRLF
159 self.trans:write(header)
160 else
161 local header = "POST " .. self.path .. " HTTP/1.1" .. self.CRLF
162 .. "Host: " .. self.trans.host .. self.CRLF
163 .. "Content-Type: application/x-thrift" .. self.CRLF
164 .. "Content-Length: " .. content_len .. self.CRLF
165 .. "Accept: application/x-thrift " .. self.CRLF
166 .. "User-Agent: Thrift/" .. self.VERSION .. " (Lua/THttpClient)"
167 .. self.CRLF .. self.CRLF
168 self.trans:write(header)
169 end
170end
171
Thomaseb684d32024-07-28 15:32:23 +0200172function THttpTransport:flushOneway()
173 self.wBuf = ''
174 self:writeHttpHeader(0)
175 self.trans:flush()
176end
177
Wang Yaofue432c6b2016-03-09 16:39:03 +0800178function THttpTransport:flush()
179 -- If the write fails we still want wBuf to be clear
180 local tmp = self.wBuf
181 self.wBuf = ''
Thomaseb684d32024-07-28 15:32:23 +0200182 local dataLen = string.len(tmp)
183 self:writeHttpHeader(dataLen)
184 if dataLen > 0 then
185 self.trans:write(tmp)
186 end
Wang Yaofue432c6b2016-03-09 16:39:03 +0800187 self.trans:flush()
188end
189
190THttpTransportFactory = TTransportFactoryBase:new{
191 __type = 'THttpTransportFactory'
192}
193function THttpTransportFactory:getTransport(trans)
194 if not trans then
195 terror(TProtocolException:new{
196 message = 'Must supply a transport to ' .. ttype(self)
197 })
198 end
199 return THttpTransport:new{trans = trans}
200end