4bf37df8d184d34bd3f7f30f2b8efb123cee1b87
[folly.git] / folly / wangle / service / ServiceTest.cpp
1 /*
2  * Copyright 2015 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include <gtest/gtest.h>
17
18 #include <folly/wangle/codec/StringCodec.h>
19 #include <folly/wangle/codec/ByteToMessageCodec.h>
20 #include <folly/wangle/service/ClientDispatcher.h>
21 #include <folly/wangle/service/ServerDispatcher.h>
22 #include <folly/wangle/service/Service.h>
23
24 namespace folly {
25
26 using namespace wangle;
27
28 typedef Pipeline<IOBufQueue&, std::string> ServicePipeline;
29
30 class SimpleDecode : public ByteToMessageCodec {
31  public:
32   virtual std::unique_ptr<IOBuf> decode(
33     Context* ctx, IOBufQueue& buf, size_t&) {
34     return buf.move();
35   }
36 };
37
38 class EchoService : public Service<std::string, std::string> {
39  public:
40   virtual Future<std::string> operator()(std::string req) override {
41     return req;
42   }
43 };
44
45 class EchoIntService : public Service<std::string, int> {
46  public:
47   virtual Future<int> operator()(std::string req) override {
48     return folly::to<int>(req);
49   }
50 };
51
52 template <typename Req, typename Resp>
53 class ServerPipelineFactory
54     : public PipelineFactory<ServicePipeline> {
55  public:
56
57   std::unique_ptr<ServicePipeline, folly::DelayedDestruction::Destructor>
58   newPipeline(std::shared_ptr<AsyncSocket> socket) override {
59     std::unique_ptr<ServicePipeline, folly::DelayedDestruction::Destructor> pipeline(
60       new ServicePipeline());
61     pipeline->addBack(AsyncSocketHandler(socket));
62     pipeline->addBack(SimpleDecode());
63     pipeline->addBack(StringCodec());
64     pipeline->addBack(SerialServerDispatcher<Req, Resp>(&service_));
65     pipeline->finalize();
66     return pipeline;
67   }
68
69  private:
70   EchoService service_;
71 };
72
73 template <typename Req, typename Resp>
74 class ClientPipelineFactory : public PipelineFactory<ServicePipeline> {
75  public:
76
77   std::unique_ptr<ServicePipeline, folly::DelayedDestruction::Destructor>
78   newPipeline(std::shared_ptr<AsyncSocket> socket) override {
79     std::unique_ptr<ServicePipeline, folly::DelayedDestruction::Destructor> pipeline(
80       new ServicePipeline());
81     pipeline->addBack(AsyncSocketHandler(socket));
82     pipeline->addBack(SimpleDecode());
83     pipeline->addBack(StringCodec());
84     pipeline->finalize();
85     return pipeline;
86    }
87 };
88
89 template <typename Pipeline, typename Req, typename Resp>
90 class ClientServiceFactory : public ServiceFactory<Pipeline, Req, Resp> {
91  public:
92   class ClientService : public Service<Req, Resp> {
93    public:
94     explicit ClientService(Pipeline* pipeline) {
95       dispatcher_.setPipeline(pipeline);
96     }
97     Future<Resp> operator()(Req request) override {
98       return dispatcher_(std::move(request));
99     }
100    private:
101     SerialClientDispatcher<Pipeline, Req, Resp> dispatcher_;
102   };
103
104   Future<std::shared_ptr<Service<Req, Resp>>> operator() (
105     std::shared_ptr<ClientBootstrap<Pipeline>> client) override {
106     return Future<std::shared_ptr<Service<Req, Resp>>>(
107       std::make_shared<ClientService>(client->getPipeline()));
108   }
109 };
110
111 TEST(Wangle, ClientServerTest) {
112   int port = 1234;
113   // server
114
115   ServerBootstrap<ServicePipeline> server;
116   server.childPipeline(
117     std::make_shared<ServerPipelineFactory<std::string, std::string>>());
118   server.bind(port);
119
120   // client
121   auto client = std::make_shared<ClientBootstrap<ServicePipeline>>();
122   ClientServiceFactory<ServicePipeline, std::string, std::string> serviceFactory;
123   client->pipelineFactory(
124     std::make_shared<ClientPipelineFactory<std::string, std::string>>());
125   SocketAddress addr("127.0.0.1", port);
126   client->connect(addr);
127   auto service = serviceFactory(client).value();
128   auto rep = (*service)("test");
129
130   rep.then([&](std::string value) {
131     EXPECT_EQ("test", value);
132     EventBaseManager::get()->getEventBase()->terminateLoopSoon();
133
134   });
135   EventBaseManager::get()->getEventBase()->loopForever();
136   server.stop();
137   client.reset();
138 }
139
140 class AppendFilter : public ServiceFilter<std::string, std::string> {
141  public:
142   explicit AppendFilter(
143     std::shared_ptr<Service<std::string, std::string>> service) :
144       ServiceFilter<std::string, std::string>(service) {}
145
146   virtual Future<std::string> operator()(std::string req) {
147     return (*service_)(req + "\n");
148   }
149 };
150
151 class IntToStringFilter
152     : public ServiceFilter<int, int, std::string, std::string> {
153  public:
154   explicit IntToStringFilter(
155     std::shared_ptr<Service<std::string, std::string>> service) :
156       ServiceFilter<int, int, std::string, std::string>(service) {}
157
158   virtual Future<int> operator()(int req) {
159     return (*service_)(folly::to<std::string>(req)).then([](std::string resp) {
160       return folly::to<int>(resp);
161     });
162   }
163 };
164
165 TEST(Wangle, FilterTest) {
166   auto service = std::make_shared<EchoService>();
167   auto filter = std::make_shared<AppendFilter>(service);
168   auto result = (*filter)("test");
169   EXPECT_EQ(result.value(), "test\n");
170 }
171
172 TEST(Wangle, ComplexFilterTest) {
173   auto service = std::make_shared<EchoService>();
174   auto filter = std::make_shared<IntToStringFilter>(service);
175   auto result = (*filter)(1);
176   EXPECT_EQ(result.value(), 1);
177 }
178
179 class ChangeTypeFilter
180     : public ServiceFilter<int, std::string, std::string, int> {
181  public:
182   explicit ChangeTypeFilter(
183     std::shared_ptr<Service<std::string, int>> service) :
184       ServiceFilter<int, std::string, std::string, int>(service) {}
185
186   virtual Future<std::string> operator()(int req) {
187     return (*service_)(folly::to<std::string>(req)).then([](int resp) {
188       return folly::to<std::string>(resp);
189     });
190   }
191 };
192
193 TEST(Wangle, SuperComplexFilterTest) {
194   auto service = std::make_shared<EchoIntService>();
195   auto filter = std::make_shared<ChangeTypeFilter>(service);
196   auto result = (*filter)(1);
197   EXPECT_EQ(result.value(), "1");
198 }
199
200 template <typename Pipeline, typename Req, typename Resp>
201 class ConnectionCountFilter : public ServiceFactoryFilter<Pipeline, Req, Resp> {
202  public:
203   explicit ConnectionCountFilter(
204     std::shared_ptr<ServiceFactory<Pipeline, Req, Resp>> factory)
205       : ServiceFactoryFilter<Pipeline, Req, Resp>(factory) {}
206
207     virtual Future<std::shared_ptr<Service<Req, Resp>>> operator()(
208       std::shared_ptr<ClientBootstrap<Pipeline>> client) {
209       connectionCount++;
210       return (*this->serviceFactory_)(client);
211     }
212
213   int connectionCount{0};
214 };
215
216 TEST(Wangle, ServiceFactoryFilter) {
217   auto clientFactory =
218     std::make_shared<
219     ClientServiceFactory<ServicePipeline, std::string, std::string>>();
220   auto countingFactory =
221     std::make_shared<
222     ConnectionCountFilter<ServicePipeline, std::string, std::string>>(
223       clientFactory);
224
225   auto client = std::make_shared<ClientBootstrap<ServicePipeline>>();
226
227   client->pipelineFactory(
228     std::make_shared<ClientPipelineFactory<std::string, std::string>>());
229   // It doesn't matter if connect succeds or not, but it needs to be called
230   // to create a pipeline
231   client->connect(folly::SocketAddress("::1", 8090));
232
233   auto service = (*countingFactory)(client).value();
234
235   // After the first service goes away, the client can be reused
236   service = (*countingFactory)(client).value();
237   EXPECT_EQ(2, countingFactory->connectionCount);
238 }
239
240 TEST(Wangle, FactoryToService) {
241   auto constfactory =
242     std::make_shared<ConstFactory<ServicePipeline, std::string, std::string>>(
243     std::make_shared<EchoService>());
244   FactoryToService<ServicePipeline, std::string, std::string> service(
245     constfactory);
246
247   EXPECT_EQ("test", service("test").value());
248 }
249
250 int main(int argc, char** argv) {
251   testing::InitGoogleTest(&argc, argv);
252   google::InitGoogleLogging(argv[0]);
253   gflags::ParseCommandLineFlags(&argc, &argv, true);
254
255   return RUN_ALL_TESTS();
256 }
257
258 } // namespace