diff --git a/lib/cpp/Makefile.am b/lib/cpp/Makefile.am index 579140cc5..bf2fe260d 100644 --- a/lib/cpp/Makefile.am +++ b/lib/cpp/Makefile.am @@ -58,7 +58,9 @@ libthrift_la_SOURCES = src/Thrift.cpp \ src/transport/TFDTransport.cpp \ src/transport/TFileTransport.cpp \ src/transport/TSimpleFileTransport.cpp \ + src/transport/THttpTransport.cpp \ src/transport/THttpClient.cpp \ + src/transport/THttpServer.cpp \ src/transport/TSocket.cpp \ src/transport/TSocketPool.cpp \ src/transport/TServerSocket.cpp \ @@ -121,7 +123,9 @@ include_transport_HEADERS = \ src/transport/TSimpleFileTransport.h \ src/transport/TServerSocket.h \ src/transport/TServerTransport.h \ + src/transport/THttpTransport.h \ src/transport/THttpClient.h \ + src/transport/THttpServer.h \ src/transport/TSocket.h \ src/transport/TSocketPool.h \ src/transport/TTransport.h \ diff --git a/lib/cpp/src/transport/THttpClient.cpp b/lib/cpp/src/transport/THttpClient.cpp index 59f233968..400597e10 100644 --- a/lib/cpp/src/transport/THttpClient.cpp +++ b/lib/cpp/src/transport/THttpClient.cpp @@ -20,250 +20,38 @@ #include #include -#include "THttpClient.h" -#include "TSocket.h" +#include +#include namespace apache { namespace thrift { namespace transport { using namespace std; -/** - * Http client implementation. - * - */ - -// Yeah, yeah, hacky to put these here, I know. -static const char* CRLF = "\r\n"; -static const int CRLF_LEN = 2; - -THttpClient::THttpClient(boost::shared_ptr transport, string host, string path) : - transport_(transport), - host_(host), - path_(path), - readHeaders_(true), - chunked_(false), - chunkedDone_(false), - chunkSize_(0), - contentLength_(0), - httpBuf_(NULL), - httpPos_(0), - httpBufLen_(0), - httpBufSize_(1024) { - init(); +THttpClient::THttpClient(boost::shared_ptr transport, std::string host, std::string path) : + THttpTransport(transport), host_(host), path_(path) { } THttpClient::THttpClient(string host, int port, string path) : - host_(host), - path_(path), - readHeaders_(true), - chunked_(false), - chunkedDone_(false), - chunkSize_(0), - contentLength_(0), - httpBuf_(NULL), - httpPos_(0), - httpBufLen_(0), - httpBufSize_(1024) { - transport_ = boost::shared_ptr(new TSocket(host, port)); - init(); + THttpTransport(boost::shared_ptr(new TSocket(host, port))), host_(host), path_(path) { } -void THttpClient::init() { - httpBuf_ = (char*)std::malloc(httpBufSize_+1); - if (httpBuf_ == NULL) { - throw TTransportException("Out of memory."); +THttpClient::~THttpClient() {} + +void THttpClient::parseHeader(char* header) { + char* colon = strchr(header, ':'); + if (colon == NULL) { + return; } - httpBuf_[httpBufLen_] = '\0'; -} + uint32_t sz = colon - header; + char* value = colon+1; -THttpClient::~THttpClient() { - if (httpBuf_ != NULL) { - std::free(httpBuf_); - } -} - -uint32_t THttpClient::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 THttpClient::readEnd() { - // Read any pending chunked data (footers etc.) - if (chunked_) { - while (!chunkedDone_) { - readChunked(); - } - } -} - -uint32_t THttpClient::readMoreData() { - // Get more data! - refill(); - - if (readHeaders_) { - readHeaders(); - } - - if (chunked_) { - return readChunked(); - } else { - return readContent(contentLength_); - } -} - -uint32_t THttpClient::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 THttpClient::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 THttpClient::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 THttpClient::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* THttpClient::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 THttpClient::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 THttpClient::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'; - - if (got == 0) { - throw TTransportException("Could not refill buffer"); - } -} - -void THttpClient::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); - } + 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); } } @@ -295,28 +83,6 @@ bool THttpClient::parseStatusLine(char* status) { } } -void THttpClient::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); - } -} - -void THttpClient::write(const uint8_t* buf, uint32_t len) { - writeBuffer_.write(buf, len); -} - void THttpClient::flush() { // Fetch the contents of the write buffer uint8_t* buf; @@ -331,7 +97,7 @@ void THttpClient::flush() { "Content-Type: application/x-thrift" << CRLF << "Content-Length: " << len << CRLF << "Accept: application/x-thrift" << CRLF << - "User-Agent: C++/THttpClient" << CRLF << + "User-Agent: Thrift/" << VERSION << " (C++/THttpClient)" << CRLF << CRLF; string header = h.str(); diff --git a/lib/cpp/src/transport/THttpClient.h b/lib/cpp/src/transport/THttpClient.h index f4be4c1a6..142063d0c 100644 --- a/lib/cpp/src/transport/THttpClient.h +++ b/lib/cpp/src/transport/THttpClient.h @@ -20,19 +20,11 @@ #ifndef _THRIFT_TRANSPORT_THTTPCLIENT_H_ #define _THRIFT_TRANSPORT_THTTPCLIENT_H_ 1 -#include +#include namespace apache { namespace thrift { namespace transport { -/** - * HTTP client implementation of the thrift transport. This was irritating - * to write, but the alternatives in C++ land are daunting. Linking CURL - * requires 23 dynamic libraries last time I checked (WTF?!?). All we have - * here is a VERY basic HTTP/1.1 client which supports HTTP 100 Continue, - * chunked transfer encoding, keepalive, etc. Tested against Apache. - * - */ -class THttpClient : public TTransport { +class THttpClient : public THttpTransport { public: THttpClient(boost::shared_ptr transport, std::string host, std::string path=""); @@ -40,69 +32,15 @@ class THttpClient : public TTransport { virtual ~THttpClient(); - void open() { - transport_->open(); - } - - bool isOpen() { - return transport_->isOpen(); - } - - bool peek() { - return transport_->peek(); - } - - void close() { - transport_->close(); - } - - uint32_t read(uint8_t* buf, uint32_t len); - - void readEnd(); - - void write(const uint8_t* buf, uint32_t len); - - void flush(); - - private: - void init(); + virtual void flush(); protected: - boost::shared_ptr transport_; - - TMemoryBuffer writeBuffer_; - TMemoryBuffer readBuffer_; - std::string host_; std::string path_; - bool readHeaders_; - bool chunked_; - bool chunkedDone_; - uint32_t chunkSize_; - uint32_t contentLength_; - - char* httpBuf_; - uint32_t httpPos_; - uint32_t httpBufLen_; - uint32_t httpBufSize_; - - uint32_t readMoreData(); - char* readLine(); - - void readHeaders(); - void parseHeader(char* header); - bool parseStatusLine(char* status); - - uint32_t readChunked(); - void readChunkedFooters(); - uint32_t parseChunkSize(char* line); - - uint32_t readContent(uint32_t size); - - void refill(); - void shift(); + virtual void parseHeader(char* header); + virtual bool parseStatusLine(char* status); }; diff --git a/lib/hs/Thrift.cabal b/lib/hs/Thrift.cabal index 8132069b7..feb2e816e 100644 --- a/lib/hs/Thrift.cabal +++ b/lib/hs/Thrift.cabal @@ -1,3 +1,22 @@ +-- +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +-- KIND, either express or implied. See the License for the +-- specific language governing permissions and limitations +-- under the License. +-- + Name: Thrift Version: 0.1.1 Cabal-Version: >= 1.2 diff --git a/lib/ocaml/README-OCamlMakefile b/lib/ocaml/README-OCamlMakefile index 54787b967..0a97c64ff 100644 --- a/lib/ocaml/README-OCamlMakefile +++ b/lib/ocaml/README-OCamlMakefile @@ -1,3 +1,6 @@ +NOTE (bryanduxbury): OCamlMakefile is safe to include in the project after +https://issues.apache.org/jira/browse/LEGAL-58. + --------------------------------------------------------------------------- Distribution of "ocaml_make" diff --git a/test/csharp/ThriftTest/ThriftTest.csproj b/test/csharp/ThriftTest/ThriftTest.csproj index bb7f1765d..a55583ab6 100644 --- a/test/csharp/ThriftTest/ThriftTest.csproj +++ b/test/csharp/ThriftTest/ThriftTest.csproj @@ -1,3 +1,22 @@ + + diff --git a/tutorial/hs/ThriftTutorial.cabal b/tutorial/hs/ThriftTutorial.cabal index 381f32a75..1655ce7cd 100644 --- a/tutorial/hs/ThriftTutorial.cabal +++ b/tutorial/hs/ThriftTutorial.cabal @@ -1,3 +1,22 @@ +-- +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +-- KIND, either express or implied. See the License for the +-- specific language governing permissions and limitations +-- under the License. +-- + Name: ThriftTutorial Version: 0.1.0 Cabal-Version: >= 1.2