| // Copyright (c) 2011 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. |
| |
| #include "net/http/http_transaction_unittest.h" |
| |
| #include <algorithm> |
| |
| #include "base/message_loop.h" |
| #include "base/string_util.h" |
| #include "net/base/net_errors.h" |
| #include "net/base/load_flags.h" |
| #include "net/disk_cache/disk_cache.h" |
| #include "net/http/http_cache.h" |
| #include "net/http/http_request_info.h" |
| #include "net/http/http_response_info.h" |
| #include "net/http/http_transaction.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace { |
| typedef base::hash_map<std::string, const MockTransaction*> MockTransactionMap; |
| static MockTransactionMap mock_transactions; |
| } // namespace |
| |
| //----------------------------------------------------------------------------- |
| // mock transaction data |
| |
| const MockTransaction kSimpleGET_Transaction = { |
| "http://www.google.com/", |
| "GET", |
| base::Time(), |
| "", |
| net::LOAD_NORMAL, |
| "HTTP/1.1 200 OK", |
| "Cache-Control: max-age=10000\n", |
| base::Time(), |
| "<html><body>Google Blah Blah</body></html>", |
| TEST_MODE_NORMAL, |
| NULL, |
| 0 |
| }; |
| |
| const MockTransaction kSimplePOST_Transaction = { |
| "http://bugdatabase.com/edit", |
| "POST", |
| base::Time(), |
| "", |
| net::LOAD_NORMAL, |
| "HTTP/1.1 200 OK", |
| "", |
| base::Time(), |
| "<html><body>Google Blah Blah</body></html>", |
| TEST_MODE_NORMAL, |
| NULL, |
| 0 |
| }; |
| |
| const MockTransaction kTypicalGET_Transaction = { |
| "http://www.example.com/~foo/bar.html", |
| "GET", |
| base::Time(), |
| "", |
| net::LOAD_NORMAL, |
| "HTTP/1.1 200 OK", |
| "Date: Wed, 28 Nov 2007 09:40:09 GMT\n" |
| "Last-Modified: Wed, 28 Nov 2007 00:40:09 GMT\n", |
| base::Time(), |
| "<html><body>Google Blah Blah</body></html>", |
| TEST_MODE_NORMAL, |
| NULL, |
| 0 |
| }; |
| |
| const MockTransaction kETagGET_Transaction = { |
| "http://www.google.com/foopy", |
| "GET", |
| base::Time(), |
| "", |
| net::LOAD_NORMAL, |
| "HTTP/1.1 200 OK", |
| "Cache-Control: max-age=10000\n" |
| "Etag: foopy\n", |
| base::Time(), |
| "<html><body>Google Blah Blah</body></html>", |
| TEST_MODE_NORMAL, |
| NULL, |
| 0 |
| }; |
| |
| const MockTransaction kRangeGET_Transaction = { |
| "http://www.google.com/", |
| "GET", |
| base::Time(), |
| "Range: 0-100\r\n", |
| net::LOAD_NORMAL, |
| "HTTP/1.1 200 OK", |
| "Cache-Control: max-age=10000\n", |
| base::Time(), |
| "<html><body>Google Blah Blah</body></html>", |
| TEST_MODE_NORMAL, |
| NULL, |
| 0 |
| }; |
| |
| static const MockTransaction* const kBuiltinMockTransactions[] = { |
| &kSimpleGET_Transaction, |
| &kSimplePOST_Transaction, |
| &kTypicalGET_Transaction, |
| &kETagGET_Transaction, |
| &kRangeGET_Transaction |
| }; |
| |
| const MockTransaction* FindMockTransaction(const GURL& url) { |
| // look for overrides: |
| MockTransactionMap::const_iterator it = mock_transactions.find(url.spec()); |
| if (it != mock_transactions.end()) |
| return it->second; |
| |
| // look for builtins: |
| for (size_t i = 0; i < arraysize(kBuiltinMockTransactions); ++i) { |
| if (url == GURL(kBuiltinMockTransactions[i]->url)) |
| return kBuiltinMockTransactions[i]; |
| } |
| return NULL; |
| } |
| |
| void AddMockTransaction(const MockTransaction* trans) { |
| mock_transactions[GURL(trans->url).spec()] = trans; |
| } |
| |
| void RemoveMockTransaction(const MockTransaction* trans) { |
| mock_transactions.erase(GURL(trans->url).spec()); |
| } |
| |
| MockHttpRequest::MockHttpRequest(const MockTransaction& t) { |
| url = GURL(t.url); |
| method = t.method; |
| extra_headers.AddHeadersFromString(t.request_headers); |
| load_flags = t.load_flags; |
| } |
| |
| //----------------------------------------------------------------------------- |
| |
| // static |
| int TestTransactionConsumer::quit_counter_ = 0; |
| |
| TestTransactionConsumer::TestTransactionConsumer( |
| net::HttpTransactionFactory* factory) |
| : state_(IDLE), |
| trans_(NULL), |
| error_(net::OK) { |
| // Disregard the error code. |
| factory->CreateTransaction(&trans_); |
| ++quit_counter_; |
| } |
| |
| TestTransactionConsumer::~TestTransactionConsumer() { |
| } |
| |
| void TestTransactionConsumer::Start(const net::HttpRequestInfo* request, |
| const net::BoundNetLog& net_log) { |
| state_ = STARTING; |
| int result = trans_->Start(request, this, net_log); |
| if (result != net::ERR_IO_PENDING) |
| DidStart(result); |
| } |
| |
| void TestTransactionConsumer::DidStart(int result) { |
| if (result != net::OK) { |
| DidFinish(result); |
| } else { |
| Read(); |
| } |
| } |
| |
| void TestTransactionConsumer::DidRead(int result) { |
| if (result <= 0) { |
| DidFinish(result); |
| } else { |
| content_.append(read_buf_->data(), result); |
| Read(); |
| } |
| } |
| |
| void TestTransactionConsumer::DidFinish(int result) { |
| state_ = DONE; |
| error_ = result; |
| if (--quit_counter_ == 0) |
| MessageLoop::current()->Quit(); |
| } |
| |
| void TestTransactionConsumer::Read() { |
| state_ = READING; |
| read_buf_ = new net::IOBuffer(1024); |
| int result = trans_->Read(read_buf_, 1024, this); |
| if (result != net::ERR_IO_PENDING) |
| DidRead(result); |
| } |
| |
| void TestTransactionConsumer::RunWithParams(const Tuple1<int>& params) { |
| int result = params.a; |
| switch (state_) { |
| case STARTING: |
| DidStart(result); |
| break; |
| case READING: |
| DidRead(result); |
| break; |
| default: |
| NOTREACHED(); |
| } |
| } |
| |
| |
| MockNetworkTransaction::MockNetworkTransaction() : |
| ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)), data_cursor_(0) { |
| } |
| |
| MockNetworkTransaction::~MockNetworkTransaction() {} |
| |
| int MockNetworkTransaction::Start(const net::HttpRequestInfo* request, |
| net::CompletionCallback* callback, |
| const net::BoundNetLog& net_log) { |
| const MockTransaction* t = FindMockTransaction(request->url); |
| if (!t) |
| return net::ERR_FAILED; |
| |
| std::string resp_status = t->status; |
| std::string resp_headers = t->response_headers; |
| std::string resp_data = t->data; |
| if (t->handler) |
| (t->handler)(request, &resp_status, &resp_headers, &resp_data); |
| |
| std::string header_data = base::StringPrintf( |
| "%s\n%s\n", resp_status.c_str(), resp_headers.c_str()); |
| std::replace(header_data.begin(), header_data.end(), '\n', '\0'); |
| |
| response_.request_time = base::Time::Now(); |
| if (!t->request_time.is_null()) |
| response_.request_time = t->request_time; |
| |
| response_.was_cached = false; |
| |
| response_.response_time = base::Time::Now(); |
| if (!t->response_time.is_null()) |
| response_.response_time = t->response_time; |
| |
| response_.headers = new net::HttpResponseHeaders(header_data); |
| response_.ssl_info.cert_status = t->cert_status; |
| data_ = resp_data; |
| test_mode_ = t->test_mode; |
| |
| if (test_mode_ & TEST_MODE_SYNC_NET_START) |
| return net::OK; |
| |
| CallbackLater(callback, net::OK); |
| return net::ERR_IO_PENDING; |
| } |
| |
| int MockNetworkTransaction::RestartIgnoringLastError( |
| net::CompletionCallback* callback) { |
| return net::ERR_FAILED; |
| } |
| |
| int MockNetworkTransaction::RestartWithCertificate( |
| net::X509Certificate* client_cert, |
| net::CompletionCallback* callback) { |
| return net::ERR_FAILED; |
| } |
| |
| int MockNetworkTransaction::RestartWithAuth(const string16& username, |
| const string16& password, |
| net::CompletionCallback* callback) { |
| return net::ERR_FAILED; |
| } |
| |
| bool MockNetworkTransaction::IsReadyToRestartForAuth() { |
| return false; |
| } |
| |
| int MockNetworkTransaction::Read(net::IOBuffer* buf, int buf_len, |
| net::CompletionCallback* callback) { |
| int data_len = static_cast<int>(data_.size()); |
| int num = std::min(buf_len, data_len - data_cursor_); |
| if (num) { |
| memcpy(buf->data(), data_.data() + data_cursor_, num); |
| data_cursor_ += num; |
| } |
| if (test_mode_ & TEST_MODE_SYNC_NET_READ) |
| return num; |
| |
| CallbackLater(callback, num); |
| return net::ERR_IO_PENDING; |
| } |
| |
| void MockNetworkTransaction::StopCaching() {} |
| |
| const net::HttpResponseInfo* MockNetworkTransaction::GetResponseInfo() const { |
| return &response_; |
| } |
| |
| net::LoadState MockNetworkTransaction::GetLoadState() const { |
| if (data_cursor_) |
| return net::LOAD_STATE_READING_RESPONSE; |
| return net::LOAD_STATE_IDLE; |
| } |
| |
| uint64 MockNetworkTransaction::GetUploadProgress() const { |
| return 0; |
| } |
| |
| void MockNetworkTransaction::CallbackLater(net::CompletionCallback* callback, |
| int result) { |
| MessageLoop::current()->PostTask(FROM_HERE, task_factory_.NewRunnableMethod( |
| &MockNetworkTransaction::RunCallback, callback, result)); |
| } |
| |
| void MockNetworkTransaction::RunCallback(net::CompletionCallback* callback, |
| int result) { |
| callback->Run(result); |
| } |
| |
| MockNetworkLayer::MockNetworkLayer() : transaction_count_(0) {} |
| |
| MockNetworkLayer::~MockNetworkLayer() {} |
| |
| int MockNetworkLayer::CreateTransaction( |
| scoped_ptr<net::HttpTransaction>* trans) { |
| transaction_count_++; |
| trans->reset(new MockNetworkTransaction()); |
| return net::OK; |
| } |
| |
| net::HttpCache* MockNetworkLayer::GetCache() { |
| return NULL; |
| } |
| |
| net::HttpNetworkSession* MockNetworkLayer::GetSession() { |
| return NULL; |
| } |
| |
| void MockNetworkLayer::Suspend(bool suspend) {} |
| |
| //----------------------------------------------------------------------------- |
| // helpers |
| |
| int ReadTransaction(net::HttpTransaction* trans, std::string* result) { |
| int rv; |
| |
| TestCompletionCallback callback; |
| |
| std::string content; |
| do { |
| scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(256)); |
| rv = trans->Read(buf, 256, &callback); |
| if (rv == net::ERR_IO_PENDING) |
| rv = callback.WaitForResult(); |
| if (rv > 0) { |
| content.append(buf->data(), rv); |
| } else if (rv < 0) { |
| return rv; |
| } |
| } while (rv > 0); |
| |
| result->swap(content); |
| return net::OK; |
| } |