forked from dreiss/old-thrift
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Split THttpClient in THttpTransport interface and THttpClient / THt…
…tpServer transports, we're now able to have C++ thrift service talking json over http for ajax ! :)
- Loading branch information
Jérémie BORDIER
committed
Jan 3, 2009
1 parent
49651ab
commit fa4b8b5
Showing
4 changed files
with
484 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
#include <cstdlib> | ||
#include <sstream> | ||
#include <iostream> | ||
|
||
#include "THttpServer.h" | ||
#include "TSocket.h" | ||
|
||
namespace facebook { namespace thrift { namespace transport { | ||
|
||
using namespace std; | ||
|
||
/** | ||
* Http server transport implementation. | ||
* | ||
* @author Jeremie BORDIER <jeremie.bordier@gmail.com> | ||
*/ | ||
|
||
THttpServer::THttpServer(boost::shared_ptr<TTransport> transport) : | ||
THttpTransport(transport) { | ||
} | ||
|
||
THttpServer::~THttpServer() {} | ||
|
||
void THttpServer::parseHeader(char* header) { | ||
char* colon = strchr(header, ':'); | ||
if (colon == NULL) { | ||
return; | ||
} | ||
uint32_t sz = colon - header; | ||
char* value = colon+1; | ||
|
||
if (strncmp(header, "Transfer-Encoding", sz) == 0) { | ||
if (strstr(value, "chunked") != NULL) { | ||
chunked_ = true; | ||
} | ||
} else if (strncmp(header, "Content-Length", sz) == 0) { | ||
chunked_ = false; | ||
contentLength_ = atoi(value); | ||
} | ||
} | ||
|
||
bool THttpServer::parseStatusLine(char* status) { | ||
char* method = status; | ||
|
||
char* path = strchr(method, ' '); | ||
if (path == NULL) { | ||
throw TTransportException(string("Bad Status: ") + status); | ||
} | ||
|
||
*path = '\0'; | ||
while (*(++path) == ' '); | ||
|
||
char* http = strchr(path, ' '); | ||
if (http == NULL) { | ||
throw TTransportException(string("Bad Status: ") + status); | ||
} | ||
*http = '\0'; | ||
|
||
if (strcmp(method, "POST") == 0) { | ||
// POST method ok, looking for content. | ||
return true; | ||
} | ||
throw TTransportException(string("Bad Status (unsupported method): ") + status); | ||
} | ||
|
||
void THttpServer::flush() { | ||
// Fetch the contents of the write buffer | ||
uint8_t* buf; | ||
uint32_t len; | ||
writeBuffer_.getBuffer(&buf, &len); | ||
|
||
// Construct the HTTP header | ||
std::ostringstream h; | ||
h << | ||
"HTTP/1.1 200 Ok" << CRLF << | ||
"Date: Fri, 02 Jan 2009 19:09:44 GMT" << CRLF << | ||
"Server: Thrift/0.1" << CRLF << | ||
"Content-Type: application/x-thrift" << CRLF << | ||
"Content-Length: " << len << CRLF << | ||
"Connection: Keep-Alive" << CRLF << | ||
CRLF; | ||
string header = h.str(); | ||
|
||
// Write the header, then the data, then flush | ||
transport_->write((const uint8_t*)header.c_str(), header.size()); | ||
transport_->write(buf, len); | ||
transport_->flush(); | ||
// printf(">>>\n%s%s\n", header.c_str(), buf); | ||
|
||
// Reset the buffer and header variables | ||
writeBuffer_.resetBuffer(); | ||
readHeaders_ = true; | ||
} | ||
|
||
}}} // facebook::thrift::transport |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
// Copyright (c) 2006- Facebook | ||
// Distributed under the Thrift Software License | ||
// | ||
// See accompanying file LICENSE or visit the Thrift site at: | ||
// http://developers.facebook.com/thrift/ | ||
|
||
#ifndef _THRIFT_TRANSPORT_THTTPSERVER_H_ | ||
#define _THRIFT_TRANSPORT_THTTPSERVER_H_ 1 | ||
|
||
#include <transport/THttpTransport.h> | ||
|
||
namespace facebook { namespace thrift { namespace transport { | ||
|
||
/** | ||
* HTTP server transport implementation. | ||
* | ||
* @author Jeremie BORDIER <jeremie.bordier@gmail.com> | ||
*/ | ||
class THttpServer : public THttpTransport { | ||
public: | ||
THttpServer(boost::shared_ptr<TTransport> transport); | ||
|
||
virtual ~THttpServer(); | ||
|
||
virtual void flush(); | ||
|
||
protected: | ||
|
||
void readHeaders(); | ||
virtual void parseHeader(char* header); | ||
virtual bool parseStatusLine(char* status); | ||
|
||
}; | ||
|
||
/** | ||
* Wraps a transport into HTTP protocol | ||
* | ||
* @author Jeremie BORDIER <jeremie.bordier@gmail.com> | ||
*/ | ||
class THttpServerTransportFactory : public TTransportFactory { | ||
public: | ||
THttpServerTransportFactory() {} | ||
|
||
virtual ~THttpServerTransportFactory() {} | ||
|
||
/** | ||
* Wraps the transport into a buffered one. | ||
*/ | ||
virtual boost::shared_ptr<TTransport> getTransport(boost::shared_ptr<TTransport> trans) { | ||
return boost::shared_ptr<TTransport>(new THttpServer(trans)); | ||
} | ||
|
||
}; | ||
|
||
}}} // facebook::thrift::transport | ||
|
||
#endif // #ifndef _THRIFT_TRANSPORT_THTTPSERVER_H_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,240 @@ | ||
#include "THttpTransport.h" | ||
|
||
/** | ||
* Http client implementation. | ||
* | ||
* @author Jeremie BORDIER <jeremie.bordier@gmail.com> | ||
* and Mark Slee <mcslee@facebook.com> | ||
*/ | ||
|
||
namespace facebook { namespace thrift { namespace transport { | ||
|
||
using namespace std; | ||
|
||
// Yeah, yeah, hacky to put these here, I know. | ||
const char* THttpTransport::CRLF = "\r\n"; | ||
const int THttpTransport::CRLF_LEN = 2; | ||
|
||
THttpTransport::THttpTransport(boost::shared_ptr<TTransport> transport) : | ||
transport_(transport), | ||
readHeaders_(true), | ||
chunked_(false), | ||
chunkedDone_(false), | ||
chunkSize_(0), | ||
contentLength_(0), | ||
httpBuf_(NULL), | ||
httpPos_(0), | ||
httpBufLen_(0), | ||
httpBufSize_(1024) { | ||
init(); | ||
} | ||
|
||
void THttpTransport::init() { | ||
httpBuf_ = (char*)std::malloc(httpBufSize_+1); | ||
if (httpBuf_ == NULL) { | ||
throw TTransportException("Out of memory."); | ||
} | ||
httpBuf_[httpBufLen_] = '\0'; | ||
} | ||
|
||
THttpTransport::~THttpTransport() { | ||
if (httpBuf_ != NULL) { | ||
std::free(httpBuf_); | ||
} | ||
} | ||
|
||
uint32_t THttpTransport::read(uint8_t* buf, uint32_t len) { | ||
if (readBuffer_.available_read() == 0) { | ||
readBuffer_.resetBuffer(); | ||
uint32_t got = readMoreData(); | ||
if (got == 0) { | ||
return 0; | ||
} | ||
} | ||
return readBuffer_.read(buf, len); | ||
} | ||
|
||
void THttpTransport::readEnd() { | ||
// Read any pending chunked data (footers etc.) | ||
if (chunked_) { | ||
while (!chunkedDone_) { | ||
readChunked(); | ||
} | ||
} | ||
} | ||
|
||
uint32_t THttpTransport::readMoreData() { | ||
uint32_t size; | ||
|
||
// Get more data! | ||
refill(); | ||
|
||
if (readHeaders_) { | ||
readHeaders(); | ||
} | ||
|
||
if (chunked_) { | ||
size = readChunked(); | ||
} else { | ||
size = readContent(contentLength_); | ||
} | ||
readHeaders_ = true; | ||
return size; | ||
} | ||
|
||
uint32_t THttpTransport::readChunked() { | ||
uint32_t length = 0; | ||
|
||
char* line = readLine(); | ||
uint32_t chunkSize = parseChunkSize(line); | ||
if (chunkSize == 0) { | ||
readChunkedFooters(); | ||
} else { | ||
// Read data content | ||
length += readContent(chunkSize); | ||
// Read trailing CRLF after content | ||
readLine(); | ||
} | ||
return length; | ||
} | ||
|
||
void THttpTransport::readChunkedFooters() { | ||
// End of data, read footer lines until a blank one appears | ||
while (true) { | ||
char* line = readLine(); | ||
if (strlen(line) == 0) { | ||
chunkedDone_ = true; | ||
break; | ||
} | ||
} | ||
} | ||
|
||
uint32_t THttpTransport::parseChunkSize(char* line) { | ||
char* semi = strchr(line, ';'); | ||
if (semi != NULL) { | ||
*semi = '\0'; | ||
} | ||
int size = 0; | ||
sscanf(line, "%x", &size); | ||
return (uint32_t)size; | ||
} | ||
|
||
uint32_t THttpTransport::readContent(uint32_t size) { | ||
uint32_t need = size; | ||
while (need > 0) { | ||
uint32_t avail = httpBufLen_ - httpPos_; | ||
if (avail == 0) { | ||
// We have given all the data, reset position to head of the buffer | ||
httpPos_ = 0; | ||
httpBufLen_ = 0; | ||
refill(); | ||
|
||
// Now have available however much we read | ||
avail = httpBufLen_; | ||
} | ||
uint32_t give = avail; | ||
if (need < give) { | ||
give = need; | ||
} | ||
readBuffer_.write((uint8_t*)(httpBuf_+httpPos_), give); | ||
httpPos_ += give; | ||
need -= give; | ||
} | ||
return size; | ||
} | ||
|
||
char* THttpTransport::readLine() { | ||
while (true) { | ||
char* eol = NULL; | ||
|
||
eol = strstr(httpBuf_+httpPos_, CRLF); | ||
|
||
// No CRLF yet? | ||
if (eol == NULL) { | ||
// Shift whatever we have now to front and refill | ||
shift(); | ||
refill(); | ||
} else { | ||
// Return pointer to next line | ||
*eol = '\0'; | ||
char* line = httpBuf_+httpPos_; | ||
httpPos_ = (eol-httpBuf_) + CRLF_LEN; | ||
return line; | ||
} | ||
} | ||
|
||
} | ||
|
||
void THttpTransport::shift() { | ||
if (httpBufLen_ > httpPos_) { | ||
// Shift down remaining data and read more | ||
uint32_t length = httpBufLen_ - httpPos_; | ||
memmove(httpBuf_, httpBuf_+httpPos_, length); | ||
httpBufLen_ = length; | ||
} else { | ||
httpBufLen_ = 0; | ||
} | ||
httpPos_ = 0; | ||
httpBuf_[httpBufLen_] = '\0'; | ||
} | ||
|
||
void THttpTransport::refill() { | ||
uint32_t avail = httpBufSize_ - httpBufLen_; | ||
if (avail <= (httpBufSize_ / 4)) { | ||
httpBufSize_ *= 2; | ||
httpBuf_ = (char*)std::realloc(httpBuf_, httpBufSize_+1); | ||
if (httpBuf_ == NULL) { | ||
throw TTransportException("Out of memory."); | ||
} | ||
} | ||
|
||
// Read more data | ||
uint32_t got = transport_->read((uint8_t*)(httpBuf_+httpBufLen_), httpBufSize_-httpBufLen_); | ||
httpBufLen_ += got; | ||
httpBuf_[httpBufLen_] = '\0'; | ||
// printf("<<<\n%s\n", httpBuf_); | ||
|
||
if (got == 0) { | ||
throw TTransportException("Could not refill buffer"); | ||
} | ||
} | ||
|
||
void THttpTransport::readHeaders() { | ||
// Initialize headers state variables | ||
contentLength_ = 0; | ||
chunked_ = false; | ||
chunkedDone_ = false; | ||
chunkSize_ = 0; | ||
|
||
// Control state flow | ||
bool statusLine = true; | ||
bool finished = false; | ||
|
||
// Loop until headers are finished | ||
while (true) { | ||
char* line = readLine(); | ||
|
||
if (strlen(line) == 0) { | ||
if (finished) { | ||
readHeaders_ = false; | ||
return; | ||
} else { | ||
// Must have been an HTTP 100, keep going for another status line | ||
statusLine = true; | ||
} | ||
} else { | ||
if (statusLine) { | ||
statusLine = false; | ||
finished = parseStatusLine(line); | ||
} else { | ||
parseHeader(line); | ||
} | ||
} | ||
} | ||
} | ||
|
||
void THttpTransport::write(const uint8_t* buf, uint32_t len) { | ||
writeBuffer_.write(buf, len); | ||
} | ||
|
||
}}} |
Oops, something went wrong.