blob: f9aab4cc1b863482668fa93cf6b65015095b3323 [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() {
cyy64750162019-02-08 13:40:59 +080086 port = 0;
Buğra Gedik36d1b0d2016-09-04 17:18:15 +090087 listenHandler.reset(new ListenEventHandler(&mutex_));
88 }
Nobuaki Sukegawa8cc91752016-05-15 00:24:41 +090089
Sebastian Zenker042580f2019-01-29 15:48:12 +010090 void run() override {
Nobuaki Sukegawa8cc91752016-05-15 00:24:41 +090091 // When binding to explicit port, allow retrying to workaround bind failures on ports in use
92 int retryCount = port ? 10 : 0;
93 startServer(retryCount);
94 }
95
Buğra Gedik36d1b0d2016-09-04 17:18:15 +090096 void readyBarrier() {
97 // block until server is listening and ready to accept connections
98 Guard g(mutex_);
99 while (!listenHandler->ready_) {
100 listenHandler->listenMonitor_.wait();
101 }
102 }
Nobuaki Sukegawa8cc91752016-05-15 00:24:41 +0900103 private:
104 void startServer(int retry_count) {
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +0900105 try {
Divya Thaluru808d1432017-08-06 16:36:36 -0700106 socket.reset(new transport::TNonblockingServerSocket(port));
107 server.reset(new server::TNonblockingServer(processor, socket));
Buğra Gedik36d1b0d2016-09-04 17:18:15 +0900108 server->setServerEventHandler(listenHandler);
Nobuaki Sukegawa8cc91752016-05-15 00:24:41 +0900109 if (userEventBase) {
110 server->registerEvents(userEventBase.get());
111 }
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +0900112 server->serve();
Nobuaki Sukegawa8cc91752016-05-15 00:24:41 +0900113 } catch (const transport::TTransportException&) {
114 if (retry_count > 0) {
115 ++port;
116 startServer(retry_count - 1);
117 } else {
118 throw;
119 }
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +0900120 }
121 }
122 };
123
Nobuaki Sukegawa8016af82015-01-02 23:14:22 +0900124 struct EventDeleter {
125 void operator()(event_base* p) { event_base_free(p); }
126 };
127
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +0900128protected:
James E. King, III82ae9572017-08-05 12:23:54 -0400129 Fixture() : processor(new test::ParentServiceProcessor(make_shared<Handler>())) {}
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +0900130
Nobuaki Sukegawa8016af82015-01-02 23:14:22 +0900131 ~Fixture() {
132 if (server) {
133 server->stop();
134 }
135 if (thread) {
136 thread->join();
137 }
138 }
139
140 void setEventBase(event_base* user_event_base) {
141 userEventBase_.reset(user_event_base, EventDeleter());
142 }
143
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +0900144 int startServer(int port) {
James E. King, III82ae9572017-08-05 12:23:54 -0400145 shared_ptr<Runner> runner(new Runner);
Nobuaki Sukegawa8cc91752016-05-15 00:24:41 +0900146 runner->port = port;
147 runner->processor = processor;
148 runner->userEventBase = userEventBase_;
149
James E. King, III82ae9572017-08-05 12:23:54 -0400150 shared_ptr<ThreadFactory> threadFactory(
cyyca8af9b2019-01-11 22:13:12 +0800151 new ThreadFactory(false));
Nobuaki Sukegawa8cc91752016-05-15 00:24:41 +0900152 thread = threadFactory->newThread(runner);
153 thread->start();
Buğra Gedik36d1b0d2016-09-04 17:18:15 +0900154 runner->readyBarrier();
155
Nobuaki Sukegawa8cc91752016-05-15 00:24:41 +0900156 server = runner->server;
157 return runner->port;
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +0900158 }
159
160 bool canCommunicate(int serverPort) {
James E. King, III82ae9572017-08-05 12:23:54 -0400161 shared_ptr<transport::TSocket> socket(new transport::TSocket("localhost", serverPort));
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +0900162 socket->open();
James E. King, III82ae9572017-08-05 12:23:54 -0400163 test::ParentServiceClient client(make_shared<protocol::TBinaryProtocol>(
164 make_shared<transport::TFramedTransport>(socket)));
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +0900165 client.addString("foo");
166 std::vector<std::string> strings;
167 client.getStrings(strings);
168 return strings.size() == 1 && !(strings[0].compare("foo"));
169 }
170
171private:
James E. King, III82ae9572017-08-05 12:23:54 -0400172 shared_ptr<event_base> userEventBase_;
173 shared_ptr<test::ParentServiceProcessor> processor;
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +0900174protected:
James E. King, III82ae9572017-08-05 12:23:54 -0400175 shared_ptr<server::TNonblockingServer> server;
Nobuaki Sukegawa8cc91752016-05-15 00:24:41 +0900176private:
James E. King III8d55c472018-03-12 14:29:55 -0400177 shared_ptr<apache::thrift::concurrency::Thread> thread;
Nobuaki Sukegawa8cc91752016-05-15 00:24:41 +0900178
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +0900179};
180
181BOOST_AUTO_TEST_SUITE(TNonblockingServerTest)
182
183BOOST_FIXTURE_TEST_CASE(get_specified_port, Fixture) {
184 int specified_port = startServer(12345);
185 BOOST_REQUIRE_GE(specified_port, 12345);
186 BOOST_REQUIRE_EQUAL(server->getListenPort(), specified_port);
187 BOOST_CHECK(canCommunicate(specified_port));
188
189 server->stop();
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +0900190}
191
192BOOST_FIXTURE_TEST_CASE(get_assigned_port, Fixture) {
193 int specified_port = startServer(0);
194 BOOST_REQUIRE_EQUAL(specified_port, 0);
195 int assigned_port = server->getListenPort();
196 BOOST_REQUIRE_NE(assigned_port, 0);
197 BOOST_CHECK(canCommunicate(assigned_port));
198
199 server->stop();
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +0900200}
201
Nobuaki Sukegawa8016af82015-01-02 23:14:22 +0900202BOOST_FIXTURE_TEST_CASE(provide_event_base, Fixture) {
203 event_base* eb = event_base_new();
204 setEventBase(eb);
205 startServer(0);
206
207 // assert that the server works
208 BOOST_CHECK(canCommunicate(server->getListenPort()));
209#if LIBEVENT_VERSION_NUMBER > 0x02010400
210 // also assert that the event_base is actually used when it's easy
211 BOOST_CHECK_GT(event_base_get_num_events(eb, EVENT_BASE_COUNT_ADDED), 0);
212#endif
213}
214
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +0900215BOOST_AUTO_TEST_SUITE_END()