blob: 3ae8bbcf88594b6d479c0c459d055c28b87e256d [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"
Christopher Wileyb691efd2012-08-09 13:51:51 -070026#include "shill/logging.h"
Peter Qiu8d6b5972014-10-28 15:33:34 -070027#include "shill/net/ip_address.h"
28#include "shill/net/sockets.h"
Paul Stewartf65320c2011-10-13 14:34:52 -070029
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
Rebecca Silbersteinc9c31d82014-10-21 15:01:00 -070037namespace Logging {
38static auto kModuleLogScope = ScopeLogger::kHTTPProxy;
39static string ObjectID(Connection *c) {
40 return c->interface_name();
41}
42}
43
Paul Stewartf65320c2011-10-13 14:34:52 -070044const int HTTPProxy::kClientHeaderTimeoutSeconds = 1;
45const int HTTPProxy::kConnectTimeoutSeconds = 10;
46const int HTTPProxy::kDNSTimeoutSeconds = 5;
47const int HTTPProxy::kDefaultServerPort = 80;
48const int HTTPProxy::kInputTimeoutSeconds = 30;
49const size_t HTTPProxy::kMaxClientQueue = 10;
50const size_t HTTPProxy::kMaxHeaderCount = 128;
51const size_t HTTPProxy::kMaxHeaderSize = 2048;
52const int HTTPProxy::kTransactionTimeoutSeconds = 600;
53
Paul Stewart58a577b2012-01-10 11:18:52 -080054const char HTTPProxy::kHTTPMethodConnect[] = "connect";
55const char HTTPProxy::kHTTPMethodTerminator[] = " ";
Paul Stewartf65320c2011-10-13 14:34:52 -070056const char HTTPProxy::kHTTPURLDelimiters[] = " /#?";
57const char HTTPProxy::kHTTPURLPrefix[] = "http://";
58const char HTTPProxy::kHTTPVersionPrefix[] = " HTTP/1";
59const char HTTPProxy::kInternalErrorMsg[] = "Proxy Failed: Internal Error";
60
Paul Stewartc8f4bef2011-12-13 09:45:51 -080061HTTPProxy::HTTPProxy(ConnectionRefPtr connection)
Paul Stewartf65320c2011-10-13 14:34:52 -070062 : state_(kStateIdle),
Paul Stewartc8f4bef2011-12-13 09:45:51 -080063 connection_(connection),
Eric Shienbrood3e20a232012-02-16 11:35:56 -050064 weak_ptr_factory_(this),
Eric Shienbrood9a245532012-03-07 14:20:39 -050065 accept_callback_(Bind(&HTTPProxy::AcceptClient,
66 weak_ptr_factory_.GetWeakPtr())),
Eric Shienbrood3e20a232012-02-16 11:35:56 -050067 connect_completion_callback_(Bind(&HTTPProxy::OnConnectCompletion,
Eric Shienbrood9a245532012-03-07 14:20:39 -050068 weak_ptr_factory_.GetWeakPtr())),
69 dns_client_callback_(Bind(&HTTPProxy::GetDNSResult,
70 weak_ptr_factory_.GetWeakPtr())),
71 read_client_callback_(Bind(&HTTPProxy::ReadFromClient,
72 weak_ptr_factory_.GetWeakPtr())),
73 read_server_callback_(Bind(&HTTPProxy::ReadFromServer,
74 weak_ptr_factory_.GetWeakPtr())),
75 write_client_callback_(Bind(&HTTPProxy::WriteToClient,
76 weak_ptr_factory_.GetWeakPtr())),
77 write_server_callback_(Bind(&HTTPProxy::WriteToServer,
78 weak_ptr_factory_.GetWeakPtr())),
Ben Chancc225ef2014-09-30 13:26:51 -070079 dispatcher_(nullptr),
Paul Stewartf65320c2011-10-13 14:34:52 -070080 proxy_port_(-1),
81 proxy_socket_(-1),
Ben Chancc225ef2014-09-30 13:26:51 -070082 sockets_(nullptr),
Paul Stewartf65320c2011-10-13 14:34:52 -070083 client_socket_(-1),
84 server_port_(kDefaultServerPort),
85 server_socket_(-1),
Eric Shienbrood3e20a232012-02-16 11:35:56 -050086 is_route_requested_(false) { }
Paul Stewartf65320c2011-10-13 14:34:52 -070087
88HTTPProxy::~HTTPProxy() {
89 Stop();
90}
91
92bool HTTPProxy::Start(EventDispatcher *dispatcher,
93 Sockets *sockets) {
Rebecca Silbersteinc9c31d82014-10-21 15:01:00 -070094 SLOG(connection_, 3) << "In " << __func__;
Paul Stewartf65320c2011-10-13 14:34:52 -070095
96 if (sockets_) {
97 // We are already running.
98 return true;
99 }
100
101 proxy_socket_ = sockets->Socket(PF_INET, SOCK_STREAM, 0);
102 if (proxy_socket_ < 0) {
103 PLOG(ERROR) << "Failed to open proxy socket";
104 return false;
105 }
106
107 struct sockaddr_in addr;
108 socklen_t addrlen = sizeof(addr);
109 memset(&addr, 0, sizeof(addr));
110 addr.sin_family = AF_INET;
111 addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
112 if (sockets->Bind(proxy_socket_,
113 reinterpret_cast<struct sockaddr *>(&addr),
114 sizeof(addr)) < 0 ||
115 sockets->GetSockName(proxy_socket_,
116 reinterpret_cast<struct sockaddr *>(&addr),
117 &addrlen) < 0 ||
118 sockets->SetNonBlocking(proxy_socket_) < 0 ||
119 sockets->Listen(proxy_socket_, kMaxClientQueue) < 0) {
120 sockets->Close(proxy_socket_);
121 proxy_socket_ = -1;
122 PLOG(ERROR) << "HTTPProxy socket setup failed";
123 return false;
124 }
125
126 accept_handler_.reset(
127 dispatcher->CreateReadyHandler(proxy_socket_, IOHandler::kModeInput,
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500128 accept_callback_));
Paul Stewartf65320c2011-10-13 14:34:52 -0700129 dispatcher_ = dispatcher;
130 dns_client_.reset(new DNSClient(IPAddress::kFamilyIPv4,
Paul Stewartc8f4bef2011-12-13 09:45:51 -0800131 connection_->interface_name(),
132 connection_->dns_servers(),
Paul Stewartf65320c2011-10-13 14:34:52 -0700133 kDNSTimeoutSeconds * 1000,
134 dispatcher,
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500135 dns_client_callback_));
Paul Stewartf65320c2011-10-13 14:34:52 -0700136 proxy_port_ = ntohs(addr.sin_port);
137 server_async_connection_.reset(
Paul Stewartc8f4bef2011-12-13 09:45:51 -0800138 new AsyncConnection(connection_->interface_name(), dispatcher, sockets,
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500139 connect_completion_callback_));
Paul Stewartf65320c2011-10-13 14:34:52 -0700140 sockets_ = sockets;
141 state_ = kStateWaitConnection;
142 return true;
143}
144
145void HTTPProxy::Stop() {
Rebecca Silbersteinc9c31d82014-10-21 15:01:00 -0700146 SLOG(connection_, 3) << "In " << __func__;
Paul Stewartf65320c2011-10-13 14:34:52 -0700147
Alex Vakulenko8a532292014-06-16 17:18:44 -0700148 if (!sockets_) {
Paul Stewartf65320c2011-10-13 14:34:52 -0700149 return;
150 }
151
152 StopClient();
153
154 accept_handler_.reset();
Ben Chancc225ef2014-09-30 13:26:51 -0700155 dispatcher_ = nullptr;
Paul Stewartf65320c2011-10-13 14:34:52 -0700156 dns_client_.reset();
157 proxy_port_ = -1;
158 server_async_connection_.reset();
159 sockets_->Close(proxy_socket_);
160 proxy_socket_ = -1;
Ben Chancc225ef2014-09-30 13:26:51 -0700161 sockets_ = nullptr;
Paul Stewartf65320c2011-10-13 14:34:52 -0700162 state_ = kStateIdle;
163}
164
165// IOReadyHandler callback routine fired when a client connects to the
166// proxy's socket. We Accept() the client and start reading a request
167// from it.
168void HTTPProxy::AcceptClient(int fd) {
Rebecca Silbersteinc9c31d82014-10-21 15:01:00 -0700169 SLOG(connection_, 3) << "In " << __func__;
Paul Stewartf65320c2011-10-13 14:34:52 -0700170
Ben Chancc225ef2014-09-30 13:26:51 -0700171 int client_fd = sockets_->Accept(fd, nullptr, nullptr);
Paul Stewartf65320c2011-10-13 14:34:52 -0700172 if (client_fd < 0) {
173 PLOG(ERROR) << "Client accept failed";
174 return;
175 }
176
177 accept_handler_->Stop();
178
179 client_socket_ = client_fd;
180
181 sockets_->SetNonBlocking(client_socket_);
Paul Stewart5f06a0e2012-12-20 11:11:33 -0800182 read_client_handler_.reset(dispatcher_->CreateInputHandler(
183 client_socket_,
184 read_client_callback_,
185 Bind(&HTTPProxy::OnReadError, weak_ptr_factory_.GetWeakPtr())));
Paul Stewartf65320c2011-10-13 14:34:52 -0700186 // Overall transaction timeout.
Paul Stewartf582b502012-04-04 21:39:22 -0700187 transaction_timeout_.Reset(Bind(&HTTPProxy::StopClient,
188 weak_ptr_factory_.GetWeakPtr()));
189 dispatcher_->PostDelayedTask(transaction_timeout_.callback(),
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500190 kTransactionTimeoutSeconds * 1000);
Paul Stewartf65320c2011-10-13 14:34:52 -0700191
192 state_ = kStateReadClientHeader;
193 StartIdleTimeout();
194}
195
196bool HTTPProxy::ConnectServer(const IPAddress &address, int port) {
197 state_ = kStateConnectServer;
198 if (!server_async_connection_->Start(address, port)) {
199 SendClientError(500, "Could not create socket to connect to server");
200 return false;
201 }
202 StartIdleTimeout();
203 return true;
204}
205
206// DNSClient callback that fires when the DNS request completes.
Paul Stewartbdb02e62012-02-22 16:24:33 -0800207void HTTPProxy::GetDNSResult(const Error &error, const IPAddress &address) {
208 if (!error.IsSuccess()) {
Paul Stewartf65320c2011-10-13 14:34:52 -0700209 SendClientError(502, string("Could not resolve hostname: ") +
Paul Stewartbdb02e62012-02-22 16:24:33 -0800210 error.message());
Paul Stewartf65320c2011-10-13 14:34:52 -0700211 return;
212 }
Paul Stewartbdb02e62012-02-22 16:24:33 -0800213 ConnectServer(address, server_port_);
Paul Stewartf65320c2011-10-13 14:34:52 -0700214}
215
216// IOReadyHandler callback routine which fires when the asynchronous Connect()
217// to the remote server completes (or fails).
218void HTTPProxy::OnConnectCompletion(bool success, int fd) {
219 if (!success) {
220 SendClientError(500, string("Socket connection delayed failure: ") +
221 server_async_connection_->error());
222 return;
223 }
224 server_socket_ = fd;
225 state_ = kStateTunnelData;
Paul Stewart58a577b2012-01-10 11:18:52 -0800226
227 // If this was a "CONNECT" request, notify the client that the connection
228 // has been established by sending an "OK" response.
229 if (LowerCaseEqualsASCII(client_method_, kHTTPMethodConnect)) {
230 SetClientResponse(200, "OK", "", "");
231 StartReceive();
232 }
233
Paul Stewartf65320c2011-10-13 14:34:52 -0700234 StartTransmit();
235}
236
Peter Qiu3161caa2014-10-29 09:47:22 -0700237void HTTPProxy::OnReadError(const string &error_msg) {
Paul Stewart5f06a0e2012-12-20 11:11:33 -0800238 StopClient();
239}
240
Paul Stewartf65320c2011-10-13 14:34:52 -0700241// Read through the header lines from the client, modifying or adding
242// lines as necessary. Perform final determination of the hostname/port
243// we should connect to and either start a DNS request or connect to a
244// numeric address.
245bool HTTPProxy::ParseClientRequest() {
Rebecca Silbersteinc9c31d82014-10-21 15:01:00 -0700246 SLOG(connection_, 3) << "In " << __func__;
Paul Stewartf65320c2011-10-13 14:34:52 -0700247
248 string host;
249 bool found_via = false;
250 bool found_connection = false;
Paul Stewart6db7b242014-05-02 15:34:21 -0700251 for (auto &header : client_headers_) {
252 if (StartsWithASCII(header, "Host:", false)) {
253 host = header.substr(5);
254 } else if (StartsWithASCII(header, "Via:", false)) {
Paul Stewartf65320c2011-10-13 14:34:52 -0700255 found_via = true;
Paul Stewart6db7b242014-05-02 15:34:21 -0700256 header.append(StringPrintf(", %s shill-proxy", client_version_.c_str()));
257 } else if (StartsWithASCII(header, "Connection:", false)) {
Paul Stewartf65320c2011-10-13 14:34:52 -0700258 found_connection = true;
Paul Stewart6db7b242014-05-02 15:34:21 -0700259 header.assign("Connection: close");
260 } else if (StartsWithASCII(header, "Proxy-Connection:", false)) {
261 header.assign("Proxy-Connection: close");
Paul Stewartf65320c2011-10-13 14:34:52 -0700262 }
263 }
264
265 if (!found_connection) {
266 client_headers_.push_back("Connection: close");
267 }
268 if (!found_via) {
269 client_headers_.push_back(
270 StringPrintf("Via: %s shill-proxy", client_version_.c_str()));
271 }
272
273 // Assemble the request as it will be sent to the server.
274 client_data_.Clear();
Paul Stewart58a577b2012-01-10 11:18:52 -0800275 if (!LowerCaseEqualsASCII(client_method_, kHTTPMethodConnect)) {
Paul Stewart6db7b242014-05-02 15:34:21 -0700276 for (const auto &header : client_headers_) {
277 client_data_.Append(ByteString(header + "\r\n", false));
Paul Stewart58a577b2012-01-10 11:18:52 -0800278 }
279 client_data_.Append(ByteString(string("\r\n"), false));
Paul Stewartf65320c2011-10-13 14:34:52 -0700280 }
Paul Stewartf65320c2011-10-13 14:34:52 -0700281
Ben Chan6fbf64f2014-05-21 18:07:01 -0700282 base::TrimWhitespaceASCII(host, base::TRIM_ALL, &host);
Paul Stewartf65320c2011-10-13 14:34:52 -0700283 if (host.empty()) {
284 // Revert to using the hostname in the URL if no "Host:" header exists.
285 host = server_hostname_;
286 }
287
288 if (host.empty()) {
289 SendClientError(400, "I don't know what host you want me to connect to");
290 return false;
291 }
292
293 server_port_ = 80;
294 vector<string> host_parts;
295 base::SplitString(host, ':', &host_parts);
296
297 if (host_parts.size() > 2) {
298 SendClientError(400, "Too many colons in hostname");
299 return false;
300 } else if (host_parts.size() == 2) {
301 server_hostname_ = host_parts[0];
302 if (!base::StringToInt(host_parts[1], &server_port_)) {
303 SendClientError(400, "Could not parse port number");
304 return false;
305 }
306 } else {
307 server_hostname_ = host;
308 }
309
Paul Stewartc8f4bef2011-12-13 09:45:51 -0800310 connection_->RequestRouting();
311 is_route_requested_ = true;
312
Paul Stewartf65320c2011-10-13 14:34:52 -0700313 IPAddress addr(IPAddress::kFamilyIPv4);
314 if (addr.SetAddressFromString(server_hostname_)) {
315 if (!ConnectServer(addr, server_port_)) {
316 return false;
317 }
318 } else {
Rebecca Silbersteinc9c31d82014-10-21 15:01:00 -0700319 SLOG(connection_, 3) << "Looking up host: " << server_hostname_;
Paul Stewartbdb02e62012-02-22 16:24:33 -0800320 Error error;
321 if (!dns_client_->Start(server_hostname_, &error)) {
322 SendClientError(502, "Could not resolve hostname: " + error.message());
Paul Stewartf65320c2011-10-13 14:34:52 -0700323 return false;
324 }
325 state_ = kStateLookupServer;
326 }
327 return true;
328}
329
330// Accept a new line into the client headers. Returns false if a parse
331// error occurs.
332bool HTTPProxy::ProcessLastHeaderLine() {
333 string *header = &client_headers_.back();
Ben Chana0ddf462014-02-06 11:32:42 -0800334 base::TrimString(*header, "\r", header);
Paul Stewartf65320c2011-10-13 14:34:52 -0700335
336 if (header->empty()) {
337 // Empty line terminates client headers.
338 client_headers_.pop_back();
339 if (!ParseClientRequest()) {
340 return false;
341 }
342 }
343
344 // Is this is the first header line?
345 if (client_headers_.size() == 1) {
Paul Stewart58a577b2012-01-10 11:18:52 -0800346 if (!ReadClientHTTPMethod(header) ||
347 !ReadClientHTTPVersion(header) ||
348 !ReadClientHostname(header)) {
Paul Stewartf65320c2011-10-13 14:34:52 -0700349 return false;
350 }
351 }
352
353 if (client_headers_.size() >= kMaxHeaderCount) {
354 SendClientError(500, kInternalErrorMsg);
355 return false;
356 }
357
358 return true;
359}
360
361// Split input from client into header lines, and consume parsed lines
362// from InputData. The passed in |data| is modified to indicate the
363// characters consumed.
364bool HTTPProxy::ReadClientHeaders(InputData *data) {
365 unsigned char *ptr = data->buf;
366 unsigned char *end = ptr + data->len;
367
368 if (client_headers_.empty()) {
369 client_headers_.push_back(string());
370 }
371
372 for (; ptr < end && state_ == kStateReadClientHeader; ++ptr) {
373 if (*ptr == '\n') {
374 if (!ProcessLastHeaderLine()) {
375 return false;
376 }
377
378 // Start a new line. New chararacters we receive will be appended there.
379 client_headers_.push_back(string());
380 continue;
381 }
382
383 string *header = &client_headers_.back();
384 // Is the first character of the header line a space or tab character?
385 if (header->empty() && (*ptr == ' ' || *ptr == '\t') &&
386 client_headers_.size() > 1) {
387 // Line Continuation: Add this character to the previous header line.
388 // This way, all of the data (including newlines and line continuation
389 // characters) related to a specific header will be contained within
390 // a single element of |client_headers_|, and manipulation of headers
391 // such as appending will be simpler. This is accomplished by removing
392 // the empty line we started, and instead appending the whitespace
393 // and following characters to the previous line.
394 client_headers_.pop_back();
395 header = &client_headers_.back();
396 header->append("\r\n");
397 }
398
399 if (header->length() >= kMaxHeaderSize) {
400 SendClientError(500, kInternalErrorMsg);
401 return false;
402 }
403 header->push_back(*ptr);
404 }
405
406 // Return the remaining data to the caller -- this could be POST data
407 // or other non-header data sent with the client request.
408 data->buf = ptr;
409 data->len = end - ptr;
410
411 return true;
412}
413
414// Finds the URL in the first line of an HTTP client header, and extracts
415// and removes the hostname (and port) from the URL. Returns false if a
416// parse error occurs, and true otherwise (whether or not the hostname was
417// found).
418bool HTTPProxy::ReadClientHostname(string *header) {
419 const string http_url_prefix(kHTTPURLPrefix);
420 size_t url_idx = header->find(http_url_prefix);
421 if (url_idx != string::npos) {
422 size_t host_start = url_idx + http_url_prefix.length();
423 size_t host_end =
424 header->find_first_of(kHTTPURLDelimiters, host_start);
425 if (host_end != string::npos) {
426 server_hostname_ = header->substr(host_start,
427 host_end - host_start);
428 // Modify the URL passed upstream to remove "http://<hostname>".
429 header->erase(url_idx, host_end - url_idx);
430 if ((*header)[url_idx] != '/') {
431 header->insert(url_idx, "/");
432 }
433 } else {
434 LOG(ERROR) << "Could not find end of hostname in request. Line was: "
435 << *header;
436 SendClientError(500, kInternalErrorMsg);
437 return false;
438 }
439 }
440 return true;
441}
442
Paul Stewart58a577b2012-01-10 11:18:52 -0800443bool HTTPProxy::ReadClientHTTPMethod(string *header) {
444 size_t method_end = header->find(kHTTPMethodTerminator);
445 if (method_end == string::npos || method_end == 0) {
446 LOG(ERROR) << "Could not parse HTTP method. Line was: " << *header;
447 SendClientError(501, "Server could not parse HTTP method");
448 return false;
449 }
450 client_method_ = header->substr(0, method_end);
451 return true;
452}
453
Paul Stewartf65320c2011-10-13 14:34:52 -0700454// Extract the HTTP version number from the first line of the client headers.
455// Returns true if found.
456bool HTTPProxy::ReadClientHTTPVersion(string *header) {
457 const string http_version_prefix(kHTTPVersionPrefix);
458 size_t http_ver_pos = header->find(http_version_prefix);
459 if (http_ver_pos != string::npos) {
460 client_version_ =
461 header->substr(http_ver_pos + http_version_prefix.length() - 1);
462 } else {
463 SendClientError(501, "Server only accepts HTTP/1.x requests");
464 return false;
465 }
466 return true;
467}
468
469// IOInputHandler callback that fires when data is read from the client.
470// This could be header data, or perhaps POST data that follows the headers.
471void HTTPProxy::ReadFromClient(InputData *data) {
Rebecca Silbersteinc9c31d82014-10-21 15:01:00 -0700472 SLOG(connection_, 3) << "In " << __func__ << " length " << data->len;
Paul Stewartf65320c2011-10-13 14:34:52 -0700473
474 if (data->len == 0) {
475 // EOF from client.
476 StopClient();
477 return;
478 }
479
480 if (state_ == kStateReadClientHeader) {
481 if (!ReadClientHeaders(data)) {
482 return;
483 }
484 if (state_ == kStateReadClientHeader) {
485 // Still consuming client headers; restart the input timer.
486 StartIdleTimeout();
487 return;
488 }
489 }
490
491 // Check data->len again since ReadClientHeaders() may have consumed some
492 // part of it.
493 if (data->len != 0) {
494 // The client sent some information after its headers. Buffer the client
495 // input and temporarily disable input events from the client.
496 client_data_.Append(ByteString(data->buf, data->len));
497 read_client_handler_->Stop();
498 StartTransmit();
499 }
500}
501
502// IOInputHandler callback which fires when data has been read from the
503// server.
504void HTTPProxy::ReadFromServer(InputData *data) {
Rebecca Silbersteinc9c31d82014-10-21 15:01:00 -0700505 SLOG(connection_, 3) << "In " << __func__ << " length " << data->len;
Paul Stewartf65320c2011-10-13 14:34:52 -0700506 if (data->len == 0) {
507 // Server closed connection.
508 if (server_data_.IsEmpty()) {
509 StopClient();
510 return;
511 }
512 state_ = kStateFlushResponse;
513 } else {
514 read_server_handler_->Stop();
515 }
516
517 server_data_.Append(ByteString(data->buf, data->len));
518
519 StartTransmit();
520}
521
522// Return an HTTP error message back to the client.
523void HTTPProxy::SendClientError(int code, const string &error) {
Rebecca Silbersteinc9c31d82014-10-21 15:01:00 -0700524 SLOG(connection_, 3) << "In " << __func__;
Paul Stewartf65320c2011-10-13 14:34:52 -0700525 LOG(ERROR) << "Sending error " << error;
Paul Stewart58a577b2012-01-10 11:18:52 -0800526 SetClientResponse(code, "ERROR", "text/plain", error);
Paul Stewartf65320c2011-10-13 14:34:52 -0700527 state_ = kStateFlushResponse;
528 StartTransmit();
529}
530
Paul Stewart58a577b2012-01-10 11:18:52 -0800531// Create an HTTP response message to be sent to the client.
532void HTTPProxy::SetClientResponse(int code, const string &type,
533 const string &content_type,
534 const string &message) {
535 string content_line;
536 if (!message.empty() && !content_type.empty()) {
537 content_line = StringPrintf("Content-Type: %s\r\n", content_type.c_str());
538 }
539 string response = StringPrintf("HTTP/1.1 %d %s\r\n"
540 "%s\r\n"
541 "%s", code, type.c_str(),
542 content_line.c_str(),
543 message.c_str());
544 server_data_ = ByteString(response, false);
545}
546
Paul Stewartf65320c2011-10-13 14:34:52 -0700547// Start a timeout for "the next event". This timeout augments the overall
548// transaction timeout to make sure there is some activity occurring at
549// reasonable intervals.
550void HTTPProxy::StartIdleTimeout() {
551 int timeout_seconds = 0;
552 switch (state_) {
553 case kStateReadClientHeader:
554 timeout_seconds = kClientHeaderTimeoutSeconds;
555 break;
556 case kStateConnectServer:
557 timeout_seconds = kConnectTimeoutSeconds;
558 break;
559 case kStateLookupServer:
560 // DNSClient has its own internal timeout, so we need not set one here.
561 timeout_seconds = 0;
562 break;
563 default:
564 timeout_seconds = kInputTimeoutSeconds;
565 break;
566 }
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500567 idle_timeout_.Cancel();
Paul Stewartf65320c2011-10-13 14:34:52 -0700568 if (timeout_seconds != 0) {
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500569 idle_timeout_.Reset(Bind(&HTTPProxy::StopClient,
570 weak_ptr_factory_.GetWeakPtr()));
571 dispatcher_->PostDelayedTask(idle_timeout_.callback(),
572 timeout_seconds * 1000);
Paul Stewartf65320c2011-10-13 14:34:52 -0700573 }
574}
575
576// Start the various input handlers. Listen for new data only if we have
577// completely written the last data we've received to the other end.
578void HTTPProxy::StartReceive() {
579 if (state_ == kStateTunnelData && client_data_.IsEmpty()) {
580 read_client_handler_->Start();
581 }
582 if (server_data_.IsEmpty()) {
583 if (state_ == kStateTunnelData) {
584 if (read_server_handler_.get()) {
585 read_server_handler_->Start();
586 } else {
Paul Stewart5f06a0e2012-12-20 11:11:33 -0800587 read_server_handler_.reset(dispatcher_->CreateInputHandler(
588 server_socket_,
589 read_server_callback_,
590 Bind(&HTTPProxy::OnReadError, weak_ptr_factory_.GetWeakPtr())));
Paul Stewartf65320c2011-10-13 14:34:52 -0700591 }
592 } else if (state_ == kStateFlushResponse) {
593 StopClient();
594 return;
595 }
596 }
597 StartIdleTimeout();
598}
599
600// Start the various output-ready handlers for the endpoints we have
601// data waiting for.
602void HTTPProxy::StartTransmit() {
603 if (state_ == kStateTunnelData && !client_data_.IsEmpty()) {
604 if (write_server_handler_.get()) {
605 write_server_handler_->Start();
606 } else {
607 write_server_handler_.reset(
608 dispatcher_->CreateReadyHandler(server_socket_,
609 IOHandler::kModeOutput,
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500610 write_server_callback_));
Paul Stewartf65320c2011-10-13 14:34:52 -0700611 }
612 }
613 if ((state_ == kStateFlushResponse || state_ == kStateTunnelData) &&
614 !server_data_.IsEmpty()) {
615 if (write_client_handler_.get()) {
616 write_client_handler_->Start();
617 } else {
618 write_client_handler_.reset(
619 dispatcher_->CreateReadyHandler(client_socket_,
620 IOHandler::kModeOutput,
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500621 write_client_callback_));
Paul Stewartf65320c2011-10-13 14:34:52 -0700622 }
623 }
624 StartIdleTimeout();
625}
626
627// End the transaction with the current client, restart the IOHandler
628// which alerts us to new clients connecting. This function is called
629// during various error conditions and is a callback for all timeouts.
630void HTTPProxy::StopClient() {
Rebecca Silbersteinc9c31d82014-10-21 15:01:00 -0700631 SLOG(connection_, 3) << "In " << __func__;
Paul Stewartf65320c2011-10-13 14:34:52 -0700632
Paul Stewartc8f4bef2011-12-13 09:45:51 -0800633 if (is_route_requested_) {
634 connection_->ReleaseRouting();
635 is_route_requested_ = false;
636 }
Paul Stewartf65320c2011-10-13 14:34:52 -0700637 write_client_handler_.reset();
638 read_client_handler_.reset();
639 if (client_socket_ != -1) {
640 sockets_->Close(client_socket_);
641 client_socket_ = -1;
642 }
643 client_headers_.clear();
Paul Stewart58a577b2012-01-10 11:18:52 -0800644 client_method_.clear();
Paul Stewartf65320c2011-10-13 14:34:52 -0700645 client_version_.clear();
646 server_port_ = kDefaultServerPort;
647 write_server_handler_.reset();
648 read_server_handler_.reset();
649 if (server_socket_ != -1) {
650 sockets_->Close(server_socket_);
651 server_socket_ = -1;
652 }
653 server_hostname_.clear();
654 client_data_.Clear();
655 server_data_.Clear();
656 dns_client_->Stop();
657 server_async_connection_->Stop();
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500658 idle_timeout_.Cancel();
Paul Stewartf582b502012-04-04 21:39:22 -0700659 transaction_timeout_.Cancel();
Paul Stewartf65320c2011-10-13 14:34:52 -0700660 accept_handler_->Start();
661 state_ = kStateWaitConnection;
662}
663
664// Output ReadyHandler callback which fires when the client socket is
665// ready for data to be sent to it.
666void HTTPProxy::WriteToClient(int fd) {
667 CHECK_EQ(client_socket_, fd);
668 int ret = sockets_->Send(fd, server_data_.GetConstData(),
669 server_data_.GetLength(), 0);
Rebecca Silbersteinc9c31d82014-10-21 15:01:00 -0700670 SLOG(connection_, 3) << "In " << __func__ << " wrote " << ret << " of "
671 << server_data_.GetLength();
Paul Stewartf65320c2011-10-13 14:34:52 -0700672 if (ret < 0) {
673 LOG(ERROR) << "Server write failed";
674 StopClient();
675 return;
676 }
677
678 server_data_ = ByteString(server_data_.GetConstData() + ret,
679 server_data_.GetLength() - ret);
680
681 if (server_data_.IsEmpty()) {
682 write_client_handler_->Stop();
683 }
684
685 StartReceive();
686}
687
688// Output ReadyHandler callback which fires when the server socket is
689// ready for data to be sent to it.
690void HTTPProxy::WriteToServer(int fd) {
691 CHECK_EQ(server_socket_, fd);
692 int ret = sockets_->Send(fd, client_data_.GetConstData(),
693 client_data_.GetLength(), 0);
Rebecca Silbersteinc9c31d82014-10-21 15:01:00 -0700694 SLOG(connection_, 3) << "In " << __func__ << " wrote " << ret << " of "
695 << client_data_.GetLength();
Paul Stewartf65320c2011-10-13 14:34:52 -0700696
697 if (ret < 0) {
698 LOG(ERROR) << "Client write failed";
699 StopClient();
700 return;
701 }
702
703 client_data_ = ByteString(client_data_.GetConstData() + ret,
704 client_data_.GetLength() - ret);
705
706 if (client_data_.IsEmpty()) {
707 write_server_handler_->Stop();
708 }
709
710 StartReceive();
711}
712
713} // namespace shill