summaryrefslogtreecommitdiffstats
path: root/third_party/libjingle/files/talk/base/httpbase.cc
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/libjingle/files/talk/base/httpbase.cc')
-rw-r--r--third_party/libjingle/files/talk/base/httpbase.cc591
1 files changed, 0 insertions, 591 deletions
diff --git a/third_party/libjingle/files/talk/base/httpbase.cc b/third_party/libjingle/files/talk/base/httpbase.cc
deleted file mode 100644
index 4524036..0000000
--- a/third_party/libjingle/files/talk/base/httpbase.cc
+++ /dev/null
@@ -1,591 +0,0 @@
-/*
- * libjingle
- * Copyright 2004--2005, Google Inc.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
- * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
- * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifdef OSX
-#include <errno.h>
-#endif
-
-#ifdef WIN32
-#include "talk/base/win32.h"
-#else // !WIN32
-#define SEC_E_CERT_EXPIRED (-2146893016)
-#endif // !WIN32
-
-#include "talk/base/common.h"
-#include "talk/base/httpbase.h"
-#include "talk/base/logging.h"
-#include "talk/base/socket.h"
-#include "talk/base/stringutils.h"
-
-namespace talk_base {
-
-//////////////////////////////////////////////////////////////////////
-// Helpers
-//////////////////////////////////////////////////////////////////////
-
-bool MatchHeader(const char* str, size_t len, HttpHeader header) {
- const char* const header_str = ToString(header);
- const size_t header_len = strlen(header_str);
- return (len == header_len) && (_strnicmp(str, header_str, header_len) == 0);
-}
-
-//////////////////////////////////////////////////////////////////////
-// HttpParser
-//////////////////////////////////////////////////////////////////////
-
-HttpParser::HttpParser() {
- reset();
-}
-
-HttpParser::~HttpParser() {
-}
-
-void
-HttpParser::reset() {
- state_ = ST_LEADER;
- chunked_ = false;
- data_size_ = SIZE_UNKNOWN;
-}
-
-bool
-HttpParser::process(const char* buffer, size_t len, size_t& processed,
- HttpError& err) {
- processed = 0;
- err = HE_NONE;
-
- if (state_ >= ST_COMPLETE) {
- ASSERT(false);
- return false;
- }
-
- while (true) {
- if (state_ < ST_DATA) {
- size_t pos = processed;
- while ((pos < len) && (buffer[pos] != '\n')) {
- pos += 1;
- }
- if (pos >= len) {
- break; // don't have a full header
- }
- const char* line = buffer + processed;
- size_t len = (pos - processed);
- processed = pos + 1;
- while ((len > 0) && isspace(static_cast<unsigned char>(line[len-1]))) {
- len -= 1;
- }
- if (!process_line(line, len, err)) {
- return false; // no more processing
- }
- } else if (data_size_ == 0) {
- if (chunked_) {
- state_ = ST_CHUNKTERM;
- } else {
- return false;
- }
- } else {
- size_t available = len - processed;
- if (available <= 0) {
- break; // no more data
- }
- if ((data_size_ != SIZE_UNKNOWN) && (available > data_size_)) {
- available = data_size_;
- }
- size_t read = 0;
- err = onHttpRecvData(buffer + processed, available, read);
- if (err != HE_NONE) {
- return false; // error occurred
- }
- processed += read;
- if (data_size_ != SIZE_UNKNOWN) {
- data_size_ -= read;
- }
- }
- }
-
- return true;
-}
-
-bool
-HttpParser::process_line(const char* line, size_t len, HttpError& err) {
- switch (state_) {
- case ST_LEADER:
- state_ = ST_HEADERS;
- err = onHttpRecvLeader(line, len);
- break;
-
- case ST_HEADERS:
- if (len > 0) {
- const char* value = strchrn(line, len, ':');
- if (!value) {
- err = HE_PROTOCOL;
- break;
- }
- size_t nlen = (value - line);
- const char* eol = line + len;
- do {
- value += 1;
- } while ((value < eol) && isspace(static_cast<unsigned char>(*value)));
- size_t vlen = eol - value;
- if (MatchHeader(line, nlen, HH_CONTENT_LENGTH)) {
- if (sscanf(value, "%zu", &data_size_) != 1) {
- err = HE_PROTOCOL;
- break;
- }
- } else if (MatchHeader(line, nlen, HH_TRANSFER_ENCODING)) {
- if ((vlen == 7) && (_strnicmp(value, "chunked", 7) == 0)) {
- chunked_ = true;
- } else if ((vlen == 8) && (_strnicmp(value, "identity", 8) == 0)) {
- chunked_ = false;
- } else {
- err = HE_PROTOCOL;
- break;
- }
- }
- err = onHttpRecvHeader(line, nlen, value, vlen);
- } else {
- state_ = chunked_ ? ST_CHUNKSIZE : ST_DATA;
- err = onHttpRecvHeaderComplete(chunked_, data_size_);
- }
- break;
-
- case ST_CHUNKSIZE:
- if (len > 0) {
- char* ptr = NULL;
- data_size_ = strtoul(line, &ptr, 16);
- if (ptr != line + len) {
- err = HE_PROTOCOL;
- break;
- }
- state_ = (data_size_ == 0) ? ST_TRAILERS : ST_DATA;
- } else {
- err = HE_PROTOCOL;
- }
- break;
-
- case ST_CHUNKTERM:
- if (len > 0) {
- err = HE_PROTOCOL;
- } else {
- state_ = chunked_ ? ST_CHUNKSIZE : ST_DATA;
- }
- break;
-
- case ST_TRAILERS:
- if (len == 0) {
- return false;
- }
- // err = onHttpRecvTrailer();
- break;
-
- default:
- break;
- }
-
- return (err == HE_NONE);
-}
-
-void
-HttpParser::end_of_input() {
- if ((state_ == ST_DATA) && (data_size_ == SIZE_UNKNOWN)) {
- complete(HE_NONE);
- } else {
- complete(HE_DISCONNECTED);
- }
-}
-
-void
-HttpParser::complete(HttpError err) {
- if (state_ < ST_COMPLETE) {
- state_ = ST_COMPLETE;
- onHttpRecvComplete(err);
- }
-}
-
-//////////////////////////////////////////////////////////////////////
-// HttpBase
-//////////////////////////////////////////////////////////////////////
-
-HttpBase::HttpBase() : mode_(HM_NONE), data_(NULL), notify_(NULL),
- stream_(NULL) {
-}
-
-HttpBase::~HttpBase() {
-}
-
-bool
-HttpBase::isConnected() const {
- return (stream_ != NULL) && (stream_->GetState() == SS_OPEN);
-}
-
-bool
-HttpBase::attach(StreamInterface* stream) {
- if ((mode_ != HM_NONE) || (stream_ != NULL) || (stream == NULL)) {
- ASSERT(false);
- return false;
- }
- stream_ = stream;
- stream_->SignalEvent.connect(this, &HttpBase::OnEvent);
- mode_ = (stream_->GetState() == SS_OPENING) ? HM_CONNECT : HM_NONE;
- return true;
-}
-
-StreamInterface*
-HttpBase::detach() {
- if (mode_ != HM_NONE) {
- ASSERT(false);
- return NULL;
- }
- StreamInterface* stream = stream_;
- stream_ = NULL;
- if (stream) {
- stream->SignalEvent.disconnect(this);
- }
- return stream;
-}
-
-/*
-bool
-HttpBase::accept(PNSocket& socket) {
- if (mode_ != HM_NONE) {
- ASSERT(false);
- return false;
- }
-
- return socket.accept(stream_);
-}
-
-void
-HttpBase::connect(const SocketAddress& addr) {
- if (mode_ != HM_NONE) {
- ASSERT(false);
- return;
- }
-
- mode_ = HM_CONNECT;
-
- SocketAddress local;
- if (!stream_.connect(local, addr) && !stream_.isBlocking()) {
- onSocketConnect(&stream_, stream_.getError());
- }
-}
-*/
-void
-HttpBase::send(HttpData* data) {
- if (mode_ != HM_NONE) {
- ASSERT(false);
- return;
- } else if (!isConnected()) {
- OnEvent(stream_, SE_CLOSE, HE_DISCONNECTED);
- return;
- }
-
- mode_ = HM_SEND;
- data_ = data;
- len_ = 0;
- ignore_data_ = chunk_data_ = false;
-
- std::string encoding;
- if (data_->hasHeader(HH_TRANSFER_ENCODING, &encoding)
- && (encoding == "chunked")) {
- chunk_data_ = true;
- }
-
- len_ = data_->formatLeader(buffer_, sizeof(buffer_));
- len_ += strcpyn(buffer_ + len_, sizeof(buffer_) - len_, "\r\n");
- header_ = data_->begin();
- queue_headers();
-
- OnEvent(stream_, SE_WRITE, 0);
-}
-
-void
-HttpBase::recv(HttpData* data) {
- if (mode_ != HM_NONE) {
- ASSERT(false);
- return;
- } else if (!isConnected()) {
- OnEvent(stream_, SE_CLOSE, HE_DISCONNECTED);
- return;
- }
-
- mode_ = HM_RECV;
- data_ = data;
- len_ = 0;
- ignore_data_ = chunk_data_ = false;
-
- reset();
- OnEvent(stream_, SE_READ, 0);
-}
-
-void
-HttpBase::abort(HttpError err) {
- if (mode_ != HM_NONE) {
- if (stream_ != NULL) {
- stream_->Close();
- }
- do_complete(err);
- }
-}
-
-void
-HttpBase::flush_data() {
- while (true) {
- for (size_t start = 0; start < len_; ) {
- size_t written;
- int error;
- StreamResult result = stream_->Write(buffer_ + start, len_ - start,
- &written, &error);
- if (result == SR_SUCCESS) {
- //LOG_F(LS_INFO) << "wrote " << res << " bytes";
- start += written;
- continue;
- } else if (result == SR_BLOCK) {
- //LOG_F(LS_INFO) << "blocking";
- len_ -= start;
- memmove(buffer_, buffer_ + start, len_);
- return;
- } else {
- ASSERT(result == SR_ERROR);
- LOG_F(LS_ERROR) << "error";
- OnEvent(stream_, SE_CLOSE, error);
- return;
- }
- }
- len_ = 0;
-
- // Check for more headers
- if (header_ != data_->end()) {
- queue_headers();
- continue;
- }
-
- // Check for document data
- if (!data_->document.get())
- break;
-
- size_t offset = 0, reserve = 0;
- if (chunk_data_) {
- // Reserve 10 characters at the start for 8-byte hex value and \r\n
- offset = 10;
- // ... and 2 characters at the end for \r\n
- reserve = offset + 2;
- ASSERT(reserve < sizeof(buffer_));
- }
-
- int error = 0;
- StreamResult result = data_->document->Read(buffer_ + offset,
- sizeof(buffer_) - reserve,
- &len_, &error);
- if (result == SR_SUCCESS) {
- if (!chunk_data_)
- continue;
-
- // Prepend the length and append \r\n
- sprintfn(buffer_, offset, "%.*x", (offset - 2), len_);
- memcpy(buffer_ + offset - 2, "\r\n", 2);
- memcpy(buffer_ + offset + len_, "\r\n", 2);
- ASSERT(len_ + reserve <= sizeof(buffer_));
- len_ += reserve;
- } else if (result == SR_EOS) {
- if (!chunk_data_)
- break;
-
- // Append the empty chunk and empty trailers, then turn off chunking.
- len_ = sprintfn(buffer_, sizeof(buffer_), "0\r\n\r\n");
- chunk_data_ = false;
- } else {
- LOG_F(LS_ERROR) << "Read error: " << error;
- do_complete(HE_STREAM);
- return;
- }
- }
-
- do_complete();
-}
-
-void
-HttpBase::queue_headers() {
- while (header_ != data_->end()) {
- size_t len = sprintfn(buffer_ + len_, sizeof(buffer_) - len_,
- "%.*s: %.*s\r\n",
- header_->first.size(), header_->first.data(),
- header_->second.size(), header_->second.data());
- if (len_ + len < sizeof(buffer_) - 3) {
- len_ += len;
- ++header_;
- } else if (len_ == 0) {
- LOG(WARNING) << "discarding header that is too long: " << header_->first;
- ++header_;
- } else {
- break;
- }
- }
- if (header_ == data_->end()) {
- len_ += strcpyn(buffer_ + len_, sizeof(buffer_) - len_, "\r\n");
- }
-}
-
-void
-HttpBase::do_complete(HttpError err) {
- ASSERT(mode_ != HM_NONE);
- HttpMode mode = mode_;
- mode_ = HM_NONE;
- data_ = NULL;
- if (notify_) {
- notify_->onHttpComplete(mode, err);
- }
-}
-
-void
-HttpBase::OnEvent(StreamInterface* stream, int events, int error) {
- if ((events & SE_OPEN) && (mode_ == HM_CONNECT)) {
- do_complete();
- return;
- }
-
- if ((events & SE_WRITE) && (mode_ == HM_SEND)) {
- flush_data();
- return;
- }
-
- if ((events & SE_READ) && (mode_ == HM_RECV)) {
- // Do to the latency between receiving read notifications from
- // pseudotcpchannel, we rely on repeated calls to read in order to acheive
- // ideal throughput. The number of reads is limited to prevent starving
- // the caller.
- size_t loop_count = 0;
- const size_t kMaxReadCount = 20;
- while (true) {
- if (len_ >= sizeof(buffer_)) {
- do_complete(HE_OVERFLOW);
- return;
- }
- size_t read;
- int error;
- StreamResult result = stream_->Read(buffer_ + len_,
- sizeof(buffer_) - len_,
- &read, &error);
- if ((result == SR_BLOCK) || (result == SR_EOS))
- return;
- if (result == SR_ERROR) {
- OnEvent(stream_, SE_CLOSE, error);
- return;
- }
- ASSERT(result == SR_SUCCESS);
- //LOG(INFO) << "HttpBase << " << std::string(buffer_ + len_, res);
- len_ += read;
- HttpError herr;
- bool more = process(buffer_, len_, read, herr);
- len_ -= read;
- memcpy(buffer_, buffer_ + read, len_);
- if (!more) {
- complete(herr);
- return;
- }
- if (++loop_count > kMaxReadCount) {
- LOG_F(LS_WARNING) << "danger of starvation";
- break;
- }
- }
- return;
- }
-
- if ((events & SE_CLOSE) == 0)
- return;
-
- if (stream_ != NULL) {
- stream_->Close();
- }
- HttpError herr;
- // TODO: Pass through errors instead of translating them?
- if (error == 0) {
- herr = HE_DISCONNECTED;
- } else if (error == SOCKET_EACCES) {
- herr = HE_AUTH;
- } else if (error == SEC_E_CERT_EXPIRED) {
- herr = HE_CERTIFICATE_EXPIRED;
- } else {
- LOG_F(LS_ERROR) << "SE_CLOSE error: " << error;
- herr = HE_SOCKET;
- }
- if ((mode_ == HM_RECV) && (error == HE_NONE)) {
- end_of_input();
- } else if (mode_ != HM_NONE) {
- do_complete(mkerr(herr, HE_DISCONNECTED));
- } else if (notify_) {
- notify_->onHttpClosed(mkerr(herr, HE_DISCONNECTED));
- }
-}
-
-//
-// HttpParser Implementation
-//
-
-HttpError
-HttpBase::onHttpRecvLeader(const char* line, size_t len) {
- return data_->parseLeader(line, len);
-}
-
-HttpError
-HttpBase::onHttpRecvHeader(const char* name, size_t nlen, const char* value,
- size_t vlen) {
- std::string sname(name, nlen), svalue(value, vlen);
- data_->addHeader(sname, svalue);
- //LOG(INFO) << sname << ": " << svalue;
- return HE_NONE;
-}
-
-HttpError
-HttpBase::onHttpRecvHeaderComplete(bool chunked, size_t& data_size) {
- return notify_ ? notify_->onHttpHeaderComplete(chunked, data_size) : HE_NONE;
-}
-
-HttpError
-HttpBase::onHttpRecvData(const char* data, size_t len, size_t& read) {
- if (ignore_data_ || !data_->document.get()) {
- read = len;
- return HE_NONE;
- }
- int error = 0;
- switch (data_->document->Write(data, len, &read, &error)) {
- case SR_SUCCESS:
- return HE_NONE;
- case SR_EOS:
- case SR_BLOCK:
- LOG_F(LS_ERROR) << "Write EOS or block";
- return HE_STREAM;
- }
- LOG_F(LS_ERROR) << "Write error: " << error;
- return HE_STREAM;
-}
-
-void
-HttpBase::onHttpRecvComplete(HttpError err) {
- do_complete(err);
-}
-
-} // namespace talk_base