blob: 0f924744cc89423c30f6b25e466f604c430a572f [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#if __LB_ENABLE_NATIVE_HTTP_STACK__
#include "net/http/shell/http_stream_shell_loader.h"
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/memory/ref_counted.h"
#include "base/string_piece.h"
#include "base/stringprintf.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread.h"
#include "googleurl/src/gurl.h"
#include "net/base/completion_callback.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/base/test_completion_callback.h"
#include "net/base/upload_bytes_element_reader.h"
#include "net/base/upload_data_stream.h"
#include "net/base/upload_file_element_reader.h"
#include "net/http/http_network_session.h" // HttpNetworkSession::Params
#include "net/http/http_request_headers.h"
#include "net/http/http_request_info.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_response_info.h"
#include "net/proxy/proxy_info.h"
#include "net/proxy/proxy_service.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
namespace net {
const size_t kOutputSize = 1024; // Just large enough for this test.
// The number of bytes that can fit in a buffer of kOutputSize.
const size_t kMaxPayloadSize =
kOutputSize - HttpStreamShellLoader::kChunkHeaderFooterSize;
// Test object for HttpStreamShellLoader
class HttpStreamShellLoaderLiveTest : public PlatformTest {
public:
HttpStreamShellLoaderLiveTest() : io_thread_("IOThread"),
event_(false, false),
using_proxy_(false) {}
virtual void SetUp() {
net::HttpStreamShellLoaderGlobalInit();
MessageLoop::current()->RunUntilIdle();
io_thread_.Start();
}
virtual void TearDown() {
io_thread_.Stop();
MessageLoop::current()->RunUntilIdle();
net::HttpStreamShellLoaderGlobalDeinit();
}
void ExecOpenConnection() {
DCHECK(io_thread_.message_loop_proxy()->BelongsToCurrentThread());
loader_ = net::CreateHttpStreamShellLoader();
if (loader_) {
res_ = loader_->Open(&request_info_, net_log_);
} else {
res_ = ERR_UNEXPECTED;
}
event_.Signal();
}
// Open/close connection
bool OpenConnection() {
DCHECK(!io_thread_.message_loop_proxy()->BelongsToCurrentThread());
io_thread_.message_loop()->PostTask(FROM_HERE,
base::Bind(&HttpStreamShellLoaderLiveTest::ExecOpenConnection,
base::Unretained(this)));
event_.Wait();
return res_;
}
void ExecCloseConnection() {
DCHECK(io_thread_.message_loop_proxy()->BelongsToCurrentThread());
loader_->Close(true);
event_.Signal();
}
void CloseConnection() {
DCHECK(!io_thread_.message_loop_proxy()->BelongsToCurrentThread());
io_thread_.message_loop()->PostTask(FROM_HERE,
base::Bind(&HttpStreamShellLoaderLiveTest::ExecCloseConnection,
base::Unretained(this)));
event_.Wait();
loader_ = NULL;
}
void ExecProxyResolver() {
DCHECK(io_thread_.message_loop_proxy()->BelongsToCurrentThread());
// Use the system proxy settings.
scoped_ptr<net::ProxyConfigService> proxy_config_service(
net::ProxyService::CreateSystemProxyConfigService(
NULL, MessageLoop::current()));
network_params_.proxy_service =
net::ProxyService::CreateUsingSystemProxyResolver(
proxy_config_service.release(), 0, NULL);
// Send out resolve request
CompletionCallback callback(
base::Bind(&HttpStreamShellLoaderLiveTest::OnCompletion,
base::Unretained(this)));
// Resolve proxy
int res = network_params_.proxy_service->ResolveProxy(request_info_.url,
&proxy_info_, callback, NULL, net_log_);
}
void OnCompletion(int result) {
DCHECK(io_thread_.message_loop_proxy()->BelongsToCurrentThread());
EXPECT_EQ(OK, result);
event_.Signal();
}
int ResolveProxy() {
DCHECK(!io_thread_.message_loop_proxy()->BelongsToCurrentThread());
if (request_info_.load_flags & LOAD_BYPASS_PROXY) {
proxy_info_.UseDirect();
return OK;
}
io_thread_.message_loop()->PostTask(FROM_HERE,
base::Bind(&HttpStreamShellLoaderLiveTest::ExecProxyResolver,
base::Unretained(this)));
event_.Wait();
// Send proxy information to stream.
using_proxy_ = !proxy_info_.is_empty() &&
(proxy_info_.is_http() || proxy_info_.is_https());
if (using_proxy_) {
// Remove unsupported proxies from the list.
proxy_info_.RemoveProxiesWithoutScheme(ProxyServer::SCHEME_DIRECT |
ProxyServer::SCHEME_HTTP | ProxyServer::SCHEME_HTTPS);
if (proxy_info_.is_empty()) {
// No proxies/direct to choose from. This happens when we don't support
// any of the proxies in the returned list.
return ERR_NO_SUPPORTED_PROXIES;
}
loader_->SetProxy(&proxy_info_);
} else {
loader_->SetProxy(NULL);
}
return OK;
}
void OnCallbackReceived(int result) {
DCHECK(io_thread_.message_loop_proxy()->BelongsToCurrentThread());
res_ = result;
event_.Signal();
}
void ExecSendRequest() {
DCHECK(io_thread_.message_loop_proxy()->BelongsToCurrentThread());
CompletionCallback callback(
base::Bind(&HttpStreamShellLoaderLiveTest::OnCallbackReceived,
base::Unretained(this)));
res_ = loader_->SendRequest(request_line_,
request_headers_, &response_, callback);
if (res_ != ERR_IO_PENDING) {
event_.Signal();
}
}
int SendRequest() {
DCHECK(!io_thread_.message_loop_proxy()->BelongsToCurrentThread());
DCHECK(loader_);
io_thread_.message_loop()->PostTask(FROM_HERE,
base::Bind(&HttpStreamShellLoaderLiveTest::ExecSendRequest,
base::Unretained(this)));
event_.Wait();
if (res_ != OK) {
io_thread_.message_loop()->PostTask(FROM_HERE,
base::Bind(&HttpStreamShellLoaderLiveTest::ExecCloseConnection,
base::Unretained(this)));
event_.Wait();
}
return res_;
}
void ExecReadResponseHeaders() {
DCHECK(io_thread_.message_loop_proxy()->BelongsToCurrentThread());
CompletionCallback callback(
base::Bind(&HttpStreamShellLoaderLiveTest::OnCallbackReceived,
base::Unretained(this)));
res_ = loader_->ReadResponseHeaders(callback);
if (res_ != ERR_IO_PENDING) {
event_.Signal();
}
}
// Read response headers and wait for the result
int ReadResponseHeaders() {
DCHECK(!io_thread_.message_loop_proxy()->BelongsToCurrentThread());
DCHECK(loader_);
io_thread_.message_loop()->PostTask(FROM_HERE,
base::Bind(&HttpStreamShellLoaderLiveTest::ExecReadResponseHeaders,
base::Unretained(this)));
event_.Wait();
if (res_ != OK) {
io_thread_.message_loop()->PostTask(FROM_HERE,
base::Bind(&HttpStreamShellLoaderLiveTest::ExecCloseConnection,
base::Unretained(this)));
event_.Wait();
}
return res_;
}
void ExecReadResponseBody(scoped_refptr<IOBuffer> buf, size_t read_len) {
DCHECK(io_thread_.message_loop_proxy()->BelongsToCurrentThread());
CompletionCallback callback(
base::Bind(&HttpStreamShellLoaderLiveTest::OnCallbackReceived,
base::Unretained(this)));
res_ = loader_->ReadResponseBody(buf, read_len, callback);
if (res_ != ERR_IO_PENDING) {
event_.Signal();
}
}
void CheckResponseBodyComplete() {
DCHECK(io_thread_.message_loop_proxy()->BelongsToCurrentThread());
EXPECT_TRUE(loader_->IsResponseBodyComplete());
event_.Signal();
}
// Read data and wait for result
int ReadResponseBody(scoped_refptr<IOBuffer> buf, size_t read_len) {
DCHECK(!io_thread_.message_loop_proxy()->BelongsToCurrentThread());
DCHECK(loader_);
io_thread_.message_loop()->PostTask(FROM_HERE,
base::Bind(&HttpStreamShellLoaderLiveTest::ExecReadResponseBody,
base::Unretained(this), buf, read_len));
event_.Wait();
EXPECT_GE(res_, 0);
if (res_ == OK) {
io_thread_.message_loop()->PostTask(FROM_HERE,
base::Bind(&HttpStreamShellLoaderLiveTest::CheckResponseBodyComplete,
base::Unretained(this)));
event_.Wait();
}
return res_;
}
// Generate request_info_, request_line_ and request_headers_.
void GenerateRequstInfo(const char* method, const char* scheme,
const char* host, const char* path) {
std::string url_str = StringPrintf("%s://%s%s", scheme, host, path);
request_info_.url = GURL(url_str);
request_info_.method = method;
request_line_ = StringPrintf("%s %s HTTP/1.1", method, path);
std::string headers_str = StringPrintf(
"User-Agent: Mozilla/5.0 [en] (Test)\r\n"
"Host: %s\r\n"
"Accept: */*\r\n"
"Accept-Encoding: gzip\r\n"
"Accept-Language: en\r\n"
"Accept-Charset: iso-8859-1, *, utf-8", host);
request_headers_.AddHeadersFromString(headers_str);
}
const HttpResponseInfo* Response() const { return &response_; }
bool UsingProxy() const { return using_proxy_; }
private:
scoped_refptr<HttpStreamShellLoader> loader_;
HttpResponseInfo response_;
BoundNetLog net_log_;
HttpNetworkSession::Params network_params_;
ProxyInfo proxy_info_;
base::Thread io_thread_;
base::WaitableEvent event_;
bool using_proxy_;
// Request data
HttpRequestInfo request_info_;
HttpRequestHeaders request_headers_;
std::string request_line_;
int res_;
};
TEST_F(HttpStreamShellLoaderLiveTest, TestHttpConnection) {
GenerateRequstInfo("GET", "http", "www.google.com", "/");
// Open connection, exit if failed
int res = OpenConnection();
ASSERT_EQ(OK, res);
res = ResolveProxy();
EXPECT_EQ(OK, res);
// Send request
res = SendRequest();
EXPECT_EQ(OK, res);
// Read response headers
res = ReadResponseHeaders();
EXPECT_EQ(OK, res);
// Read response body
const int kLoadBufferSize = 16 * 1024;
scoped_refptr<GrowableIOBuffer> buffer = new GrowableIOBuffer();
buffer->SetCapacity(kLoadBufferSize);
while ((res = ReadResponseBody(buffer, buffer->capacity())) > 0) {
EXPECT_GE(buffer->capacity(), res);
}
EXPECT_EQ(res, OK);
EXPECT_NE(Response()->headers.get(), static_cast<HttpResponseHeaders*>(NULL));
if (Response()->headers.get())
EXPECT_EQ(Response()->headers->GetStatusLine(), "HTTP/1.1 200 OK");
// Close connection
CloseConnection();
}
TEST_F(HttpStreamShellLoaderLiveTest, TestHttpsConnection) {
GenerateRequstInfo("GET", "https", "www.google.com", "/");
// Open connection, exit if failed
int res = OpenConnection();
ASSERT_EQ(OK, res);
res = ResolveProxy();
EXPECT_EQ(OK, res);
// Send request
res = SendRequest();
EXPECT_EQ(OK, res);
// Read response headers
res = ReadResponseHeaders();
EXPECT_EQ(OK, res);
// Read response body
const int kLoadBufferSize = 16 * 1024;
scoped_refptr<GrowableIOBuffer> buffer = new GrowableIOBuffer();
buffer->SetCapacity(kLoadBufferSize);
while ((res = ReadResponseBody(buffer, buffer->capacity())) > 0) {
EXPECT_GE(buffer->capacity(), res);
}
EXPECT_NE(Response()->headers.get(), static_cast<HttpResponseHeaders*>(NULL));
if (Response()->headers.get())
EXPECT_EQ(Response()->headers->GetStatusLine(), "HTTP/1.1 200 OK");
// Close connection
CloseConnection();
}
TEST_F(HttpStreamShellLoaderLiveTest, TestWrongURLConnection) {
GenerateRequstInfo("GET", "http", "SERVER.NOT.FOUND", "/");
// Open connection, exit if failed
int res = OpenConnection();
ASSERT_EQ(OK, res);
res = ResolveProxy();
EXPECT_EQ(OK, res);
// Send request
res = SendRequest();
if (res == OK) {
// Error may happen in SendRequest or ReadResponseHeaders. So if
// SendRequest returns OK, continue to read response headers.
res = ReadResponseHeaders();
}
// Errors are different with or without proxy.
if (UsingProxy()) {
// With a proxy, 503 error is returned by proxy server
EXPECT_EQ(ERR_SERVICE_UNAVAILABLE, res);
} else {
// Without a proxy, url can not be resolved.
EXPECT_EQ(ERR_NAME_NOT_RESOLVED, res);
}
// Close connection
CloseConnection();
}
} // namespace net
#endif