From 83da552062409ac195cad8bd09f5b208b30bd888 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20M=C3=B6ller?= Date: Wed, 26 Sep 2018 16:18:38 +0200 Subject: [PATCH] Delete unused HTTP server code There were remnants of use in proxy_unittest.cc, instantiating an HttpListenServer but not using it for anything. Also trim down httpcommon.h, the only function still in use is HttpAuthenticate. Bug: webrtc:6424 Change-Id: I9b122dedd6e8c923ed7bc721a336fe54192328c4 Reviewed-on: https://webrtc-review.googlesource.com/102141 Reviewed-by: Karl Wiberg Commit-Queue: Niels Moller Cr-Commit-Position: refs/heads/master@{#24884} --- rtc_base/BUILD.gn | 7 - rtc_base/httpbase.cc | 713 -------------------------------- rtc_base/httpbase.h | 189 --------- rtc_base/httpbase_unittest.cc | 358 ---------------- rtc_base/httpcommon.cc | 421 +------------------ rtc_base/httpcommon.h | 239 ----------- rtc_base/httpcommon_unittest.cc | 59 --- rtc_base/httpserver.cc | 268 ------------ rtc_base/httpserver.h | 140 ------- rtc_base/httpserver_unittest.cc | 127 ------ rtc_base/proxy_unittest.cc | 9 +- 11 files changed, 5 insertions(+), 2525 deletions(-) delete mode 100644 rtc_base/httpbase.cc delete mode 100644 rtc_base/httpbase.h delete mode 100644 rtc_base/httpbase_unittest.cc delete mode 100644 rtc_base/httpcommon_unittest.cc delete mode 100644 rtc_base/httpserver.cc delete mode 100644 rtc_base/httpserver.h delete mode 100644 rtc_base/httpserver_unittest.cc diff --git a/rtc_base/BUILD.gn b/rtc_base/BUILD.gn index f90b7eefa0..e72e740e05 100644 --- a/rtc_base/BUILD.gn +++ b/rtc_base/BUILD.gn @@ -1013,10 +1013,6 @@ rtc_source_set("rtc_base_tests_utils") { "firewallsocketserver.h", "gunit.cc", "gunit.h", - "httpbase.cc", - "httpbase.h", - "httpserver.cc", - "httpserver.h", "memory_usage.cc", "memory_usage.h", "natserver.cc", @@ -1286,9 +1282,6 @@ if (rtc_include_tests) { "crc32_unittest.cc", "data_rate_limiter_unittest.cc", "helpers_unittest.cc", - "httpbase_unittest.cc", - "httpcommon_unittest.cc", - "httpserver_unittest.cc", "ipaddress_unittest.cc", "memory_usage_unittest.cc", "messagedigest_unittest.cc", diff --git a/rtc_base/httpbase.cc b/rtc_base/httpbase.cc deleted file mode 100644 index 8f2869d73f..0000000000 --- a/rtc_base/httpbase.cc +++ /dev/null @@ -1,713 +0,0 @@ -/* - * Copyright 2004 The WebRTC Project Authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include - -#if defined(WEBRTC_WIN) -#include "rtc_base/win32.h" -#else // !WEBRTC_WIN -#define SEC_E_CERT_EXPIRED (-2146893016) -#endif // !WEBRTC_WIN - -#include "rtc_base/checks.h" -#include "rtc_base/httpbase.h" -#include "rtc_base/logging.h" -#include "rtc_base/socket.h" -#include "rtc_base/stringutils.h" -#include "rtc_base/system/fallthrough.h" -#include "rtc_base/thread.h" - -namespace rtc { - -////////////////////////////////////////////////////////////////////// -// 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); -} - -enum { MSG_READ }; - -////////////////////////////////////////////////////////////////////// -// HttpParser -////////////////////////////////////////////////////////////////////// - -HttpParser::HttpParser() { - reset(); -} - -HttpParser::~HttpParser() {} - -void HttpParser::reset() { - state_ = ST_LEADER; - chunked_ = false; - data_size_ = SIZE_UNKNOWN; -} - -HttpParser::ProcessResult HttpParser::Process(const char* buffer, - size_t len, - size_t* processed, - HttpError* error) { - *processed = 0; - *error = HE_NONE; - - if (state_ >= ST_COMPLETE) { - RTC_NOTREACHED(); - return PR_COMPLETE; - } - - 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(line[len - 1]))) { - len -= 1; - } - ProcessResult result = ProcessLine(line, len, error); - RTC_LOG(LS_VERBOSE) << "Processed line, result=" << result; - - if (PR_CONTINUE != result) { - return result; - } - } else if (data_size_ == 0) { - if (chunked_) { - state_ = ST_CHUNKTERM; - } else { - return PR_COMPLETE; - } - } 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; - ProcessResult result = - ProcessData(buffer + *processed, available, read, error); - RTC_LOG(LS_VERBOSE) << "Processed data, result: " << result - << " read: " << read << " err: " << error; - - if (PR_CONTINUE != result) { - return result; - } - *processed += read; - if (data_size_ != SIZE_UNKNOWN) { - data_size_ -= read; - } - } - } - - return PR_CONTINUE; -} - -HttpParser::ProcessResult HttpParser::ProcessLine(const char* line, - size_t len, - HttpError* error) { - RTC_LOG_F(LS_VERBOSE) << " state: " << state_ - << " line: " << std::string(line, len) - << " len: " << len << " err: " << error; - - switch (state_) { - case ST_LEADER: - state_ = ST_HEADERS; - return ProcessLeader(line, len, error); - - case ST_HEADERS: - if (len > 0) { - const char* value = strchrn(line, len, ':'); - if (!value) { - *error = HE_PROTOCOL; - return PR_COMPLETE; - } - size_t nlen = (value - line); - const char* eol = line + len; - do { - value += 1; - } while ((value < eol) && isspace(static_cast(*value))); - size_t vlen = eol - value; - if (MatchHeader(line, nlen, HH_CONTENT_LENGTH)) { - // sscanf isn't safe with strings that aren't null-terminated, and - // there is no guarantee that |value| is. Create a local copy that is - // null-terminated. - std::string value_str(value, vlen); - unsigned int temp_size; - if (sscanf(value_str.c_str(), "%u", &temp_size) != 1) { - *error = HE_PROTOCOL; - return PR_COMPLETE; - } - data_size_ = static_cast(temp_size); - } 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 { - *error = HE_PROTOCOL; - return PR_COMPLETE; - } - } - return ProcessHeader(line, nlen, value, vlen, error); - } else { - state_ = chunked_ ? ST_CHUNKSIZE : ST_DATA; - return ProcessHeaderComplete(chunked_, data_size_, error); - } - break; - - case ST_CHUNKSIZE: - if (len > 0) { - char* ptr = nullptr; - data_size_ = strtoul(line, &ptr, 16); - if (ptr != line + len) { - *error = HE_PROTOCOL; - return PR_COMPLETE; - } - state_ = (data_size_ == 0) ? ST_TRAILERS : ST_DATA; - } else { - *error = HE_PROTOCOL; - return PR_COMPLETE; - } - break; - - case ST_CHUNKTERM: - if (len > 0) { - *error = HE_PROTOCOL; - return PR_COMPLETE; - } else { - state_ = chunked_ ? ST_CHUNKSIZE : ST_DATA; - } - break; - - case ST_TRAILERS: - if (len == 0) { - return PR_COMPLETE; - } - // *error = onHttpRecvTrailer(); - break; - - default: - RTC_NOTREACHED(); - break; - } - - return PR_CONTINUE; -} - -bool HttpParser::is_valid_end_of_input() const { - return (state_ == ST_DATA) && (data_size_ == SIZE_UNKNOWN); -} - -void HttpParser::complete(HttpError error) { - if (state_ < ST_COMPLETE) { - state_ = ST_COMPLETE; - OnComplete(error); - } -} - -////////////////////////////////////////////////////////////////////// -// HttpBase -////////////////////////////////////////////////////////////////////// - -HttpBase::HttpBase() - : mode_(HM_NONE), data_(nullptr), notify_(nullptr), http_stream_(nullptr) {} - -HttpBase::~HttpBase() { - RTC_DCHECK(HM_NONE == mode_); -} - -bool HttpBase::isConnected() const { - return (http_stream_ != nullptr) && (http_stream_->GetState() == SS_OPEN); -} - -bool HttpBase::attach(StreamInterface* stream) { - if ((mode_ != HM_NONE) || (http_stream_ != nullptr) || (stream == nullptr)) { - RTC_NOTREACHED(); - return false; - } - http_stream_ = stream; - http_stream_->SignalEvent.connect(this, &HttpBase::OnHttpStreamEvent); - mode_ = (http_stream_->GetState() == SS_OPENING) ? HM_CONNECT : HM_NONE; - return true; -} - -StreamInterface* HttpBase::detach() { - RTC_DCHECK(HM_NONE == mode_); - if (mode_ != HM_NONE) { - return nullptr; - } - StreamInterface* stream = http_stream_; - http_stream_ = nullptr; - if (stream) { - stream->SignalEvent.disconnect(this); - } - return stream; -} - -void HttpBase::send(HttpData* data) { - RTC_DCHECK(HM_NONE == mode_); - if (mode_ != HM_NONE) { - return; - } else if (!isConnected()) { - OnHttpStreamEvent(http_stream_, SE_CLOSE, HE_DISCONNECTED); - return; - } - - mode_ = HM_SEND; - data_ = data; - len_ = 0; - ignore_data_ = chunk_data_ = false; - - if (data_->document) { - data_->document->SignalEvent.connect(this, &HttpBase::OnDocumentEvent); - } - - 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(); - if (header_ == data_->end()) { - // We must call this at least once, in the case where there are no headers. - queue_headers(); - } - - flush_data(); -} - -void HttpBase::recv(HttpData* data) { - RTC_DCHECK(HM_NONE == mode_); - if (mode_ != HM_NONE) { - return; - } else if (!isConnected()) { - OnHttpStreamEvent(http_stream_, SE_CLOSE, HE_DISCONNECTED); - return; - } - - mode_ = HM_RECV; - data_ = data; - len_ = 0; - ignore_data_ = chunk_data_ = false; - - reset(); - read_and_process_data(); -} - -void HttpBase::abort(HttpError err) { - if (mode_ != HM_NONE) { - if (http_stream_ != nullptr) { - http_stream_->Close(); - } - do_complete(err); - } -} - -HttpError HttpBase::HandleStreamClose(int error) { - if (http_stream_ != nullptr) { - http_stream_->Close(); - } - if (error == 0) { - if ((mode_ == HM_RECV) && is_valid_end_of_input()) { - return HE_NONE; - } else { - return HE_DISCONNECTED; - } - } else if (error == SOCKET_EACCES) { - return HE_AUTH; - } else if (error == SEC_E_CERT_EXPIRED) { - return HE_CERTIFICATE_EXPIRED; - } - RTC_LOG_F(LS_ERROR) << "(" << error << ")"; - return (HM_CONNECT == mode_) ? HE_CONNECT_FAILED : HE_SOCKET_ERROR; -} - -bool HttpBase::DoReceiveLoop(HttpError* error) { - RTC_DCHECK(HM_RECV == mode_); - RTC_DCHECK(nullptr != error); - - // 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; - bool process_requires_more_data = false; - do { - // The most frequent use of this function is response to new data available - // on http_stream_. Therefore, we optimize by attempting to read from the - // network first (as opposed to processing existing data first). - - if (len_ < sizeof(buffer_)) { - // Attempt to buffer more data. - size_t read; - int read_error; - StreamResult read_result = http_stream_->Read( - buffer_ + len_, sizeof(buffer_) - len_, &read, &read_error); - switch (read_result) { - case SR_SUCCESS: - RTC_DCHECK(len_ + read <= sizeof(buffer_)); - len_ += read; - break; - case SR_BLOCK: - if (process_requires_more_data) { - // We're can't make progress until more data is available. - return false; - } - // Attempt to process the data already in our buffer. - break; - case SR_EOS: - // Clean close, with no error. - read_error = 0; - RTC_FALLTHROUGH(); // Fall through to HandleStreamClose. - case SR_ERROR: - *error = HandleStreamClose(read_error); - return true; - } - } else if (process_requires_more_data) { - // We have too much unprocessed data in our buffer. This should only - // occur when a single HTTP header is longer than the buffer size (32K). - // Anything longer than that is almost certainly an error. - *error = HE_OVERFLOW; - return true; - } - - // Process data in our buffer. Process is not guaranteed to process all - // the buffered data. In particular, it will wait until a complete - // protocol element (such as http header, or chunk size) is available, - // before processing it in its entirety. Also, it is valid and sometimes - // necessary to call Process with an empty buffer, since the state machine - // may have interrupted state transitions to complete. - size_t processed; - ProcessResult process_result = Process(buffer_, len_, &processed, error); - RTC_DCHECK(processed <= len_); - len_ -= processed; - memmove(buffer_, buffer_ + processed, len_); - switch (process_result) { - case PR_CONTINUE: - // We need more data to make progress. - process_requires_more_data = true; - break; - case PR_BLOCK: - // We're stalled on writing the processed data. - return false; - case PR_COMPLETE: - // *error already contains the correct code. - return true; - } - } while (++loop_count <= kMaxReadCount); - - RTC_LOG_F(LS_WARNING) << "danger of starvation"; - return false; -} - -void HttpBase::read_and_process_data() { - HttpError error; - if (DoReceiveLoop(&error)) { - complete(error); - } -} - -void HttpBase::flush_data() { - RTC_DCHECK(HM_SEND == mode_); - - // When send_required is true, no more buffering can occur without a network - // write. - bool send_required = (len_ >= sizeof(buffer_)); - - while (true) { - RTC_DCHECK(len_ <= sizeof(buffer_)); - - // HTTP is inherently sensitive to round trip latency, since a frequent use - // case is for small requests and responses to be sent back and forth, and - // the lack of pipelining forces a single request to take a minimum of the - // round trip time. As a result, it is to our benefit to pack as much data - // into each packet as possible. Thus, we defer network writes until we've - // buffered as much data as possible. - - if (!send_required && (header_ != data_->end())) { - // First, attempt to queue more header data. - send_required = queue_headers(); - } - - if (!send_required && data_->document) { - // Next, attempt to queue document data. - - const size_t kChunkDigits = 8; - size_t offset, reserve; - if (chunk_data_) { - // Reserve characters at the start for X-byte hex value and \r\n - offset = len_ + kChunkDigits + 2; - // ... and 2 characters at the end for \r\n - reserve = offset + 2; - } else { - offset = len_; - reserve = offset; - } - - if (reserve >= sizeof(buffer_)) { - send_required = true; - } else { - size_t read; - int error; - StreamResult result = data_->document->Read( - buffer_ + offset, sizeof(buffer_) - reserve, &read, &error); - if (result == SR_SUCCESS) { - RTC_DCHECK(reserve + read <= sizeof(buffer_)); - if (chunk_data_) { - // Prepend the chunk length in hex. - // Note: sprintfn appends a null terminator, which is why we can't - // combine it with the line terminator. - sprintfn(buffer_ + len_, kChunkDigits + 1, "%.*x", kChunkDigits, - read); - // Add line terminator to the chunk length. - memcpy(buffer_ + len_ + kChunkDigits, "\r\n", 2); - // Add line terminator to the end of the chunk. - memcpy(buffer_ + offset + read, "\r\n", 2); - } - len_ = reserve + read; - } else if (result == SR_BLOCK) { - // Nothing to do but flush data to the network. - send_required = true; - } else if (result == SR_EOS) { - if (chunk_data_) { - // Append the empty chunk and empty trailers, then turn off - // chunking. - RTC_DCHECK(len_ + 5 <= sizeof(buffer_)); - memcpy(buffer_ + len_, "0\r\n\r\n", 5); - len_ += 5; - chunk_data_ = false; - } else if (0 == len_) { - // No more data to read, and no more data to write. - do_complete(); - return; - } - // Although we are done reading data, there is still data which needs - // to be flushed to the network. - send_required = true; - } else { - RTC_LOG_F(LS_ERROR) << "Read error: " << error; - do_complete(HE_STREAM); - return; - } - } - } - - if (0 == len_) { - // No data currently available to send. - if (!data_->document) { - // If there is no source document, that means we're done. - do_complete(); - } - return; - } - - size_t written; - int error; - StreamResult result = http_stream_->Write(buffer_, len_, &written, &error); - if (result == SR_SUCCESS) { - RTC_DCHECK(written <= len_); - len_ -= written; - memmove(buffer_, buffer_ + written, len_); - send_required = false; - } else if (result == SR_BLOCK) { - if (send_required) { - // Nothing more we can do until network is writeable. - return; - } - } else { - RTC_DCHECK(result == SR_ERROR); - RTC_LOG_F(LS_ERROR) << "error"; - OnHttpStreamEvent(http_stream_, SE_CLOSE, error); - return; - } - } - - RTC_NOTREACHED(); -} - -bool HttpBase::queue_headers() { - RTC_DCHECK(HM_SEND == mode_); - 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) { - RTC_LOG(WARNING) << "discarding header that is too long: " - << header_->first; - ++header_; - } else { - // Not enough room for the next header, write to network first. - return true; - } - } - // End of headers - len_ += strcpyn(buffer_ + len_, sizeof(buffer_) - len_, "\r\n"); - return false; -} - -void HttpBase::do_complete(HttpError err) { - RTC_DCHECK(mode_ != HM_NONE); - HttpMode mode = mode_; - mode_ = HM_NONE; - if (data_ && data_->document) { - data_->document->SignalEvent.disconnect(this); - } - data_ = nullptr; - if (notify_) { - notify_->onHttpComplete(mode, err); - } -} - -// -// Stream Signals -// - -void HttpBase::OnHttpStreamEvent(StreamInterface* stream, - int events, - int error) { - RTC_DCHECK(stream == http_stream_); - 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)) { - read_and_process_data(); - return; - } - - if ((events & SE_CLOSE) == 0) - return; - - HttpError http_error = HandleStreamClose(error); - if (mode_ == HM_RECV) { - complete(http_error); - } else if (mode_ != HM_NONE) { - do_complete(http_error); - } else if (notify_) { - notify_->onHttpClosed(http_error); - } -} - -void HttpBase::OnDocumentEvent(StreamInterface* stream, int events, int error) { - RTC_DCHECK(stream == data_->document.get()); - if ((events & SE_WRITE) && (mode_ == HM_RECV)) { - read_and_process_data(); - return; - } - - if ((events & SE_READ) && (mode_ == HM_SEND)) { - flush_data(); - return; - } - - if (events & SE_CLOSE) { - RTC_LOG_F(LS_ERROR) << "Read error: " << error; - do_complete(HE_STREAM); - return; - } -} - -// -// HttpParser Implementation -// - -HttpParser::ProcessResult HttpBase::ProcessLeader(const char* line, - size_t len, - HttpError* error) { - *error = data_->parseLeader(line, len); - return (HE_NONE == *error) ? PR_CONTINUE : PR_COMPLETE; -} - -HttpParser::ProcessResult HttpBase::ProcessHeader(const char* name, - size_t nlen, - const char* value, - size_t vlen, - HttpError* error) { - std::string sname(name, nlen), svalue(value, vlen); - data_->addHeader(sname, svalue); - return PR_CONTINUE; -} - -HttpParser::ProcessResult HttpBase::ProcessHeaderComplete(bool chunked, - size_t& data_size, - HttpError* error) { - if (notify_) { - *error = notify_->onHttpHeaderComplete(chunked, data_size); - // The request must not be aborted as a result of this callback. - RTC_DCHECK(nullptr != data_); - } - if ((HE_NONE == *error) && data_->document) { - data_->document->SignalEvent.connect(this, &HttpBase::OnDocumentEvent); - } - if (HE_NONE != *error) { - return PR_COMPLETE; - } - return PR_CONTINUE; -} - -HttpParser::ProcessResult HttpBase::ProcessData(const char* data, - size_t len, - size_t& read, - HttpError* error) { - if (ignore_data_ || !data_->document) { - read = len; - return PR_CONTINUE; - } - int write_error = 0; - switch (data_->document->Write(data, len, &read, &write_error)) { - case SR_SUCCESS: - return PR_CONTINUE; - case SR_BLOCK: - return PR_BLOCK; - case SR_EOS: - RTC_LOG_F(LS_ERROR) << "Unexpected EOS"; - *error = HE_STREAM; - return PR_COMPLETE; - case SR_ERROR: - default: - RTC_LOG_F(LS_ERROR) << "Write error: " << write_error; - *error = HE_STREAM; - return PR_COMPLETE; - } -} - -void HttpBase::OnComplete(HttpError err) { - RTC_LOG_F(LS_VERBOSE); - do_complete(err); -} - -} // namespace rtc diff --git a/rtc_base/httpbase.h b/rtc_base/httpbase.h deleted file mode 100644 index b0e24254be..0000000000 --- a/rtc_base/httpbase.h +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright 2004 The WebRTC Project Authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef RTC_BASE_HTTPBASE_H_ -#define RTC_BASE_HTTPBASE_H_ - -#include "rtc_base/httpcommon.h" - -namespace rtc { - -class StreamInterface; - -/////////////////////////////////////////////////////////////////////////////// -// HttpParser - Parses an HTTP stream provided via Process and end_of_input, and -// generates events for: -// Structural Elements: Leader, Headers, Document Data -// Events: End of Headers, End of Document, Errors -/////////////////////////////////////////////////////////////////////////////// - -class HttpParser { - public: - enum ProcessResult { PR_CONTINUE, PR_BLOCK, PR_COMPLETE }; - HttpParser(); - virtual ~HttpParser(); - - void reset(); - ProcessResult Process(const char* buffer, - size_t len, - size_t* processed, - HttpError* error); - bool is_valid_end_of_input() const; - void complete(HttpError err); - - size_t GetDataRemaining() const { return data_size_; } - - protected: - ProcessResult ProcessLine(const char* line, size_t len, HttpError* error); - - // HttpParser Interface - virtual ProcessResult ProcessLeader(const char* line, - size_t len, - HttpError* error) = 0; - virtual ProcessResult ProcessHeader(const char* name, - size_t nlen, - const char* value, - size_t vlen, - HttpError* error) = 0; - virtual ProcessResult ProcessHeaderComplete(bool chunked, - size_t& data_size, - HttpError* error) = 0; - virtual ProcessResult ProcessData(const char* data, - size_t len, - size_t& read, - HttpError* error) = 0; - virtual void OnComplete(HttpError err) = 0; - - private: - enum State { - ST_LEADER, - ST_HEADERS, - ST_CHUNKSIZE, - ST_CHUNKTERM, - ST_TRAILERS, - ST_DATA, - ST_COMPLETE - } state_; - bool chunked_; - size_t data_size_; -}; - -/////////////////////////////////////////////////////////////////////////////// -// IHttpNotify -/////////////////////////////////////////////////////////////////////////////// - -enum HttpMode { HM_NONE, HM_CONNECT, HM_RECV, HM_SEND }; - -class IHttpNotify { - public: - virtual ~IHttpNotify() {} - virtual HttpError onHttpHeaderComplete(bool chunked, size_t& data_size) = 0; - virtual void onHttpComplete(HttpMode mode, HttpError err) = 0; - virtual void onHttpClosed(HttpError err) = 0; -}; - -/////////////////////////////////////////////////////////////////////////////// -// HttpBase - Provides a state machine for implementing HTTP-based components. -// Attach HttpBase to a StreamInterface which represents a bidirectional HTTP -// stream, and then call send() or recv() to initiate sending or receiving one -// side of an HTTP transaction. By default, HttpBase operates as an I/O pump, -// moving data from the HTTP stream to the HttpData object and vice versa. -// However, it can also operate in stream mode, in which case the user of the -// stream interface drives I/O via calls to Read(). -/////////////////////////////////////////////////////////////////////////////// - -class HttpBase : private HttpParser, public sigslot::has_slots<> { - public: - HttpBase(); - ~HttpBase() override; - - void notify(IHttpNotify* notify) { notify_ = notify; } - bool attach(StreamInterface* stream); - StreamInterface* stream() { return http_stream_; } - StreamInterface* detach(); - bool isConnected() const; - - void send(HttpData* data); - void recv(HttpData* data); - void abort(HttpError err); - - HttpMode mode() const { return mode_; } - - void set_ignore_data(bool ignore) { ignore_data_ = ignore; } - bool ignore_data() const { return ignore_data_; } - - protected: - // Do cleanup when the http stream closes (error may be 0 for a clean - // shutdown), and return the error code to signal. - HttpError HandleStreamClose(int error); - - // DoReceiveLoop acts as a data pump, pulling data from the http stream, - // pushing it through the HttpParser, and then populating the HttpData object - // based on the callbacks from the parser. One of the most interesting - // callbacks is ProcessData, which provides the actual http document body. - // This data is then written to the HttpData::document. As a result, data - // flows from the network to the document, with some incidental protocol - // parsing in between. - // Ideally, we would pass in the document* to DoReceiveLoop, to more easily - // support GetDocumentStream(). However, since the HttpParser is callback - // driven, we are forced to store the pointer somewhere until the callback - // is triggered. - // Returns true if the received document has finished, and - // HttpParser::complete should be called. - bool DoReceiveLoop(HttpError* err); - - void read_and_process_data(); - void flush_data(); - bool queue_headers(); - void do_complete(HttpError err = HE_NONE); - - void OnHttpStreamEvent(StreamInterface* stream, int events, int error); - void OnDocumentEvent(StreamInterface* stream, int events, int error); - - // HttpParser Interface - ProcessResult ProcessLeader(const char* line, - size_t len, - HttpError* error) override; - ProcessResult ProcessHeader(const char* name, - size_t nlen, - const char* value, - size_t vlen, - HttpError* error) override; - ProcessResult ProcessHeaderComplete(bool chunked, - size_t& data_size, - HttpError* error) override; - ProcessResult ProcessData(const char* data, - size_t len, - size_t& read, - HttpError* error) override; - void OnComplete(HttpError err) override; - - private: - class DocumentStream; - friend class DocumentStream; - - enum { kBufferSize = 32 * 1024 }; - - HttpMode mode_; - HttpData* data_; - IHttpNotify* notify_; - StreamInterface* http_stream_; - char buffer_[kBufferSize]; - size_t len_; - - bool ignore_data_, chunk_data_; - HttpData::const_iterator header_; -}; - -/////////////////////////////////////////////////////////////////////////////// - -} // namespace rtc - -#endif // RTC_BASE_HTTPBASE_H_ diff --git a/rtc_base/httpbase_unittest.cc b/rtc_base/httpbase_unittest.cc deleted file mode 100644 index 35321da243..0000000000 --- a/rtc_base/httpbase_unittest.cc +++ /dev/null @@ -1,358 +0,0 @@ -/* - * Copyright 2004 The WebRTC Project Authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include - -#include "rtc_base/gunit.h" -#include "rtc_base/httpbase.h" -#include "rtc_base/testutils.h" - -namespace rtc { - -const char* const kHttpResponse = - "HTTP/1.1 200\r\n" - "Connection: Keep-Alive\r\n" - "Content-Type: text/plain\r\n" - "Proxy-Authorization: 42\r\n" - "Transfer-Encoding: chunked\r\n" - "\r\n" - "00000008\r\n" - "Goodbye!\r\n" - "0\r\n\r\n"; - -const char* const kHttpEmptyResponse = - "HTTP/1.1 200\r\n" - "Connection: Keep-Alive\r\n" - "Content-Length: 0\r\n" - "Proxy-Authorization: 42\r\n" - "\r\n"; - -class HttpBaseTest : public testing::Test, public IHttpNotify { - public: - enum EventType { E_HEADER_COMPLETE, E_COMPLETE, E_CLOSED }; - struct Event { - EventType event; - bool chunked; - size_t data_size; - HttpMode mode; - HttpError err; - }; - HttpBaseTest() : mem(nullptr), http_stream(nullptr) {} - - void TearDown() override { - delete http_stream; - // Avoid an ASSERT, in case a test doesn't clean up properly - base.abort(HE_NONE); - } - - HttpError onHttpHeaderComplete(bool chunked, size_t& data_size) override { - RTC_LOG_F(LS_VERBOSE) << "chunked: " << chunked << " size: " << data_size; - Event e = {E_HEADER_COMPLETE, chunked, data_size, HM_NONE, HE_NONE}; - events.push_back(e); - return HE_NONE; - } - void onHttpComplete(HttpMode mode, HttpError err) override { - RTC_LOG_F(LS_VERBOSE) << "mode: " << mode << " err: " << err; - Event e = {E_COMPLETE, false, 0, mode, err}; - events.push_back(e); - } - void onHttpClosed(HttpError err) override { - RTC_LOG_F(LS_VERBOSE) << "err: " << err; - Event e = {E_CLOSED, false, 0, HM_NONE, err}; - events.push_back(e); - } - - void SetupSource(const char* response); - - void VerifyHeaderComplete(size_t event_count, bool empty_doc); - void VerifyDocumentContents(const char* expected_data, - size_t expected_length = SIZE_UNKNOWN); - - void VerifyDocumentStreamIsOpening(); - void VerifyDocumentStreamOpenEvent(); - void ReadDocumentStreamData(const char* expected_data); - void VerifyDocumentStreamIsEOS(); - - void SetupDocument(const char* response); - void VerifySourceContents(const char* expected_data, - size_t expected_length = SIZE_UNKNOWN); - - void VerifyTransferComplete(HttpMode mode, HttpError error); - - HttpBase base; - MemoryStream* mem; - HttpResponseData data; - - // The source of http data, and source events - webrtc::testing::StreamSource src; - std::vector events; - - // Stream events - StreamInterface* http_stream; - webrtc::testing::StreamSink sink; -}; - -void HttpBaseTest::SetupSource(const char* http_data) { - RTC_LOG_F(LS_VERBOSE) << "Enter"; - - src.SetState(SS_OPENING); - src.QueueString(http_data); - - base.notify(this); - base.attach(&src); - EXPECT_TRUE(events.empty()); - - src.SetState(SS_OPEN); - ASSERT_EQ(1U, events.size()); - EXPECT_EQ(E_COMPLETE, events[0].event); - EXPECT_EQ(HM_CONNECT, events[0].mode); - EXPECT_EQ(HE_NONE, events[0].err); - events.clear(); - - mem = new MemoryStream; - data.document.reset(mem); - RTC_LOG_F(LS_VERBOSE) << "Exit"; -} - -void HttpBaseTest::VerifyHeaderComplete(size_t event_count, bool empty_doc) { - RTC_LOG_F(LS_VERBOSE) << "Enter"; - - ASSERT_EQ(event_count, events.size()); - EXPECT_EQ(E_HEADER_COMPLETE, events[0].event); - - std::string header; - EXPECT_EQ(HVER_1_1, data.version); - EXPECT_EQ(static_cast(HC_OK), data.scode); - EXPECT_TRUE(data.hasHeader(HH_PROXY_AUTHORIZATION, &header)); - EXPECT_EQ("42", header); - EXPECT_TRUE(data.hasHeader(HH_CONNECTION, &header)); - EXPECT_EQ("Keep-Alive", header); - - if (empty_doc) { - EXPECT_FALSE(events[0].chunked); - EXPECT_EQ(0U, events[0].data_size); - - EXPECT_TRUE(data.hasHeader(HH_CONTENT_LENGTH, &header)); - EXPECT_EQ("0", header); - } else { - EXPECT_TRUE(events[0].chunked); - EXPECT_EQ(SIZE_UNKNOWN, events[0].data_size); - - EXPECT_TRUE(data.hasHeader(HH_CONTENT_TYPE, &header)); - EXPECT_EQ("text/plain", header); - EXPECT_TRUE(data.hasHeader(HH_TRANSFER_ENCODING, &header)); - EXPECT_EQ("chunked", header); - } - RTC_LOG_F(LS_VERBOSE) << "Exit"; -} - -void HttpBaseTest::VerifyDocumentContents(const char* expected_data, - size_t expected_length) { - RTC_LOG_F(LS_VERBOSE) << "Enter"; - - if (SIZE_UNKNOWN == expected_length) { - expected_length = strlen(expected_data); - } - EXPECT_EQ(mem, data.document.get()); - - size_t length; - mem->GetSize(&length); - EXPECT_EQ(expected_length, length); - EXPECT_TRUE(0 == memcmp(expected_data, mem->GetBuffer(), length)); - RTC_LOG_F(LS_VERBOSE) << "Exit"; -} - -void HttpBaseTest::VerifyDocumentStreamIsOpening() { - RTC_LOG_F(LS_VERBOSE) << "Enter"; - ASSERT_TRUE(nullptr != http_stream); - EXPECT_EQ(0, sink.Events(http_stream)); - EXPECT_EQ(SS_OPENING, http_stream->GetState()); - - size_t read = 0; - char buffer[5] = {0}; - EXPECT_EQ(SR_BLOCK, - http_stream->Read(buffer, sizeof(buffer), &read, nullptr)); - RTC_LOG_F(LS_VERBOSE) << "Exit"; -} - -void HttpBaseTest::VerifyDocumentStreamOpenEvent() { - RTC_LOG_F(LS_VERBOSE) << "Enter"; - - ASSERT_TRUE(nullptr != http_stream); - EXPECT_EQ(SE_OPEN | SE_READ, sink.Events(http_stream)); - EXPECT_EQ(SS_OPEN, http_stream->GetState()); - - // HTTP headers haven't arrived yet - EXPECT_EQ(0U, events.size()); - EXPECT_EQ(static_cast(HC_INTERNAL_SERVER_ERROR), data.scode); - RTC_LOG_F(LS_VERBOSE) << "Exit"; -} - -void HttpBaseTest::ReadDocumentStreamData(const char* expected_data) { - RTC_LOG_F(LS_VERBOSE) << "Enter"; - - ASSERT_TRUE(nullptr != http_stream); - EXPECT_EQ(SS_OPEN, http_stream->GetState()); - - // Pump the HTTP I/O using Read, and verify the results. - size_t verified_length = 0; - const size_t expected_length = strlen(expected_data); - while (verified_length < expected_length) { - size_t read = 0; - char buffer[5] = {0}; - size_t amt_to_read = - std::min(expected_length - verified_length, sizeof(buffer)); - EXPECT_EQ(SR_SUCCESS, - http_stream->Read(buffer, amt_to_read, &read, nullptr)); - EXPECT_EQ(amt_to_read, read); - EXPECT_TRUE(0 == memcmp(expected_data + verified_length, buffer, read)); - verified_length += read; - } - RTC_LOG_F(LS_VERBOSE) << "Exit"; -} - -void HttpBaseTest::VerifyDocumentStreamIsEOS() { - RTC_LOG_F(LS_VERBOSE) << "Enter"; - - ASSERT_TRUE(nullptr != http_stream); - size_t read = 0; - char buffer[5] = {0}; - EXPECT_EQ(SR_EOS, http_stream->Read(buffer, sizeof(buffer), &read, nullptr)); - EXPECT_EQ(SS_CLOSED, http_stream->GetState()); - - // When EOS is caused by Read, we don't expect SE_CLOSE - EXPECT_EQ(0, sink.Events(http_stream)); - RTC_LOG_F(LS_VERBOSE) << "Exit"; -} - -void HttpBaseTest::SetupDocument(const char* document_data) { - RTC_LOG_F(LS_VERBOSE) << "Enter"; - src.SetState(SS_OPEN); - - base.notify(this); - base.attach(&src); - EXPECT_TRUE(events.empty()); - - if (document_data) { - // Note: we could just call data.set_success("text/plain", mem), but that - // won't allow us to use the chunked transfer encoding. - mem = new MemoryStream(document_data); - data.document.reset(mem); - data.setHeader(HH_CONTENT_TYPE, "text/plain"); - data.setHeader(HH_TRANSFER_ENCODING, "chunked"); - } else { - data.setHeader(HH_CONTENT_LENGTH, "0"); - } - data.scode = HC_OK; - data.setHeader(HH_PROXY_AUTHORIZATION, "42"); - data.setHeader(HH_CONNECTION, "Keep-Alive"); - RTC_LOG_F(LS_VERBOSE) << "Exit"; -} - -void HttpBaseTest::VerifySourceContents(const char* expected_data, - size_t expected_length) { - RTC_LOG_F(LS_VERBOSE) << "Enter"; - if (SIZE_UNKNOWN == expected_length) { - expected_length = strlen(expected_data); - } - std::string contents = src.ReadData(); - EXPECT_EQ(expected_length, contents.length()); - EXPECT_TRUE(0 == memcmp(expected_data, contents.data(), expected_length)); - RTC_LOG_F(LS_VERBOSE) << "Exit"; -} - -void HttpBaseTest::VerifyTransferComplete(HttpMode mode, HttpError error) { - RTC_LOG_F(LS_VERBOSE) << "Enter"; - // Verify that http operation has completed - ASSERT_TRUE(events.size() > 0); - size_t last_event = events.size() - 1; - EXPECT_EQ(E_COMPLETE, events[last_event].event); - EXPECT_EQ(mode, events[last_event].mode); - EXPECT_EQ(error, events[last_event].err); - RTC_LOG_F(LS_VERBOSE) << "Exit"; -} - -// -// Tests -// - -TEST_F(HttpBaseTest, SupportsSend) { - // Queue response document - SetupDocument("Goodbye!"); - - // Begin send - base.send(&data); - - // Send completed successfully - VerifyTransferComplete(HM_SEND, HE_NONE); - VerifySourceContents(kHttpResponse); -} - -TEST_F(HttpBaseTest, SupportsSendNoDocument) { - // Queue response document - SetupDocument(nullptr); - - // Begin send - base.send(&data); - - // Send completed successfully - VerifyTransferComplete(HM_SEND, HE_NONE); - VerifySourceContents(kHttpEmptyResponse); -} - -TEST_F(HttpBaseTest, SignalsCompleteOnInterruptedSend) { - // This test is attempting to expose a bug that occurs when a particular - // base objects is used for receiving, and then used for sending. In - // particular, the HttpParser state is different after receiving. Simulate - // that here. - SetupSource(kHttpResponse); - base.recv(&data); - VerifyTransferComplete(HM_RECV, HE_NONE); - - src.Clear(); - data.clear(true); - events.clear(); - base.detach(); - - // Queue response document - SetupDocument("Goodbye!"); - - // Prevent entire response from being sent - const size_t kInterruptedLength = strlen(kHttpResponse) - 1; - src.SetWriteBlock(kInterruptedLength); - - // Begin send - base.send(&data); - - // Document is mostly complete, but no completion signal yet. - EXPECT_TRUE(events.empty()); - VerifySourceContents(kHttpResponse, kInterruptedLength); - - src.SetState(SS_CLOSED); - - // Send completed with disconnect error, and no additional data. - VerifyTransferComplete(HM_SEND, HE_DISCONNECTED); - EXPECT_TRUE(src.ReadData().empty()); -} - -TEST_F(HttpBaseTest, SupportsReceiveViaDocumentPush) { - // Queue response document - SetupSource(kHttpResponse); - - // Begin receive - base.recv(&data); - - // Document completed successfully - VerifyHeaderComplete(2, false); - VerifyTransferComplete(HM_RECV, HE_NONE); - VerifyDocumentContents("Goodbye!"); -} - -} // namespace rtc diff --git a/rtc_base/httpcommon.cc b/rtc_base/httpcommon.cc index 40a9cb97aa..716a41e16e 100644 --- a/rtc_base/httpcommon.cc +++ b/rtc_base/httpcommon.cc @@ -108,154 +108,9 @@ const ConstantToLabel SECURITY_ERRORS[] = { #undef KLABEL #undef LASTLABEL #endif // defined(WEBRTC_WIN) -} // namespace -////////////////////////////////////////////////////////////////////// -// Enum - TODO: expose globally later? -////////////////////////////////////////////////////////////////////// - -bool find_string(size_t& index, - const std::string& needle, - const char* const haystack[], - size_t max_index) { - for (index = 0; index < max_index; ++index) { - if (_stricmp(needle.c_str(), haystack[index]) == 0) { - return true; - } - } - return false; -} - -template -struct Enum { - static const char** Names; - static size_t Size; - - static inline const char* Name(E val) { return Names[val]; } - static inline bool Parse(E& val, const std::string& name) { - size_t index; - if (!find_string(index, name, Names, Size)) - return false; - val = static_cast(index); - return true; - } - - E val; - - inline operator E&() { return val; } - inline Enum& operator=(E rhs) { - val = rhs; - return *this; - } - - inline const char* name() const { return Name(val); } - inline bool assign(const std::string& name) { return Parse(val, name); } - inline Enum& operator=(const std::string& rhs) { - assign(rhs); - return *this; - } -}; - -#define ENUM(e, n) \ - template <> \ - const char** Enum::Names = n; \ - template <> \ - size_t Enum::Size = sizeof(n) / sizeof(n[0]) - -////////////////////////////////////////////////////////////////////// -// HttpCommon -////////////////////////////////////////////////////////////////////// - -static const char* kHttpVersions[HVER_LAST + 1] = {"1.0", "1.1", "Unknown"}; -ENUM(HttpVersion, kHttpVersions); - -static const char* kHttpHeaders[HH_LAST + 1] = { - "Age", - "Cache-Control", - "Connection", - "Content-Disposition", - "Content-Length", - "Content-Range", - "Content-Type", - "Cookie", - "Date", - "ETag", - "Expires", - "Host", - "If-Modified-Since", - "If-None-Match", - "Keep-Alive", - "Last-Modified", - "Location", - "Proxy-Authenticate", - "Proxy-Authorization", - "Proxy-Connection", - "Range", - "Set-Cookie", - "TE", - "Trailers", - "Transfer-Encoding", - "Upgrade", - "User-Agent", - "WWW-Authenticate", -}; -ENUM(HttpHeader, kHttpHeaders); - -const char* ToString(HttpVersion version) { - return Enum::Name(version); -} - -bool FromString(HttpVersion& version, const std::string& str) { - return Enum::Parse(version, str); -} - -const char* ToString(HttpHeader header) { - return Enum::Name(header); -} - -bool FromString(HttpHeader& header, const std::string& str) { - return Enum::Parse(header, str); -} - -bool HttpHeaderIsEndToEnd(HttpHeader header) { - switch (header) { - case HH_CONNECTION: - case HH_KEEP_ALIVE: - case HH_PROXY_AUTHENTICATE: - case HH_PROXY_AUTHORIZATION: - case HH_PROXY_CONNECTION: // Note part of RFC... this is non-standard - // header - case HH_TE: - case HH_TRAILERS: - case HH_TRANSFER_ENCODING: - case HH_UPGRADE: - return false; - default: - return true; - } -} - -bool HttpHeaderIsCollapsible(HttpHeader header) { - switch (header) { - case HH_SET_COOKIE: - case HH_PROXY_AUTHENTICATE: - case HH_WWW_AUTHENTICATE: - return false; - default: - return true; - } -} - -bool HttpShouldKeepAlive(const HttpData& data) { - std::string connection; - if ((data.hasHeader(HH_PROXY_CONNECTION, &connection) || - data.hasHeader(HH_CONNECTION, &connection))) { - return (_stricmp(connection.c_str(), "Keep-Alive") == 0); - } - return (data.version >= HVER_1_1); -} - -namespace { +typedef std::pair HttpAttribute; +typedef std::vector HttpAttributeList; inline bool IsEndOfAttributeName(size_t pos, size_t len, const char* data) { if (pos >= len) @@ -272,8 +127,6 @@ inline bool IsEndOfAttributeName(size_t pos, size_t len, const char* data) { return false; } -} // anonymous namespace - void HttpParseAttributes(const char* data, size_t len, HttpAttributeList& attributes) { @@ -354,274 +207,6 @@ bool HttpHasNthAttribute(HttpAttributeList& attributes, return true; } -bool HttpDateToSeconds(const std::string& date, time_t* seconds) { - const char* const kTimeZones[] = { - "UT", "GMT", "EST", "EDT", "CST", "CDT", "MST", "MDT", "PST", - "PDT", "A", "B", "C", "D", "E", "F", "G", "H", - "I", "K", "L", "M", "N", "O", "P", "Q", "R", - "S", "T", "U", "V", "W", "X", "Y"}; - const int kTimeZoneOffsets[] = { - 0, 0, -5, -4, -6, -5, -7, -6, -8, -7, -1, -2, -3, -4, -5, -6, -7, - -8, -9, -10, -11, -12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; - - RTC_DCHECK(nullptr != seconds); - struct tm tval; - memset(&tval, 0, sizeof(tval)); - char month[4], zone[6]; - memset(month, 0, sizeof(month)); - memset(zone, 0, sizeof(zone)); - - if (7 != sscanf(date.c_str(), "%*3s, %d %3s %d %d:%d:%d %5c", &tval.tm_mday, - month, &tval.tm_year, &tval.tm_hour, &tval.tm_min, - &tval.tm_sec, zone)) { - return false; - } - switch (toupper(month[2])) { - case 'N': - tval.tm_mon = (month[1] == 'A') ? 0 : 5; - break; - case 'B': - tval.tm_mon = 1; - break; - case 'R': - tval.tm_mon = (month[0] == 'M') ? 2 : 3; - break; - case 'Y': - tval.tm_mon = 4; - break; - case 'L': - tval.tm_mon = 6; - break; - case 'G': - tval.tm_mon = 7; - break; - case 'P': - tval.tm_mon = 8; - break; - case 'T': - tval.tm_mon = 9; - break; - case 'V': - tval.tm_mon = 10; - break; - case 'C': - tval.tm_mon = 11; - break; - } - tval.tm_year -= 1900; - time_t gmt, non_gmt = mktime(&tval); - if ((zone[0] == '+') || (zone[0] == '-')) { - if (!isdigit(zone[1]) || !isdigit(zone[2]) || !isdigit(zone[3]) || - !isdigit(zone[4])) { - return false; - } - int hours = (zone[1] - '0') * 10 + (zone[2] - '0'); - int minutes = (zone[3] - '0') * 10 + (zone[4] - '0'); - int offset = (hours * 60 + minutes) * 60; - gmt = non_gmt + ((zone[0] == '+') ? offset : -offset); - } else { - size_t zindex; - if (!find_string(zindex, zone, kTimeZones, arraysize(kTimeZones))) { - return false; - } - gmt = non_gmt + kTimeZoneOffsets[zindex] * 60 * 60; - } -// TODO: Android should support timezone, see b/2441195 -#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) || defined(WEBRTC_ANDROID) || \ - defined(BSD) - tm* tm_for_timezone = localtime(&gmt); - *seconds = gmt + tm_for_timezone->tm_gmtoff; -#else -#if defined(_MSC_VER) && _MSC_VER >= 1900 - long timezone = 0; - _get_timezone(&timezone); -#endif - *seconds = gmt - timezone; -#endif - return true; -} - -std::string HttpAddress(const SocketAddress& address, bool secure) { - return (address.port() == HttpDefaultPort(secure)) ? address.hostname() - : address.ToString(); -} - -////////////////////////////////////////////////////////////////////// -// HttpData -////////////////////////////////////////////////////////////////////// - -HttpData::HttpData() : version(HVER_1_1) {} - -HttpData::~HttpData() = default; - -void HttpData::clear(bool release_document) { - // Clear headers first, since releasing a document may have far-reaching - // effects. - headers_.clear(); - if (release_document) { - document.reset(); - } -} - -void HttpData::changeHeader(const std::string& name, - const std::string& value, - HeaderCombine combine) { - if (combine == HC_AUTO) { - HttpHeader header; - // Unrecognized headers are collapsible - combine = !FromString(header, name) || HttpHeaderIsCollapsible(header) - ? HC_YES - : HC_NO; - } else if (combine == HC_REPLACE) { - headers_.erase(name); - combine = HC_NO; - } - // At this point, combine is one of (YES, NO, NEW) - if (combine != HC_NO) { - HeaderMap::iterator it = headers_.find(name); - if (it != headers_.end()) { - if (combine == HC_YES) { - it->second.append(","); - it->second.append(value); - } - return; - } - } - headers_.insert(HeaderMap::value_type(name, value)); -} - -size_t HttpData::clearHeader(const std::string& name) { - return headers_.erase(name); -} - -HttpData::iterator HttpData::clearHeader(iterator header) { - iterator deprecated = header++; - headers_.erase(deprecated); - return header; -} - -bool HttpData::hasHeader(const std::string& name, std::string* value) const { - HeaderMap::const_iterator it = headers_.find(name); - if (it == headers_.end()) { - return false; - } else if (value) { - *value = it->second; - } - return true; -} - -// -// HttpRequestData -// - -void HttpRequestData::clear(bool release_document) { - path.clear(); - HttpData::clear(release_document); -} - -size_t HttpRequestData::formatLeader(char* buffer, size_t size) const { - RTC_DCHECK(path.find(' ') == std::string::npos); - return sprintfn(buffer, size, "GET %.*s HTTP/%s", path.size(), path.data(), - ToString(version)); -} - -HttpError HttpRequestData::parseLeader(const char* line, size_t len) { - unsigned int vmajor, vminor; - int vend, dstart, dend; - // sscanf isn't safe with strings that aren't null-terminated, and there is - // no guarantee that |line| is. Create a local copy that is null-terminated. - std::string line_str(line, len); - line = line_str.c_str(); - if ((sscanf(line, "%*s%n %n%*s%n HTTP/%u.%u", &vend, &dstart, &dend, &vmajor, - &vminor) != 2) || - (vmajor != 1)) { - return HE_PROTOCOL; - } - if (vminor == 0) { - version = HVER_1_0; - } else if (vminor == 1) { - version = HVER_1_1; - } else { - return HE_PROTOCOL; - } - if (vend != 3 || memcmp(line, "GET", 3)) { - return HE_PROTOCOL; // !?! HC_METHOD_NOT_SUPPORTED? - } - path.assign(line + dstart, line + dend); - return HE_NONE; -} - -// -// HttpResponseData -// - -void HttpResponseData::clear(bool release_document) { - scode = HC_INTERNAL_SERVER_ERROR; - message.clear(); - HttpData::clear(release_document); -} - -void HttpResponseData::set_success(uint32_t scode) { - this->scode = scode; - message.clear(); - setHeader(HH_CONTENT_LENGTH, "0", false); -} - -void HttpResponseData::set_error(uint32_t scode) { - this->scode = scode; - message.clear(); - setHeader(HH_CONTENT_LENGTH, "0", false); -} - -size_t HttpResponseData::formatLeader(char* buffer, size_t size) const { - size_t len = sprintfn(buffer, size, "HTTP/%s %lu", ToString(version), scode); - if (!message.empty()) { - len += sprintfn(buffer + len, size - len, " %.*s", message.size(), - message.data()); - } - return len; -} - -HttpError HttpResponseData::parseLeader(const char* line, size_t len) { - size_t pos = 0; - unsigned int vmajor, vminor, temp_scode; - int temp_pos; - // sscanf isn't safe with strings that aren't null-terminated, and there is - // no guarantee that |line| is. Create a local copy that is null-terminated. - std::string line_str(line, len); - line = line_str.c_str(); - if (sscanf(line, "HTTP %u%n", &temp_scode, &temp_pos) == 1) { - // This server's response has no version. :( NOTE: This happens for every - // response to requests made from Chrome plugins, regardless of the server's - // behaviour. - RTC_LOG(LS_VERBOSE) << "HTTP version missing from response"; - version = HVER_UNKNOWN; - } else if ((sscanf(line, "HTTP/%u.%u %u%n", &vmajor, &vminor, &temp_scode, - &temp_pos) == 3) && - (vmajor == 1)) { - // This server's response does have a version. - if (vminor == 0) { - version = HVER_1_0; - } else if (vminor == 1) { - version = HVER_1_1; - } else { - return HE_PROTOCOL; - } - } else { - return HE_PROTOCOL; - } - scode = temp_scode; - pos = static_cast(temp_pos); - while ((pos < len) && isspace(static_cast(line[pos]))) - ++pos; - message.assign(line + pos, len - pos); - return HE_NONE; -} - -////////////////////////////////////////////////////////////////////// -// Http Authentication -////////////////////////////////////////////////////////////////////// - std::string quote(const std::string& str) { std::string result; result.push_back('"'); @@ -655,6 +240,8 @@ struct NegotiateAuthContext : public HttpAuthContext { }; #endif // WEBRTC_WIN +} // anonymous namespace + HttpAuthResult HttpAuthenticate(const char* challenge, size_t len, const SocketAddress& server, diff --git a/rtc_base/httpcommon.h b/rtc_base/httpcommon.h index a5a684adc7..581bd1ee8a 100644 --- a/rtc_base/httpcommon.h +++ b/rtc_base/httpcommon.h @@ -25,245 +25,6 @@ namespace rtc { class CryptString; class SocketAddress; -////////////////////////////////////////////////////////////////////// -// Constants -////////////////////////////////////////////////////////////////////// - -enum HttpCode { - HC_OK = 200, - HC_INTERNAL_SERVER_ERROR = 500, -}; - -enum HttpVersion { HVER_1_0, HVER_1_1, HVER_UNKNOWN, HVER_LAST = HVER_UNKNOWN }; - -enum HttpError { - HE_NONE, - HE_PROTOCOL, // Received non-valid HTTP data - HE_DISCONNECTED, // Connection closed unexpectedly - HE_OVERFLOW, // Received too much data for internal buffers - HE_CONNECT_FAILED, // The socket failed to connect. - HE_SOCKET_ERROR, // An error occurred on a connected socket - HE_SHUTDOWN, // Http object is being destroyed - HE_OPERATION_CANCELLED, // Connection aborted locally - HE_AUTH, // Proxy Authentication Required - HE_CERTIFICATE_EXPIRED, // During SSL negotiation - HE_STREAM, // Problem reading or writing to the document - HE_CACHE, // Problem reading from cache - HE_DEFAULT -}; - -enum HttpHeader { - HH_AGE, - HH_CACHE_CONTROL, - HH_CONNECTION, - HH_CONTENT_DISPOSITION, - HH_CONTENT_LENGTH, - HH_CONTENT_RANGE, - HH_CONTENT_TYPE, - HH_COOKIE, - HH_DATE, - HH_ETAG, - HH_EXPIRES, - HH_HOST, - HH_IF_MODIFIED_SINCE, - HH_IF_NONE_MATCH, - HH_KEEP_ALIVE, - HH_LAST_MODIFIED, - HH_LOCATION, - HH_PROXY_AUTHENTICATE, - HH_PROXY_AUTHORIZATION, - HH_PROXY_CONNECTION, - HH_RANGE, - HH_SET_COOKIE, - HH_TE, - HH_TRAILERS, - HH_TRANSFER_ENCODING, - HH_UPGRADE, - HH_USER_AGENT, - HH_WWW_AUTHENTICATE, - HH_LAST = HH_WWW_AUTHENTICATE -}; - -const uint16_t HTTP_DEFAULT_PORT = 80; -const uint16_t HTTP_SECURE_PORT = 443; - -////////////////////////////////////////////////////////////////////// -// Utility Functions -////////////////////////////////////////////////////////////////////// - -const char* ToString(HttpVersion version); -bool FromString(HttpVersion& version, const std::string& str); - -const char* ToString(HttpHeader header); -bool FromString(HttpHeader& header, const std::string& str); - -bool HttpHeaderIsEndToEnd(HttpHeader header); -bool HttpHeaderIsCollapsible(HttpHeader header); - -struct HttpData; -bool HttpShouldKeepAlive(const HttpData& data); - -typedef std::pair HttpAttribute; -typedef std::vector HttpAttributeList; -void HttpParseAttributes(const char* data, - size_t len, - HttpAttributeList& attributes); -bool HttpHasAttribute(const HttpAttributeList& attributes, - const std::string& name, - std::string* value); -bool HttpHasNthAttribute(HttpAttributeList& attributes, - size_t index, - std::string* name, - std::string* value); - -// Convert RFC1123 date (DoW, DD Mon YYYY HH:MM:SS TZ) to unix timestamp -bool HttpDateToSeconds(const std::string& date, time_t* seconds); - -inline uint16_t HttpDefaultPort(bool secure) { - return secure ? HTTP_SECURE_PORT : HTTP_DEFAULT_PORT; -} - -// Returns the http server notation for a given address -std::string HttpAddress(const SocketAddress& address, bool secure); - -// functional for insensitive std::string compare -struct iless { - bool operator()(const std::string& lhs, const std::string& rhs) const { - return (::_stricmp(lhs.c_str(), rhs.c_str()) < 0); - } -}; - -// put quotes around a string and escape any quotes inside it -std::string quote(const std::string& str); - -////////////////////////////////////////////////////////////////////// -// HttpData -////////////////////////////////////////////////////////////////////// - -struct HttpData { - typedef std::multimap HeaderMap; - typedef HeaderMap::const_iterator const_iterator; - typedef HeaderMap::iterator iterator; - - HttpVersion version; - std::unique_ptr document; - - HttpData(); - - enum HeaderCombine { HC_YES, HC_NO, HC_AUTO, HC_REPLACE, HC_NEW }; - void changeHeader(const std::string& name, - const std::string& value, - HeaderCombine combine); - inline void addHeader(const std::string& name, - const std::string& value, - bool append = true) { - changeHeader(name, value, append ? HC_AUTO : HC_NO); - } - inline void setHeader(const std::string& name, - const std::string& value, - bool overwrite = true) { - changeHeader(name, value, overwrite ? HC_REPLACE : HC_NEW); - } - // Returns count of erased headers - size_t clearHeader(const std::string& name); - // Returns iterator to next header - iterator clearHeader(iterator header); - - // keep in mind, this may not do what you want in the face of multiple headers - bool hasHeader(const std::string& name, std::string* value) const; - - inline const_iterator begin() const { return headers_.begin(); } - inline const_iterator end() const { return headers_.end(); } - inline iterator begin() { return headers_.begin(); } - inline iterator end() { return headers_.end(); } - inline const_iterator begin(const std::string& name) const { - return headers_.lower_bound(name); - } - inline const_iterator end(const std::string& name) const { - return headers_.upper_bound(name); - } - inline iterator begin(const std::string& name) { - return headers_.lower_bound(name); - } - inline iterator end(const std::string& name) { - return headers_.upper_bound(name); - } - - // Convenience methods using HttpHeader - inline void changeHeader(HttpHeader header, - const std::string& value, - HeaderCombine combine) { - changeHeader(ToString(header), value, combine); - } - inline void addHeader(HttpHeader header, - const std::string& value, - bool append = true) { - addHeader(ToString(header), value, append); - } - inline void setHeader(HttpHeader header, - const std::string& value, - bool overwrite = true) { - setHeader(ToString(header), value, overwrite); - } - inline void clearHeader(HttpHeader header) { clearHeader(ToString(header)); } - inline bool hasHeader(HttpHeader header, std::string* value) const { - return hasHeader(ToString(header), value); - } - inline const_iterator begin(HttpHeader header) const { - return headers_.lower_bound(ToString(header)); - } - inline const_iterator end(HttpHeader header) const { - return headers_.upper_bound(ToString(header)); - } - inline iterator begin(HttpHeader header) { - return headers_.lower_bound(ToString(header)); - } - inline iterator end(HttpHeader header) { - return headers_.upper_bound(ToString(header)); - } - - virtual size_t formatLeader(char* buffer, size_t size) const = 0; - virtual HttpError parseLeader(const char* line, size_t len) = 0; - - protected: - virtual ~HttpData(); - void clear(bool release_document); - - private: - HeaderMap headers_; -}; - -struct HttpRequestData : public HttpData { - std::string path; - - HttpRequestData() {} - - void clear(bool release_document); - - size_t formatLeader(char* buffer, size_t size) const override; - HttpError parseLeader(const char* line, size_t len) override; -}; - -struct HttpResponseData : public HttpData { - uint32_t scode; - std::string message; - - HttpResponseData() : scode(HC_INTERNAL_SERVER_ERROR) {} - void clear(bool release_document); - - // Convenience methods - void set_success(uint32_t scode = HC_OK); - void set_error(uint32_t scode); - - size_t formatLeader(char* buffer, size_t size) const override; - HttpError parseLeader(const char* line, size_t len) override; -}; - -struct HttpTransaction { - HttpRequestData request; - HttpResponseData response; -}; - ////////////////////////////////////////////////////////////////////// // Http Authentication ////////////////////////////////////////////////////////////////////// diff --git a/rtc_base/httpcommon_unittest.cc b/rtc_base/httpcommon_unittest.cc deleted file mode 100644 index 12ded2465a..0000000000 --- a/rtc_base/httpcommon_unittest.cc +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2004 The WebRTC Project Authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "rtc_base/httpcommon.h" -#include "rtc_base/gunit.h" - -namespace rtc { - -TEST(HttpResponseData, parseLeaderHttp1_0) { - static const char kResponseString[] = "HTTP/1.0 200 OK"; - HttpResponseData response; - EXPECT_EQ(HE_NONE, - response.parseLeader(kResponseString, sizeof(kResponseString) - 1)); - EXPECT_EQ(HVER_1_0, response.version); - EXPECT_EQ(200U, response.scode); -} - -TEST(HttpResponseData, parseLeaderHttp1_1) { - static const char kResponseString[] = "HTTP/1.1 200 OK"; - HttpResponseData response; - EXPECT_EQ(HE_NONE, - response.parseLeader(kResponseString, sizeof(kResponseString) - 1)); - EXPECT_EQ(HVER_1_1, response.version); - EXPECT_EQ(200U, response.scode); -} - -TEST(HttpResponseData, parseLeaderHttpUnknown) { - static const char kResponseString[] = "HTTP 200 OK"; - HttpResponseData response; - EXPECT_EQ(HE_NONE, - response.parseLeader(kResponseString, sizeof(kResponseString) - 1)); - EXPECT_EQ(HVER_UNKNOWN, response.version); - EXPECT_EQ(200U, response.scode); -} - -TEST(HttpResponseData, parseLeaderHttpFailure) { - static const char kResponseString[] = "HTTP/1.1 503 Service Unavailable"; - HttpResponseData response; - EXPECT_EQ(HE_NONE, - response.parseLeader(kResponseString, sizeof(kResponseString) - 1)); - EXPECT_EQ(HVER_1_1, response.version); - EXPECT_EQ(503U, response.scode); -} - -TEST(HttpResponseData, parseLeaderHttpInvalid) { - static const char kResponseString[] = "Durrrrr, what's HTTP?"; - HttpResponseData response; - EXPECT_EQ(HE_PROTOCOL, - response.parseLeader(kResponseString, sizeof(kResponseString) - 1)); -} - -} // namespace rtc diff --git a/rtc_base/httpserver.cc b/rtc_base/httpserver.cc deleted file mode 100644 index 8c0e6248a5..0000000000 --- a/rtc_base/httpserver.cc +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Copyright 2004 The WebRTC Project Authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include - -#include "rtc_base/asyncsocket.h" -#include "rtc_base/checks.h" -#include "rtc_base/httpserver.h" -#include "rtc_base/logging.h" -#include "rtc_base/socketstream.h" -#include "rtc_base/thread.h" - -namespace rtc { - -/////////////////////////////////////////////////////////////////////////////// -// HttpServer -/////////////////////////////////////////////////////////////////////////////// - -HttpServer::HttpServer() : next_connection_id_(1), closing_(false) {} - -HttpServer::~HttpServer() { - if (closing_) { - RTC_LOG(LS_WARNING) << "HttpServer::CloseAll has not completed"; - } - for (ConnectionMap::iterator it = connections_.begin(); - it != connections_.end(); ++it) { - StreamInterface* stream = it->second->EndProcess(); - delete stream; - delete it->second; - } -} - -int HttpServer::HandleConnection(StreamInterface* stream) { - int connection_id = next_connection_id_++; - RTC_DCHECK(connection_id != HTTP_INVALID_CONNECTION_ID); - Connection* connection = new Connection(connection_id, this); - connections_.insert(ConnectionMap::value_type(connection_id, connection)); - connection->BeginProcess(stream); - return connection_id; -} - -void HttpServer::Respond(HttpServerTransaction* transaction) { - int connection_id = transaction->connection_id(); - if (Connection* connection = Find(connection_id)) { - connection->Respond(transaction); - } else { - delete transaction; - // We may be tempted to SignalHttpComplete, but that implies that a - // connection still exists. - } -} - -void HttpServer::Close(int connection_id, bool force) { - if (Connection* connection = Find(connection_id)) { - connection->InitiateClose(force); - } -} - -void HttpServer::CloseAll(bool force) { - if (connections_.empty()) { - SignalCloseAllComplete(this); - return; - } - closing_ = true; - std::list connections; - for (ConnectionMap::const_iterator it = connections_.begin(); - it != connections_.end(); ++it) { - connections.push_back(it->second); - } - for (std::list::const_iterator it = connections.begin(); - it != connections.end(); ++it) { - (*it)->InitiateClose(force); - } -} - -HttpServer::Connection* HttpServer::Find(int connection_id) { - ConnectionMap::iterator it = connections_.find(connection_id); - if (it == connections_.end()) - return nullptr; - return it->second; -} - -void HttpServer::Remove(int connection_id) { - ConnectionMap::iterator it = connections_.find(connection_id); - if (it == connections_.end()) { - RTC_NOTREACHED(); - return; - } - Connection* connection = it->second; - connections_.erase(it); - SignalConnectionClosed(this, connection_id, connection->EndProcess()); - delete connection; - if (closing_ && connections_.empty()) { - closing_ = false; - SignalCloseAllComplete(this); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// HttpServer::Connection -/////////////////////////////////////////////////////////////////////////////// - -HttpServer::Connection::Connection(int connection_id, HttpServer* server) - : connection_id_(connection_id), - server_(server), - current_(nullptr), - signalling_(false), - close_(false) {} - -HttpServer::Connection::~Connection() { - // It's possible that an object hosted inside this transaction signalled - // an event which caused the connection to close. - Thread::Current()->Dispose(current_); -} - -void HttpServer::Connection::BeginProcess(StreamInterface* stream) { - base_.notify(this); - base_.attach(stream); - current_ = new HttpServerTransaction(connection_id_); - if (base_.mode() != HM_CONNECT) - base_.recv(¤t_->request); -} - -StreamInterface* HttpServer::Connection::EndProcess() { - base_.notify(nullptr); - base_.abort(HE_DISCONNECTED); - return base_.detach(); -} - -void HttpServer::Connection::Respond(HttpServerTransaction* transaction) { - RTC_DCHECK(current_ == nullptr); - current_ = transaction; - if (current_->response.begin() == current_->response.end()) { - current_->response.set_error(HC_INTERNAL_SERVER_ERROR); - } - bool keep_alive = HttpShouldKeepAlive(current_->request); - current_->response.setHeader(HH_CONNECTION, - keep_alive ? "Keep-Alive" : "Close", false); - close_ = !HttpShouldKeepAlive(current_->response); - base_.send(¤t_->response); -} - -void HttpServer::Connection::InitiateClose(bool force) { - bool request_in_progress = (HM_SEND == base_.mode()) || (nullptr == current_); - if (!signalling_ && (force || !request_in_progress)) { - server_->Remove(connection_id_); - } else { - close_ = true; - } -} - -// -// IHttpNotify Implementation -// - -HttpError HttpServer::Connection::onHttpHeaderComplete(bool chunked, - size_t& data_size) { - if (data_size == SIZE_UNKNOWN) { - data_size = 0; - } - RTC_DCHECK(current_ != nullptr); - bool custom_document = false; - server_->SignalHttpRequestHeader(server_, current_, &custom_document); - if (!custom_document) { - current_->request.document.reset(new MemoryStream); - } - return HE_NONE; -} - -void HttpServer::Connection::onHttpComplete(HttpMode mode, HttpError err) { - if (mode == HM_SEND) { - RTC_DCHECK(current_ != nullptr); - signalling_ = true; - server_->SignalHttpRequestComplete(server_, current_, err); - signalling_ = false; - if (close_) { - // Force a close - err = HE_DISCONNECTED; - } - } - if (err != HE_NONE) { - server_->Remove(connection_id_); - } else if (mode == HM_CONNECT) { - base_.recv(¤t_->request); - } else if (mode == HM_RECV) { - RTC_DCHECK(current_ != nullptr); - // TODO: do we need this? - // request_.document_->rewind(); - HttpServerTransaction* transaction = current_; - current_ = nullptr; - server_->SignalHttpRequest(server_, transaction); - } else if (mode == HM_SEND) { - Thread::Current()->Dispose(current_->response.document.release()); - current_->request.clear(true); - current_->response.clear(true); - base_.recv(¤t_->request); - } else { - RTC_NOTREACHED(); - } -} - -void HttpServer::Connection::onHttpClosed(HttpError err) { - server_->Remove(connection_id_); -} - -/////////////////////////////////////////////////////////////////////////////// -// HttpListenServer -/////////////////////////////////////////////////////////////////////////////// - -HttpListenServer::HttpListenServer() { - SignalConnectionClosed.connect(this, &HttpListenServer::OnConnectionClosed); -} - -HttpListenServer::~HttpListenServer() {} - -int HttpListenServer::Listen(const SocketAddress& address) { - AsyncSocket* sock = Thread::Current()->socketserver()->CreateAsyncSocket( - address.family(), SOCK_STREAM); - if (!sock) { - return SOCKET_ERROR; - } - listener_.reset(sock); - listener_->SignalReadEvent.connect(this, &HttpListenServer::OnReadEvent); - if ((listener_->Bind(address) != SOCKET_ERROR) && - (listener_->Listen(5) != SOCKET_ERROR)) - return 0; - return listener_->GetError(); -} - -bool HttpListenServer::GetAddress(SocketAddress* address) const { - if (!listener_) { - return false; - } - *address = listener_->GetLocalAddress(); - return !address->IsNil(); -} - -void HttpListenServer::StopListening() { - if (listener_) { - listener_->Close(); - } -} - -void HttpListenServer::OnReadEvent(AsyncSocket* socket) { - RTC_DCHECK(socket == listener_.get()); - AsyncSocket* incoming = listener_->Accept(nullptr); - if (incoming) { - StreamInterface* stream = new SocketStream(incoming); - HandleConnection(stream); - } -} - -void HttpListenServer::OnConnectionClosed(HttpServer* server, - int connection_id, - StreamInterface* stream) { - Thread::Current()->Dispose(stream); -} - -/////////////////////////////////////////////////////////////////////////////// - -} // namespace rtc diff --git a/rtc_base/httpserver.h b/rtc_base/httpserver.h deleted file mode 100644 index e4d94445ed..0000000000 --- a/rtc_base/httpserver.h +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright 2004 The WebRTC Project Authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef RTC_BASE_HTTPSERVER_H_ -#define RTC_BASE_HTTPSERVER_H_ - -#include -#include - -#include "rtc_base/httpbase.h" - -namespace rtc { - -class AsyncSocket; -class HttpServer; -class SocketAddress; - -////////////////////////////////////////////////////////////////////// -// HttpServer -////////////////////////////////////////////////////////////////////// - -const int HTTP_INVALID_CONNECTION_ID = 0; - -struct HttpServerTransaction : public HttpTransaction { - public: - HttpServerTransaction(int id) : connection_id_(id) {} - int connection_id() const { return connection_id_; } - - private: - int connection_id_; -}; - -class HttpServer { - public: - HttpServer(); - virtual ~HttpServer(); - - int HandleConnection(StreamInterface* stream); - // Due to sigslot issues, we can't destroy some streams at an arbitrary time. - sigslot::signal3 SignalConnectionClosed; - - // This signal occurs when the HTTP request headers have been received, but - // before the request body is written to the request document. By default, - // the request document is a MemoryStream. By handling this signal, the - // document can be overridden, in which case the third signal argument should - // be set to true. In the case where the request body should be ignored, - // the document can be set to null. Note that the transaction object is still - // owened by the HttpServer at this point. - sigslot::signal3 - SignalHttpRequestHeader; - - // An HTTP request has been made, and is available in the transaction object. - // Populate the transaction's response, and then return the object via the - // Respond method. Note that during this time, ownership of the transaction - // object is transferred, so it may be passed between threads, although - // respond must be called on the server's active thread. - sigslot::signal2 SignalHttpRequest; - void Respond(HttpServerTransaction* transaction); - - // If you want to know when a request completes, listen to this event. - sigslot::signal3 - SignalHttpRequestComplete; - - // Stop processing the connection indicated by connection_id. - // Unless force is true, the server will complete sending a response that is - // in progress. - void Close(int connection_id, bool force); - void CloseAll(bool force); - - // After calling CloseAll, this event is signalled to indicate that all - // outstanding connections have closed. - sigslot::signal1 SignalCloseAllComplete; - - private: - class Connection : private IHttpNotify { - public: - Connection(int connection_id, HttpServer* server); - ~Connection() override; - - void BeginProcess(StreamInterface* stream); - StreamInterface* EndProcess(); - - void Respond(HttpServerTransaction* transaction); - void InitiateClose(bool force); - - // IHttpNotify Interface - HttpError onHttpHeaderComplete(bool chunked, size_t& data_size) override; - void onHttpComplete(HttpMode mode, HttpError err) override; - void onHttpClosed(HttpError err) override; - - int connection_id_; - HttpServer* server_; - HttpBase base_; - HttpServerTransaction* current_; - bool signalling_, close_; - }; - - Connection* Find(int connection_id); - void Remove(int connection_id); - - friend class Connection; - typedef std::map ConnectionMap; - - ConnectionMap connections_; - int next_connection_id_; - bool closing_; -}; - -////////////////////////////////////////////////////////////////////// - -class HttpListenServer : public HttpServer, public sigslot::has_slots<> { - public: - HttpListenServer(); - ~HttpListenServer() override; - - int Listen(const SocketAddress& address); - bool GetAddress(SocketAddress* address) const; - void StopListening(); - - private: - void OnReadEvent(AsyncSocket* socket); - void OnConnectionClosed(HttpServer* server, - int connection_id, - StreamInterface* stream); - - std::unique_ptr listener_; -}; - -////////////////////////////////////////////////////////////////////// - -} // namespace rtc - -#endif // RTC_BASE_HTTPSERVER_H_ diff --git a/rtc_base/httpserver_unittest.cc b/rtc_base/httpserver_unittest.cc deleted file mode 100644 index 0dde6c5bde..0000000000 --- a/rtc_base/httpserver_unittest.cc +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright 2007 The WebRTC Project Authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "rtc_base/httpserver.h" -#include "rtc_base/gunit.h" -#include "rtc_base/testutils.h" - -using namespace webrtc::testing; - -namespace rtc { - -namespace { -const char* const kRequest = - "GET /index.html HTTP/1.1\r\n" - "Host: localhost\r\n" - "\r\n"; - -struct HttpServerMonitor : public sigslot::has_slots<> { - HttpServerTransaction* transaction; - bool server_closed, connection_closed; - - HttpServerMonitor(HttpServer* server) - : transaction(nullptr), server_closed(false), connection_closed(false) { - server->SignalCloseAllComplete.connect(this, &HttpServerMonitor::OnClosed); - server->SignalHttpRequest.connect(this, &HttpServerMonitor::OnRequest); - server->SignalHttpRequestComplete.connect( - this, &HttpServerMonitor::OnRequestComplete); - server->SignalConnectionClosed.connect( - this, &HttpServerMonitor::OnConnectionClosed); - } - void OnRequest(HttpServer*, HttpServerTransaction* t) { - ASSERT_FALSE(transaction); - transaction = t; - transaction->response.set_success(); - transaction->response.setHeader(HH_CONNECTION, "Close"); - } - void OnRequestComplete(HttpServer*, HttpServerTransaction* t, int) { - ASSERT_EQ(transaction, t); - transaction = nullptr; - } - void OnClosed(HttpServer*) { server_closed = true; } - void OnConnectionClosed(HttpServer*, int, StreamInterface* stream) { - connection_closed = true; - delete stream; - } -}; - -void CreateClientConnection(HttpServer& server, - HttpServerMonitor& monitor, - bool send_request) { - StreamSource* client = new StreamSource; - client->SetState(SS_OPEN); - server.HandleConnection(client); - EXPECT_FALSE(monitor.server_closed); - EXPECT_FALSE(monitor.transaction); - - if (send_request) { - // Simulate a request - client->QueueString(kRequest); - EXPECT_FALSE(monitor.server_closed); - } -} -} // anonymous namespace - -TEST(HttpServer, DoesNotSignalCloseUnlessCloseAllIsCalled) { - HttpServer server; - HttpServerMonitor monitor(&server); - // Add an active client connection - CreateClientConnection(server, monitor, true); - // Simulate a response - ASSERT_TRUE(nullptr != monitor.transaction); - server.Respond(monitor.transaction); - EXPECT_FALSE(monitor.transaction); - // Connection has closed, but no server close signal - EXPECT_FALSE(monitor.server_closed); - EXPECT_TRUE(monitor.connection_closed); -} - -TEST(HttpServer, SignalsCloseWhenNoConnectionsAreActive) { - HttpServer server; - HttpServerMonitor monitor(&server); - // Add an idle client connection - CreateClientConnection(server, monitor, false); - // Perform graceful close - server.CloseAll(false); - // Connections have all closed - EXPECT_TRUE(monitor.server_closed); - EXPECT_TRUE(monitor.connection_closed); -} - -TEST(HttpServer, SignalsCloseAfterGracefulCloseAll) { - HttpServer server; - HttpServerMonitor monitor(&server); - // Add an active client connection - CreateClientConnection(server, monitor, true); - // Initiate a graceful close - server.CloseAll(false); - EXPECT_FALSE(monitor.server_closed); - // Simulate a response - ASSERT_TRUE(nullptr != monitor.transaction); - server.Respond(monitor.transaction); - EXPECT_FALSE(monitor.transaction); - // Connections have all closed - EXPECT_TRUE(monitor.server_closed); - EXPECT_TRUE(monitor.connection_closed); -} - -TEST(HttpServer, SignalsCloseAfterForcedCloseAll) { - HttpServer server; - HttpServerMonitor monitor(&server); - // Add an active client connection - CreateClientConnection(server, monitor, true); - // Initiate a forceful close - server.CloseAll(true); - // Connections have all closed - EXPECT_TRUE(monitor.server_closed); - EXPECT_TRUE(monitor.connection_closed); -} - -} // namespace rtc diff --git a/rtc_base/proxy_unittest.cc b/rtc_base/proxy_unittest.cc index d81b3125cb..f42039f50b 100644 --- a/rtc_base/proxy_unittest.cc +++ b/rtc_base/proxy_unittest.cc @@ -11,7 +11,6 @@ #include #include #include "rtc_base/gunit.h" -#include "rtc_base/httpserver.h" #include "rtc_base/proxyserver.h" #include "rtc_base/socketadapters.h" #include "rtc_base/testclient.h" @@ -24,18 +23,14 @@ using rtc::SocketAddress; static const SocketAddress kSocksProxyIntAddr("1.2.3.4", 1080); static const SocketAddress kSocksProxyExtAddr("1.2.3.5", 0); -static const SocketAddress kHttpsProxyIntAddr("1.2.3.4", 443); -static const SocketAddress kHttpsProxyExtAddr("1.2.3.5", 0); static const SocketAddress kBogusProxyIntAddr("1.2.3.4", 999); -// Sets up a virtual socket server and HTTPS/SOCKS5 proxy servers. +// Sets up a virtual socket server and a SOCKS5 proxy server. class ProxyTest : public testing::Test { public: ProxyTest() : ss_(new rtc::VirtualSocketServer()), thread_(ss_.get()) { socks_.reset(new rtc::SocksProxyServer(ss_.get(), kSocksProxyIntAddr, ss_.get(), kSocksProxyExtAddr)); - https_.reset(new rtc::HttpListenServer()); - https_->Listen(kHttpsProxyIntAddr); } rtc::SocketServer* ss() { return ss_.get(); } @@ -44,8 +39,6 @@ class ProxyTest : public testing::Test { std::unique_ptr ss_; rtc::AutoSocketServerThread thread_; std::unique_ptr socks_; - // TODO: Make this a real HTTPS proxy server. - std::unique_ptr https_; }; // Tests whether we can use a SOCKS5 proxy to connect to a server.