93da3e2db919e49811dea70dc10ef1f5362221f9
[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   std::unique_ptr<IOBuf> decode(Context* ctx,
33                                 IOBufQueue& buf,
34                                 size_t&) override {
35     return buf.move();
36   }
37 };
38
39 class EchoService : public Service<std::string, std::string> {
40  public:
41   Future<std::string> operator()(std::string req) override { return req; }
42 };
43
44 class EchoIntService : public Service<std::string, int> {
45  public:
46   Future<int> operator()(std::string req) override {
47     return folly::to<int>(req);
48   }
49 };
50
51 template <typename Req, typename Resp>
52 class ServerPipelineFactory
53     : public PipelineFactory<ServicePipeline> {
54  public:
55
56   std::unique_ptr<ServicePipeline, folly::DelayedDestruction::Destructor>
57   newPipeline(std::shared_ptr<AsyncSocket> socket) override {
58     std::unique_ptr<ServicePipeline, folly::DelayedDestruction::Destructor> pipeline(
59       new ServicePipeline());
60     pipeline->addBack(AsyncSocketHandler(socket));
61     pipeline->addBack(SimpleDecode());
62     pipeline->addBack(StringCodec());
63     pipeline->addBack(SerialServerDispatcher<Req, Resp>(&service_));
64     pipeline->finalize();
65     return pipeline;
66   }
67
68  private:
69   EchoService service_;
70 };
71
72 template <typename Req, typename Resp>
73 class ClientPipelineFactory : public PipelineFactory<ServicePipeline> {
74  public:
75
76   std::unique_ptr<ServicePipeline, folly::DelayedDestruction::Destructor>
77   newPipeline(std::shared_ptr<AsyncSocket> socket) override {
78     std::unique_ptr<ServicePipeline, folly::DelayedDestruction::Destructor> pipeline(
79       new ServicePipeline());
80     pipeline->addBack(AsyncSocketHandler(socket));
81     pipeline->addBack(SimpleDecode());
82     pipeline->addBack(StringCodec());
83     pipeline->finalize();
84     return pipeline;
85    }
86 };
87
88 template <typename Pipeline, typename Req, typename Resp>
89 class ClientServiceFactory : public ServiceFactory<Pipeline, Req, Resp> {
90  public:
91   class ClientService : public Service<Req, Resp> {
92    public:
93     explicit ClientService(Pipeline* pipeline) {
94       dispatcher_.setPipeline(pipeline);
95     }
96     Future<Resp> operator()(Req request) override {
97       return dispatcher_(std::move(request));
98     }
99    private:
100     SerialClientDispatcher<Pipeline, Req, Resp> dispatcher_;
101   };
102
103   Future<std::shared_ptr<Service<Req, Resp>>> operator() (
104     std::shared_ptr<ClientBootstrap<Pipeline>> client) override {
105     return Future<std::shared_ptr<Service<Req, Resp>>>(
106       std::make_shared<ClientService>(client->getPipeline()));
107   }
108 };
109
110 TEST(Wangle, ClientServerTest) {
111   int port = 1234;
112   // server
113
114   ServerBootstrap<ServicePipeline> server;
115   server.childPipeline(
116     std::make_shared<ServerPipelineFactory<std::string, std::string>>());
117   server.bind(port);
118
119   // client
120   auto client = std::make_shared<ClientBootstrap<ServicePipeline>>();
121   ClientServiceFactory<ServicePipeline, std::string, std::string> serviceFactory;
122   client->pipelineFactory(
123     std::make_shared<ClientPipelineFactory<std::string, std::string>>());
124   SocketAddress addr("127.0.0.1", port);
125   client->connect(addr);
126   auto service = serviceFactory(client).value();
127   auto rep = (*service)("test");
128
129   rep.then([&](std::string value) {
130     EXPECT_EQ("test", value);
131     EventBaseManager::get()->getEventBase()->terminateLoopSoon();
132
133   });
134   EventBaseManager::get()->getEventBase()->loopForever();
135   server.stop();
136   client.reset();
137 }
138
139 class AppendFilter : public ServiceFilter<std::string, std::string> {
140  public:
141   explicit AppendFilter(
142     std::shared_ptr<Service<std::string, std::string>> service) :
143       ServiceFilter<std::string, std::string>(service) {}
144
145   Future<std::string> operator()(std::string req) override {
146     return (*service_)(req + "\n");
147   }
148 };
149
150 class IntToStringFilter
151     : public ServiceFilter<int, int, std::string, std::string> {
152  public:
153   explicit IntToStringFilter(
154     std::shared_ptr<Service<std::string, std::string>> service) :
155       ServiceFilter<int, int, std::string, std::string>(service) {}
156
157   Future<int> operator()(int req) override {
158     return (*service_)(folly::to<std::string>(req)).then([](std::string resp) {
159       return folly::to<int>(resp);
160     });
161   }
162 };
163
164 TEST(Wangle, FilterTest) {
165   auto service = std::make_shared<EchoService>();
166   auto filter = std::make_shared<AppendFilter>(service);
167   auto result = (*filter)("test");
168   EXPECT_EQ(result.value(), "test\n");
169 }
170
171 TEST(Wangle, ComplexFilterTest) {
172   auto service = std::make_shared<EchoService>();
173   auto filter = std::make_shared<IntToStringFilter>(service);
174   auto result = (*filter)(1);
175   EXPECT_EQ(result.value(), 1);
176 }
177
178 class ChangeTypeFilter
179     : public ServiceFilter<int, std::string, std::string, int> {
180  public:
181   explicit ChangeTypeFilter(
182     std::shared_ptr<Service<std::string, int>> service) :
183       ServiceFilter<int, std::string, std::string, int>(service) {}
184
185   Future<std::string> operator()(int req) override {
186     return (*service_)(folly::to<std::string>(req)).then([](int resp) {
187       return folly::to<std::string>(resp);
188     });
189   }
190 };
191
192 TEST(Wangle, SuperComplexFilterTest) {
193   auto service = std::make_shared<EchoIntService>();
194   auto filter = std::make_shared<ChangeTypeFilter>(service);
195   auto result = (*filter)(1);
196   EXPECT_EQ(result.value(), "1");
197 }
198
199 template <typename Pipeline, typename Req, typename Resp>
200 class ConnectionCountFilter : public ServiceFactoryFilter<Pipeline, Req, Resp> {
201  public:
202   explicit ConnectionCountFilter(
203     std::shared_ptr<ServiceFactory<Pipeline, Req, Resp>> factory)
204       : ServiceFactoryFilter<Pipeline, Req, Resp>(factory) {}
205
206   Future<std::shared_ptr<Service<Req, Resp>>> operator()(
207       std::shared_ptr<ClientBootstrap<Pipeline>> client) override {
208       connectionCount++;
209       return (*this->serviceFactory_)(client);
210     }
211
212   int connectionCount{0};
213 };
214
215 TEST(Wangle, ServiceFactoryFilter) {
216   auto clientFactory =
217     std::make_shared<
218     ClientServiceFactory<ServicePipeline, std::string, std::string>>();
219   auto countingFactory =
220     std::make_shared<
221     ConnectionCountFilter<ServicePipeline, std::string, std::string>>(
222       clientFactory);
223
224   auto client = std::make_shared<ClientBootstrap<ServicePipeline>>();
225
226   client->pipelineFactory(
227     std::make_shared<ClientPipelineFactory<std::string, std::string>>());
228   // It doesn't matter if connect succeds or not, but it needs to be called
229   // to create a pipeline
230   client->connect(folly::SocketAddress("::1", 8090));
231
232   auto service = (*countingFactory)(client).value();
233
234   // After the first service goes away, the client can be reused
235   service = (*countingFactory)(client).value();
236   EXPECT_EQ(2, countingFactory->connectionCount);
237 }
238
239 TEST(Wangle, FactoryToService) {
240   auto constfactory =
241     std::make_shared<ConstFactory<ServicePipeline, std::string, std::string>>(
242     std::make_shared<EchoService>());
243   FactoryToService<ServicePipeline, std::string, std::string> service(
244     constfactory);
245
246   EXPECT_EQ("test", service("test").value());
247 }
248
249 int main(int argc, char** argv) {
250   testing::InitGoogleTest(&argc, argv);
251   google::InitGoogleLogging(argv[0]);
252   gflags::ParseCommandLineFlags(&argc, &argv, true);
253
254   return RUN_ALL_TESTS();
255 }
256
257 } // namespace