blob: c56809805cf8d5edd8fceae3fe0c4985e221811d [file] [log] [blame]
Paul Stewart58a577b2012-01-10 11:18:52 -08001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Paul Stewartf65320c2011-10-13 14:34:52 -07002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "shill/http_proxy.h"
6
7#include <errno.h>
8#include <netinet/in.h>
9#include <linux/if.h> // Needs definitions from netinet/in.h
10#include <stdio.h>
11#include <time.h>
12
13#include <string>
14#include <vector>
15
Eric Shienbrood3e20a232012-02-16 11:35:56 -050016#include <base/bind.h>
Paul Stewartf65320c2011-10-13 14:34:52 -070017#include <base/string_number_conversions.h>
18#include <base/string_split.h>
19#include <base/string_util.h>
20#include <base/stringprintf.h>
21
22#include "shill/async_connection.h"
Paul Stewartc8f4bef2011-12-13 09:45:51 -080023#include "shill/connection.h"
Paul Stewartf65320c2011-10-13 14:34:52 -070024#include "shill/dns_client.h"
25#include "shill/event_dispatcher.h"
26#include "shill/ip_address.h"
Christopher Wileyb691efd2012-08-09 13:51:51 -070027#include "shill/logging.h"
Paul Stewartf65320c2011-10-13 14:34:52 -070028#include "shill/sockets.h"
29
Eric Shienbrood3e20a232012-02-16 11:35:56 -050030using base::Bind;
Paul Stewartf65320c2011-10-13 14:34:52 -070031using base::StringPrintf;
32using std::string;
33using std::vector;
34
35namespace shill {
36
37const int HTTPProxy::kClientHeaderTimeoutSeconds = 1;
38const int HTTPProxy::kConnectTimeoutSeconds = 10;
39const int HTTPProxy::kDNSTimeoutSeconds = 5;
40const int HTTPProxy::kDefaultServerPort = 80;
41const int HTTPProxy::kInputTimeoutSeconds = 30;
42const size_t HTTPProxy::kMaxClientQueue = 10;
43const size_t HTTPProxy::kMaxHeaderCount = 128;
44const size_t HTTPProxy::kMaxHeaderSize = 2048;
45const int HTTPProxy::kTransactionTimeoutSeconds = 600;
46
Paul Stewart58a577b2012-01-10 11:18:52 -080047const char HTTPProxy::kHTTPMethodConnect[] = "connect";
48const char HTTPProxy::kHTTPMethodTerminator[] = " ";
Paul Stewartf65320c2011-10-13 14:34:52 -070049const char HTTPProxy::kHTTPURLDelimiters[] = " /#?";
50const char HTTPProxy::kHTTPURLPrefix[] = "http://";
51const char HTTPProxy::kHTTPVersionPrefix[] = " HTTP/1";
52const char HTTPProxy::kInternalErrorMsg[] = "Proxy Failed: Internal Error";
53
Paul Stewartc8f4bef2011-12-13 09:45:51 -080054HTTPProxy::HTTPProxy(ConnectionRefPtr connection)
Paul Stewartf65320c2011-10-13 14:34:52 -070055 : state_(kStateIdle),
Paul Stewartc8f4bef2011-12-13 09:45:51 -080056 connection_(connection),
Eric Shienbrood3e20a232012-02-16 11:35:56 -050057 weak_ptr_factory_(this),
Eric Shienbrood9a245532012-03-07 14:20:39 -050058 accept_callback_(Bind(&HTTPProxy::AcceptClient,
59 weak_ptr_factory_.GetWeakPtr())),
Eric Shienbrood3e20a232012-02-16 11:35:56 -050060 connect_completion_callback_(Bind(&HTTPProxy::OnConnectCompletion,
Eric Shienbrood9a245532012-03-07 14:20:39 -050061 weak_ptr_factory_.GetWeakPtr())),
62 dns_client_callback_(Bind(&HTTPProxy::GetDNSResult,
63 weak_ptr_factory_.GetWeakPtr())),
64 read_client_callback_(Bind(&HTTPProxy::ReadFromClient,
65 weak_ptr_factory_.GetWeakPtr())),
66 read_server_callback_(Bind(&HTTPProxy::ReadFromServer,
67 weak_ptr_factory_.GetWeakPtr())),
68 write_client_callback_(Bind(&HTTPProxy::WriteToClient,
69 weak_ptr_factory_.GetWeakPtr())),
70 write_server_callback_(Bind(&HTTPProxy::WriteToServer,
71 weak_ptr_factory_.GetWeakPtr())),
Paul Stewartf65320c2011-10-13 14:34:52 -070072 dispatcher_(NULL),
73 dns_client_(NULL),
74 proxy_port_(-1),
75 proxy_socket_(-1),
76 server_async_connection_(NULL),
77 sockets_(NULL),
78 client_socket_(-1),
79 server_port_(kDefaultServerPort),
80 server_socket_(-1),
Eric Shienbrood3e20a232012-02-16 11:35:56 -050081 is_route_requested_(false) { }
Paul Stewartf65320c2011-10-13 14:34:52 -070082
83HTTPProxy::~HTTPProxy() {
84 Stop();
85}
86
87bool HTTPProxy::Start(EventDispatcher *dispatcher,
88 Sockets *sockets) {
Ben Chanfad4a0b2012-04-18 15:49:59 -070089 SLOG(HTTPProxy, 3) << "In " << __func__;
Paul Stewartf65320c2011-10-13 14:34:52 -070090
91 if (sockets_) {
92 // We are already running.
93 return true;
94 }
95
96 proxy_socket_ = sockets->Socket(PF_INET, SOCK_STREAM, 0);
97 if (proxy_socket_ < 0) {
98 PLOG(ERROR) << "Failed to open proxy socket";
99 return false;
100 }
101
102 struct sockaddr_in addr;
103 socklen_t addrlen = sizeof(addr);
104 memset(&addr, 0, sizeof(addr));
105 addr.sin_family = AF_INET;
106 addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
107 if (sockets->Bind(proxy_socket_,
108 reinterpret_cast<struct sockaddr *>(&addr),
109 sizeof(addr)) < 0 ||
110 sockets->GetSockName(proxy_socket_,
111 reinterpret_cast<struct sockaddr *>(&addr),
112 &addrlen) < 0 ||
113 sockets->SetNonBlocking(proxy_socket_) < 0 ||
114 sockets->Listen(proxy_socket_, kMaxClientQueue) < 0) {
115 sockets->Close(proxy_socket_);
116 proxy_socket_ = -1;
117 PLOG(ERROR) << "HTTPProxy socket setup failed";
118 return false;
119 }
120
121 accept_handler_.reset(
122 dispatcher->CreateReadyHandler(proxy_socket_, IOHandler::kModeInput,
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500123 accept_callback_));
Paul Stewartf65320c2011-10-13 14:34:52 -0700124 dispatcher_ = dispatcher;
125 dns_client_.reset(new DNSClient(IPAddress::kFamilyIPv4,
Paul Stewartc8f4bef2011-12-13 09:45:51 -0800126 connection_->interface_name(),
127 connection_->dns_servers(),
Paul Stewartf65320c2011-10-13 14:34:52 -0700128 kDNSTimeoutSeconds * 1000,
129 dispatcher,
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500130 dns_client_callback_));
Paul Stewartf65320c2011-10-13 14:34:52 -0700131 proxy_port_ = ntohs(addr.sin_port);
132 server_async_connection_.reset(
Paul Stewartc8f4bef2011-12-13 09:45:51 -0800133 new AsyncConnection(connection_->interface_name(), dispatcher, sockets,
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500134 connect_completion_callback_));
Paul Stewartf65320c2011-10-13 14:34:52 -0700135 sockets_ = sockets;
136 state_ = kStateWaitConnection;
137 return true;
138}
139
140void HTTPProxy::Stop() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700141 SLOG(HTTPProxy, 3) << "In " << __func__;
Paul Stewartf65320c2011-10-13 14:34:52 -0700142
143 if (!sockets_ ) {
144 return;
145 }
146
147 StopClient();
148
149 accept_handler_.reset();
150 dispatcher_ = NULL;
151 dns_client_.reset();
152 proxy_port_ = -1;
153 server_async_connection_.reset();
154 sockets_->Close(proxy_socket_);
155 proxy_socket_ = -1;
156 sockets_ = NULL;
157 state_ = kStateIdle;
158}
159
160// IOReadyHandler callback routine fired when a client connects to the
161// proxy's socket. We Accept() the client and start reading a request
162// from it.
163void HTTPProxy::AcceptClient(int fd) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700164 SLOG(HTTPProxy, 3) << "In " << __func__;
Paul Stewartf65320c2011-10-13 14:34:52 -0700165
166 int client_fd = sockets_->Accept(fd, NULL, NULL);
167 if (client_fd < 0) {
168 PLOG(ERROR) << "Client accept failed";
169 return;
170 }
171
172 accept_handler_->Stop();
173
174 client_socket_ = client_fd;
175
176 sockets_->SetNonBlocking(client_socket_);
Paul Stewart5f06a0e2012-12-20 11:11:33 -0800177 read_client_handler_.reset(dispatcher_->CreateInputHandler(
178 client_socket_,
179 read_client_callback_,
180 Bind(&HTTPProxy::OnReadError, weak_ptr_factory_.GetWeakPtr())));
Paul Stewartf65320c2011-10-13 14:34:52 -0700181 // Overall transaction timeout.
Paul Stewartf582b502012-04-04 21:39:22 -0700182 transaction_timeout_.Reset(Bind(&HTTPProxy::StopClient,
183 weak_ptr_factory_.GetWeakPtr()));
184 dispatcher_->PostDelayedTask(transaction_timeout_.callback(),
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500185 kTransactionTimeoutSeconds * 1000);
Paul Stewartf65320c2011-10-13 14:34:52 -0700186
187 state_ = kStateReadClientHeader;
188 StartIdleTimeout();
189}
190
191bool HTTPProxy::ConnectServer(const IPAddress &address, int port) {
192 state_ = kStateConnectServer;
193 if (!server_async_connection_->Start(address, port)) {
194 SendClientError(500, "Could not create socket to connect to server");
195 return false;
196 }
197 StartIdleTimeout();
198 return true;
199}
200
201// DNSClient callback that fires when the DNS request completes.
Paul Stewartbdb02e62012-02-22 16:24:33 -0800202void HTTPProxy::GetDNSResult(const Error &error, const IPAddress &address) {
203 if (!error.IsSuccess()) {
Paul Stewartf65320c2011-10-13 14:34:52 -0700204 SendClientError(502, string("Could not resolve hostname: ") +
Paul Stewartbdb02e62012-02-22 16:24:33 -0800205 error.message());
Paul Stewartf65320c2011-10-13 14:34:52 -0700206 return;
207 }
Paul Stewartbdb02e62012-02-22 16:24:33 -0800208 ConnectServer(address, server_port_);
Paul Stewartf65320c2011-10-13 14:34:52 -0700209}
210
211// IOReadyHandler callback routine which fires when the asynchronous Connect()
212// to the remote server completes (or fails).
213void HTTPProxy::OnConnectCompletion(bool success, int fd) {
214 if (!success) {
215 SendClientError(500, string("Socket connection delayed failure: ") +
216 server_async_connection_->error());
217 return;
218 }
219 server_socket_ = fd;
220 state_ = kStateTunnelData;
Paul Stewart58a577b2012-01-10 11:18:52 -0800221
222 // If this was a "CONNECT" request, notify the client that the connection
223 // has been established by sending an "OK" response.
224 if (LowerCaseEqualsASCII(client_method_, kHTTPMethodConnect)) {
225 SetClientResponse(200, "OK", "", "");
226 StartReceive();
227 }
228
Paul Stewartf65320c2011-10-13 14:34:52 -0700229 StartTransmit();
230}
231
Paul Stewart5f06a0e2012-12-20 11:11:33 -0800232void HTTPProxy::OnReadError(const Error &error) {
233 StopClient();
234}
235
Paul Stewartf65320c2011-10-13 14:34:52 -0700236// Read through the header lines from the client, modifying or adding
237// lines as necessary. Perform final determination of the hostname/port
238// we should connect to and either start a DNS request or connect to a
239// numeric address.
240bool HTTPProxy::ParseClientRequest() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700241 SLOG(HTTPProxy, 3) << "In " << __func__;
Paul Stewartf65320c2011-10-13 14:34:52 -0700242
243 string host;
244 bool found_via = false;
245 bool found_connection = false;
246 for (vector<string>::iterator it = client_headers_.begin();
247 it != client_headers_.end(); ++it) {
248 if (StartsWithASCII(*it, "Host:", false)) {
249 host = it->substr(5);
250 } else if (StartsWithASCII(*it, "Via:", false)) {
251 found_via = true;
252 (*it).append(StringPrintf(", %s shill-proxy", client_version_.c_str()));
253 } else if (StartsWithASCII(*it, "Connection:", false)) {
254 found_connection = true;
255 (*it).assign("Connection: close");
256 } else if (StartsWithASCII(*it, "Proxy-Connection:", false)) {
257 (*it).assign("Proxy-Connection: close");
258 }
259 }
260
261 if (!found_connection) {
262 client_headers_.push_back("Connection: close");
263 }
264 if (!found_via) {
265 client_headers_.push_back(
266 StringPrintf("Via: %s shill-proxy", client_version_.c_str()));
267 }
268
269 // Assemble the request as it will be sent to the server.
270 client_data_.Clear();
Paul Stewart58a577b2012-01-10 11:18:52 -0800271 if (!LowerCaseEqualsASCII(client_method_, kHTTPMethodConnect)) {
272 for (vector<string>::iterator it = client_headers_.begin();
273 it != client_headers_.end(); ++it) {
274 client_data_.Append(ByteString(*it + "\r\n", false));
275 }
276 client_data_.Append(ByteString(string("\r\n"), false));
Paul Stewartf65320c2011-10-13 14:34:52 -0700277 }
Paul Stewartf65320c2011-10-13 14:34:52 -0700278
279 TrimWhitespaceASCII(host, TRIM_ALL, &host);
280 if (host.empty()) {
281 // Revert to using the hostname in the URL if no "Host:" header exists.
282 host = server_hostname_;
283 }
284
285 if (host.empty()) {
286 SendClientError(400, "I don't know what host you want me to connect to");
287 return false;
288 }
289
290 server_port_ = 80;
291 vector<string> host_parts;
292 base::SplitString(host, ':', &host_parts);
293
294 if (host_parts.size() > 2) {
295 SendClientError(400, "Too many colons in hostname");
296 return false;
297 } else if (host_parts.size() == 2) {
298 server_hostname_ = host_parts[0];
299 if (!base::StringToInt(host_parts[1], &server_port_)) {
300 SendClientError(400, "Could not parse port number");
301 return false;
302 }
303 } else {
304 server_hostname_ = host;
305 }
306
Paul Stewartc8f4bef2011-12-13 09:45:51 -0800307 connection_->RequestRouting();
308 is_route_requested_ = true;
309
Paul Stewartf65320c2011-10-13 14:34:52 -0700310 IPAddress addr(IPAddress::kFamilyIPv4);
311 if (addr.SetAddressFromString(server_hostname_)) {
312 if (!ConnectServer(addr, server_port_)) {
313 return false;
314 }
315 } else {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700316 SLOG(HTTPProxy, 3) << "Looking up host: " << server_hostname_;
Paul Stewartbdb02e62012-02-22 16:24:33 -0800317 Error error;
318 if (!dns_client_->Start(server_hostname_, &error)) {
319 SendClientError(502, "Could not resolve hostname: " + error.message());
Paul Stewartf65320c2011-10-13 14:34:52 -0700320 return false;
321 }
322 state_ = kStateLookupServer;
323 }
324 return true;
325}
326
327// Accept a new line into the client headers. Returns false if a parse
328// error occurs.
329bool HTTPProxy::ProcessLastHeaderLine() {
330 string *header = &client_headers_.back();
331 TrimString(*header, "\r", header);
332
333 if (header->empty()) {
334 // Empty line terminates client headers.
335 client_headers_.pop_back();
336 if (!ParseClientRequest()) {
337 return false;
338 }
339 }
340
341 // Is this is the first header line?
342 if (client_headers_.size() == 1) {
Paul Stewart58a577b2012-01-10 11:18:52 -0800343 if (!ReadClientHTTPMethod(header) ||
344 !ReadClientHTTPVersion(header) ||
345 !ReadClientHostname(header)) {
Paul Stewartf65320c2011-10-13 14:34:52 -0700346 return false;
347 }
348 }
349
350 if (client_headers_.size() >= kMaxHeaderCount) {
351 SendClientError(500, kInternalErrorMsg);
352 return false;
353 }
354
355 return true;
356}
357
358// Split input from client into header lines, and consume parsed lines
359// from InputData. The passed in |data| is modified to indicate the
360// characters consumed.
361bool HTTPProxy::ReadClientHeaders(InputData *data) {
362 unsigned char *ptr = data->buf;
363 unsigned char *end = ptr + data->len;
364
365 if (client_headers_.empty()) {
366 client_headers_.push_back(string());
367 }
368
369 for (; ptr < end && state_ == kStateReadClientHeader; ++ptr) {
370 if (*ptr == '\n') {
371 if (!ProcessLastHeaderLine()) {
372 return false;
373 }
374
375 // Start a new line. New chararacters we receive will be appended there.
376 client_headers_.push_back(string());
377 continue;
378 }
379
380 string *header = &client_headers_.back();
381 // Is the first character of the header line a space or tab character?
382 if (header->empty() && (*ptr == ' ' || *ptr == '\t') &&
383 client_headers_.size() > 1) {
384 // Line Continuation: Add this character to the previous header line.
385 // This way, all of the data (including newlines and line continuation
386 // characters) related to a specific header will be contained within
387 // a single element of |client_headers_|, and manipulation of headers
388 // such as appending will be simpler. This is accomplished by removing
389 // the empty line we started, and instead appending the whitespace
390 // and following characters to the previous line.
391 client_headers_.pop_back();
392 header = &client_headers_.back();
393 header->append("\r\n");
394 }
395
396 if (header->length() >= kMaxHeaderSize) {
397 SendClientError(500, kInternalErrorMsg);
398 return false;
399 }
400 header->push_back(*ptr);
401 }
402
403 // Return the remaining data to the caller -- this could be POST data
404 // or other non-header data sent with the client request.
405 data->buf = ptr;
406 data->len = end - ptr;
407
408 return true;
409}
410
411// Finds the URL in the first line of an HTTP client header, and extracts
412// and removes the hostname (and port) from the URL. Returns false if a
413// parse error occurs, and true otherwise (whether or not the hostname was
414// found).
415bool HTTPProxy::ReadClientHostname(string *header) {
416 const string http_url_prefix(kHTTPURLPrefix);
417 size_t url_idx = header->find(http_url_prefix);
418 if (url_idx != string::npos) {
419 size_t host_start = url_idx + http_url_prefix.length();
420 size_t host_end =
421 header->find_first_of(kHTTPURLDelimiters, host_start);
422 if (host_end != string::npos) {
423 server_hostname_ = header->substr(host_start,
424 host_end - host_start);
425 // Modify the URL passed upstream to remove "http://<hostname>".
426 header->erase(url_idx, host_end - url_idx);
427 if ((*header)[url_idx] != '/') {
428 header->insert(url_idx, "/");
429 }
430 } else {
431 LOG(ERROR) << "Could not find end of hostname in request. Line was: "
432 << *header;
433 SendClientError(500, kInternalErrorMsg);
434 return false;
435 }
436 }
437 return true;
438}
439
Paul Stewart58a577b2012-01-10 11:18:52 -0800440bool HTTPProxy::ReadClientHTTPMethod(string *header) {
441 size_t method_end = header->find(kHTTPMethodTerminator);
442 if (method_end == string::npos || method_end == 0) {
443 LOG(ERROR) << "Could not parse HTTP method. Line was: " << *header;
444 SendClientError(501, "Server could not parse HTTP method");
445 return false;
446 }
447 client_method_ = header->substr(0, method_end);
448 return true;
449}
450
Paul Stewartf65320c2011-10-13 14:34:52 -0700451// Extract the HTTP version number from the first line of the client headers.
452// Returns true if found.
453bool HTTPProxy::ReadClientHTTPVersion(string *header) {
454 const string http_version_prefix(kHTTPVersionPrefix);
455 size_t http_ver_pos = header->find(http_version_prefix);
456 if (http_ver_pos != string::npos) {
457 client_version_ =
458 header->substr(http_ver_pos + http_version_prefix.length() - 1);
459 } else {
460 SendClientError(501, "Server only accepts HTTP/1.x requests");
461 return false;
462 }
463 return true;
464}
465
466// IOInputHandler callback that fires when data is read from the client.
467// This could be header data, or perhaps POST data that follows the headers.
468void HTTPProxy::ReadFromClient(InputData *data) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700469 SLOG(HTTPProxy, 3) << "In " << __func__ << " length " << data->len;
Paul Stewartf65320c2011-10-13 14:34:52 -0700470
471 if (data->len == 0) {
472 // EOF from client.
473 StopClient();
474 return;
475 }
476
477 if (state_ == kStateReadClientHeader) {
478 if (!ReadClientHeaders(data)) {
479 return;
480 }
481 if (state_ == kStateReadClientHeader) {
482 // Still consuming client headers; restart the input timer.
483 StartIdleTimeout();
484 return;
485 }
486 }
487
488 // Check data->len again since ReadClientHeaders() may have consumed some
489 // part of it.
490 if (data->len != 0) {
491 // The client sent some information after its headers. Buffer the client
492 // input and temporarily disable input events from the client.
493 client_data_.Append(ByteString(data->buf, data->len));
494 read_client_handler_->Stop();
495 StartTransmit();
496 }
497}
498
499// IOInputHandler callback which fires when data has been read from the
500// server.
501void HTTPProxy::ReadFromServer(InputData *data) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700502 SLOG(HTTPProxy, 3) << "In " << __func__ << " length " << data->len;
Paul Stewartf65320c2011-10-13 14:34:52 -0700503 if (data->len == 0) {
504 // Server closed connection.
505 if (server_data_.IsEmpty()) {
506 StopClient();
507 return;
508 }
509 state_ = kStateFlushResponse;
510 } else {
511 read_server_handler_->Stop();
512 }
513
514 server_data_.Append(ByteString(data->buf, data->len));
515
516 StartTransmit();
517}
518
519// Return an HTTP error message back to the client.
520void HTTPProxy::SendClientError(int code, const string &error) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700521 SLOG(HTTPProxy, 3) << "In " << __func__;
Paul Stewartf65320c2011-10-13 14:34:52 -0700522 LOG(ERROR) << "Sending error " << error;
Paul Stewart58a577b2012-01-10 11:18:52 -0800523 SetClientResponse(code, "ERROR", "text/plain", error);
Paul Stewartf65320c2011-10-13 14:34:52 -0700524 state_ = kStateFlushResponse;
525 StartTransmit();
526}
527
Paul Stewart58a577b2012-01-10 11:18:52 -0800528// Create an HTTP response message to be sent to the client.
529void HTTPProxy::SetClientResponse(int code, const string &type,
530 const string &content_type,
531 const string &message) {
532 string content_line;
533 if (!message.empty() && !content_type.empty()) {
534 content_line = StringPrintf("Content-Type: %s\r\n", content_type.c_str());
535 }
536 string response = StringPrintf("HTTP/1.1 %d %s\r\n"
537 "%s\r\n"
538 "%s", code, type.c_str(),
539 content_line.c_str(),
540 message.c_str());
541 server_data_ = ByteString(response, false);
542}
543
Paul Stewartf65320c2011-10-13 14:34:52 -0700544// Start a timeout for "the next event". This timeout augments the overall
545// transaction timeout to make sure there is some activity occurring at
546// reasonable intervals.
547void HTTPProxy::StartIdleTimeout() {
548 int timeout_seconds = 0;
549 switch (state_) {
550 case kStateReadClientHeader:
551 timeout_seconds = kClientHeaderTimeoutSeconds;
552 break;
553 case kStateConnectServer:
554 timeout_seconds = kConnectTimeoutSeconds;
555 break;
556 case kStateLookupServer:
557 // DNSClient has its own internal timeout, so we need not set one here.
558 timeout_seconds = 0;
559 break;
560 default:
561 timeout_seconds = kInputTimeoutSeconds;
562 break;
563 }
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500564 idle_timeout_.Cancel();
Paul Stewartf65320c2011-10-13 14:34:52 -0700565 if (timeout_seconds != 0) {
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500566 idle_timeout_.Reset(Bind(&HTTPProxy::StopClient,
567 weak_ptr_factory_.GetWeakPtr()));
568 dispatcher_->PostDelayedTask(idle_timeout_.callback(),
569 timeout_seconds * 1000);
Paul Stewartf65320c2011-10-13 14:34:52 -0700570 }
571}
572
573// Start the various input handlers. Listen for new data only if we have
574// completely written the last data we've received to the other end.
575void HTTPProxy::StartReceive() {
576 if (state_ == kStateTunnelData && client_data_.IsEmpty()) {
577 read_client_handler_->Start();
578 }
579 if (server_data_.IsEmpty()) {
580 if (state_ == kStateTunnelData) {
581 if (read_server_handler_.get()) {
582 read_server_handler_->Start();
583 } else {
Paul Stewart5f06a0e2012-12-20 11:11:33 -0800584 read_server_handler_.reset(dispatcher_->CreateInputHandler(
585 server_socket_,
586 read_server_callback_,
587 Bind(&HTTPProxy::OnReadError, weak_ptr_factory_.GetWeakPtr())));
Paul Stewartf65320c2011-10-13 14:34:52 -0700588 }
589 } else if (state_ == kStateFlushResponse) {
590 StopClient();
591 return;
592 }
593 }
594 StartIdleTimeout();
595}
596
597// Start the various output-ready handlers for the endpoints we have
598// data waiting for.
599void HTTPProxy::StartTransmit() {
600 if (state_ == kStateTunnelData && !client_data_.IsEmpty()) {
601 if (write_server_handler_.get()) {
602 write_server_handler_->Start();
603 } else {
604 write_server_handler_.reset(
605 dispatcher_->CreateReadyHandler(server_socket_,
606 IOHandler::kModeOutput,
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500607 write_server_callback_));
Paul Stewartf65320c2011-10-13 14:34:52 -0700608 }
609 }
610 if ((state_ == kStateFlushResponse || state_ == kStateTunnelData) &&
611 !server_data_.IsEmpty()) {
612 if (write_client_handler_.get()) {
613 write_client_handler_->Start();
614 } else {
615 write_client_handler_.reset(
616 dispatcher_->CreateReadyHandler(client_socket_,
617 IOHandler::kModeOutput,
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500618 write_client_callback_));
Paul Stewartf65320c2011-10-13 14:34:52 -0700619 }
620 }
621 StartIdleTimeout();
622}
623
624// End the transaction with the current client, restart the IOHandler
625// which alerts us to new clients connecting. This function is called
626// during various error conditions and is a callback for all timeouts.
627void HTTPProxy::StopClient() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700628 SLOG(HTTPProxy, 3) << "In " << __func__;
Paul Stewartf65320c2011-10-13 14:34:52 -0700629
Paul Stewartc8f4bef2011-12-13 09:45:51 -0800630 if (is_route_requested_) {
631 connection_->ReleaseRouting();
632 is_route_requested_ = false;
633 }
Paul Stewartf65320c2011-10-13 14:34:52 -0700634 write_client_handler_.reset();
635 read_client_handler_.reset();
636 if (client_socket_ != -1) {
637 sockets_->Close(client_socket_);
638 client_socket_ = -1;
639 }
640 client_headers_.clear();
Paul Stewart58a577b2012-01-10 11:18:52 -0800641 client_method_.clear();
Paul Stewartf65320c2011-10-13 14:34:52 -0700642 client_version_.clear();
643 server_port_ = kDefaultServerPort;
644 write_server_handler_.reset();
645 read_server_handler_.reset();
646 if (server_socket_ != -1) {
647 sockets_->Close(server_socket_);
648 server_socket_ = -1;
649 }
650 server_hostname_.clear();
651 client_data_.Clear();
652 server_data_.Clear();
653 dns_client_->Stop();
654 server_async_connection_->Stop();
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500655 idle_timeout_.Cancel();
Paul Stewartf582b502012-04-04 21:39:22 -0700656 transaction_timeout_.Cancel();
Paul Stewartf65320c2011-10-13 14:34:52 -0700657 accept_handler_->Start();
658 state_ = kStateWaitConnection;
659}
660
661// Output ReadyHandler callback which fires when the client socket is
662// ready for data to be sent to it.
663void HTTPProxy::WriteToClient(int fd) {
664 CHECK_EQ(client_socket_, fd);
665 int ret = sockets_->Send(fd, server_data_.GetConstData(),
666 server_data_.GetLength(), 0);
Ben Chanfad4a0b2012-04-18 15:49:59 -0700667 SLOG(HTTPProxy, 3) << "In " << __func__ << " wrote " << ret << " of "
668 << server_data_.GetLength();
Paul Stewartf65320c2011-10-13 14:34:52 -0700669 if (ret < 0) {
670 LOG(ERROR) << "Server write failed";
671 StopClient();
672 return;
673 }
674
675 server_data_ = ByteString(server_data_.GetConstData() + ret,
676 server_data_.GetLength() - ret);
677
678 if (server_data_.IsEmpty()) {
679 write_client_handler_->Stop();
680 }
681
682 StartReceive();
683}
684
685// Output ReadyHandler callback which fires when the server socket is
686// ready for data to be sent to it.
687void HTTPProxy::WriteToServer(int fd) {
688 CHECK_EQ(server_socket_, fd);
689 int ret = sockets_->Send(fd, client_data_.GetConstData(),
690 client_data_.GetLength(), 0);
Ben Chanfad4a0b2012-04-18 15:49:59 -0700691 SLOG(HTTPProxy, 3) << "In " << __func__ << " wrote " << ret << " of "
692 << client_data_.GetLength();
Paul Stewartf65320c2011-10-13 14:34:52 -0700693
694 if (ret < 0) {
695 LOG(ERROR) << "Client write failed";
696 StopClient();
697 return;
698 }
699
700 client_data_ = ByteString(client_data_.GetConstData() + ret,
701 client_data_.GetLength() - ret);
702
703 if (client_data_.IsEmpty()) {
704 write_server_handler_->Stop();
705 }
706
707 StartReceive();
708}
709
710} // namespace shill