summaryrefslogtreecommitdiffstats
path: root/src/test/rgw/test_http_manager.cc
blob: f2daeddca79297994bd21a5fa1119ad3ab515e2b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
/*
 * Ceph - scalable distributed file system
 *
 * Copyright (C) 2015 Red Hat
 *
 * This is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1, as published by the Free Software
 * Foundation. See file COPYING.
 *
 */
#include "rgw_rados.h"
#include "rgw_http_client.h"
#include "global/global_init.h"
#include "common/ceph_argparse.h"
#include <unistd.h>
#include <curl/curl.h>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/write.hpp>
#include <thread>
#include <gtest/gtest.h>

using namespace std;

namespace {
  using tcp = boost::asio::ip::tcp;

  // if we have a racing where another thread manages to bind and listen the
  // port picked by this acceptor, try again.
  static constexpr int MAX_BIND_RETRIES = 60;

  tcp::acceptor try_bind(boost::asio::io_context& ioctx) {
    using tcp = boost::asio::ip::tcp;
    tcp::endpoint endpoint(tcp::v4(), 0);
    tcp::acceptor acceptor(ioctx);
    acceptor.open(endpoint.protocol());
    for (int retries = 0;; retries++) {
      try {
	acceptor.bind(endpoint);
	// yay!
	break;
      } catch (const boost::system::system_error& e) {
	if (retries == MAX_BIND_RETRIES) {
	  throw;
	}
	if (e.code() != boost::system::errc::address_in_use) {
	  throw;
	}
      }
      // backoff a little bit
      sleep(1);
    }
    return acceptor;
  }
}

TEST(HTTPManager, ReadTruncated)
{
  using tcp = boost::asio::ip::tcp;
  boost::asio::io_context ioctx;
  auto acceptor = try_bind(ioctx);
  acceptor.listen();

  std::thread server{[&] {
    tcp::socket socket{ioctx};
    acceptor.accept(socket);
    std::string_view response =
        "HTTP/1.1 200 OK\r\n"
        "Content-Length: 1024\r\n"
        "\r\n"
        "short body";
    boost::asio::write(socket, boost::asio::buffer(response));
  }};
  const auto url = std::string{"http://127.0.0.1:"} + std::to_string(acceptor.local_endpoint().port());

  RGWHTTPClient client{g_ceph_context, "GET", url};
  EXPECT_EQ(-EAGAIN, RGWHTTP::process(&client, null_yield));

  server.join();
}

TEST(HTTPManager, Head)
{
  using tcp = boost::asio::ip::tcp;
  boost::asio::io_context ioctx;
  auto acceptor = try_bind(ioctx);
  acceptor.listen();

  std::thread server{[&] {
    tcp::socket socket{ioctx};
    acceptor.accept(socket);
    std::string_view response =
        "HTTP/1.1 200 OK\r\n"
        "Content-Length: 1024\r\n"
        "\r\n";
    boost::asio::write(socket, boost::asio::buffer(response));
  }};
  const auto url = std::string{"http://127.0.0.1:"} + std::to_string(acceptor.local_endpoint().port());

  RGWHTTPClient client{g_ceph_context, "HEAD", url};
  EXPECT_EQ(0, RGWHTTP::process(&client, null_yield));

  server.join();
}

TEST(HTTPManager, SignalThread)
{
  auto cct = g_ceph_context;
  RGWHTTPManager http(cct);

  ASSERT_EQ(0, http.start());

  // default pipe buffer size according to man pipe
  constexpr size_t max_pipe_buffer_size = 65536;
  // each signal writes 4 bytes to the pipe
  constexpr size_t max_pipe_signals = max_pipe_buffer_size / sizeof(uint32_t);
  // add_request and unregister_request
  constexpr size_t pipe_signals_per_request = 2;
  // number of http requests to fill the pipe buffer
  constexpr size_t max_requests = max_pipe_signals / pipe_signals_per_request;

  // send one extra request to test that we don't deadlock
  constexpr size_t num_requests = max_requests + 1;

  for (size_t i = 0; i < num_requests; i++) {
    RGWHTTPClient client{cct, "PUT", "http://127.0.0.1:80"};
    http.add_request(&client);
  }
}

int main(int argc, char** argv)
{
  auto args = argv_to_vec(argc, argv);
  auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
			 CODE_ENVIRONMENT_UTILITY,
			 CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
  common_init_finish(g_ceph_context);

  rgw_http_client_init(cct->get());
  rgw_setup_saved_curl_handles();
  ::testing::InitGoogleTest(&argc, argv);
  int r = RUN_ALL_TESTS();
  rgw_release_all_curl_handles();
  rgw_http_client_cleanup();
  return r;
}