// Copyright (c) 2006-2009 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.
#ifndef NET_HTTP_HTTP_TRANSACTION_UNITTEST_H_
#define NET_HTTP_HTTP_TRANSACTION_UNITTEST_H_
#include "net/http/http_transaction.h"
#include <algorithm>
#include <string>
#include "base/compiler_specific.h"
#include "base/message_loop.h"
#include "base/string_util.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/base/load_flags.h"
#include "net/base/test_completion_callback.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_headers.h"
#include "net/http/http_response_info.h"
//-----------------------------------------------------------------------------
// mock transaction data
// these flags may be combined to form the test_mode field
enum {
TEST_MODE_NORMAL = 0,
TEST_MODE_SYNC_NET_START = 1 << 0,
TEST_MODE_SYNC_NET_READ = 1 << 1,
TEST_MODE_SYNC_CACHE_START = 1 << 2,
TEST_MODE_SYNC_CACHE_READ = 1 << 3,
TEST_MODE_SYNC_CACHE_WRITE = 1 << 4,
TEST_MODE_SYNC_ALL = TEST_MODE_SYNC_NET_START | TEST_MODE_SYNC_NET_READ |
TEST_MODE_SYNC_CACHE_START | TEST_MODE_SYNC_CACHE_READ |
TEST_MODE_SYNC_CACHE_WRITE
};
typedef void (*MockTransactionHandler)(const net::HttpRequestInfo* request,
std::string* response_status,
std::string* response_headers,
std::string* response_data);
struct MockTransaction {
const char* url;
const char* method;
// If |request_time| is unspecified, the current time will be used.
base::Time request_time;
const char* request_headers;
int load_flags;
const char* status;
const char* response_headers;
// If |response_time| is unspecified, the current time will be used.
base::Time response_time;
const char* data;
int test_mode;
MockTransactionHandler handler;
int cert_status;
};
extern const MockTransaction kSimpleGET_Transaction;
extern const MockTransaction kSimplePOST_Transaction;
extern const MockTransaction kTypicalGET_Transaction;
extern const MockTransaction kETagGET_Transaction;
extern const MockTransaction kRangeGET_Transaction;
// returns the mock transaction for the given URL
const MockTransaction* FindMockTransaction(const GURL& url);
// Add/Remove a mock transaction that can be accessed via FindMockTransaction.
// There can be only one MockTransaction associated with a given URL.
void AddMockTransaction(const MockTransaction* trans);
void RemoveMockTransaction(const MockTransaction* trans);
struct ScopedMockTransaction : MockTransaction {
ScopedMockTransaction() {
AddMockTransaction(this);
}
explicit ScopedMockTransaction(const MockTransaction& t)
: MockTransaction(t) {
AddMockTransaction(this);
}
~ScopedMockTransaction() {
RemoveMockTransaction(this);
}
};
//-----------------------------------------------------------------------------
// mock http request
class MockHttpRequest : public net::HttpRequestInfo {
public:
explicit MockHttpRequest(const MockTransaction& t) {
url = GURL(t.url);
method = t.method;
extra_headers = t.request_headers;
load_flags = t.load_flags;
}
};
//-----------------------------------------------------------------------------
// use this class to test completely consuming a transaction
class TestTransactionConsumer : public CallbackRunner< Tuple1<int> > {
public:
explicit TestTransactionConsumer(net::HttpTransactionFactory* factory)
: state_(IDLE),
trans_(NULL),
error_(net::OK) {
// Disregard the error code.
factory->CreateTransaction(&trans_);
++quit_counter_;
}
~TestTransactionConsumer() {
}
void Start(const net::HttpRequestInfo* request, net::LoadLog* load_log) {
state_ = STARTING;
int result = trans_->Start(request, this, load_log);
if (result != net::ERR_IO_PENDING)
DidStart(result);
}
bool is_done() const { return state_ == DONE; }
int error() const { return error_; }
const net::HttpResponseInfo* response_info() const {
return trans_->GetResponseInfo();
}
const std::string& content() const { return content_; }
private:
// Callback implementation:
virtual void RunWithParams(const Tuple1<int>& params) {
int result = params.a;
switch (state_) {
case STARTING:
DidStart(result);
break;
case READING:
DidRead(result);
break;
default:
NOTREACHED();
}
}
void DidStart(int result) {
if (result != net::OK) {
DidFinish(result);
} else {
Read();
}
}
void DidRead(int result) {
if (result <= 0) {
DidFinish(result);
} else {
content_.append(read_buf_->data(), result);
Read();
}
}
void DidFinish(int result) {
state_ = DONE;
error_ = result;
if (--quit_counter_ == 0)
MessageLoop::current()->Quit();
}
void 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);
}
enum State {
IDLE,
STARTING,
READING,
DONE
} state_;
scoped_ptr<net::HttpTransaction> trans_;
std::string content_;
scoped_refptr<net::IOBuffer> read_buf_;
int error_;
static int quit_counter_;
};
//-----------------------------------------------------------------------------
// mock network layer
// This transaction class inspects the available set of mock transactions to
// find data for the request URL. It supports IO operations that complete
// synchronously or asynchronously to help exercise different code paths in the
// HttpCache implementation.
class MockNetworkTransaction : public net::HttpTransaction {
public:
MockNetworkTransaction() :
ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)), data_cursor_(0) {
}
virtual int Start(const net::HttpRequestInfo* request,
net::CompletionCallback* callback,
net::LoadLog* load_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 =
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;
}
virtual int RestartIgnoringLastError(net::CompletionCallback* callback) {
return net::ERR_FAILED;
}
virtual int RestartWithCertificate(net::X509Certificate* client_cert,
net::CompletionCallback* callback) {
return net::ERR_FAILED;
}
virtual int RestartWithAuth(const std::wstring& username,
const std::wstring& password,
net::CompletionCallback* callback) {
return net::ERR_FAILED;
}
virtual bool IsReadyToRestartForAuth() {
return false;
}
virtual int 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;
}
virtual const net::HttpResponseInfo* GetResponseInfo() const {
return &response_;
}
virtual net::LoadState GetLoadState() const {
NOTREACHED() << "define some mock state transitions";
return net::LOAD_STATE_IDLE;
}
virtual uint64 GetUploadProgress() const {
return 0;
}
private:
void CallbackLater(net::CompletionCallback* callback, int result) {
MessageLoop::current()->PostTask(FROM_HERE, task_factory_.NewRunnableMethod(
&MockNetworkTransaction::RunCallback, callback, result));
}
void RunCallback(net::CompletionCallback* callback, int result) {
callback->Run(result);
}
ScopedRunnableMethodFactory<MockNetworkTransaction> task_factory_;
net::HttpResponseInfo response_;
std::string data_;
int data_cursor_;
int test_mode_;
};
class MockNetworkLayer : public net::HttpTransactionFactory {
public:
MockNetworkLayer() : transaction_count_(0) {
}
virtual int CreateTransaction(scoped_ptr<net::HttpTransaction>* trans) {
transaction_count_++;
trans->reset(new MockNetworkTransaction());
return net::OK;
}
virtual net::HttpCache* GetCache() {
return NULL;
}
virtual net::HttpNetworkSession* GetSession() {
return NULL;
}
virtual void Suspend(bool suspend) {}
int transaction_count() const { return transaction_count_; }
private:
int transaction_count_;
};
//-----------------------------------------------------------------------------
// helpers
// read the transaction completely
int ReadTransaction(net::HttpTransaction* trans, std::string* result);
#endif // NET_HTTP_HTTP_TRANSACTION_UNITTEST_H_