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