TensorFlow Serving C++ API Documentation
evhttp_request_test.cc
1 /* Copyright 2018 Google Inc. All Rights Reserved.
2 
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6 
7  http://www.apache.org/licenses/LICENSE-2.0
8 
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15 
16 #include <cstdint>
17 #include <functional>
18 #include <memory>
19 #include <random>
20 #include <string>
21 #include <utility>
22 
23 #include <gtest/gtest.h>
24 #include "absl/memory/memory.h"
25 #include "tensorflow_serving/util/net_http/client/test_client/internal/evhttp_connection.h"
26 #include "tensorflow_serving/util/net_http/compression/gzip_zlib.h"
27 #include "tensorflow_serving/util/net_http/internal/fixed_thread_pool.h"
28 #include "tensorflow_serving/util/net_http/public/response_code_enum.h"
29 #include "tensorflow_serving/util/net_http/server/internal/evhttp_server.h"
30 #include "tensorflow_serving/util/net_http/server/public/httpserver.h"
31 #include "tensorflow_serving/util/net_http/server/public/httpserver_interface.h"
32 #include "tensorflow_serving/util/net_http/server/public/server_request_interface.h"
33 
34 namespace tensorflow {
35 namespace serving {
36 namespace net_http {
37 namespace {
38 
39 class MyExecutor final : public EventExecutor {
40  public:
41  explicit MyExecutor(int num_threads) : thread_pool_(num_threads) {}
42 
43  void Schedule(std::function<void()> fn) override {
44  thread_pool_.Schedule(fn);
45  }
46 
47  private:
48  FixedThreadPool thread_pool_;
49 };
50 
51 class EvHTTPRequestTest : public ::testing::Test {
52  public:
53  void SetUp() override { InitServer(); }
54 
55  void TearDown() override {
56  if (!server->is_terminating()) {
57  server->Terminate();
58  server->WaitForTermination();
59  }
60  }
61 
62  protected:
63  std::unique_ptr<HTTPServerInterface> server;
64 
65  private:
66  void InitServer() {
67  auto options = absl::make_unique<ServerOptions>();
68  options->AddPort(0);
69  options->SetExecutor(absl::make_unique<MyExecutor>(4));
70 
71  server = CreateEvHTTPServer(std::move(options));
72 
73  ASSERT_TRUE(server != nullptr);
74  }
75 };
76 
77 // Test basic GET with 404
78 TEST_F(EvHTTPRequestTest, SimpleGETNotFound) {
79  server->StartAcceptingRequests();
80 
81  auto connection =
82  TestEvHTTPConnection::Connect("localhost", server->listen_port());
83  ASSERT_TRUE(connection != nullptr);
84 
85  TestClientRequest request = {"/noop", "GET", {}, ""};
86  TestClientResponse response = {};
87 
88  EXPECT_TRUE(connection->BlockingSendRequest(request, &response));
89  EXPECT_EQ(response.status, HTTPStatusCode::NOT_FOUND);
90  EXPECT_FALSE(response.body.empty());
91 
92  server->Terminate();
93  server->WaitForTermination();
94 }
95 
96 // Test basic GET with 200
97 TEST_F(EvHTTPRequestTest, SimpleGETOK) {
98  auto handler = [](ServerRequestInterface* request) {
99  request->WriteResponseString("OK");
100  request->Reply();
101  };
102  server->RegisterRequestHandler("/ok", std::move(handler),
103  RequestHandlerOptions());
104  server->StartAcceptingRequests();
105 
106  auto connection =
107  TestEvHTTPConnection::Connect("localhost", server->listen_port());
108  ASSERT_TRUE(connection != nullptr);
109 
110  TestClientRequest request = {"/ok", "GET", {}, ""};
111  TestClientResponse response = {};
112 
113  EXPECT_TRUE(connection->BlockingSendRequest(request, &response));
114  EXPECT_EQ(response.status, HTTPStatusCode::OK);
115  EXPECT_EQ(response.body, "OK");
116 
117  server->Terminate();
118  server->WaitForTermination();
119 }
120 
121 // Test basic POST with 200
122 TEST_F(EvHTTPRequestTest, SimplePOST) {
123  auto handler = [](ServerRequestInterface* request) {
124  int64_t num_bytes;
125  auto request_chunk = request->ReadRequestBytes(&num_bytes);
126  while (request_chunk != nullptr) {
127  request->WriteResponseBytes(request_chunk.get(), num_bytes);
128  request_chunk = request->ReadRequestBytes(&num_bytes);
129  }
130  request->Reply();
131  };
132  server->RegisterRequestHandler("/ok", std::move(handler),
133  RequestHandlerOptions());
134  server->StartAcceptingRequests();
135 
136  auto connection =
137  TestEvHTTPConnection::Connect("localhost", server->listen_port());
138  ASSERT_TRUE(connection != nullptr);
139 
140  TestClientRequest request = {"/ok", "POST", {}, "abcde"};
141  TestClientResponse response = {};
142 
143  EXPECT_TRUE(connection->BlockingSendRequest(request, &response));
144  EXPECT_EQ(response.status, HTTPStatusCode::OK);
145  EXPECT_EQ(response.body, "abcde");
146 
147  server->Terminate();
148  server->WaitForTermination();
149 }
150 
151 // Test request's uri_path() method.
152 TEST_F(EvHTTPRequestTest, RequestUri) {
153  static const char* const kUriPath[] = {
154  "/",
155  "/path",
156  "/path/",
157  "/path?query=value",
158  "/path#fragment",
159  "/path?param=value#fragment",
160  "/path?param=value%20value",
161  };
162 
163  int counter = 0;
164  auto handler = [&counter](ServerRequestInterface* request) {
165  EXPECT_EQ(kUriPath[counter++], request->uri_path());
166  request->Reply();
167  };
168  server->RegisterRequestDispatcher(
169  [&handler](ServerRequestInterface* request) -> RequestHandler {
170  return handler;
171  },
172  RequestHandlerOptions());
173 
174  server->StartAcceptingRequests();
175 
176  auto connection =
177  TestEvHTTPConnection::Connect("localhost", server->listen_port());
178  ASSERT_TRUE(connection != nullptr);
179 
180  for (const char* path : kUriPath) {
181  TestClientRequest request = {path, "GET", {}, ""};
182  TestClientResponse response = {};
183 
184  EXPECT_TRUE(connection->BlockingSendRequest(request, &response));
185  }
186 
187  server->Terminate();
188  server->WaitForTermination();
189 }
190 
191 // Test request headers
192 TEST_F(EvHTTPRequestTest, RequestHeaders) {
193  auto handler = [](ServerRequestInterface* request) {
194  EXPECT_GT(request->request_headers().size(), 2);
195  EXPECT_EQ(request->GetRequestHeader("H1"), "v1");
196  EXPECT_EQ(request->GetRequestHeader("h1"), "v1");
197  EXPECT_EQ(request->GetRequestHeader("H2"), "v2");
198  request->WriteResponseString("OK");
199  request->Reply();
200  };
201  server->RegisterRequestHandler("/ok", std::move(handler),
202  RequestHandlerOptions());
203  server->StartAcceptingRequests();
204 
205  auto connection =
206  TestEvHTTPConnection::Connect("localhost", server->listen_port());
207  ASSERT_TRUE(connection != nullptr);
208 
209  TestClientRequest request = {"/ok",
210  "GET",
211  {TestClientRequest::HeaderKeyValue("H1", "v1"),
212  TestClientRequest::HeaderKeyValue("H2", "v2")},
213  ""};
214  TestClientResponse response = {};
215 
216  EXPECT_TRUE(connection->BlockingSendRequest(request, &response));
217  EXPECT_EQ(response.status, HTTPStatusCode::OK);
218  EXPECT_EQ(response.body, "OK");
219 
220  server->Terminate();
221  server->WaitForTermination();
222 }
223 
224 // Test response headers
225 TEST_F(EvHTTPRequestTest, ResponseHeaders) {
226  auto handler = [](ServerRequestInterface* request) {
227  request->AppendResponseHeader("H1", "V1");
228  request->AppendResponseHeader("H2", "V2");
229  request->OverwriteResponseHeader("h2", "v2");
230  request->WriteResponseString("OK");
231  request->Reply();
232  };
233  server->RegisterRequestHandler("/ok", std::move(handler),
234  RequestHandlerOptions());
235  server->StartAcceptingRequests();
236 
237  auto connection =
238  TestEvHTTPConnection::Connect("localhost", server->listen_port());
239  ASSERT_TRUE(connection != nullptr);
240 
241  TestClientRequest request = {"/ok", "GET", {}, ""};
242  TestClientResponse response = {};
243 
244  EXPECT_TRUE(connection->BlockingSendRequest(request, &response));
245  for (auto keyvalue : response.headers) {
246  if (keyvalue.first == "H1") {
247  EXPECT_EQ(keyvalue.second, "V1");
248  } else if (keyvalue.first == "H2") {
249  FAIL() << "H2 should have been overwritten by h2";
250  } else if (keyvalue.first == "h2") {
251  EXPECT_EQ(keyvalue.second, "v2");
252  }
253  }
254  EXPECT_EQ(response.status, HTTPStatusCode::OK);
255  EXPECT_EQ(response.body, "OK");
256 
257  server->Terminate();
258  server->WaitForTermination();
259 }
260 
261 // === gzip support ====
262 
263 // Test invalid gzip body
264 TEST_F(EvHTTPRequestTest, InvalidGzipPost) {
265  auto handler = [](ServerRequestInterface* request) {
266  int64_t num_bytes;
267  auto request_body = request->ReadRequestBytes(&num_bytes);
268  EXPECT_TRUE(request_body == nullptr);
269  EXPECT_EQ(0, num_bytes);
270 
271  request->Reply();
272  };
273  server->RegisterRequestHandler("/ok", std::move(handler),
274  RequestHandlerOptions());
275  server->StartAcceptingRequests();
276 
277  auto connection =
278  TestEvHTTPConnection::Connect("localhost", server->listen_port());
279  ASSERT_TRUE(connection != nullptr);
280 
281  TestClientRequest request = {"/ok", "POST", {}, "abcde"};
282  request.headers.emplace_back("Content-Encoding", "my_gzip");
283  TestClientResponse response = {};
284 
285  EXPECT_TRUE(connection->BlockingSendRequest(request, &response));
286  EXPECT_EQ(response.status, HTTPStatusCode::OK);
287 
288  server->Terminate();
289  server->WaitForTermination();
290 }
291 
292 // Test disabled gzip
293 TEST_F(EvHTTPRequestTest, DisableGzipPost) {
294  auto handler = [](ServerRequestInterface* request) {
295  int64_t num_bytes;
296  auto request_body = request->ReadRequestBytes(&num_bytes);
297  EXPECT_EQ(5, num_bytes);
298 
299  request->Reply();
300  };
301  RequestHandlerOptions options;
302  options.set_auto_uncompress_input(false);
303  server->RegisterRequestHandler("/ok", std::move(handler), options);
304  server->StartAcceptingRequests();
305 
306  auto connection =
307  TestEvHTTPConnection::Connect("localhost", server->listen_port());
308  ASSERT_TRUE(connection != nullptr);
309 
310  TestClientRequest request = {"/ok", "POST", {}, "abcde"};
311  request.headers.emplace_back("Content-Encoding", "my_gzip");
312  TestClientResponse response = {};
313 
314  EXPECT_TRUE(connection->BlockingSendRequest(request, &response));
315  EXPECT_EQ(response.status, HTTPStatusCode::OK);
316 
317  server->Terminate();
318  server->WaitForTermination();
319 }
320 
321 std::string CompressLargeString(const char* data, size_t size,
322  size_t buf_size) {
323  ZLib zlib;
324  std::string buf(buf_size, '\0');
325  size_t compressed_size = buf.size();
326  zlib.Compress((Bytef*)buf.data(), &compressed_size, (Bytef*)data, size);
327 
328  return std::string(buf.data(), compressed_size);
329 }
330 
331 std::string CompressString(const char* data, size_t size) {
332  return CompressLargeString(data, size, 1024);
333 }
334 
335 // Test valid gzip body
336 TEST_F(EvHTTPRequestTest, ValidGzipPost) {
337  constexpr char kBody[] = "abcdefg12345";
338  std::string compressed = CompressString(kBody, sizeof(kBody) - 1);
339 
340  auto handler = [&](ServerRequestInterface* request) {
341  int64_t num_bytes;
342  auto request_body = request->ReadRequestBytes(&num_bytes);
343 
344  std::string body_str(request_body.get(), static_cast<size_t>(num_bytes));
345  EXPECT_EQ(body_str, std::string(kBody));
346  EXPECT_EQ(sizeof(kBody) - 1, num_bytes);
347 
348  EXPECT_EQ(nullptr, request->ReadRequestBytes(&num_bytes));
349  EXPECT_EQ(0, num_bytes);
350 
351  request->Reply();
352  };
353  server->RegisterRequestHandler("/ok", std::move(handler),
354  RequestHandlerOptions());
355  server->StartAcceptingRequests();
356 
357  auto connection =
358  TestEvHTTPConnection::Connect("localhost", server->listen_port());
359  ASSERT_TRUE(connection != nullptr);
360 
361  TestClientRequest request = {"/ok", "POST", {}, compressed};
362  request.headers.emplace_back("Content-Encoding", "my_gzip");
363  TestClientResponse response = {};
364 
365  EXPECT_TRUE(connection->BlockingSendRequest(request, &response));
366  EXPECT_EQ(response.status, HTTPStatusCode::OK);
367 
368  server->Terminate();
369  server->WaitForTermination();
370 }
371 
372 // Test gzip exceeding the max uncompressed limit
373 TEST_F(EvHTTPRequestTest, GzipExceedingLimit) {
374  constexpr char kBody[] = "abcdefg12345";
375  constexpr int bodySize = sizeof(kBody) - 1;
376  std::string compressed = CompressString(kBody, static_cast<size_t>(bodySize));
377 
378  auto handler = [&](ServerRequestInterface* request) {
379  int64_t num_bytes;
380  auto request_body = request->ReadRequestBytes(&num_bytes);
381 
382  std::string body_str(request_body.get(), static_cast<size_t>(num_bytes));
383  EXPECT_TRUE(request_body == nullptr);
384  EXPECT_EQ(0, num_bytes);
385 
386  request->Reply();
387  };
388 
389  RequestHandlerOptions options;
390  options.set_auto_uncompress_max_size(bodySize - 1); // not enough buffer
391  server->RegisterRequestHandler("/ok", std::move(handler), options);
392  server->StartAcceptingRequests();
393 
394  auto connection =
395  TestEvHTTPConnection::Connect("localhost", server->listen_port());
396  ASSERT_TRUE(connection != nullptr);
397 
398  TestClientRequest request = {"/ok", "POST", {}, compressed};
399  request.headers.emplace_back("Content-Encoding", "my_gzip");
400  TestClientResponse response = {};
401 
402  EXPECT_TRUE(connection->BlockingSendRequest(request, &response));
403  EXPECT_EQ(response.status, HTTPStatusCode::OK);
404 
405  server->Terminate();
406  server->WaitForTermination();
407 }
408 
409 std::string MakeRandomString(int64_t len) {
410  std::random_device rd;
411  std::mt19937_64 gen(rd());
412  std::uniform_int_distribution<> dis('a', 'z');
413  std::string s(len, '0');
414  for (char& c : s) {
415  c = dis(gen);
416  }
417  return s;
418 }
419 
420 // Test large gzip body
421 TEST_F(EvHTTPRequestTest, LargeGzipPost) {
422  constexpr int64_t uncompress_len = 1024 * 1024;
423  std::string uncompressed = MakeRandomString(uncompress_len);
424  std::string compressed = CompressLargeString(
425  uncompressed.data(), uncompressed.size(), 2 * uncompress_len);
426 
427  auto handler = [&](ServerRequestInterface* request) {
428  int64_t num_bytes;
429  auto request_body = request->ReadRequestBytes(&num_bytes);
430 
431  std::string body_str(request_body.get(), static_cast<size_t>(num_bytes));
432  EXPECT_EQ(body_str, uncompressed);
433  EXPECT_EQ(uncompressed.size(), num_bytes);
434 
435  EXPECT_EQ(nullptr, request->ReadRequestBytes(&num_bytes));
436  EXPECT_EQ(0, num_bytes);
437 
438  request->Reply();
439  };
440  server->RegisterRequestHandler("/ok", std::move(handler),
441  RequestHandlerOptions());
442  server->StartAcceptingRequests();
443 
444  auto connection =
445  TestEvHTTPConnection::Connect("localhost", server->listen_port());
446  ASSERT_TRUE(connection != nullptr);
447 
448  TestClientRequest request = {"/ok", "POST", {}, compressed};
449  request.headers.emplace_back("Content-Encoding", "my_gzip");
450  TestClientResponse response = {};
451 
452  EXPECT_TRUE(connection->BlockingSendRequest(request, &response));
453  EXPECT_EQ(response.status, HTTPStatusCode::OK);
454 
455  server->Terminate();
456  server->WaitForTermination();
457 }
458 
459 } // namespace
460 } // namespace net_http
461 } // namespace serving
462 } // namespace tensorflow