Home | History | Annotate | Download | only in http
      1 // Copyright 2014 The Chromium OS Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include <brillo/http/http_request.h>
      6 
      7 #include <string>
      8 
      9 #include <base/callback.h>
     10 #include <brillo/bind_lambda.h>
     11 #include <brillo/http/mock_connection.h>
     12 #include <brillo/http/mock_transport.h>
     13 #include <brillo/mime_utils.h>
     14 #include <brillo/streams/mock_stream.h>
     15 #include <gmock/gmock.h>
     16 #include <gtest/gtest.h>
     17 
     18 using testing::DoAll;
     19 using testing::Invoke;
     20 using testing::Return;
     21 using testing::SetArgPointee;
     22 using testing::Unused;
     23 using testing::WithArg;
     24 using testing::_;
     25 
     26 namespace brillo {
     27 namespace http {
     28 
     29 MATCHER_P(ContainsStringData, str, "") {
     30   if (arg->GetSize() != str.size())
     31     return false;
     32 
     33   std::string data;
     34   char buf[100];
     35   size_t read = 0;
     36   while (arg->ReadBlocking(buf, sizeof(buf), &read, nullptr) && read > 0) {
     37     data.append(buf, read);
     38   }
     39   return data == str;
     40 }
     41 
     42 class HttpRequestTest : public testing::Test {
     43  public:
     44   void SetUp() override {
     45     transport_ = std::make_shared<MockTransport>();
     46     connection_ = std::make_shared<MockConnection>(transport_);
     47   }
     48 
     49   void TearDown() override {
     50     // Having shared pointers to mock objects (some of methods in these tests
     51     // return shared pointers to connection and transport) could cause the
     52     // test expectations to hold on to the mock object without releasing them
     53     // at the end of a test, causing Mock's object leak detection to erroneously
     54     // detect mock object "leaks". Verify and clear the expectations manually
     55     // and explicitly to ensure the shared pointer refcounters are not
     56     // preventing the mocks to be destroyed at the end of each test.
     57     testing::Mock::VerifyAndClearExpectations(connection_.get());
     58     connection_.reset();
     59     testing::Mock::VerifyAndClearExpectations(transport_.get());
     60     transport_.reset();
     61   }
     62 
     63  protected:
     64   std::shared_ptr<MockTransport> transport_;
     65   std::shared_ptr<MockConnection> connection_;
     66 };
     67 
     68 TEST_F(HttpRequestTest, Defaults) {
     69   Request request{"http://www.foo.bar", request_type::kPost, transport_};
     70   EXPECT_TRUE(request.GetContentType().empty());
     71   EXPECT_TRUE(request.GetReferer().empty());
     72   EXPECT_TRUE(request.GetUserAgent().empty());
     73   EXPECT_EQ("*/*", request.GetAccept());
     74   EXPECT_EQ("http://www.foo.bar", request.GetRequestURL());
     75   EXPECT_EQ(request_type::kPost, request.GetRequestMethod());
     76 
     77   Request request2{"http://www.foo.bar/baz", request_type::kGet, transport_};
     78   EXPECT_EQ("http://www.foo.bar/baz", request2.GetRequestURL());
     79   EXPECT_EQ(request_type::kGet, request2.GetRequestMethod());
     80 }
     81 
     82 TEST_F(HttpRequestTest, ContentType) {
     83   Request request{"http://www.foo.bar", request_type::kPost, transport_};
     84   request.SetContentType(mime::image::kJpeg);
     85   EXPECT_EQ(mime::image::kJpeg, request.GetContentType());
     86 }
     87 
     88 TEST_F(HttpRequestTest, Referer) {
     89   Request request{"http://www.foo.bar", request_type::kPost, transport_};
     90   request.SetReferer("http://www.foo.bar/baz");
     91   EXPECT_EQ("http://www.foo.bar/baz", request.GetReferer());
     92 }
     93 
     94 TEST_F(HttpRequestTest, UserAgent) {
     95   Request request{"http://www.foo.bar", request_type::kPost, transport_};
     96   request.SetUserAgent("FooBar Browser");
     97   EXPECT_EQ("FooBar Browser", request.GetUserAgent());
     98 }
     99 
    100 TEST_F(HttpRequestTest, Accept) {
    101   Request request{"http://www.foo.bar", request_type::kPost, transport_};
    102   request.SetAccept("text/*, text/html, text/html;level=1, */*");
    103   EXPECT_EQ("text/*, text/html, text/html;level=1, */*", request.GetAccept());
    104 }
    105 
    106 TEST_F(HttpRequestTest, GetResponseAndBlock) {
    107   Request request{"http://www.foo.bar", request_type::kPost, transport_};
    108   request.SetUserAgent("FooBar Browser");
    109   request.SetReferer("http://www.foo.bar/baz");
    110   request.SetAccept("text/*, text/html, text/html;level=1, */*");
    111   request.AddHeader(request_header::kAcceptEncoding, "compress, gzip");
    112   request.AddHeaders({
    113       {request_header::kAcceptLanguage, "da, en-gb;q=0.8, en;q=0.7"},
    114       {request_header::kConnection, "close"},
    115   });
    116   request.AddRange(-10);
    117   request.AddRange(100, 200);
    118   request.AddRange(300);
    119   std::string req_body{"Foo bar baz"};
    120   request.AddHeader(request_header::kContentType, mime::text::kPlain);
    121 
    122   EXPECT_CALL(*transport_, CreateConnection(
    123       "http://www.foo.bar",
    124       request_type::kPost,
    125       HeaderList{
    126         {request_header::kAcceptEncoding, "compress, gzip"},
    127         {request_header::kAcceptLanguage, "da, en-gb;q=0.8, en;q=0.7"},
    128         {request_header::kConnection, "close"},
    129         {request_header::kContentType, mime::text::kPlain},
    130         {request_header::kRange, "bytes=-10,100-200,300-"},
    131         {request_header::kAccept, "text/*, text/html, text/html;level=1, */*"},
    132       },
    133       "FooBar Browser",
    134       "http://www.foo.bar/baz",
    135       nullptr)).WillOnce(Return(connection_));
    136 
    137   EXPECT_CALL(*connection_, MockSetRequestData(ContainsStringData(req_body), _))
    138       .WillOnce(Return(true));
    139 
    140   EXPECT_TRUE(
    141       request.AddRequestBody(req_body.data(), req_body.size(), nullptr));
    142 
    143   EXPECT_CALL(*connection_, FinishRequest(_)).WillOnce(Return(true));
    144   auto resp = request.GetResponseAndBlock(nullptr);
    145   EXPECT_NE(nullptr, resp.get());
    146 }
    147 
    148 TEST_F(HttpRequestTest, GetResponse) {
    149   Request request{"http://foo.bar", request_type::kGet, transport_};
    150 
    151   std::string resp_data{"FooBar response body"};
    152   auto read_data =
    153       [&resp_data](void* buffer, Unused, size_t* read, Unused) -> bool {
    154     memcpy(buffer, resp_data.data(), resp_data.size());
    155     *read = resp_data.size();
    156     return true;
    157   };
    158 
    159   auto success_callback = [](decltype(this) test,
    160                              const std::string& resp_data,
    161                              RequestID request_id,
    162                              std::unique_ptr<Response> resp) {
    163     EXPECT_EQ(23, request_id);
    164     EXPECT_CALL(*test->connection_, GetResponseStatusCode())
    165         .WillOnce(Return(status_code::Partial));
    166     EXPECT_EQ(status_code::Partial, resp->GetStatusCode());
    167 
    168     EXPECT_CALL(*test->connection_, GetResponseStatusText())
    169         .WillOnce(Return("Partial completion"));
    170     EXPECT_EQ("Partial completion", resp->GetStatusText());
    171 
    172     EXPECT_CALL(*test->connection_,
    173                 GetResponseHeader(response_header::kContentType))
    174         .WillOnce(Return(mime::text::kHtml));
    175     EXPECT_EQ(mime::text::kHtml, resp->GetContentType());
    176 
    177     EXPECT_EQ(resp_data, resp->ExtractDataAsString());
    178   };
    179 
    180   auto finish_request_async =
    181       [this, &read_data](const SuccessCallback& success_callback) {
    182     std::unique_ptr<MockStream> mock_stream{new MockStream};
    183     EXPECT_CALL(*mock_stream, ReadBlocking(_, _, _, _))
    184         .WillOnce(Invoke(read_data))
    185         .WillOnce(DoAll(SetArgPointee<2>(0), Return(true)));
    186 
    187     EXPECT_CALL(*connection_, MockExtractDataStream(_))
    188       .WillOnce(Return(mock_stream.release()));
    189     std::unique_ptr<Response> resp{new Response{connection_}};
    190     success_callback.Run(23, std::move(resp));
    191   };
    192 
    193   EXPECT_CALL(
    194       *transport_,
    195       CreateConnection("http://foo.bar", request_type::kGet, _, "", "", _))
    196       .WillOnce(Return(connection_));
    197 
    198   EXPECT_CALL(*connection_, FinishRequestAsync(_, _))
    199       .WillOnce(DoAll(WithArg<0>(Invoke(finish_request_async)), Return(23)));
    200 
    201   EXPECT_EQ(
    202       23,
    203       request.GetResponse(
    204           base::Bind(success_callback, base::Unretained(this), resp_data), {}));
    205 }
    206 
    207 }  // namespace http
    208 }  // namespace brillo
    209