blob: 434217e4884d39eff5c77c1d92da773502a8cdee [file] [log] [blame]
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +09001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20#define BOOST_TEST_MODULE TNonblockingServerTest
21#include <boost/test/unit_test.hpp>
cyy316723a2019-01-05 16:35:14 +080022#include <memory>
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +090023
Buğra Gedik36d1b0d2016-09-04 17:18:15 +090024#include "thrift/concurrency/Monitor.h"
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +090025#include "thrift/concurrency/Thread.h"
26#include "thrift/server/TNonblockingServer.h"
Divya Thaluru808d1432017-08-06 16:36:36 -070027#include "thrift/transport/TNonblockingServerSocket.h"
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +090028
29#include "gen-cpp/ParentService.h"
30
Nobuaki Sukegawa8016af82015-01-02 23:14:22 +090031#include <event.h>
32
Buğra Gedik36d1b0d2016-09-04 17:18:15 +090033using apache::thrift::concurrency::Guard;
34using apache::thrift::concurrency::Monitor;
35using apache::thrift::concurrency::Mutex;
cyyca8af9b2019-01-11 22:13:12 +080036using apache::thrift::concurrency::ThreadFactory;
James E. King, III82ae9572017-08-05 12:23:54 -040037using apache::thrift::concurrency::Runnable;
38using apache::thrift::concurrency::Thread;
39using apache::thrift::concurrency::ThreadFactory;
Buğra Gedik36d1b0d2016-09-04 17:18:15 +090040using apache::thrift::server::TServerEventHandler;
cyy316723a2019-01-05 16:35:14 +080041using std::make_shared;
42using std::shared_ptr;
James E. King, III82ae9572017-08-05 12:23:54 -040043
44using namespace apache::thrift;
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +090045
46struct Handler : public test::ParentServiceIf {
Sebastian Zenker042580f2019-01-29 15:48:12 +010047 void addString(const std::string& s) override { strings_.push_back(s); }
48 void getStrings(std::vector<std::string>& _return) override { _return = strings_; }
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +090049 std::vector<std::string> strings_;
50
51 // dummy overrides not used in this test
Sebastian Zenker042580f2019-01-29 15:48:12 +010052 int32_t incrementGeneration() override { return 0; }
53 int32_t getGeneration() override { return 0; }
54 void getDataWait(std::string&, const int32_t) override {}
55 void onewayWait() override {}
56 void exceptionWait(const std::string&) override {}
57 void unexpectedExceptionWait(const std::string&) override {}
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +090058};
59
60class Fixture {
61private:
Buğra Gedik36d1b0d2016-09-04 17:18:15 +090062 struct ListenEventHandler : public TServerEventHandler {
63 public:
64 ListenEventHandler(Mutex* mutex) : listenMonitor_(mutex), ready_(false) {}
65
Sebastian Zenker042580f2019-01-29 15:48:12 +010066 void preServe() override /* override */ {
Buğra Gedik36d1b0d2016-09-04 17:18:15 +090067 Guard g(listenMonitor_.mutex());
68 ready_ = true;
69 listenMonitor_.notify();
70 }
71
72 Monitor listenMonitor_;
73 bool ready_;
74 };
75
James E. King, III82ae9572017-08-05 12:23:54 -040076 struct Runner : public Runnable {
Nobuaki Sukegawa8cc91752016-05-15 00:24:41 +090077 int port;
James E. King, III82ae9572017-08-05 12:23:54 -040078 shared_ptr<event_base> userEventBase;
79 shared_ptr<TProcessor> processor;
80 shared_ptr<server::TNonblockingServer> server;
81 shared_ptr<ListenEventHandler> listenHandler;
82 shared_ptr<transport::TNonblockingServerSocket> socket;
Buğra Gedik36d1b0d2016-09-04 17:18:15 +090083 Mutex mutex_;
84
85 Runner() {
86 listenHandler.reset(new ListenEventHandler(&mutex_));
87 }
Nobuaki Sukegawa8cc91752016-05-15 00:24:41 +090088
Sebastian Zenker042580f2019-01-29 15:48:12 +010089 void run() override {
Nobuaki Sukegawa8cc91752016-05-15 00:24:41 +090090 // When binding to explicit port, allow retrying to workaround bind failures on ports in use
91 int retryCount = port ? 10 : 0;
92 startServer(retryCount);
93 }
94
Buğra Gedik36d1b0d2016-09-04 17:18:15 +090095 void readyBarrier() {
96 // block until server is listening and ready to accept connections
97 Guard g(mutex_);
98 while (!listenHandler->ready_) {
99 listenHandler->listenMonitor_.wait();
100 }
101 }
Nobuaki Sukegawa8cc91752016-05-15 00:24:41 +0900102 private:
103 void startServer(int retry_count) {
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +0900104 try {
Divya Thaluru808d1432017-08-06 16:36:36 -0700105 socket.reset(new transport::TNonblockingServerSocket(port));
106 server.reset(new server::TNonblockingServer(processor, socket));
Buğra Gedik36d1b0d2016-09-04 17:18:15 +0900107 server->setServerEventHandler(listenHandler);
Nobuaki Sukegawa8cc91752016-05-15 00:24:41 +0900108 if (userEventBase) {
109 server->registerEvents(userEventBase.get());
110 }
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +0900111 server->serve();
Nobuaki Sukegawa8cc91752016-05-15 00:24:41 +0900112 } catch (const transport::TTransportException&) {
113 if (retry_count > 0) {
114 ++port;
115 startServer(retry_count - 1);
116 } else {
117 throw;
118 }
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +0900119 }
120 }
121 };
122
Nobuaki Sukegawa8016af82015-01-02 23:14:22 +0900123 struct EventDeleter {
124 void operator()(event_base* p) { event_base_free(p); }
125 };
126
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +0900127protected:
James E. King, III82ae9572017-08-05 12:23:54 -0400128 Fixture() : processor(new test::ParentServiceProcessor(make_shared<Handler>())) {}
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +0900129
Nobuaki Sukegawa8016af82015-01-02 23:14:22 +0900130 ~Fixture() {
131 if (server) {
132 server->stop();
133 }
134 if (thread) {
135 thread->join();
136 }
137 }
138
139 void setEventBase(event_base* user_event_base) {
140 userEventBase_.reset(user_event_base, EventDeleter());
141 }
142
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +0900143 int startServer(int port) {
James E. King, III82ae9572017-08-05 12:23:54 -0400144 shared_ptr<Runner> runner(new Runner);
Nobuaki Sukegawa8cc91752016-05-15 00:24:41 +0900145 runner->port = port;
146 runner->processor = processor;
147 runner->userEventBase = userEventBase_;
148
James E. King, III82ae9572017-08-05 12:23:54 -0400149 shared_ptr<ThreadFactory> threadFactory(
cyyca8af9b2019-01-11 22:13:12 +0800150 new ThreadFactory(false));
Nobuaki Sukegawa8cc91752016-05-15 00:24:41 +0900151 thread = threadFactory->newThread(runner);
152 thread->start();
Buğra Gedik36d1b0d2016-09-04 17:18:15 +0900153 runner->readyBarrier();
154
Nobuaki Sukegawa8cc91752016-05-15 00:24:41 +0900155 server = runner->server;
156 return runner->port;
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +0900157 }
158
159 bool canCommunicate(int serverPort) {
James E. King, III82ae9572017-08-05 12:23:54 -0400160 shared_ptr<transport::TSocket> socket(new transport::TSocket("localhost", serverPort));
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +0900161 socket->open();
James E. King, III82ae9572017-08-05 12:23:54 -0400162 test::ParentServiceClient client(make_shared<protocol::TBinaryProtocol>(
163 make_shared<transport::TFramedTransport>(socket)));
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +0900164 client.addString("foo");
165 std::vector<std::string> strings;
166 client.getStrings(strings);
167 return strings.size() == 1 && !(strings[0].compare("foo"));
168 }
169
170private:
James E. King, III82ae9572017-08-05 12:23:54 -0400171 shared_ptr<event_base> userEventBase_;
172 shared_ptr<test::ParentServiceProcessor> processor;
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +0900173protected:
James E. King, III82ae9572017-08-05 12:23:54 -0400174 shared_ptr<server::TNonblockingServer> server;
Nobuaki Sukegawa8cc91752016-05-15 00:24:41 +0900175private:
James E. King III8d55c472018-03-12 14:29:55 -0400176 shared_ptr<apache::thrift::concurrency::Thread> thread;
Nobuaki Sukegawa8cc91752016-05-15 00:24:41 +0900177
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +0900178};
179
180BOOST_AUTO_TEST_SUITE(TNonblockingServerTest)
181
182BOOST_FIXTURE_TEST_CASE(get_specified_port, Fixture) {
183 int specified_port = startServer(12345);
184 BOOST_REQUIRE_GE(specified_port, 12345);
185 BOOST_REQUIRE_EQUAL(server->getListenPort(), specified_port);
186 BOOST_CHECK(canCommunicate(specified_port));
187
188 server->stop();
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +0900189}
190
191BOOST_FIXTURE_TEST_CASE(get_assigned_port, Fixture) {
192 int specified_port = startServer(0);
193 BOOST_REQUIRE_EQUAL(specified_port, 0);
194 int assigned_port = server->getListenPort();
195 BOOST_REQUIRE_NE(assigned_port, 0);
196 BOOST_CHECK(canCommunicate(assigned_port));
197
198 server->stop();
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +0900199}
200
Nobuaki Sukegawa8016af82015-01-02 23:14:22 +0900201BOOST_FIXTURE_TEST_CASE(provide_event_base, Fixture) {
202 event_base* eb = event_base_new();
203 setEventBase(eb);
204 startServer(0);
205
206 // assert that the server works
207 BOOST_CHECK(canCommunicate(server->getListenPort()));
208#if LIBEVENT_VERSION_NUMBER > 0x02010400
209 // also assert that the event_base is actually used when it's easy
210 BOOST_CHECK_GT(event_base_get_num_events(eb, EVENT_BASE_COUNT_ADDED), 0);
211#endif
212}
213
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +0900214BOOST_AUTO_TEST_SUITE_END()