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
20 #include <sstream>
21
22 #include <thrift/transport/THttpTransport.h>
23
24 using std::string;
25
26 namespace apache {
27 namespace thrift {
28 namespace transport {
29
30 // Yeah, yeah, hacky to put these here, I know.
31 const char* THttpTransport::CRLF = "\r\n";
32 const int THttpTransport::CRLF_LEN = 2;
33
THttpTransport(std::shared_ptr<TTransport> transport,std::shared_ptr<TConfiguration> config)34 THttpTransport::THttpTransport(std::shared_ptr<TTransport> transport, std::shared_ptr<TConfiguration> config)
35 : TVirtualTransport(config),
36 transport_(transport),
37 origin_(""),
38 readHeaders_(true),
39 chunked_(false),
40 chunkedDone_(false),
41 chunkSize_(0),
42 contentLength_(0),
43 httpBuf_(nullptr),
44 httpPos_(0),
45 httpBufLen_(0),
46 httpBufSize_(1024) {
47 init();
48 }
49
init()50 void THttpTransport::init() {
51 httpBuf_ = (char*)std::malloc(httpBufSize_ + 1);
52 if (httpBuf_ == nullptr) {
53 throw std::bad_alloc();
54 }
55 httpBuf_[httpBufLen_] = '\0';
56 }
57
~THttpTransport()58 THttpTransport::~THttpTransport() {
59 if (httpBuf_ != nullptr) {
60 std::free(httpBuf_);
61 }
62 }
63
read(uint8_t * buf,uint32_t len)64 uint32_t THttpTransport::read(uint8_t* buf, uint32_t len) {
65 checkReadBytesAvailable(len);
66 if (readBuffer_.available_read() == 0) {
67 readBuffer_.resetBuffer();
68 uint32_t got = readMoreData();
69 if (got == 0) {
70 return 0;
71 }
72 }
73 return readBuffer_.read(buf, len);
74 }
75
readEnd()76 uint32_t THttpTransport::readEnd() {
77 // Read any pending chunked data (footers etc.)
78 if (chunked_) {
79 while (!chunkedDone_) {
80 readChunked();
81 }
82 }
83 return 0;
84 }
85
readMoreData()86 uint32_t THttpTransport::readMoreData() {
87 uint32_t size;
88
89 if (httpPos_ == httpBufLen_) {
90 // Get more data!
91 refill();
92 }
93
94 if (readHeaders_) {
95 readHeaders();
96 }
97
98 if (chunked_) {
99 size = readChunked();
100 } else {
101 size = readContent(contentLength_);
102 readHeaders_ = true;
103 }
104
105 return size;
106 }
107
readChunked()108 uint32_t THttpTransport::readChunked() {
109 uint32_t length = 0;
110
111 char* line = readLine();
112 uint32_t chunkSize = parseChunkSize(line);
113 if (chunkSize == 0) {
114 readChunkedFooters();
115 } else {
116 // Read data content
117 length += readContent(chunkSize);
118 // Read trailing CRLF after content
119 readLine();
120 }
121 return length;
122 }
123
readChunkedFooters()124 void THttpTransport::readChunkedFooters() {
125 // End of data, read footer lines until a blank one appears
126 while (true) {
127 char* line = readLine();
128 if (strlen(line) == 0) {
129 chunkedDone_ = true;
130 break;
131 }
132 }
133 }
134
parseChunkSize(char * line)135 uint32_t THttpTransport::parseChunkSize(char* line) {
136 char* semi = strchr(line, ';');
137 if (semi != nullptr) {
138 *semi = '\0';
139 }
140 uint32_t size = 0;
141 sscanf(line, "%x", &size);
142 return size;
143 }
144
readContent(uint32_t size)145 uint32_t THttpTransport::readContent(uint32_t size) {
146 uint32_t need = size;
147 while (need > 0) {
148 uint32_t avail = httpBufLen_ - httpPos_;
149 if (avail == 0) {
150 // We have given all the data, reset position to head of the buffer
151 httpPos_ = 0;
152 httpBufLen_ = 0;
153 refill();
154
155 // Now have available however much we read
156 avail = httpBufLen_;
157 }
158 uint32_t give = avail;
159 if (need < give) {
160 give = need;
161 }
162 readBuffer_.write((uint8_t*)(httpBuf_ + httpPos_), give);
163 httpPos_ += give;
164 need -= give;
165 }
166 return size;
167 }
168
readLine()169 char* THttpTransport::readLine() {
170 while (true) {
171 char* eol = nullptr;
172
173 eol = strstr(httpBuf_ + httpPos_, CRLF);
174
175 // No CRLF yet?
176 if (eol == nullptr) {
177 // Shift whatever we have now to front and refill
178 shift();
179 refill();
180 } else {
181 // Return pointer to next line
182 *eol = '\0';
183 char* line = httpBuf_ + httpPos_;
184 httpPos_ = static_cast<uint32_t>((eol - httpBuf_) + CRLF_LEN);
185 return line;
186 }
187 }
188 }
189
shift()190 void THttpTransport::shift() {
191 if (httpBufLen_ > httpPos_) {
192 // Shift down remaining data and read more
193 uint32_t length = httpBufLen_ - httpPos_;
194 memmove(httpBuf_, httpBuf_ + httpPos_, length);
195 httpBufLen_ = length;
196 } else {
197 httpBufLen_ = 0;
198 }
199 httpPos_ = 0;
200 httpBuf_[httpBufLen_] = '\0';
201 }
202
refill()203 void THttpTransport::refill() {
204 uint32_t avail = httpBufSize_ - httpBufLen_;
205 if (avail <= (httpBufSize_ / 4)) {
206 httpBufSize_ *= 2;
207 char* tmpBuf = (char*)std::realloc(httpBuf_, httpBufSize_ + 1);
208 if (tmpBuf == nullptr) {
209 throw std::bad_alloc();
210 }
211 httpBuf_ = tmpBuf;
212 }
213
214 // Read more data
215 uint32_t got = transport_->read((uint8_t*)(httpBuf_ + httpBufLen_), httpBufSize_ - httpBufLen_);
216 httpBufLen_ += got;
217 httpBuf_[httpBufLen_] = '\0';
218
219 if (got == 0) {
220 throw TTransportException(TTransportException::END_OF_FILE, "Could not refill buffer");
221 }
222 }
223
readHeaders()224 void THttpTransport::readHeaders() {
225 // Initialize headers state variables
226 contentLength_ = 0;
227 chunked_ = false;
228 chunkedDone_ = false;
229 chunkSize_ = 0;
230
231 // Control state flow
232 bool statusLine = true;
233 bool finished = false;
234
235 // Loop until headers are finished
236 while (true) {
237 char* line = readLine();
238
239 if (strlen(line) == 0) {
240 if (finished) {
241 readHeaders_ = false;
242 return;
243 } else {
244 // Must have been an HTTP 100, keep going for another status line
245 statusLine = true;
246 }
247 } else {
248 if (statusLine) {
249 statusLine = false;
250 finished = parseStatusLine(line);
251 } else {
252 parseHeader(line);
253 }
254 }
255 }
256 }
257
write(const uint8_t * buf,uint32_t len)258 void THttpTransport::write(const uint8_t* buf, uint32_t len) {
259 writeBuffer_.write(buf, len);
260 }
261
getOrigin() const262 const std::string THttpTransport::getOrigin() const {
263 std::ostringstream oss;
264 if (!origin_.empty()) {
265 oss << origin_ << ", ";
266 }
267 oss << transport_->getOrigin();
268 return oss.str();
269 }
270 }
271 }
272 }
273