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