blob: 02638c99493d1fa76cb1971c7111de03487904bf [file] [log] [blame]
// Copyright 2014 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/url_request/url_request_simple_job.h"
#include <memory>
#include <utility>
#include "base/bind_helpers.h"
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/sequenced_task_runner.h"
#include "base/strings/string_piece.h"
#include "base/strings/stringprintf.h"
#include "net/base/request_priority.h"
#include "net/test/gtest_util.h"
#include "net/test/test_with_scoped_task_environment.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "net/url_request/url_request_job.h"
#include "net/url_request/url_request_job_factory.h"
#include "net/url_request/url_request_job_factory_impl.h"
#include "net/url_request/url_request_test_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using net::test::IsError;
using net::test::IsOk;
namespace net {
namespace {
const char kTestData[] = "Huge data array";
const int kRangeFirstPosition = 5;
const int kRangeLastPosition = 8;
static_assert(kRangeFirstPosition > 0 &&
kRangeFirstPosition < kRangeLastPosition &&
kRangeLastPosition <
static_cast<int>(arraysize(kTestData) - 1),
"invalid range");
class MockSimpleJob : public URLRequestSimpleJob {
public:
MockSimpleJob(URLRequest* request,
NetworkDelegate* network_delegate,
base::StringPiece data)
: URLRequestSimpleJob(request, network_delegate),
data_(data.as_string()) {}
protected:
// URLRequestSimpleJob implementation:
int GetData(std::string* mime_type,
std::string* charset,
std::string* data,
CompletionOnceCallback callback) const override {
mime_type->assign("text/plain");
charset->assign("US-ASCII");
data->assign(data_);
return OK;
}
private:
~MockSimpleJob() override = default;
const std::string data_;
DISALLOW_COPY_AND_ASSIGN(MockSimpleJob);
};
class CancelAfterFirstReadURLRequestDelegate : public TestDelegate {
public:
CancelAfterFirstReadURLRequestDelegate() : run_loop_(new base::RunLoop) {}
~CancelAfterFirstReadURLRequestDelegate() override = default;
void OnResponseStarted(URLRequest* request, int net_error) override {
DCHECK_NE(ERR_IO_PENDING, net_error);
// net::TestDelegate will start the first read.
TestDelegate::OnResponseStarted(request, net_error);
request->Cancel();
run_loop_->Quit();
}
void WaitUntilHeadersReceived() const { run_loop_->Run(); }
private:
std::unique_ptr<base::RunLoop> run_loop_;
DISALLOW_COPY_AND_ASSIGN(CancelAfterFirstReadURLRequestDelegate);
};
class SimpleJobProtocolHandler :
public URLRequestJobFactory::ProtocolHandler {
public:
SimpleJobProtocolHandler() = default;
URLRequestJob* MaybeCreateJob(
URLRequest* request,
NetworkDelegate* network_delegate) const override {
if (request->url().spec() == "data:empty")
return new MockSimpleJob(request, network_delegate, "");
return new MockSimpleJob(request, network_delegate, kTestData);
}
~SimpleJobProtocolHandler() override = default;
private:
DISALLOW_COPY_AND_ASSIGN(SimpleJobProtocolHandler);
};
class URLRequestSimpleJobTest : public TestWithScopedTaskEnvironment {
public:
URLRequestSimpleJobTest() : context_(true) {
job_factory_.SetProtocolHandler(
"data", std::make_unique<SimpleJobProtocolHandler>());
context_.set_job_factory(&job_factory_);
context_.Init();
request_ = context_.CreateRequest(GURL("data:test"), DEFAULT_PRIORITY,
&delegate_, TRAFFIC_ANNOTATION_FOR_TESTS);
}
void StartRequest(const HttpRequestHeaders* headers) {
if (headers)
request_->SetExtraRequestHeaders(*headers);
request_->Start();
EXPECT_TRUE(request_->is_pending());
delegate_.RunUntilComplete();
EXPECT_FALSE(request_->is_pending());
}
protected:
TestURLRequestContext context_;
URLRequestJobFactoryImpl job_factory_;
TestDelegate delegate_;
std::unique_ptr<URLRequest> request_;
private:
DISALLOW_COPY_AND_ASSIGN(URLRequestSimpleJobTest);
};
} // namespace
TEST_F(URLRequestSimpleJobTest, SimpleRequest) {
StartRequest(NULL);
EXPECT_THAT(delegate_.request_status(), IsOk());
EXPECT_EQ(kTestData, delegate_.data_received());
}
TEST_F(URLRequestSimpleJobTest, RangeRequest) {
const std::string kExpectedBody = std::string(
kTestData + kRangeFirstPosition, kTestData + kRangeLastPosition + 1);
HttpRequestHeaders headers;
headers.SetHeader(
HttpRequestHeaders::kRange,
HttpByteRange::Bounded(kRangeFirstPosition, kRangeLastPosition)
.GetHeaderValue());
StartRequest(&headers);
EXPECT_THAT(delegate_.request_status(), IsOk());
EXPECT_EQ(kExpectedBody, delegate_.data_received());
}
TEST_F(URLRequestSimpleJobTest, MultipleRangeRequest) {
HttpRequestHeaders headers;
int middle_pos = (kRangeFirstPosition + kRangeLastPosition)/2;
std::string range = base::StringPrintf("bytes=%d-%d,%d-%d",
kRangeFirstPosition,
middle_pos,
middle_pos + 1,
kRangeLastPosition);
headers.SetHeader(HttpRequestHeaders::kRange, range);
StartRequest(&headers);
EXPECT_TRUE(delegate_.request_failed());
EXPECT_EQ(ERR_REQUEST_RANGE_NOT_SATISFIABLE, delegate_.request_status());
}
TEST_F(URLRequestSimpleJobTest, InvalidRangeRequest) {
HttpRequestHeaders headers;
std::string range = base::StringPrintf(
"bytes=%d-%d", kRangeLastPosition, kRangeFirstPosition);
headers.SetHeader(HttpRequestHeaders::kRange, range);
StartRequest(&headers);
EXPECT_THAT(delegate_.request_status(), IsOk());
EXPECT_EQ(kTestData, delegate_.data_received());
}
TEST_F(URLRequestSimpleJobTest, EmptyDataRequest) {
request_ = context_.CreateRequest(GURL("data:empty"), DEFAULT_PRIORITY,
&delegate_, TRAFFIC_ANNOTATION_FOR_TESTS);
StartRequest(nullptr);
EXPECT_THAT(delegate_.request_status(), IsOk());
EXPECT_EQ("", delegate_.data_received());
}
TEST_F(URLRequestSimpleJobTest, CancelBeforeResponseStarts) {
request_ = context_.CreateRequest(GURL("data:cancel"), DEFAULT_PRIORITY,
&delegate_, TRAFFIC_ANNOTATION_FOR_TESTS);
request_->Start();
request_->Cancel();
base::RunLoop().RunUntilIdle();
EXPECT_THAT(delegate_.request_status(), IsError(ERR_ABORTED));
EXPECT_EQ(1, delegate_.response_started_count());
}
TEST_F(URLRequestSimpleJobTest, CancelAfterFirstReadStarted) {
CancelAfterFirstReadURLRequestDelegate cancel_delegate;
request_ =
context_.CreateRequest(GURL("data:cancel"), DEFAULT_PRIORITY,
&cancel_delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
request_->Start();
cancel_delegate.WaitUntilHeadersReceived();
base::RunLoop().RunUntilIdle();
EXPECT_THAT(cancel_delegate.request_status(), IsError(ERR_ABORTED));
EXPECT_EQ(1, cancel_delegate.response_started_count());
EXPECT_EQ("", cancel_delegate.data_received());
// Destroy the request so it doesn't outlive its delegate.
request_.reset();
}
} // namespace net