You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
204 lines
7.1 KiB
204 lines
7.1 KiB
// Copyright 2014 The Chromium OS 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 <brillo/http/http_request.h>
|
|
|
|
#include <string>
|
|
|
|
#include <base/bind.h>
|
|
#include <base/callback.h>
|
|
#include <brillo/http/mock_connection.h>
|
|
#include <brillo/http/mock_transport.h>
|
|
#include <brillo/mime_utils.h>
|
|
#include <brillo/streams/mock_stream.h>
|
|
#include <gmock/gmock.h>
|
|
#include <gtest/gtest.h>
|
|
|
|
using testing::DoAll;
|
|
using testing::Invoke;
|
|
using testing::Return;
|
|
using testing::SetArgPointee;
|
|
using testing::Unused;
|
|
using testing::WithArg;
|
|
using testing::_;
|
|
|
|
namespace brillo {
|
|
namespace http {
|
|
|
|
MATCHER_P(ContainsStringData, str, "") {
|
|
if (arg->GetSize() != str.size())
|
|
return false;
|
|
|
|
std::string data;
|
|
char buf[100];
|
|
size_t read = 0;
|
|
while (arg->ReadBlocking(buf, sizeof(buf), &read, nullptr) && read > 0) {
|
|
data.append(buf, read);
|
|
}
|
|
return data == str;
|
|
}
|
|
|
|
class HttpRequestTest : public testing::Test {
|
|
public:
|
|
void SetUp() override {
|
|
transport_ = std::make_shared<MockTransport>();
|
|
connection_ = std::make_shared<MockConnection>(transport_);
|
|
}
|
|
|
|
void TearDown() override {
|
|
// Having shared pointers to mock objects (some of methods in these tests
|
|
// return shared pointers to connection and transport) could cause the
|
|
// test expectations to hold on to the mock object without releasing them
|
|
// at the end of a test, causing Mock's object leak detection to erroneously
|
|
// detect mock object "leaks". Verify and clear the expectations manually
|
|
// and explicitly to ensure the shared pointer refcounters are not
|
|
// preventing the mocks to be destroyed at the end of each test.
|
|
testing::Mock::VerifyAndClearExpectations(connection_.get());
|
|
connection_.reset();
|
|
testing::Mock::VerifyAndClearExpectations(transport_.get());
|
|
transport_.reset();
|
|
}
|
|
|
|
protected:
|
|
std::shared_ptr<MockTransport> transport_;
|
|
std::shared_ptr<MockConnection> connection_;
|
|
};
|
|
|
|
TEST_F(HttpRequestTest, Defaults) {
|
|
Request request{"http://www.foo.bar", request_type::kPost, transport_};
|
|
EXPECT_TRUE(request.GetContentType().empty());
|
|
EXPECT_TRUE(request.GetReferer().empty());
|
|
EXPECT_TRUE(request.GetUserAgent().empty());
|
|
EXPECT_EQ("*/*", request.GetAccept());
|
|
EXPECT_EQ("http://www.foo.bar", request.GetRequestURL());
|
|
EXPECT_EQ(request_type::kPost, request.GetRequestMethod());
|
|
|
|
Request request2{"http://www.foo.bar/baz", request_type::kGet, transport_};
|
|
EXPECT_EQ("http://www.foo.bar/baz", request2.GetRequestURL());
|
|
EXPECT_EQ(request_type::kGet, request2.GetRequestMethod());
|
|
}
|
|
|
|
TEST_F(HttpRequestTest, ContentType) {
|
|
Request request{"http://www.foo.bar", request_type::kPost, transport_};
|
|
request.SetContentType(mime::image::kJpeg);
|
|
EXPECT_EQ(mime::image::kJpeg, request.GetContentType());
|
|
}
|
|
|
|
TEST_F(HttpRequestTest, Referer) {
|
|
Request request{"http://www.foo.bar", request_type::kPost, transport_};
|
|
request.SetReferer("http://www.foo.bar/baz");
|
|
EXPECT_EQ("http://www.foo.bar/baz", request.GetReferer());
|
|
}
|
|
|
|
TEST_F(HttpRequestTest, UserAgent) {
|
|
Request request{"http://www.foo.bar", request_type::kPost, transport_};
|
|
request.SetUserAgent("FooBar Browser");
|
|
EXPECT_EQ("FooBar Browser", request.GetUserAgent());
|
|
}
|
|
|
|
TEST_F(HttpRequestTest, Accept) {
|
|
Request request{"http://www.foo.bar", request_type::kPost, transport_};
|
|
request.SetAccept("text/*, text/html, text/html;level=1, */*");
|
|
EXPECT_EQ("text/*, text/html, text/html;level=1, */*", request.GetAccept());
|
|
}
|
|
|
|
TEST_F(HttpRequestTest, GetResponseAndBlock) {
|
|
Request request{"http://www.foo.bar", request_type::kPost, transport_};
|
|
request.SetUserAgent("FooBar Browser");
|
|
request.SetReferer("http://www.foo.bar/baz");
|
|
request.SetAccept("text/*, text/html, text/html;level=1, */*");
|
|
request.AddHeader(request_header::kAcceptEncoding, "compress, gzip");
|
|
request.AddHeaders({
|
|
{request_header::kAcceptLanguage, "da, en-gb;q=0.8, en;q=0.7"},
|
|
{request_header::kConnection, "close"},
|
|
});
|
|
request.AddRange(-10);
|
|
request.AddRange(100, 200);
|
|
request.AddRange(300);
|
|
std::string req_body{"Foo bar baz"};
|
|
request.AddHeader(request_header::kContentType, mime::text::kPlain);
|
|
|
|
EXPECT_CALL(*transport_, CreateConnection(
|
|
"http://www.foo.bar",
|
|
request_type::kPost,
|
|
HeaderList{
|
|
{request_header::kAcceptEncoding, "compress, gzip"},
|
|
{request_header::kAcceptLanguage, "da, en-gb;q=0.8, en;q=0.7"},
|
|
{request_header::kConnection, "close"},
|
|
{request_header::kContentType, mime::text::kPlain},
|
|
{request_header::kRange, "bytes=-10,100-200,300-"},
|
|
{request_header::kAccept, "text/*, text/html, text/html;level=1, */*"},
|
|
},
|
|
"FooBar Browser",
|
|
"http://www.foo.bar/baz",
|
|
nullptr)).WillOnce(Return(connection_));
|
|
|
|
EXPECT_CALL(*connection_, MockSetRequestData(ContainsStringData(req_body), _))
|
|
.WillOnce(Return(true));
|
|
|
|
EXPECT_TRUE(
|
|
request.AddRequestBody(req_body.data(), req_body.size(), nullptr));
|
|
|
|
EXPECT_CALL(*connection_, FinishRequest(_)).WillOnce(Return(true));
|
|
auto resp = request.GetResponseAndBlock(nullptr);
|
|
EXPECT_NE(nullptr, resp.get());
|
|
}
|
|
|
|
TEST_F(HttpRequestTest, GetResponse) {
|
|
Request request{"http://foo.bar", request_type::kGet, transport_};
|
|
|
|
std::string resp_data{"FooBar response body"};
|
|
auto read_data =
|
|
[&resp_data](void* buffer, Unused, size_t* read, Unused) -> bool {
|
|
memcpy(buffer, resp_data.data(), resp_data.size());
|
|
*read = resp_data.size();
|
|
return true;
|
|
};
|
|
|
|
auto success_callback = base::Bind(
|
|
[](MockConnection* connection, const std::string& resp_data,
|
|
RequestID request_id, std::unique_ptr<Response> resp) {
|
|
EXPECT_EQ(23, request_id);
|
|
EXPECT_CALL(*connection, GetResponseStatusCode())
|
|
.WillOnce(Return(status_code::Partial));
|
|
EXPECT_EQ(status_code::Partial, resp->GetStatusCode());
|
|
|
|
EXPECT_CALL(*connection, GetResponseStatusText())
|
|
.WillOnce(Return("Partial completion"));
|
|
EXPECT_EQ("Partial completion", resp->GetStatusText());
|
|
|
|
EXPECT_CALL(*connection, GetResponseHeader(response_header::kContentType))
|
|
.WillOnce(Return(mime::text::kHtml));
|
|
EXPECT_EQ(mime::text::kHtml, resp->GetContentType());
|
|
|
|
EXPECT_EQ(resp_data, resp->ExtractDataAsString());
|
|
}, connection_.get(), resp_data);
|
|
|
|
auto finish_request_async =
|
|
[this, &read_data](const SuccessCallback& success_callback) {
|
|
std::unique_ptr<MockStream> mock_stream{new MockStream};
|
|
EXPECT_CALL(*mock_stream, ReadBlocking(_, _, _, _))
|
|
.WillOnce(Invoke(read_data))
|
|
.WillOnce(DoAll(SetArgPointee<2>(0), Return(true)));
|
|
|
|
EXPECT_CALL(*connection_, MockExtractDataStream(_))
|
|
.WillOnce(Return(mock_stream.release()));
|
|
std::unique_ptr<Response> resp{new Response{connection_}};
|
|
success_callback.Run(23, std::move(resp));
|
|
};
|
|
|
|
EXPECT_CALL(
|
|
*transport_,
|
|
CreateConnection("http://foo.bar", request_type::kGet, _, "", "", _))
|
|
.WillOnce(Return(connection_));
|
|
|
|
EXPECT_CALL(*connection_, FinishRequestAsync(_, _))
|
|
.WillOnce(DoAll(WithArg<0>(Invoke(finish_request_async)), Return(23)));
|
|
|
|
EXPECT_EQ(23, request.GetResponse(success_callback, {}));
|
|
}
|
|
|
|
} // namespace http
|
|
} // namespace brillo
|