blob: 5bbfece2526ed712085530a8554631d245f119c4 [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',
28 VERSION = '1.0.0',
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