blob: 36c64b17394d080cdbe0a81378126454de3f5027 [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>
22#include <boost/smart_ptr.hpp>
23
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
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +090033using namespace apache::thrift;
Buğra Gedik36d1b0d2016-09-04 17:18:15 +090034using apache::thrift::concurrency::Guard;
35using apache::thrift::concurrency::Monitor;
36using apache::thrift::concurrency::Mutex;
37using apache::thrift::server::TServerEventHandler;
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +090038
39struct Handler : public test::ParentServiceIf {
40 void addString(const std::string& s) { strings_.push_back(s); }
41 void getStrings(std::vector<std::string>& _return) { _return = strings_; }
42 std::vector<std::string> strings_;
43
44 // dummy overrides not used in this test
45 int32_t incrementGeneration() { return 0; }
46 int32_t getGeneration() { return 0; }
ben-craigfae08e72015-07-15 11:34:47 -050047 void getDataWait(std::string&, const int32_t) {}
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +090048 void onewayWait() {}
49 void exceptionWait(const std::string&) {}
50 void unexpectedExceptionWait(const std::string&) {}
51};
52
53class Fixture {
54private:
Buğra Gedik36d1b0d2016-09-04 17:18:15 +090055 struct ListenEventHandler : public TServerEventHandler {
56 public:
57 ListenEventHandler(Mutex* mutex) : listenMonitor_(mutex), ready_(false) {}
58
59 void preServe() /* override */ {
60 Guard g(listenMonitor_.mutex());
61 ready_ = true;
62 listenMonitor_.notify();
63 }
64
65 Monitor listenMonitor_;
66 bool ready_;
67 };
68
Ben Craig7207c222015-07-06 08:40:35 -050069 struct Runner : public apache::thrift::concurrency::Runnable {
Nobuaki Sukegawa8cc91752016-05-15 00:24:41 +090070 int port;
71 boost::shared_ptr<event_base> userEventBase;
72 boost::shared_ptr<TProcessor> processor;
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +090073 boost::shared_ptr<server::TNonblockingServer> server;
Buğra Gedik36d1b0d2016-09-04 17:18:15 +090074 boost::shared_ptr<ListenEventHandler> listenHandler;
Divya Thaluru808d1432017-08-06 16:36:36 -070075 boost::shared_ptr<transport::TNonblockingServerSocket> socket;
Buğra Gedik36d1b0d2016-09-04 17:18:15 +090076 Mutex mutex_;
77
78 Runner() {
79 listenHandler.reset(new ListenEventHandler(&mutex_));
80 }
Nobuaki Sukegawa8cc91752016-05-15 00:24:41 +090081
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +090082 virtual void run() {
Nobuaki Sukegawa8cc91752016-05-15 00:24:41 +090083 // When binding to explicit port, allow retrying to workaround bind failures on ports in use
84 int retryCount = port ? 10 : 0;
85 startServer(retryCount);
86 }
87
Buğra Gedik36d1b0d2016-09-04 17:18:15 +090088 void readyBarrier() {
89 // block until server is listening and ready to accept connections
90 Guard g(mutex_);
91 while (!listenHandler->ready_) {
92 listenHandler->listenMonitor_.wait();
93 }
94 }
Nobuaki Sukegawa8cc91752016-05-15 00:24:41 +090095 private:
96 void startServer(int retry_count) {
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +090097 try {
Divya Thaluru808d1432017-08-06 16:36:36 -070098 socket.reset(new transport::TNonblockingServerSocket(port));
99 server.reset(new server::TNonblockingServer(processor, socket));
Buğra Gedik36d1b0d2016-09-04 17:18:15 +0900100 server->setServerEventHandler(listenHandler);
Nobuaki Sukegawa8cc91752016-05-15 00:24:41 +0900101 if (userEventBase) {
102 server->registerEvents(userEventBase.get());
103 }
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +0900104 server->serve();
Nobuaki Sukegawa8cc91752016-05-15 00:24:41 +0900105 } catch (const transport::TTransportException&) {
106 if (retry_count > 0) {
107 ++port;
108 startServer(retry_count - 1);
109 } else {
110 throw;
111 }
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +0900112 }
113 }
114 };
115
Nobuaki Sukegawa8016af82015-01-02 23:14:22 +0900116 struct EventDeleter {
117 void operator()(event_base* p) { event_base_free(p); }
118 };
119
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +0900120protected:
121 Fixture() : processor(new test::ParentServiceProcessor(boost::make_shared<Handler>())) {}
122
Nobuaki Sukegawa8016af82015-01-02 23:14:22 +0900123 ~Fixture() {
124 if (server) {
125 server->stop();
126 }
127 if (thread) {
128 thread->join();
129 }
130 }
131
132 void setEventBase(event_base* user_event_base) {
133 userEventBase_.reset(user_event_base, EventDeleter());
134 }
135
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +0900136 int startServer(int port) {
Nobuaki Sukegawa8cc91752016-05-15 00:24:41 +0900137 boost::shared_ptr<Runner> runner(new Runner);
138 runner->port = port;
139 runner->processor = processor;
140 runner->userEventBase = userEventBase_;
141
Ben Craig7207c222015-07-06 08:40:35 -0500142 boost::scoped_ptr<apache::thrift::concurrency::ThreadFactory> threadFactory(
Nobuaki Sukegawa8cc91752016-05-15 00:24:41 +0900143 new apache::thrift::concurrency::PlatformThreadFactory(
Nobuaki Sukegawa28256642014-12-16 03:24:37 +0900144#if !USE_BOOST_THREAD && !USE_STD_THREAD
Nobuaki Sukegawa8cc91752016-05-15 00:24:41 +0900145 concurrency::PlatformThreadFactory::OTHER, concurrency::PlatformThreadFactory::NORMAL,
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +0900146 1,
147#endif
Nobuaki Sukegawa8cc91752016-05-15 00:24:41 +0900148 false));
149 thread = threadFactory->newThread(runner);
150 thread->start();
Buğra Gedik36d1b0d2016-09-04 17:18:15 +0900151 runner->readyBarrier();
152
Nobuaki Sukegawa8cc91752016-05-15 00:24:41 +0900153 server = runner->server;
154 return runner->port;
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +0900155 }
156
157 bool canCommunicate(int serverPort) {
158 boost::shared_ptr<transport::TSocket> socket(new transport::TSocket("localhost", serverPort));
159 socket->open();
160 test::ParentServiceClient client(boost::make_shared<protocol::TBinaryProtocol>(
161 boost::make_shared<transport::TFramedTransport>(socket)));
162 client.addString("foo");
163 std::vector<std::string> strings;
164 client.getStrings(strings);
165 return strings.size() == 1 && !(strings[0].compare("foo"));
166 }
167
168private:
Nobuaki Sukegawa8016af82015-01-02 23:14:22 +0900169 boost::shared_ptr<event_base> userEventBase_;
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +0900170 boost::shared_ptr<test::ParentServiceProcessor> processor;
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +0900171protected:
172 boost::shared_ptr<server::TNonblockingServer> server;
Nobuaki Sukegawa8cc91752016-05-15 00:24:41 +0900173private:
174 boost::shared_ptr<apache::thrift::concurrency::Thread> thread;
175
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +0900176};
177
178BOOST_AUTO_TEST_SUITE(TNonblockingServerTest)
179
180BOOST_FIXTURE_TEST_CASE(get_specified_port, Fixture) {
181 int specified_port = startServer(12345);
182 BOOST_REQUIRE_GE(specified_port, 12345);
183 BOOST_REQUIRE_EQUAL(server->getListenPort(), specified_port);
184 BOOST_CHECK(canCommunicate(specified_port));
185
186 server->stop();
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +0900187}
188
189BOOST_FIXTURE_TEST_CASE(get_assigned_port, Fixture) {
190 int specified_port = startServer(0);
191 BOOST_REQUIRE_EQUAL(specified_port, 0);
192 int assigned_port = server->getListenPort();
193 BOOST_REQUIRE_NE(assigned_port, 0);
194 BOOST_CHECK(canCommunicate(assigned_port));
195
196 server->stop();
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +0900197}
198
Nobuaki Sukegawa8016af82015-01-02 23:14:22 +0900199BOOST_FIXTURE_TEST_CASE(provide_event_base, Fixture) {
200 event_base* eb = event_base_new();
201 setEventBase(eb);
202 startServer(0);
203
204 // assert that the server works
205 BOOST_CHECK(canCommunicate(server->getListenPort()));
206#if LIBEVENT_VERSION_NUMBER > 0x02010400
207 // also assert that the event_base is actually used when it's easy
208 BOOST_CHECK_GT(event_base_get_num_events(eb, EVENT_BASE_COUNT_ADDED), 0);
209#endif
210}
211
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +0900212BOOST_AUTO_TEST_SUITE_END()