blob: 8f9aad3d6069ce5faa1fe901099948173ad88ae7 [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>
Alex Vakulenkoa41ab512014-07-23 14:24:23 -07009#include <linux/if.h> // NOLINT - Needs definitions from netinet/in.h
Paul Stewartf65320c2011-10-13 14:34:52 -070010#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>
Ben Chana0ddf462014-02-06 11:32:42 -080017#include <base/strings/string_number_conversions.h>
18#include <base/strings/string_split.h>
19#include <base/strings/string_util.h>
20#include <base/strings/stringprintf.h>
Paul Stewartf65320c2011-10-13 14:34:52 -070021
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),
Paul Stewartf65320c2011-10-13 14:34:52 -070073 proxy_port_(-1),
74 proxy_socket_(-1),
Paul Stewartf65320c2011-10-13 14:34:52 -070075 sockets_(NULL),
76 client_socket_(-1),
77 server_port_(kDefaultServerPort),
78 server_socket_(-1),
Eric Shienbrood3e20a232012-02-16 11:35:56 -050079 is_route_requested_(false) { }
Paul Stewartf65320c2011-10-13 14:34:52 -070080
81HTTPProxy::~HTTPProxy() {
82 Stop();
83}
84
85bool HTTPProxy::Start(EventDispatcher *dispatcher,
86 Sockets *sockets) {
Ben Chanfad4a0b2012-04-18 15:49:59 -070087 SLOG(HTTPProxy, 3) << "In " << __func__;
Paul Stewartf65320c2011-10-13 14:34:52 -070088
89 if (sockets_) {
90 // We are already running.
91 return true;
92 }
93
94 proxy_socket_ = sockets->Socket(PF_INET, SOCK_STREAM, 0);
95 if (proxy_socket_ < 0) {
96 PLOG(ERROR) << "Failed to open proxy socket";
97 return false;
98 }
99
100 struct sockaddr_in addr;
101 socklen_t addrlen = sizeof(addr);
102 memset(&addr, 0, sizeof(addr));
103 addr.sin_family = AF_INET;
104 addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
105 if (sockets->Bind(proxy_socket_,
106 reinterpret_cast<struct sockaddr *>(&addr),
107 sizeof(addr)) < 0 ||
108 sockets->GetSockName(proxy_socket_,
109 reinterpret_cast<struct sockaddr *>(&addr),
110 &addrlen) < 0 ||
111 sockets->SetNonBlocking(proxy_socket_) < 0 ||
112 sockets->Listen(proxy_socket_, kMaxClientQueue) < 0) {
113 sockets->Close(proxy_socket_);
114 proxy_socket_ = -1;
115 PLOG(ERROR) << "HTTPProxy socket setup failed";
116 return false;
117 }
118
119 accept_handler_.reset(
120 dispatcher->CreateReadyHandler(proxy_socket_, IOHandler::kModeInput,
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500121 accept_callback_));
Paul Stewartf65320c2011-10-13 14:34:52 -0700122 dispatcher_ = dispatcher;
123 dns_client_.reset(new DNSClient(IPAddress::kFamilyIPv4,
Paul Stewartc8f4bef2011-12-13 09:45:51 -0800124 connection_->interface_name(),
125 connection_->dns_servers(),
Paul Stewartf65320c2011-10-13 14:34:52 -0700126 kDNSTimeoutSeconds * 1000,
127 dispatcher,
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500128 dns_client_callback_));
Paul Stewartf65320c2011-10-13 14:34:52 -0700129 proxy_port_ = ntohs(addr.sin_port);
130 server_async_connection_.reset(
Paul Stewartc8f4bef2011-12-13 09:45:51 -0800131 new AsyncConnection(connection_->interface_name(), dispatcher, sockets,
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500132 connect_completion_callback_));
Paul Stewartf65320c2011-10-13 14:34:52 -0700133 sockets_ = sockets;
134 state_ = kStateWaitConnection;
135 return true;
136}
137
138void HTTPProxy::Stop() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700139 SLOG(HTTPProxy, 3) << "In " << __func__;
Paul Stewartf65320c2011-10-13 14:34:52 -0700140
Alex Vakulenko8a532292014-06-16 17:18:44 -0700141 if (!sockets_) {
Paul Stewartf65320c2011-10-13 14:34:52 -0700142 return;
143 }
144
145 StopClient();
146
147 accept_handler_.reset();
148 dispatcher_ = NULL;
149 dns_client_.reset();
150 proxy_port_ = -1;
151 server_async_connection_.reset();
152 sockets_->Close(proxy_socket_);
153 proxy_socket_ = -1;
154 sockets_ = NULL;
155 state_ = kStateIdle;
156}
157
158// IOReadyHandler callback routine fired when a client connects to the
159// proxy's socket. We Accept() the client and start reading a request
160// from it.
161void HTTPProxy::AcceptClient(int fd) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700162 SLOG(HTTPProxy, 3) << "In " << __func__;
Paul Stewartf65320c2011-10-13 14:34:52 -0700163
164 int client_fd = sockets_->Accept(fd, NULL, NULL);
165 if (client_fd < 0) {
166 PLOG(ERROR) << "Client accept failed";
167 return;
168 }
169
170 accept_handler_->Stop();
171
172 client_socket_ = client_fd;
173
174 sockets_->SetNonBlocking(client_socket_);
Paul Stewart5f06a0e2012-12-20 11:11:33 -0800175 read_client_handler_.reset(dispatcher_->CreateInputHandler(
176 client_socket_,
177 read_client_callback_,
178 Bind(&HTTPProxy::OnReadError, weak_ptr_factory_.GetWeakPtr())));
Paul Stewartf65320c2011-10-13 14:34:52 -0700179 // Overall transaction timeout.
Paul Stewartf582b502012-04-04 21:39:22 -0700180 transaction_timeout_.Reset(Bind(&HTTPProxy::StopClient,
181 weak_ptr_factory_.GetWeakPtr()));
182 dispatcher_->PostDelayedTask(transaction_timeout_.callback(),
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500183 kTransactionTimeoutSeconds * 1000);
Paul Stewartf65320c2011-10-13 14:34:52 -0700184
185 state_ = kStateReadClientHeader;
186 StartIdleTimeout();
187}
188
189bool HTTPProxy::ConnectServer(const IPAddress &address, int port) {
190 state_ = kStateConnectServer;
191 if (!server_async_connection_->Start(address, port)) {
192 SendClientError(500, "Could not create socket to connect to server");
193 return false;
194 }
195 StartIdleTimeout();
196 return true;
197}
198
199// DNSClient callback that fires when the DNS request completes.
Paul Stewartbdb02e62012-02-22 16:24:33 -0800200void HTTPProxy::GetDNSResult(const Error &error, const IPAddress &address) {
201 if (!error.IsSuccess()) {
Paul Stewartf65320c2011-10-13 14:34:52 -0700202 SendClientError(502, string("Could not resolve hostname: ") +
Paul Stewartbdb02e62012-02-22 16:24:33 -0800203 error.message());
Paul Stewartf65320c2011-10-13 14:34:52 -0700204 return;
205 }
Paul Stewartbdb02e62012-02-22 16:24:33 -0800206 ConnectServer(address, server_port_);
Paul Stewartf65320c2011-10-13 14:34:52 -0700207}
208
209// IOReadyHandler callback routine which fires when the asynchronous Connect()
210// to the remote server completes (or fails).
211void HTTPProxy::OnConnectCompletion(bool success, int fd) {
212 if (!success) {
213 SendClientError(500, string("Socket connection delayed failure: ") +
214 server_async_connection_->error());
215 return;
216 }
217 server_socket_ = fd;
218 state_ = kStateTunnelData;
Paul Stewart58a577b2012-01-10 11:18:52 -0800219
220 // If this was a "CONNECT" request, notify the client that the connection
221 // has been established by sending an "OK" response.
222 if (LowerCaseEqualsASCII(client_method_, kHTTPMethodConnect)) {
223 SetClientResponse(200, "OK", "", "");
224 StartReceive();
225 }
226
Paul Stewartf65320c2011-10-13 14:34:52 -0700227 StartTransmit();
228}
229
Paul Stewart5f06a0e2012-12-20 11:11:33 -0800230void HTTPProxy::OnReadError(const Error &error) {
231 StopClient();
232}
233
Paul Stewartf65320c2011-10-13 14:34:52 -0700234// Read through the header lines from the client, modifying or adding
235// lines as necessary. Perform final determination of the hostname/port
236// we should connect to and either start a DNS request or connect to a
237// numeric address.
238bool HTTPProxy::ParseClientRequest() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700239 SLOG(HTTPProxy, 3) << "In " << __func__;
Paul Stewartf65320c2011-10-13 14:34:52 -0700240
241 string host;
242 bool found_via = false;
243 bool found_connection = false;
Paul Stewart6db7b242014-05-02 15:34:21 -0700244 for (auto &header : client_headers_) {
245 if (StartsWithASCII(header, "Host:", false)) {
246 host = header.substr(5);
247 } else if (StartsWithASCII(header, "Via:", false)) {
Paul Stewartf65320c2011-10-13 14:34:52 -0700248 found_via = true;
Paul Stewart6db7b242014-05-02 15:34:21 -0700249 header.append(StringPrintf(", %s shill-proxy", client_version_.c_str()));
250 } else if (StartsWithASCII(header, "Connection:", false)) {
Paul Stewartf65320c2011-10-13 14:34:52 -0700251 found_connection = true;
Paul Stewart6db7b242014-05-02 15:34:21 -0700252 header.assign("Connection: close");
253 } else if (StartsWithASCII(header, "Proxy-Connection:", false)) {
254 header.assign("Proxy-Connection: close");
Paul Stewartf65320c2011-10-13 14:34:52 -0700255 }
256 }
257
258 if (!found_connection) {
259 client_headers_.push_back("Connection: close");
260 }
261 if (!found_via) {
262 client_headers_.push_back(
263 StringPrintf("Via: %s shill-proxy", client_version_.c_str()));
264 }
265
266 // Assemble the request as it will be sent to the server.
267 client_data_.Clear();
Paul Stewart58a577b2012-01-10 11:18:52 -0800268 if (!LowerCaseEqualsASCII(client_method_, kHTTPMethodConnect)) {
Paul Stewart6db7b242014-05-02 15:34:21 -0700269 for (const auto &header : client_headers_) {
270 client_data_.Append(ByteString(header + "\r\n", false));
Paul Stewart58a577b2012-01-10 11:18:52 -0800271 }
272 client_data_.Append(ByteString(string("\r\n"), false));
Paul Stewartf65320c2011-10-13 14:34:52 -0700273 }
Paul Stewartf65320c2011-10-13 14:34:52 -0700274
Ben Chan6fbf64f2014-05-21 18:07:01 -0700275 base::TrimWhitespaceASCII(host, base::TRIM_ALL, &host);
Paul Stewartf65320c2011-10-13 14:34:52 -0700276 if (host.empty()) {
277 // Revert to using the hostname in the URL if no "Host:" header exists.
278 host = server_hostname_;
279 }
280
281 if (host.empty()) {
282 SendClientError(400, "I don't know what host you want me to connect to");
283 return false;
284 }
285
286 server_port_ = 80;
287 vector<string> host_parts;
288 base::SplitString(host, ':', &host_parts);
289
290 if (host_parts.size() > 2) {
291 SendClientError(400, "Too many colons in hostname");
292 return false;
293 } else if (host_parts.size() == 2) {
294 server_hostname_ = host_parts[0];
295 if (!base::StringToInt(host_parts[1], &server_port_)) {
296 SendClientError(400, "Could not parse port number");
297 return false;
298 }
299 } else {
300 server_hostname_ = host;
301 }
302
Paul Stewartc8f4bef2011-12-13 09:45:51 -0800303 connection_->RequestRouting();
304 is_route_requested_ = true;
305
Paul Stewartf65320c2011-10-13 14:34:52 -0700306 IPAddress addr(IPAddress::kFamilyIPv4);
307 if (addr.SetAddressFromString(server_hostname_)) {
308 if (!ConnectServer(addr, server_port_)) {
309 return false;
310 }
311 } else {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700312 SLOG(HTTPProxy, 3) << "Looking up host: " << server_hostname_;
Paul Stewartbdb02e62012-02-22 16:24:33 -0800313 Error error;
314 if (!dns_client_->Start(server_hostname_, &error)) {
315 SendClientError(502, "Could not resolve hostname: " + error.message());
Paul Stewartf65320c2011-10-13 14:34:52 -0700316 return false;
317 }
318 state_ = kStateLookupServer;
319 }
320 return true;
321}
322
323// Accept a new line into the client headers. Returns false if a parse
324// error occurs.
325bool HTTPProxy::ProcessLastHeaderLine() {
326 string *header = &client_headers_.back();
Ben Chana0ddf462014-02-06 11:32:42 -0800327 base::TrimString(*header, "\r", header);
Paul Stewartf65320c2011-10-13 14:34:52 -0700328
329 if (header->empty()) {
330 // Empty line terminates client headers.
331 client_headers_.pop_back();
332 if (!ParseClientRequest()) {
333 return false;
334 }
335 }
336
337 // Is this is the first header line?
338 if (client_headers_.size() == 1) {
Paul Stewart58a577b2012-01-10 11:18:52 -0800339 if (!ReadClientHTTPMethod(header) ||
340 !ReadClientHTTPVersion(header) ||
341 !ReadClientHostname(header)) {
Paul Stewartf65320c2011-10-13 14:34:52 -0700342 return false;
343 }
344 }
345
346 if (client_headers_.size() >= kMaxHeaderCount) {
347 SendClientError(500, kInternalErrorMsg);
348 return false;
349 }
350
351 return true;
352}
353
354// Split input from client into header lines, and consume parsed lines
355// from InputData. The passed in |data| is modified to indicate the
356// characters consumed.
357bool HTTPProxy::ReadClientHeaders(InputData *data) {
358 unsigned char *ptr = data->buf;
359 unsigned char *end = ptr + data->len;
360
361 if (client_headers_.empty()) {
362 client_headers_.push_back(string());
363 }
364
365 for (; ptr < end && state_ == kStateReadClientHeader; ++ptr) {
366 if (*ptr == '\n') {
367 if (!ProcessLastHeaderLine()) {
368 return false;
369 }
370
371 // Start a new line. New chararacters we receive will be appended there.
372 client_headers_.push_back(string());
373 continue;
374 }
375
376 string *header = &client_headers_.back();
377 // Is the first character of the header line a space or tab character?
378 if (header->empty() && (*ptr == ' ' || *ptr == '\t') &&
379 client_headers_.size() > 1) {
380 // Line Continuation: Add this character to the previous header line.
381 // This way, all of the data (including newlines and line continuation
382 // characters) related to a specific header will be contained within
383 // a single element of |client_headers_|, and manipulation of headers
384 // such as appending will be simpler. This is accomplished by removing
385 // the empty line we started, and instead appending the whitespace
386 // and following characters to the previous line.
387 client_headers_.pop_back();
388 header = &client_headers_.back();
389 header->append("\r\n");
390 }
391
392 if (header->length() >= kMaxHeaderSize) {
393 SendClientError(500, kInternalErrorMsg);
394 return false;
395 }
396 header->push_back(*ptr);
397 }
398
399 // Return the remaining data to the caller -- this could be POST data
400 // or other non-header data sent with the client request.
401 data->buf = ptr;
402 data->len = end - ptr;
403
404 return true;
405}
406
407// Finds the URL in the first line of an HTTP client header, and extracts
408// and removes the hostname (and port) from the URL. Returns false if a
409// parse error occurs, and true otherwise (whether or not the hostname was
410// found).
411bool HTTPProxy::ReadClientHostname(string *header) {
412 const string http_url_prefix(kHTTPURLPrefix);
413 size_t url_idx = header->find(http_url_prefix);
414 if (url_idx != string::npos) {
415 size_t host_start = url_idx + http_url_prefix.length();
416 size_t host_end =
417 header->find_first_of(kHTTPURLDelimiters, host_start);
418 if (host_end != string::npos) {
419 server_hostname_ = header->substr(host_start,
420 host_end - host_start);
421 // Modify the URL passed upstream to remove "http://<hostname>".
422 header->erase(url_idx, host_end - url_idx);
423 if ((*header)[url_idx] != '/') {
424 header->insert(url_idx, "/");
425 }
426 } else {
427 LOG(ERROR) << "Could not find end of hostname in request. Line was: "
428 << *header;
429 SendClientError(500, kInternalErrorMsg);
430 return false;
431 }
432 }
433 return true;
434}
435
Paul Stewart58a577b2012-01-10 11:18:52 -0800436bool HTTPProxy::ReadClientHTTPMethod(string *header) {
437 size_t method_end = header->find(kHTTPMethodTerminator);
438 if (method_end == string::npos || method_end == 0) {
439 LOG(ERROR) << "Could not parse HTTP method. Line was: " << *header;
440 SendClientError(501, "Server could not parse HTTP method");
441 return false;
442 }
443 client_method_ = header->substr(0, method_end);
444 return true;
445}
446
Paul Stewartf65320c2011-10-13 14:34:52 -0700447// Extract the HTTP version number from the first line of the client headers.
448// Returns true if found.
449bool HTTPProxy::ReadClientHTTPVersion(string *header) {
450 const string http_version_prefix(kHTTPVersionPrefix);
451 size_t http_ver_pos = header->find(http_version_prefix);
452 if (http_ver_pos != string::npos) {
453 client_version_ =
454 header->substr(http_ver_pos + http_version_prefix.length() - 1);
455 } else {
456 SendClientError(501, "Server only accepts HTTP/1.x requests");
457 return false;
458 }
459 return true;
460}
461
462// IOInputHandler callback that fires when data is read from the client.
463// This could be header data, or perhaps POST data that follows the headers.
464void HTTPProxy::ReadFromClient(InputData *data) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700465 SLOG(HTTPProxy, 3) << "In " << __func__ << " length " << data->len;
Paul Stewartf65320c2011-10-13 14:34:52 -0700466
467 if (data->len == 0) {
468 // EOF from client.
469 StopClient();
470 return;
471 }
472
473 if (state_ == kStateReadClientHeader) {
474 if (!ReadClientHeaders(data)) {
475 return;
476 }
477 if (state_ == kStateReadClientHeader) {
478 // Still consuming client headers; restart the input timer.
479 StartIdleTimeout();
480 return;
481 }
482 }
483
484 // Check data->len again since ReadClientHeaders() may have consumed some
485 // part of it.
486 if (data->len != 0) {
487 // The client sent some information after its headers. Buffer the client
488 // input and temporarily disable input events from the client.
489 client_data_.Append(ByteString(data->buf, data->len));
490 read_client_handler_->Stop();
491 StartTransmit();
492 }
493}
494
495// IOInputHandler callback which fires when data has been read from the
496// server.
497void HTTPProxy::ReadFromServer(InputData *data) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700498 SLOG(HTTPProxy, 3) << "In " << __func__ << " length " << data->len;
Paul Stewartf65320c2011-10-13 14:34:52 -0700499 if (data->len == 0) {
500 // Server closed connection.
501 if (server_data_.IsEmpty()) {
502 StopClient();
503 return;
504 }
505 state_ = kStateFlushResponse;
506 } else {
507 read_server_handler_->Stop();
508 }
509
510 server_data_.Append(ByteString(data->buf, data->len));
511
512 StartTransmit();
513}
514
515// Return an HTTP error message back to the client.
516void HTTPProxy::SendClientError(int code, const string &error) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700517 SLOG(HTTPProxy, 3) << "In " << __func__;
Paul Stewartf65320c2011-10-13 14:34:52 -0700518 LOG(ERROR) << "Sending error " << error;
Paul Stewart58a577b2012-01-10 11:18:52 -0800519 SetClientResponse(code, "ERROR", "text/plain", error);
Paul Stewartf65320c2011-10-13 14:34:52 -0700520 state_ = kStateFlushResponse;
521 StartTransmit();
522}
523
Paul Stewart58a577b2012-01-10 11:18:52 -0800524// Create an HTTP response message to be sent to the client.
525void HTTPProxy::SetClientResponse(int code, const string &type,
526 const string &content_type,
527 const string &message) {
528 string content_line;
529 if (!message.empty() && !content_type.empty()) {
530 content_line = StringPrintf("Content-Type: %s\r\n", content_type.c_str());
531 }
532 string response = StringPrintf("HTTP/1.1 %d %s\r\n"
533 "%s\r\n"
534 "%s", code, type.c_str(),
535 content_line.c_str(),
536 message.c_str());
537 server_data_ = ByteString(response, false);
538}
539
Paul Stewartf65320c2011-10-13 14:34:52 -0700540// Start a timeout for "the next event". This timeout augments the overall
541// transaction timeout to make sure there is some activity occurring at
542// reasonable intervals.
543void HTTPProxy::StartIdleTimeout() {
544 int timeout_seconds = 0;
545 switch (state_) {
546 case kStateReadClientHeader:
547 timeout_seconds = kClientHeaderTimeoutSeconds;
548 break;
549 case kStateConnectServer:
550 timeout_seconds = kConnectTimeoutSeconds;
551 break;
552 case kStateLookupServer:
553 // DNSClient has its own internal timeout, so we need not set one here.
554 timeout_seconds = 0;
555 break;
556 default:
557 timeout_seconds = kInputTimeoutSeconds;
558 break;
559 }
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500560 idle_timeout_.Cancel();
Paul Stewartf65320c2011-10-13 14:34:52 -0700561 if (timeout_seconds != 0) {
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500562 idle_timeout_.Reset(Bind(&HTTPProxy::StopClient,
563 weak_ptr_factory_.GetWeakPtr()));
564 dispatcher_->PostDelayedTask(idle_timeout_.callback(),
565 timeout_seconds * 1000);
Paul Stewartf65320c2011-10-13 14:34:52 -0700566 }
567}
568
569// Start the various input handlers. Listen for new data only if we have
570// completely written the last data we've received to the other end.
571void HTTPProxy::StartReceive() {
572 if (state_ == kStateTunnelData && client_data_.IsEmpty()) {
573 read_client_handler_->Start();
574 }
575 if (server_data_.IsEmpty()) {
576 if (state_ == kStateTunnelData) {
577 if (read_server_handler_.get()) {
578 read_server_handler_->Start();
579 } else {
Paul Stewart5f06a0e2012-12-20 11:11:33 -0800580 read_server_handler_.reset(dispatcher_->CreateInputHandler(
581 server_socket_,
582 read_server_callback_,
583 Bind(&HTTPProxy::OnReadError, weak_ptr_factory_.GetWeakPtr())));
Paul Stewartf65320c2011-10-13 14:34:52 -0700584 }
585 } else if (state_ == kStateFlushResponse) {
586 StopClient();
587 return;
588 }
589 }
590 StartIdleTimeout();
591}
592
593// Start the various output-ready handlers for the endpoints we have
594// data waiting for.
595void HTTPProxy::StartTransmit() {
596 if (state_ == kStateTunnelData && !client_data_.IsEmpty()) {
597 if (write_server_handler_.get()) {
598 write_server_handler_->Start();
599 } else {
600 write_server_handler_.reset(
601 dispatcher_->CreateReadyHandler(server_socket_,
602 IOHandler::kModeOutput,
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500603 write_server_callback_));
Paul Stewartf65320c2011-10-13 14:34:52 -0700604 }
605 }
606 if ((state_ == kStateFlushResponse || state_ == kStateTunnelData) &&
607 !server_data_.IsEmpty()) {
608 if (write_client_handler_.get()) {
609 write_client_handler_->Start();
610 } else {
611 write_client_handler_.reset(
612 dispatcher_->CreateReadyHandler(client_socket_,
613 IOHandler::kModeOutput,
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500614 write_client_callback_));
Paul Stewartf65320c2011-10-13 14:34:52 -0700615 }
616 }
617 StartIdleTimeout();
618}
619
620// End the transaction with the current client, restart the IOHandler
621// which alerts us to new clients connecting. This function is called
622// during various error conditions and is a callback for all timeouts.
623void HTTPProxy::StopClient() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700624 SLOG(HTTPProxy, 3) << "In " << __func__;
Paul Stewartf65320c2011-10-13 14:34:52 -0700625
Paul Stewartc8f4bef2011-12-13 09:45:51 -0800626 if (is_route_requested_) {
627 connection_->ReleaseRouting();
628 is_route_requested_ = false;
629 }
Paul Stewartf65320c2011-10-13 14:34:52 -0700630 write_client_handler_.reset();
631 read_client_handler_.reset();
632 if (client_socket_ != -1) {
633 sockets_->Close(client_socket_);
634 client_socket_ = -1;
635 }
636 client_headers_.clear();
Paul Stewart58a577b2012-01-10 11:18:52 -0800637 client_method_.clear();
Paul Stewartf65320c2011-10-13 14:34:52 -0700638 client_version_.clear();
639 server_port_ = kDefaultServerPort;
640 write_server_handler_.reset();
641 read_server_handler_.reset();
642 if (server_socket_ != -1) {
643 sockets_->Close(server_socket_);
644 server_socket_ = -1;
645 }
646 server_hostname_.clear();
647 client_data_.Clear();
648 server_data_.Clear();
649 dns_client_->Stop();
650 server_async_connection_->Stop();
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500651 idle_timeout_.Cancel();
Paul Stewartf582b502012-04-04 21:39:22 -0700652 transaction_timeout_.Cancel();
Paul Stewartf65320c2011-10-13 14:34:52 -0700653 accept_handler_->Start();
654 state_ = kStateWaitConnection;
655}
656
657// Output ReadyHandler callback which fires when the client socket is
658// ready for data to be sent to it.
659void HTTPProxy::WriteToClient(int fd) {
660 CHECK_EQ(client_socket_, fd);
661 int ret = sockets_->Send(fd, server_data_.GetConstData(),
662 server_data_.GetLength(), 0);
Ben Chanfad4a0b2012-04-18 15:49:59 -0700663 SLOG(HTTPProxy, 3) << "In " << __func__ << " wrote " << ret << " of "
664 << server_data_.GetLength();
Paul Stewartf65320c2011-10-13 14:34:52 -0700665 if (ret < 0) {
666 LOG(ERROR) << "Server write failed";
667 StopClient();
668 return;
669 }
670
671 server_data_ = ByteString(server_data_.GetConstData() + ret,
672 server_data_.GetLength() - ret);
673
674 if (server_data_.IsEmpty()) {
675 write_client_handler_->Stop();
676 }
677
678 StartReceive();
679}
680
681// Output ReadyHandler callback which fires when the server socket is
682// ready for data to be sent to it.
683void HTTPProxy::WriteToServer(int fd) {
684 CHECK_EQ(server_socket_, fd);
685 int ret = sockets_->Send(fd, client_data_.GetConstData(),
686 client_data_.GetLength(), 0);
Ben Chanfad4a0b2012-04-18 15:49:59 -0700687 SLOG(HTTPProxy, 3) << "In " << __func__ << " wrote " << ret << " of "
688 << client_data_.GetLength();
Paul Stewartf65320c2011-10-13 14:34:52 -0700689
690 if (ret < 0) {
691 LOG(ERROR) << "Client write failed";
692 StopClient();
693 return;
694 }
695
696 client_data_ = ByteString(client_data_.GetConstData() + ret,
697 client_data_.GetLength() - ret);
698
699 if (client_data_.IsEmpty()) {
700 write_server_handler_->Stop();
701 }
702
703 StartReceive();
704}
705
706} // namespace shill