Subversion Repositories SmartDukaan

Rev

Rev 30 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
30 ashish 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 <cstdlib>
21
#include <sstream>
22
 
23
#include "THttpClient.h"
24
#include "TSocket.h"
25
 
26
namespace apache { namespace thrift { namespace transport {
27
 
28
using namespace std;
29
 
30
/**
31
 * Http client implementation.
32
 *
33
 */
34
 
35
// Yeah, yeah, hacky to put these here, I know.
36
static const char* CRLF = "\r\n";
37
static const int CRLF_LEN = 2;
38
 
39
THttpClient::THttpClient(boost::shared_ptr<TTransport> transport, string host, string path) :
40
  transport_(transport),
41
  host_(host),
42
  path_(path),
43
  readHeaders_(true),
44
  chunked_(false),
45
  chunkedDone_(false),
46
  chunkSize_(0),
47
  contentLength_(0),
48
  httpBuf_(NULL),
49
  httpPos_(0),
50
  httpBufLen_(0),
51
  httpBufSize_(1024) {
52
  init();
53
}
54
 
55
THttpClient::THttpClient(string host, int port, string path) :
56
  host_(host),
57
  path_(path),
58
  readHeaders_(true),
59
  chunked_(false),
60
  chunkedDone_(false),
61
  chunkSize_(0),
62
  contentLength_(0),
63
  httpBuf_(NULL),
64
  httpPos_(0),
65
  httpBufLen_(0),
66
  httpBufSize_(1024) {
67
  transport_ = boost::shared_ptr<TTransport>(new TSocket(host, port));
68
  init();
69
}
70
 
71
void THttpClient::init() {
72
  httpBuf_ = (char*)std::malloc(httpBufSize_+1);
73
  if (httpBuf_ == NULL) {
74
    throw TTransportException("Out of memory.");
75
  }
76
  httpBuf_[httpBufLen_] = '\0';
77
}
78
 
79
THttpClient::~THttpClient() {
80
  if (httpBuf_ != NULL) {
81
    std::free(httpBuf_);
82
  }
83
}
84
 
85
uint32_t THttpClient::read(uint8_t* buf, uint32_t len) {
86
  if (readBuffer_.available_read() == 0) {
87
    readBuffer_.resetBuffer();
88
    uint32_t got = readMoreData();
89
    if (got == 0) {
90
      return 0;
91
    }
92
  }
93
  return readBuffer_.read(buf, len);
94
}
95
 
96
void THttpClient::readEnd() {
97
  // Read any pending chunked data (footers etc.)
98
  if (chunked_) {
99
    while (!chunkedDone_) {
100
      readChunked();
101
    }
102
  }
103
}
104
 
105
uint32_t THttpClient::readMoreData() {
106
  // Get more data!
107
  refill();
108
 
109
  if (readHeaders_) {
110
    readHeaders();
111
  }
112
 
113
  if (chunked_) {
114
    return readChunked();
115
  } else {
116
    return readContent(contentLength_);
117
  }
118
}
119
 
120
uint32_t THttpClient::readChunked() {
121
  uint32_t length = 0;
122
 
123
  char* line = readLine();
124
  uint32_t chunkSize = parseChunkSize(line);
125
  if (chunkSize == 0) {
126
    readChunkedFooters();
127
  } else {
128
    // Read data content
129
    length += readContent(chunkSize);
130
    // Read trailing CRLF after content
131
    readLine();
132
  }
133
  return length;
134
}
135
 
136
void THttpClient::readChunkedFooters() {
137
  // End of data, read footer lines until a blank one appears
138
  while (true) {
139
    char* line = readLine();
140
    if (strlen(line) == 0) {
141
      chunkedDone_ = true;
142
      break;
143
    }
144
  }
145
}
146
 
147
uint32_t THttpClient::parseChunkSize(char* line) {
148
  char* semi = strchr(line, ';');
149
  if (semi != NULL) {
150
    *semi = '\0';
151
  }
152
  int size = 0;
153
  sscanf(line, "%x", &size);
154
  return (uint32_t)size;
155
}
156
 
157
uint32_t THttpClient::readContent(uint32_t size) {
158
  uint32_t need = size;
159
  while (need > 0) {
160
    uint32_t avail = httpBufLen_ - httpPos_;
161
    if (avail == 0) {
162
      // We have given all the data, reset position to head of the buffer
163
      httpPos_ = 0;
164
      httpBufLen_ = 0;
165
      refill();
166
 
167
      // Now have available however much we read
168
      avail = httpBufLen_;
169
    }
170
    uint32_t give = avail;
171
    if (need < give) {
172
      give = need;
173
    }
174
    readBuffer_.write((uint8_t*)(httpBuf_+httpPos_), give);
175
    httpPos_ += give;
176
    need -= give;
177
  }
178
  return size;
179
}
180
 
181
char* THttpClient::readLine() {
182
  while (true) {
183
    char* eol = NULL;
184
 
185
    eol = strstr(httpBuf_+httpPos_, CRLF);
186
 
187
    // No CRLF yet?
188
    if (eol == NULL) {
189
      // Shift whatever we have now to front and refill
190
      shift();
191
      refill();
192
    } else {
193
      // Return pointer to next line
194
      *eol = '\0';
195
      char* line = httpBuf_+httpPos_;
196
      httpPos_ = (eol-httpBuf_) + CRLF_LEN;
197
      return line;
198
    }
199
  }
200
 
201
}
202
 
203
void THttpClient::shift() {
204
  if (httpBufLen_ > httpPos_) {
205
    // Shift down remaining data and read more
206
    uint32_t length = httpBufLen_ - httpPos_;
207
    memmove(httpBuf_, httpBuf_+httpPos_, length);
208
    httpBufLen_ = length;
209
  } else {
210
    httpBufLen_ = 0;
211
  }
212
  httpPos_ = 0;
213
  httpBuf_[httpBufLen_] = '\0';
214
}
215
 
216
void THttpClient::refill() {
217
  uint32_t avail = httpBufSize_ - httpBufLen_;
218
  if (avail <= (httpBufSize_ / 4)) {
219
    httpBufSize_ *= 2;
220
    httpBuf_ = (char*)std::realloc(httpBuf_, httpBufSize_+1);
221
    if (httpBuf_ == NULL) {
222
      throw TTransportException("Out of memory.");
223
    }
224
  }
225
 
226
  // Read more data
227
  uint32_t got = transport_->read((uint8_t*)(httpBuf_+httpBufLen_), httpBufSize_-httpBufLen_);
228
  httpBufLen_ += got;
229
  httpBuf_[httpBufLen_] = '\0';
230
 
231
  if (got == 0) {
232
    throw TTransportException("Could not refill buffer");
233
  }
234
}
235
 
236
void THttpClient::readHeaders() {
237
  // Initialize headers state variables
238
  contentLength_ = 0;
239
  chunked_ = false;
240
  chunkedDone_ = false;
241
  chunkSize_ = 0;
242
 
243
  // Control state flow
244
  bool statusLine = true;
245
  bool finished = false;
246
 
247
  // Loop until headers are finished
248
  while (true) {
249
    char* line = readLine();
250
 
251
    if (strlen(line) == 0) {
252
      if (finished) {
253
        readHeaders_ = false;
254
        return;
255
      } else {
256
        // Must have been an HTTP 100, keep going for another status line
257
        statusLine = true;
258
      }
259
    } else {
260
      if (statusLine) {
261
        statusLine = false;
262
        finished = parseStatusLine(line);
263
      } else {
264
        parseHeader(line);
265
      }
266
    }
267
  }
268
}
269
 
270
bool THttpClient::parseStatusLine(char* status) {
271
  char* http = status;
272
 
273
  char* code = strchr(http, ' ');
274
  if (code == NULL) {
275
    throw TTransportException(string("Bad Status: ") + status);
276
  }
277
 
278
  *code = '\0';
279
  while (*(code++) == ' ');
280
 
281
  char* msg = strchr(code, ' ');
282
  if (msg == NULL) {
283
    throw TTransportException(string("Bad Status: ") + status);
284
  }
285
  *msg = '\0';
286
 
287
  if (strcmp(code, "200") == 0) {
288
    // HTTP 200 = OK, we got the response
289
    return true;
290
  } else if (strcmp(code, "100") == 0) {
291
    // HTTP 100 = continue, just keep reading
292
    return false;
293
  } else {
294
    throw TTransportException(string("Bad Status: ") + status);
295
  }
296
}
297
 
298
void THttpClient::parseHeader(char* header) {
299
  char* colon = strchr(header, ':');
300
  if (colon == NULL) {
301
    return;
302
  }
303
  uint32_t sz = colon - header;
304
  char* value = colon+1;
305
 
306
  if (strncmp(header, "Transfer-Encoding", sz) == 0) {
307
    if (strstr(value, "chunked") != NULL) {
308
      chunked_ = true;
309
    }
310
  } else if (strncmp(header, "Content-Length", sz) == 0) {
311
    chunked_ = false;
312
    contentLength_ = atoi(value);
313
  }
314
}
315
 
316
void THttpClient::write(const uint8_t* buf, uint32_t len) {
317
  writeBuffer_.write(buf, len);
318
}
319
 
320
void THttpClient::flush() {
321
  // Fetch the contents of the write buffer
322
  uint8_t* buf;
323
  uint32_t len;
324
  writeBuffer_.getBuffer(&buf, &len);
325
 
326
  // Construct the HTTP header
327
  std::ostringstream h;
328
  h <<
329
    "POST " << path_ << " HTTP/1.1" << CRLF <<
330
    "Host: " << host_ << CRLF <<
331
    "Content-Type: application/x-thrift" << CRLF <<
332
    "Content-Length: " << len << CRLF <<
333
    "Accept: application/x-thrift" << CRLF <<
334
    "User-Agent: C++/THttpClient" << CRLF <<
335
    CRLF;
336
  string header = h.str();
337
 
338
  // Write the header, then the data, then flush
339
  transport_->write((const uint8_t*)header.c_str(), header.size());
340
  transport_->write(buf, len);
341
  transport_->flush();
342
 
343
  // Reset the buffer and header variables
344
  writeBuffer_.resetBuffer();
345
  readHeaders_ = true;
346
}
347
 
348
}}} // apache::thrift::transport