| Index: chrome/browser/chromeos/net/webproxy/serv.cc
|
| diff --git a/chrome/browser/chromeos/net/webproxy/serv.cc b/chrome/browser/chromeos/net/webproxy/serv.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..e57d32f3c5e462093f6b717ea01793ea3873a4ac
|
| --- /dev/null
|
| +++ b/chrome/browser/chromeos/net/webproxy/serv.cc
|
| @@ -0,0 +1,213 @@
|
| +// Copyright (c) 2011 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 "serv.h"
|
| +
|
| +#include <stdio.h>
|
| +#include <stdlib.h>
|
| +#include <string.h>
|
| +
|
| +#include <algorithm>
|
| +
|
| +#include <arpa/inet.h>
|
| +#include <fcntl.h>
|
| +#include <netinet/in.h>
|
| +#include <signal.h>
|
| +#include <sys/types.h>
|
| +#include <sys/wait.h>
|
| +
|
| +#include "base/logging.h"
|
| +#include "chrome/browser/chromeos/net/webproxy/conn.h"
|
| +#include "third_party/libevent/evdns.h"
|
| +
|
| +namespace chromeos {
|
| +namespace webproxy {
|
| +
|
| +Serv::Serv(const std::vector<std::string>& allowed_origins,
|
| + struct sockaddr* addr, int addr_len)
|
| + : allowed_origins_(allowed_origins),
|
| + addr_(addr),
|
| + addr_len_(addr_len),
|
| + evbase_(NULL),
|
| + listening_sock_(-1) {
|
| + std::sort(allowed_origins_.begin(), allowed_origins_.end());
|
| +}
|
| +
|
| +Serv::~Serv() {
|
| + while (!conn_pool_.empty())
|
| + ZapConn(conn_pool_.back());
|
| + if (listening_sock_ >= 0) {
|
| + shutdown(listening_sock_, SHUT_RDWR);
|
| + close(listening_sock_);
|
| + }
|
| + if (evbase_)
|
| + event_base_free(evbase_);
|
| +}
|
| +
|
| +void Serv::Run() {
|
| + if (evbase_) {
|
| + // One run at a time is all we do.
|
| + return;
|
| + }
|
| +
|
| + evbase_ = event_init();
|
| + if (!evbase_) {
|
| + LOG(ERROR) << "Webproxy: Couldn't create libevent base";
|
| + return;
|
| + }
|
| +
|
| + listening_sock_ = socket(AF_INET, SOCK_STREAM, 0);
|
| + if (listening_sock_ < 0) {
|
| + LOG(ERROR) << "Webproxy: Failed to create socket";
|
| + return;
|
| + }
|
| + if (bind(listening_sock_, addr_, addr_len_)) {
|
| + LOG(ERROR) << "Webproxy: Failed to bind server socket";
|
| + return;
|
| + }
|
| + if (listen(listening_sock_, 12)) {
|
| + LOG(ERROR) << "Webproxy: Failed to listen server socket";
|
| + return;
|
| + }
|
| + {
|
| + int on = 1;
|
| + setsockopt(listening_sock_, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
|
| + }
|
| + if (!SetNonBlock(listening_sock_)) {
|
| + LOG(ERROR) << "Webproxy: Failed to go non block";
|
| + return;
|
| + }
|
| + {
|
| + struct event listen_event;
|
| + event_set(&listen_event, listening_sock_, EV_READ | EV_PERSIST,
|
| + &OnConnect, this);
|
| + event_base_set(evbase_, &listen_event);
|
| + if (event_add(&listen_event, NULL)) {
|
| + LOG(ERROR) << "Webproxy: Failed to add listening event";
|
| + return;
|
| + }
|
| + }
|
| + if (evdns_init()) {
|
| + LOG(ERROR) << "Webproxy: Failed to initialize evDNS";
|
| + return;
|
| + }
|
| + if (!IgnoreSigPipe())
|
| + return;
|
| + LOG(INFO) << "Webproxy: Starting event dispatch loop, accepting connections.";
|
| + event_base_dispatch(evbase_);
|
| + LOG(ERROR) << "Webproxy: Event dispatch loop terminated";
|
| +}
|
| +
|
| +void Serv::ZapConn(Conn* cs) {
|
| + RevMap::iterator rit = rev_map_.find(cs);
|
| + if (rit != rev_map_.end()) {
|
| + conn_pool_.erase(rit->second);
|
| + rev_map_.erase(rit);
|
| + delete cs;
|
| + }
|
| +}
|
| +
|
| +void Serv::MarkConnImportance(Conn* cs, bool important) {
|
| + if (conn_pool_.size() < kConnPoolLimit / 4) {
|
| + // Fast common path.
|
| + return;
|
| + }
|
| + RevMap::iterator rit = rev_map_.find(cs);
|
| + if (rit != rev_map_.end()) {
|
| + ConnPool::iterator it = rit->second;
|
| + CHECK(*it == cs);
|
| + if (important && it == conn_pool_.begin()) {
|
| + // Already at the top. Shortcut.
|
| + return;
|
| + }
|
| + conn_pool_.erase(it);
|
| + }
|
| + if (important) {
|
| + conn_pool_.push_front(cs);
|
| + rev_map_[cs] = conn_pool_.begin();
|
| + } else {
|
| + conn_pool_.push_back(cs);
|
| + rev_map_[cs] = conn_pool_.end();
|
| + --rev_map_[cs];
|
| + }
|
| +}
|
| +
|
| +Conn* Serv::GetFreshConn() {
|
| + if (conn_pool_.size() > kConnPoolLimit) {
|
| + // Connections overflow. Shut those oldest not active.
|
| + ConnPool::iterator it = conn_pool_.end();
|
| + --it;
|
| + for (int i = conn_pool_.size() - kConnPoolLimit; i-- > 0;) {
|
| + // Shut may invalidate an iterator; hence postdecrement.
|
| + (*it--)->Shut();
|
| + }
|
| + if (conn_pool_.size() > kConnPoolLimit + 12) {
|
| + // Connections overflow. Zap the oldest not active.
|
| + ZapConn(conn_pool_.back());
|
| + }
|
| + }
|
| + Conn* cs = new Conn(this);
|
| + conn_pool_.push_front(cs);
|
| + rev_map_[cs] = conn_pool_.begin();
|
| + return cs;
|
| +}
|
| +
|
| +bool Serv::IsConnSane(Conn* cs) {
|
| + return rev_map_.find(cs) != rev_map_.end();
|
| +}
|
| +
|
| +bool Serv::IsOriginAllowed(const std::string& origin) {
|
| + return allowed_origins_.empty() || std::binary_search(
|
| + allowed_origins_.begin(), allowed_origins_.end(), origin);
|
| +}
|
| +
|
| +void Serv::OnConnect(int listening_sock, short event, void* ctx) {
|
| + Serv* self = static_cast<Serv*>(ctx);
|
| + Conn* cs = self->GetFreshConn();
|
| + cs->primchan().sock = accept(listening_sock, NULL, NULL);
|
| + if (cs->primchan().sock < 0
|
| + || !SetNonBlock(cs->primchan().sock)) {
|
| + self->ZapConn(cs);
|
| + // Read readiness was triggered on listening socket
|
| + // yet we failed to accept a connection; definitely weird.
|
| + sleep(1);
|
| + return;
|
| + }
|
| +
|
| + cs->primchan().bev = bufferevent_new(cs->primchan().sock,
|
| + &Conn::OnPrimchanRead,
|
| + &Conn::OnPrimchanWrite,
|
| + &Conn::OnPrimchanError,
|
| + cs->token());
|
| + if (cs->primchan().bev == NULL) {
|
| + self->ZapConn(cs);
|
| + return;
|
| + }
|
| + bufferevent_base_set(self->evbase_, cs->primchan().bev);
|
| + bufferevent_setwatermark(cs->primchan().bev, EV_READ, 0, kReadBufferLimit);
|
| + if (bufferevent_enable(cs->primchan().bev, EV_READ | EV_WRITE)) {
|
| + self->ZapConn(cs);
|
| + return;
|
| + }
|
| +}
|
| +
|
| +bool Serv::SetNonBlock(int fd) {
|
| + int flags = fcntl(fd, F_GETFL, 0);
|
| + return flags >= 0 && fcntl(fd, F_SETFL, flags | O_NONBLOCK) == 0;
|
| +}
|
| +
|
| +bool Serv::IgnoreSigPipe() {
|
| + struct sigaction sa;
|
| + sa.sa_handler = SIG_IGN;
|
| + sa.sa_flags = 0;
|
| + if (sigemptyset(&sa.sa_mask) ||
|
| + sigaction(SIGPIPE, &sa, 0)) {
|
| + LOG(ERROR) << "Webproxy: Failed to disable sigpipe";
|
| + return false;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +} // namespace chromeos
|
| +} // namespace webproxy
|
|
|