Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(639)

Unified Diff: net/web2socket_proxy/web2socket_conn.cc

Issue 5484001: Web2socket proxy. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: fix for signed/unsigned confusion + cosmetic Created 9 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « net/web2socket_proxy/web2socket_conn.h ('k') | net/web2socket_proxy/web2socket_main.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: net/web2socket_proxy/web2socket_conn.cc
diff --git a/net/web2socket_proxy/web2socket_conn.cc b/net/web2socket_proxy/web2socket_conn.cc
new file mode 100644
index 0000000000000000000000000000000000000000..4257836fc04f6283b65b693903b09cf8ed45243b
--- /dev/null
+++ b/net/web2socket_proxy/web2socket_conn.cc
@@ -0,0 +1,743 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "web2socket_conn.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <algorithm>
+#include <limits>
+#include <vector>
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "base/base64.h"
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "third_party/libevent/evdns.h"
+
+namespace {
+
+const uint8_t kSpaceOctet = 0x20;
+const uint8_t kCRLF[] = "\r\n";
+const uint8_t kCRLFCRLF[] = "\r\n\r\n";
+
+int CountSpaces(const std::string& s) {
+ int rv = 0;
+ for (size_t i = 0; i < s.size(); ++i)
+ rv += (s[i] == kSpaceOctet);
+ return rv;
+}
+
+std::string FetchLowerCasedASCIISnippet(uint8_t* begin, uint8_t* end) {
+ std::string rv;
+ for (; begin < end; ++begin) {
+ if (!isascii(*begin))
+ return rv;
+ rv += base::ToLowerASCII(*begin);
+ }
+ return rv;
+}
+
+// Returns true on success.
+bool FetchDecimalDigits(const std::string& s, uint32_t* result) {
+ *result = 0;
+ bool got_something = false;
+ for (size_t i = 0; i < s.size(); ++i) {
+ if (IsAsciiDigit(s[i])) {
+ got_something = true;
+ if (*result > std::numeric_limits<uint32_t>::max() / 10)
+ return false;
+ *result *= 10;
+ int digit = s[i] - '0';
+ if (*result > std::numeric_limits<uint32_t>::max() - digit)
+ return false;
+ *result += digit;
+ }
+ }
+ return got_something;
+}
+
+// Returns true on success.
+bool FetchNamePort(uint8_t* begin, uint8_t* end,
+ std::string* name, uint32_t* port) {
+ name->assign(begin, end);
+ *port = 22;
+ if (begin == end)
+ return false;
+ for (uint8_t* pos = end; --pos != begin;) {
+ if (!IsAsciiDigit(*pos)) {
+ if (*pos == ':' && end - pos > 1) {
+ if (!FetchDecimalDigits(std::string(pos + 1, end), port) ||
+ *port <= 0 ||
+ *port >= (1 << 16)) {
+ return false;
+ }
+ name->assign(begin, pos);
+ }
+ break;
+ }
+ }
+ return !name->empty();
+}
+
+inline size_t strlen(const uint8_t* s) {
+ return ::strlen(reinterpret_cast<const char*>(s));
+}
+
+} // namespace
+
+Conn::Conn(Web2SocketServ* master)
+ : master_(master),
+ phase_(PHASE_WAIT_HANDSHAKE),
+ destresolution_ipv4_failed_(false),
+ destresolution_ipv6_failed_(false) {
+ while (token_map_.find(last_token_) != token_map_.end()) {
+ token_ = last_token_ =
+ reinterpret_cast<Token>(reinterpret_cast<size_t>(last_token_) + 1);
+ }
+ token_map_[token_] = this;
+ // Schedule timeout for initial phase of connection.
+ evtimer_set(&destconnect_timeout_event_,
+ &OnDestConnectTimeout, token_);
+ event_base_set(master_->evbase(),
+ &destconnect_timeout_event_);
+ struct timeval tv;
+ tv.tv_sec = 30;
+ tv.tv_usec = 0;
+ evtimer_add(&destconnect_timeout_event_, &tv);
+}
+
+Conn::~Conn() {
+ phase_ = PHASE_DEFUNCT;
+ event_del(&destconnect_timeout_event_);
+ if (token_map_[token_] == this)
+ token_map_.erase(token_);
+}
+
+Conn* Conn::Get(Token token) {
+ TokenMap::iterator it = token_map_.find(token);
+ if (it == token_map_.end())
+ return NULL;
+ Conn* cs = it->second;
+ if (cs == NULL ||
+ cs->token_ != token ||
+ cs->master_ == NULL ||
+ cs->phase_ < 0 ||
+ cs->phase_ > PHASE_SHUT ||
+ !cs->master_->IsConnSane(cs)) {
+ return NULL;
+ }
+ return cs;
+}
+
+void Conn::Shut() {
+ if (phase_ >= PHASE_SHUT)
+ return;
+ master_->MarkConnImportance(this, false);
+ if (primchan_.sock >= 0 && primchan_.bev != NULL) {
+ static const uint8_t closing_handshake[9] = { 0 };
+ bufferevent_write(primchan_.bev,
+ closing_handshake, sizeof(closing_handshake));
+ }
+ phase_ = PHASE_SHUT;
+}
+
+Conn::Status Conn::ConsumeHeader(struct evbuffer* evb) {
+ uint8_t* buf = EVBUFFER_DATA(evb);
+ size_t buf_size = EVBUFFER_LENGTH(evb);
+
+ static const uint8_t kGetMagic[] = "GET /echo ";
+ static const uint8_t kKeyValueDelimiter[] = ": ";
+
+ if (buf_size <= 0)
+ return STATUS_INCOMPLETE;
+ if (!buf)
+ return STATUS_ABORT;
+ if (!std::equal(buf, buf + std::min(buf_size, strlen(kGetMagic)),
+ kGetMagic)) {
+ // Data head does not match what is expected.
+ return STATUS_ABORT;
+ }
+
+ if (buf_size >= Web2SocketServ::kReadBufferLimit)
+ return STATUS_ABORT;
+ uint8_t* buf_end = buf + buf_size;
+ uint8_t* term_pos = std::search(buf, buf_end, kCRLFCRLF,
+ kCRLFCRLF + strlen(kCRLFCRLF));
+ uint8_t key3[8]; // Notation (key3) matches websocket RFC.
+ if (buf_end - term_pos <
+ static_cast<ptrdiff_t>(strlen(kCRLFCRLF) + sizeof(key3))) {
+ return STATUS_INCOMPLETE;
+ }
+ term_pos += strlen(kCRLFCRLF);
+ memcpy(key3, term_pos, sizeof(key3));
+ term_pos += sizeof(key3);
+ // First line is "GET /echo" line, so we skip it.
+ uint8_t* pos = std::search(buf, term_pos, kCRLF, kCRLF + strlen(kCRLF));
+ if (pos == term_pos)
+ return STATUS_ABORT;
+ for (;;) {
+ pos += strlen(kCRLF);
+ if (term_pos - pos <
+ static_cast<ptrdiff_t>(sizeof(key3) + strlen(kCRLF))) {
+ return STATUS_ABORT;
+ }
+ if (term_pos - pos ==
+ static_cast<ptrdiff_t>(sizeof(key3) + strlen(kCRLF))) {
+ break;
+ }
+ uint8_t* npos = std::search(pos, term_pos, kKeyValueDelimiter,
+ kKeyValueDelimiter + strlen(kKeyValueDelimiter));
+ if (npos == term_pos)
+ return STATUS_ABORT;
+ std::string key = FetchLowerCasedASCIISnippet(pos, npos);
+ pos = std::search(npos += strlen(kKeyValueDelimiter), term_pos,
+ kCRLF, kCRLF + strlen(kCRLF));
+ if (pos == term_pos)
+ return STATUS_ABORT;
+ if (!key.empty())
+ header_fields_[key] = FetchLowerCasedASCIISnippet(npos, pos);
+ }
+
+ // Values of Upgrade and Connection fields are hardcoded in the protocol.
+ if (header_fields_["upgrade"] != "websocket" ||
+ header_fields_["connection"] != "upgrade") {
+ return STATUS_ABORT;
+ }
+
+ if (!master_->origin().empty() &&
+ master_->origin() != "any") {
+ if (header_fields_["origin"] != master_->origin())
+ return STATUS_ABORT;
+ }
+
+ static const std::string kSecKey1 = "sec-websocket-key1";
+ static const std::string kSecKey2 = "sec-websocket-key2";
+ uint32_t key_number1, key_number2;
+ if (!FetchDecimalDigits(header_fields_[kSecKey1],
+ &key_number1) ||
+ !FetchDecimalDigits(header_fields_[kSecKey2],
+ &key_number2)) {
+ return STATUS_ABORT;
+ }
+
+ // We limit incoming header size so following numbers shall not be too high.
+ int spaces1 = CountSpaces(header_fields_[kSecKey1]);
+ int spaces2 = CountSpaces(header_fields_[kSecKey2]);
+ if (spaces1 == 0 ||
+ spaces2 == 0 ||
+ key_number1 % spaces1 != 0 ||
+ key_number2 % spaces2 != 0) {
+ return STATUS_ABORT;
+ }
+
+ uint8_t challenge[4 + 4 + sizeof(key3)];
+ uint32_t part1 = htonl(key_number1 / spaces1);
+ uint32_t part2 = htonl(key_number2 / spaces2);
+ memcpy(challenge, &part1, 4);
+ memcpy(challenge + 4, &part2, 4);
+ memcpy(challenge + sizeof(challenge) - sizeof(key3), key3, sizeof(key3));
+ MD5Sum(challenge, sizeof(challenge), &handshake_response_);
+
+ evbuffer_drain(evb, term_pos - buf);
+ return STATUS_OK;
+}
+
+bool Conn::EmitHandshake(struct bufferevent* bev) {
+ std::vector<std::string> boilerplate;
+ boilerplate.push_back("HTTP/1.1 101 WebSocket Protocol Handshake");
+ boilerplate.push_back("Upgrade: WebSocket");
+ boilerplate.push_back("Connection: Upgrade");
+
+ {
+ // Take care of Location field.
+ char buf[128];
+ int rv = snprintf(buf, sizeof(buf),
+ "Sec-WebSocket-Location: ws://%s/echo",
+ header_fields_["host"].c_str());
+ if (rv <= 0 || rv + 0u >= sizeof(buf))
+ return false;
+ boilerplate.push_back(buf);
+ }
+ {
+ // Take care of Origin field.
+ if (header_fields_.find("origin") != header_fields_.end()) {
+ char buf[128];
+ int rv = snprintf(buf, sizeof(buf),
+ "Sec-WebSocket-Origin: %s",
+ header_fields_["origin"].c_str());
+ if (rv <= 0 || rv + 0u >= sizeof(buf))
+ return false;
+ boilerplate.push_back(buf);
+ }
+ }
+
+ boilerplate.push_back("");
+ for (size_t i = 0; i < boilerplate.size(); ++i) {
+ if (bufferevent_write(bev, boilerplate[i].c_str(),
+ boilerplate[i].size()) ||
+ bufferevent_write(bev, kCRLF, strlen(kCRLF))) {
+ return false;
+ }
+ }
+ return !bufferevent_write(bev, &handshake_response_,
+ sizeof(handshake_response_));
+}
+
+Conn::Status Conn::ConsumeDestframe(struct evbuffer* evb) {
+ uint8_t* buf = EVBUFFER_DATA(evb);
+ size_t buf_size = EVBUFFER_LENGTH(evb);
+
+ if (buf_size < 1)
+ return STATUS_INCOMPLETE;
+ if (buf[0] != 0)
+ return STATUS_ABORT;
+ if (buf_size < 1 + 1)
+ return STATUS_INCOMPLETE;
+ uint8_t* buf_end = buf + buf_size;
+ uint8_t* term_pos = std::find(buf + 1, buf_end, 0xff);
+ if (term_pos == buf_end) {
+ if (buf_size >= Web2SocketServ::kReadBufferLimit) {
+ // So big and still worth nothing.
+ return STATUS_ABORT;
+ }
+ return STATUS_INCOMPLETE;
+ }
+ if (!FetchNamePort(buf + 1, term_pos, &destname_, &destport_))
+ return STATUS_ABORT;
+ evbuffer_drain(evb, term_pos - buf + 1);
+ return STATUS_OK;
+}
+
+Conn::Status Conn::ConsumeFrameHeader(struct evbuffer* evb) {
+ uint8_t* buf = EVBUFFER_DATA(evb);
+ size_t buf_size = EVBUFFER_LENGTH(evb);
+
+ if (buf_size < 1)
+ return STATUS_INCOMPLETE;
+ if (buf[0] != 0)
+ return STATUS_ABORT;
+ evbuffer_drain(evb, 1);
+ return STATUS_OK;
+}
+
+Conn::Status Conn::ProcessFrameData(struct evbuffer* evb) {
+ uint8_t* buf = EVBUFFER_DATA(evb);
+ size_t buf_size = EVBUFFER_LENGTH(evb);
+
+ if (buf_size < 1)
+ return STATUS_INCOMPLETE;
+ uint8_t* buf_end = buf + buf_size;
+ uint8_t* term_pos = std::find(buf, buf_end, 0xff);
+ bool term_detected = (term_pos != buf_end);
+ if (term_detected)
+ buf_size = term_pos - buf;
+ switch (phase_) {
+ case PHASE_INSIDE_FRAME_BASE64: {
+ if (term_detected && buf_size % 4) {
+ // base64 is encoded in chunks of 4 bytes.
+ return STATUS_ABORT;
+ }
+ if (buf_size < 4) {
+ DCHECK(!term_detected);
+ return STATUS_INCOMPLETE;
+ }
+ size_t bytes_to_process_atm = (buf_size / 4) * 4;
+ std::string out_bytes;
+ base::Base64Decode(std::string(buf, buf + bytes_to_process_atm),
+ &out_bytes);
+ evbuffer_drain(evb, bytes_to_process_atm);
+ DCHECK(destchan_.bev != NULL);
+ if (bufferevent_write(destchan_.bev,
+ out_bytes.c_str(), out_bytes.size())) {
+ return STATUS_ABORT;
+ }
+ break;
+ }
+ case PHASE_INSIDE_FRAME_SKIP: {
+ evbuffer_drain(evb, buf_size);
+ break;
+ }
+ default: {
+ return STATUS_ABORT;
+ }
+ }
+ if (term_detected) {
+ evbuffer_drain(evb, 1);
+ return STATUS_OK;
+ }
+ return STATUS_INCOMPLETE;
+}
+
+bool Conn::TryConnectDest(const struct sockaddr* addr,
+ socklen_t addrlen) {
+ if (destchan_.sock >= 0 ||
+ destchan_.bev != NULL) {
+ return false;
+ }
+ destchan_.sock = socket(addr->sa_family, SOCK_STREAM, 0);
+ if (destchan_.sock < 0)
+ return false;
+ if (!Web2SocketServ::SetNonBlock(destchan_.sock))
+ return false;
+ if (connect(destchan_.sock, addr, addrlen)) {
+ if (errno != EINPROGRESS)
+ return false;
+ }
+ destchan_.bev = bufferevent_new(destchan_.sock,
+ &OnDestchanRead,
+ &OnDestchanWrite,
+ &OnDestchanError,
+ token_);
+ if (destchan_.bev == NULL)
+ return false;
+ if (bufferevent_base_set(master_->evbase(), destchan_.bev))
+ return false;
+ bufferevent_setwatermark(destchan_.bev, EV_READ,
+ 0, Web2SocketServ::kReadBufferLimit);
+ return !bufferevent_enable(destchan_.bev, EV_READ | EV_WRITE);
+}
+
+// static
+void Conn::OnPrimchanRead(struct bufferevent* bev, Token token) {
+ Conn* cs = Conn::Get(token);
+ if (bev == NULL ||
+ cs == NULL ||
+ bev != cs->primchan_.bev) {
+ // Sanity check failed.
+ return;
+ }
+ if (EVBUFFER_LENGTH(EVBUFFER_INPUT(bev)) <= 0)
+ return;
+ cs->master_->MarkConnImportance(cs, true);
+ for (;;) {
+ switch (cs->phase_) {
+ case PHASE_WAIT_HANDSHAKE: {
+ switch (cs->ConsumeHeader(EVBUFFER_INPUT(bev))) {
+ case STATUS_OK: {
+ break;
+ }
+ case STATUS_INCOMPLETE: {
+ return;
+ }
+ case STATUS_ABORT:
+ default: {
+ cs->master_->ZapConn(cs);
+ return;
+ }
+ }
+ // Header consumed OK. Do respond.
+ if (!cs->EmitHandshake(bev)) {
+ cs->master_->ZapConn(cs);
+ return;
+ }
+ cs->phase_ = PHASE_WAIT_DESTFRAME;
+ return;
+ }
+ case PHASE_WAIT_DESTFRAME: {
+ switch (cs->ConsumeDestframe(EVBUFFER_INPUT(bev))) {
+ case STATUS_OK: {
+ {
+ struct sockaddr_in sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sin_port = htons(cs->destport_);
+ if (inet_pton(sa.sin_family = AF_INET,
+ cs->destname_.c_str(),
+ &sa.sin_addr) == 1) {
+ // valid IPv4 address supplied.
+ if (cs->TryConnectDest((struct sockaddr*)&sa, sizeof(sa))) {
+ cs->phase_ = PHASE_WAIT_DESTCONNECT;
+ return;
+ }
+ }
+ }
+ {
+ if (cs->destname_.size() >= 2 &&
+ cs->destname_[0] == '[' &&
+ cs->destname_[cs->destname_.size() - 1] == ']') {
+ // Literal IPv6 address in brackets.
+ cs->destname_ =
+ cs->destname_.substr(1, cs->destname_.size() - 2);
+ }
+ struct sockaddr_in6 sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sin6_port = htons(cs->destport_);
+ if (inet_pton(sa.sin6_family = AF_INET6,
+ cs->destname_.c_str(),
+ &sa.sin6_addr) == 1) {
+ // valid IPv6 address supplied.
+ if (cs->TryConnectDest((struct sockaddr*)&sa, sizeof(sa))) {
+ cs->phase_ = PHASE_WAIT_DESTCONNECT;
+ return;
+ }
+ }
+ }
+ // Try to asynchronously perform DNS resolution.
+ evdns_resolve_ipv4(cs->destname_.c_str(), 0,
+ &OnDestResolutionIPv4, token);
+ evdns_resolve_ipv6(cs->destname_.c_str(), 0,
+ &OnDestResolutionIPv6, token);
+ cs->phase_ = PHASE_WAIT_DESTCONNECT;
+ return;
+ }
+ case STATUS_INCOMPLETE: {
+ return;
+ }
+ case STATUS_ABORT:
+ default: {
+ cs->Shut();
+ return;
+ }
+ }
+ }
+ case PHASE_WAIT_DESTCONNECT: {
+ if (EVBUFFER_LENGTH(EVBUFFER_INPUT(bev)) >=
+ Web2SocketServ::kReadBufferLimit) {
+ cs->Shut();
+ }
+ return;
+ }
+ case PHASE_OUTSIDE_FRAME: {
+ switch (cs->ConsumeFrameHeader(EVBUFFER_INPUT(bev))) {
+ case STATUS_OK: {
+ cs->phase_ = PHASE_INSIDE_FRAME_BASE64;
+ // Process remaining data if any.
+ break;
+ }
+ case STATUS_SKIP: {
+ cs->phase_ = PHASE_INSIDE_FRAME_SKIP;
+ // Process remaining data if any.
+ break;
+ }
+ case STATUS_INCOMPLETE: {
+ return;
+ }
+ case STATUS_ABORT:
+ default: {
+ cs->Shut();
+ return;
+ }
+ }
+ break;
+ }
+ case PHASE_INSIDE_FRAME_BASE64:
+ case PHASE_INSIDE_FRAME_SKIP: {
+ switch (cs->ProcessFrameData(EVBUFFER_INPUT(bev))) {
+ case STATUS_OK: {
+ cs->phase_ = PHASE_OUTSIDE_FRAME;
+ // Handle remaining data if any.
+ break;
+ }
+ case STATUS_INCOMPLETE: {
+ return;
+ }
+ case STATUS_ABORT:
+ default: {
+ cs->Shut();
+ return;
+ }
+ }
+ break;
+ }
+ case PHASE_SHUT: {
+ evbuffer_drain(EVBUFFER_INPUT(bev),
+ EVBUFFER_LENGTH(EVBUFFER_INPUT(bev)));
+ return;
+ }
+ case PHASE_DEFUNCT:
+ default: {
+ // Must not reach here.
+ cs->master_->ZapConn(cs);
+ return;
+ }
+ }
+ }
+}
+
+// static
+void Conn::OnPrimchanWrite(struct bufferevent* bev, Token token) {
+ Conn* cs = Conn::Get(token);
+ if (bev == NULL ||
+ cs == NULL ||
+ bev != cs->primchan_.bev) {
+ // Sanity check failed.
+ return;
+ }
+ if (cs->phase_ >= PHASE_SHUT) {
+ cs->master_->ZapConn(cs);
+ return;
+ }
+ if (cs->phase_ > PHASE_WAIT_DESTCONNECT)
+ OnDestchanRead(cs->destchan_.bev, token);
+}
+
+// static
+void Conn::OnPrimchanError(struct bufferevent* bev,
+ short what, Token token) {
+ Conn* cs = Conn::Get(token);
+ if (bev == NULL ||
+ cs == NULL ||
+ bev != cs->primchan_.bev) {
+ // Sanity check failed.
+ return;
+ }
+ if (cs->phase_ >= PHASE_SHUT)
+ cs->master_->ZapConn(cs);
+ else
+ cs->Shut();
+}
+
+// static
+void Conn::OnDestResolutionIPv4(int result, char type,
+ int count, int ttl,
+ void* addr_list, Token token) {
+ Conn* cs = Conn::Get(token);
+ if (cs == NULL) {
+ // Sanity check failed.
+ return;
+ }
+ if (cs->phase_ != PHASE_WAIT_DESTCONNECT)
+ return;
+ if (result == DNS_ERR_NONE &&
+ count >= 1 &&
+ addr_list != NULL &&
+ type == DNS_IPv4_A) {
+ for (int i = 0; i < count; ++i) {
+ struct sockaddr_in sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons(cs->destport_);
+ DCHECK(sizeof(sa.sin_addr) == sizeof(struct in_addr));
+ memcpy(&sa.sin_addr,
+ static_cast<struct in_addr*>(addr_list) + i,
+ sizeof(sa.sin_addr));
+ if (cs->TryConnectDest((struct sockaddr*)&sa, sizeof(sa)))
+ return;
+ }
+ }
+ cs->destresolution_ipv4_failed_ = true;
+ if (cs->destresolution_ipv4_failed_ && cs->destresolution_ipv6_failed_)
+ cs->Shut();
+}
+
+// static
+void Conn::OnDestResolutionIPv6(int result, char type,
+ int count, int ttl,
+ void* addr_list, Token token) {
+ Conn* cs = Conn::Get(token);
+ if (cs == NULL) {
+ // Sanity check failed.
+ return;
+ }
+ if (cs->phase_ != PHASE_WAIT_DESTCONNECT)
+ return;
+ if (result == DNS_ERR_NONE &&
+ count >= 1 &&
+ addr_list != NULL &&
+ type == DNS_IPv6_AAAA) {
+ for (int i = 0; i < count; ++i) {
+ struct sockaddr_in6 sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sin6_family = AF_INET6;
+ sa.sin6_port = htons(cs->destport_);
+ DCHECK(sizeof(sa.sin6_addr) == sizeof(struct in6_addr));
+ memcpy(&sa.sin6_addr,
+ static_cast<struct in6_addr*>(addr_list) + i,
+ sizeof(sa.sin6_addr));
+ if (cs->TryConnectDest((struct sockaddr*)&sa, sizeof(sa)))
+ return;
+ }
+ }
+ cs->destresolution_ipv6_failed_ = true;
+ if (cs->destresolution_ipv4_failed_ && cs->destresolution_ipv6_failed_)
+ cs->Shut();
+}
+
+// static
+void Conn::OnDestConnectTimeout(int, short, Token token) {
+ Conn* cs = Conn::Get(token);
+ if (cs == NULL) {
+ // Sanity check failed.
+ }
+ if (cs->phase_ > PHASE_WAIT_DESTCONNECT)
+ return;
+ cs->Shut();
+}
+
+// static
+void Conn::OnDestchanRead(struct bufferevent* bev, Token token) {
+ Conn* cs = Conn::Get(token);
+ if (bev == NULL ||
+ cs == NULL ||
+ bev != cs->destchan_.bev) {
+ // Sanity check failed.
+ return;
+ }
+ if (EVBUFFER_LENGTH(EVBUFFER_INPUT(bev)) <= 0)
+ return;
+ if (cs->primchan_.bev == NULL) {
+ cs->master_->ZapConn(cs);
+ return;
+ }
+ cs->master_->MarkConnImportance(cs, true);
+ std::string out_bytes;
+ base::Base64Encode(
+ std::string(
+ static_cast<const char*>(static_cast<void*>(
+ EVBUFFER_DATA(EVBUFFER_INPUT(bev)))),
+ EVBUFFER_LENGTH(EVBUFFER_INPUT(bev))),
+ &out_bytes);
+ evbuffer_drain(EVBUFFER_INPUT(bev), EVBUFFER_LENGTH(EVBUFFER_INPUT(bev)));
+ static const uint8_t frame_header[] = { 0x00 };
+ static const uint8_t frame_terminator[] = { 0xff };
+ if (bufferevent_write(cs->primchan_.bev,
+ frame_header, sizeof(frame_header)) ||
+ bufferevent_write(cs->primchan_.bev,
+ out_bytes.c_str(), out_bytes.size()) ||
+ bufferevent_write(cs->primchan_.bev,
+ frame_terminator, sizeof(frame_terminator))) {
+ cs->Shut();
+ }
+}
+
+// static
+void Conn::OnDestchanWrite(struct bufferevent* bev, Token token) {
+ Conn* cs = Conn::Get(token);
+ if (bev == NULL ||
+ cs == NULL ||
+ bev != cs->destchan_.bev) {
+ // Sanity check failed.
+ return;
+ }
+ if (cs->phase_ == PHASE_WAIT_DESTCONNECT)
+ cs->phase_ = PHASE_OUTSIDE_FRAME;
+ OnPrimchanRead(cs->primchan_.bev, token);
+}
+
+// static
+void Conn::OnDestchanError(struct bufferevent* bev,
+ short what, Token token) {
+ Conn* cs = Conn::Get(token);
+ if (bev == NULL ||
+ cs == NULL ||
+ bev != cs->destchan_.bev) {
+ // Sanity check failed.
+ return;
+ }
+ if (cs->phase_ >= PHASE_SHUT)
+ cs->master_->ZapConn(cs);
+ else
+ cs->Shut();
+}
+
+Conn::Token Conn::last_token_ = 0;
+Conn::TokenMap Conn::token_map_;
« no previous file with comments | « net/web2socket_proxy/web2socket_conn.h ('k') | net/web2socket_proxy/web2socket_main.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698