OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "serv.h" |
| 6 |
| 7 #include <stdio.h> |
| 8 #include <stdlib.h> |
| 9 #include <string.h> |
| 10 |
| 11 #include <algorithm> |
| 12 |
| 13 #include <arpa/inet.h> |
| 14 #include <fcntl.h> |
| 15 #include <netinet/in.h> |
| 16 #include <signal.h> |
| 17 #include <sys/types.h> |
| 18 #include <sys/wait.h> |
| 19 |
| 20 #include "base/logging.h" |
| 21 #include "chrome/browser/chromeos/net/webproxy/conn.h" |
| 22 #include "third_party/libevent/evdns.h" |
| 23 |
| 24 namespace chromeos { |
| 25 namespace webproxy { |
| 26 |
| 27 Serv::Serv(const std::vector<std::string>& allowed_origins, |
| 28 struct sockaddr* addr, int addr_len) |
| 29 : allowed_origins_(allowed_origins), |
| 30 addr_(addr), |
| 31 addr_len_(addr_len), |
| 32 evbase_(NULL), |
| 33 listening_sock_(-1) { |
| 34 std::sort(allowed_origins_.begin(), allowed_origins_.end()); |
| 35 } |
| 36 |
| 37 Serv::~Serv() { |
| 38 while (!conn_pool_.empty()) |
| 39 ZapConn(conn_pool_.back()); |
| 40 if (listening_sock_ >= 0) { |
| 41 shutdown(listening_sock_, SHUT_RDWR); |
| 42 close(listening_sock_); |
| 43 } |
| 44 if (evbase_) |
| 45 event_base_free(evbase_); |
| 46 } |
| 47 |
| 48 void Serv::Run() { |
| 49 if (evbase_) { |
| 50 // One run at a time is all we do. |
| 51 return; |
| 52 } |
| 53 |
| 54 evbase_ = event_init(); |
| 55 if (!evbase_) { |
| 56 LOG(ERROR) << "Webproxy: Couldn't create libevent base"; |
| 57 return; |
| 58 } |
| 59 |
| 60 listening_sock_ = socket(AF_INET, SOCK_STREAM, 0); |
| 61 if (listening_sock_ < 0) { |
| 62 LOG(ERROR) << "Webproxy: Failed to create socket"; |
| 63 return; |
| 64 } |
| 65 if (bind(listening_sock_, addr_, addr_len_)) { |
| 66 LOG(ERROR) << "Webproxy: Failed to bind server socket"; |
| 67 return; |
| 68 } |
| 69 if (listen(listening_sock_, 12)) { |
| 70 LOG(ERROR) << "Webproxy: Failed to listen server socket"; |
| 71 return; |
| 72 } |
| 73 { |
| 74 int on = 1; |
| 75 setsockopt(listening_sock_, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); |
| 76 } |
| 77 if (!SetNonBlock(listening_sock_)) { |
| 78 LOG(ERROR) << "Webproxy: Failed to go non block"; |
| 79 return; |
| 80 } |
| 81 { |
| 82 struct event listen_event; |
| 83 event_set(&listen_event, listening_sock_, EV_READ | EV_PERSIST, |
| 84 &OnConnect, this); |
| 85 event_base_set(evbase_, &listen_event); |
| 86 if (event_add(&listen_event, NULL)) { |
| 87 LOG(ERROR) << "Webproxy: Failed to add listening event"; |
| 88 return; |
| 89 } |
| 90 } |
| 91 if (evdns_init()) { |
| 92 LOG(ERROR) << "Webproxy: Failed to initialize evDNS"; |
| 93 return; |
| 94 } |
| 95 if (!IgnoreSigPipe()) |
| 96 return; |
| 97 LOG(INFO) << "Webproxy: Starting event dispatch loop, accepting connections."; |
| 98 event_base_dispatch(evbase_); |
| 99 LOG(ERROR) << "Webproxy: Event dispatch loop terminated"; |
| 100 } |
| 101 |
| 102 void Serv::ZapConn(Conn* cs) { |
| 103 RevMap::iterator rit = rev_map_.find(cs); |
| 104 if (rit != rev_map_.end()) { |
| 105 conn_pool_.erase(rit->second); |
| 106 rev_map_.erase(rit); |
| 107 delete cs; |
| 108 } |
| 109 } |
| 110 |
| 111 void Serv::MarkConnImportance(Conn* cs, bool important) { |
| 112 if (conn_pool_.size() < kConnPoolLimit / 4) { |
| 113 // Fast common path. |
| 114 return; |
| 115 } |
| 116 RevMap::iterator rit = rev_map_.find(cs); |
| 117 if (rit != rev_map_.end()) { |
| 118 ConnPool::iterator it = rit->second; |
| 119 CHECK(*it == cs); |
| 120 if (important && it == conn_pool_.begin()) { |
| 121 // Already at the top. Shortcut. |
| 122 return; |
| 123 } |
| 124 conn_pool_.erase(it); |
| 125 } |
| 126 if (important) { |
| 127 conn_pool_.push_front(cs); |
| 128 rev_map_[cs] = conn_pool_.begin(); |
| 129 } else { |
| 130 conn_pool_.push_back(cs); |
| 131 rev_map_[cs] = conn_pool_.end(); |
| 132 --rev_map_[cs]; |
| 133 } |
| 134 } |
| 135 |
| 136 Conn* Serv::GetFreshConn() { |
| 137 if (conn_pool_.size() > kConnPoolLimit) { |
| 138 // Connections overflow. Shut those oldest not active. |
| 139 ConnPool::iterator it = conn_pool_.end(); |
| 140 --it; |
| 141 for (int i = conn_pool_.size() - kConnPoolLimit; i-- > 0;) { |
| 142 // Shut may invalidate an iterator; hence postdecrement. |
| 143 (*it--)->Shut(); |
| 144 } |
| 145 if (conn_pool_.size() > kConnPoolLimit + 12) { |
| 146 // Connections overflow. Zap the oldest not active. |
| 147 ZapConn(conn_pool_.back()); |
| 148 } |
| 149 } |
| 150 Conn* cs = new Conn(this); |
| 151 conn_pool_.push_front(cs); |
| 152 rev_map_[cs] = conn_pool_.begin(); |
| 153 return cs; |
| 154 } |
| 155 |
| 156 bool Serv::IsConnSane(Conn* cs) { |
| 157 return rev_map_.find(cs) != rev_map_.end(); |
| 158 } |
| 159 |
| 160 bool Serv::IsOriginAllowed(const std::string& origin) { |
| 161 return allowed_origins_.empty() || std::binary_search( |
| 162 allowed_origins_.begin(), allowed_origins_.end(), origin); |
| 163 } |
| 164 |
| 165 void Serv::OnConnect(int listening_sock, short event, void* ctx) { |
| 166 Serv* self = static_cast<Serv*>(ctx); |
| 167 Conn* cs = self->GetFreshConn(); |
| 168 cs->primchan().sock = accept(listening_sock, NULL, NULL); |
| 169 if (cs->primchan().sock < 0 |
| 170 || !SetNonBlock(cs->primchan().sock)) { |
| 171 self->ZapConn(cs); |
| 172 // Read readiness was triggered on listening socket |
| 173 // yet we failed to accept a connection; definitely weird. |
| 174 sleep(1); |
| 175 return; |
| 176 } |
| 177 |
| 178 cs->primchan().bev = bufferevent_new(cs->primchan().sock, |
| 179 &Conn::OnPrimchanRead, |
| 180 &Conn::OnPrimchanWrite, |
| 181 &Conn::OnPrimchanError, |
| 182 cs->token()); |
| 183 if (cs->primchan().bev == NULL) { |
| 184 self->ZapConn(cs); |
| 185 return; |
| 186 } |
| 187 bufferevent_base_set(self->evbase_, cs->primchan().bev); |
| 188 bufferevent_setwatermark(cs->primchan().bev, EV_READ, 0, kReadBufferLimit); |
| 189 if (bufferevent_enable(cs->primchan().bev, EV_READ | EV_WRITE)) { |
| 190 self->ZapConn(cs); |
| 191 return; |
| 192 } |
| 193 } |
| 194 |
| 195 bool Serv::SetNonBlock(int fd) { |
| 196 int flags = fcntl(fd, F_GETFL, 0); |
| 197 return flags >= 0 && fcntl(fd, F_SETFL, flags | O_NONBLOCK) == 0; |
| 198 } |
| 199 |
| 200 bool Serv::IgnoreSigPipe() { |
| 201 struct sigaction sa; |
| 202 sa.sa_handler = SIG_IGN; |
| 203 sa.sa_flags = 0; |
| 204 if (sigemptyset(&sa.sa_mask) || |
| 205 sigaction(SIGPIPE, &sa, 0)) { |
| 206 LOG(ERROR) << "Webproxy: Failed to disable sigpipe"; |
| 207 return false; |
| 208 } |
| 209 return true; |
| 210 } |
| 211 |
| 212 } // namespace chromeos |
| 213 } // namespace webproxy |
OLD | NEW |