TensorFlow Serving C++ API Documentation
ev_print_req_server.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 <dirent.h>
17 #include <sys/socket.h>
18 #include <sys/types.h>
19 
20 #include <cstdio>
21 #include <cstdlib>
22 #include <cstring>
23 
24 #include "absl/strings/numbers.h"
25 
26 #include "libevent/include/event2/buffer.h"
27 #include "libevent/include/event2/event.h"
28 #include "libevent/include/event2/http.h"
29 #include "libevent/include/event2/keyvalq_struct.h"
30 #include "libevent/include/event2/util.h"
31 
32 namespace {
33 
34 char uri_root[512];
35 
36 void request_cb(struct evhttp_request *req, void *arg) {
37  const char *method;
38 
39  switch (evhttp_request_get_command(req)) {
40  case EVHTTP_REQ_GET:
41  method = "GET";
42  break;
43  case EVHTTP_REQ_POST:
44  method = "POST";
45  break;
46  case EVHTTP_REQ_HEAD:
47  method = "HEAD";
48  break;
49  case EVHTTP_REQ_PUT:
50  method = "PUT";
51  break;
52  case EVHTTP_REQ_DELETE:
53  method = "DELETE";
54  break;
55  case EVHTTP_REQ_OPTIONS:
56  method = "OPTIONS";
57  break;
58  case EVHTTP_REQ_TRACE:
59  method = "TRACE";
60  break;
61  case EVHTTP_REQ_CONNECT:
62  method = "CONNECT";
63  break;
64  case EVHTTP_REQ_PATCH:
65  method = "PATCH";
66  break;
67  default:
68  method = "unknown";
69  break;
70  }
71 
72  struct evbuffer *response_body = evbuffer_new();
73  if (response_body == nullptr) {
74  evhttp_send_error(req, HTTP_SERVUNAVAIL, nullptr);
75  return;
76  }
77 
78  evbuffer_add_printf(response_body,
79  "<!DOCTYPE html>\n"
80  "<html>\n <head>\n"
81  " <meta charset='utf-8'>\n"
82  " <title>Mini libevent httpserver</title>\n"
83  " </head>\n"
84  " <body>\n"
85  " <h1>Print the HTTP request detail</h1>\n"
86  " <ul>\n");
87 
88  evbuffer_add_printf(response_body, "HTTP Method: %s <br>\n", method);
89 
90  const char *uri = evhttp_request_get_uri(req); // no malloc
91  evbuffer_add_printf(response_body, "Request Uri: %s <br>\n", uri);
92 
93  struct evhttp_uri *decoded_url = evhttp_uri_parse(uri);
94  if (decoded_url == nullptr) {
95  evhttp_send_error(req, HTTP_BADREQUEST, nullptr);
96  return;
97  }
98 
99  evbuffer_add_printf(response_body, "Decoded Uri:<br>\n");
100 
101  evbuffer_add_printf(response_body, "&nbsp;&nbsp;scheme : %s <br>\n",
102  evhttp_uri_get_scheme(decoded_url));
103  evbuffer_add_printf(response_body, "&nbsp;&nbsp;host : %s <br>\n",
104  evhttp_uri_get_host(decoded_url));
105  evbuffer_add_printf(response_body, "&nbsp;&nbsp;port : %d <br>\n",
106  evhttp_uri_get_port(decoded_url));
107  evbuffer_add_printf(response_body, "&nbsp;&nbsp;path : %s <br>\n",
108  evhttp_uri_get_path(decoded_url));
109  evbuffer_add_printf(response_body, "&nbsp;&nbsp;query : %s <br>\n",
110  evhttp_uri_get_query(decoded_url));
111 
112  const char *path = evhttp_uri_get_path(decoded_url);
113  if (path == nullptr) {
114  path = "/";
115  }
116 
117  evbuffer_add_printf(response_body, "Uri path: %s <br>\n", path);
118 
119  char *decoded_path = evhttp_uridecode(path, 1, nullptr);
120  if (decoded_path == nullptr) {
121  evhttp_send_error(req, HTTP_BADREQUEST, nullptr);
122  return;
123  }
124 
125  evbuffer_add_printf(response_body, "Decoded path: %s <br>\n", decoded_path);
126 
127  evbuffer_add_printf(response_body, "<br><br>====<br><br>\n");
128 
129  struct evkeyvalq *headers = evhttp_request_get_input_headers(req);
130 
131  struct evkeyval *header;
132  for (header = headers->tqh_first; header; header = header->next.tqe_next) {
133  evbuffer_add_printf(response_body, "%s : %s<br>\n", header->key,
134  header->value);
135  }
136 
137  struct evbuffer *request_body = evhttp_request_get_input_buffer(req);
138  if (request_body != nullptr) {
139  evbuffer_add_printf(response_body, "<br><br>====<br><br>\n");
140  int result = evbuffer_add_buffer_reference(response_body, request_body);
141  if (result < 0) {
142  evbuffer_add_printf(response_body, ">>> Failed to print the body<br>\n");
143  }
144  }
145 
146  evhttp_add_header(evhttp_request_get_output_headers(req), "Content-Type",
147  "text/html");
148 
149  evhttp_send_reply(req, 200, "OK", response_body);
150 
151  evhttp_uri_free(decoded_url);
152  free(decoded_path);
153  evbuffer_free(response_body);
154 }
155 
156 void help() { fprintf(stdout, "Usage: ev_print_req_server <port:8080>\n"); }
157 
158 } // namespace
159 
160 int main(int argc, char **argv) {
161  fprintf(stdout, "Start the http server ...\n");
162 
163  struct event_base *base;
164  struct evhttp *http;
165  struct evhttp_bound_socket *handle;
166 
167  ev_uint32_t port = 8080;
168 
169  if (argc < 2) {
170  help();
171  return 1;
172  }
173 
174  bool port_parsed = absl::SimpleAtoi(argv[1], &port);
175  if (!port_parsed) {
176  fprintf(stderr, "Invalid port: %s\n", argv[1]);
177  }
178 
179  base = event_base_new();
180  if (!base) {
181  fprintf(stderr, "Couldn't create an event_base: exiting\n");
182  return 1;
183  }
184 
185  http = evhttp_new(base);
186  if (!http) {
187  fprintf(stderr, "couldn't create evhttp. Exiting.\n");
188  return 1;
189  }
190 
191  // catch all
192  evhttp_set_gencb(http, request_cb, NULL);
193 
194  // nullptr will bind to ipv4, which will fail to accept
195  // requests from clients where getaddressinfo() defaults to AF_INET6
196  handle = evhttp_bind_socket_with_handle(http, "::0", (ev_uint16_t)port);
197  if (!handle) {
198  fprintf(stderr, "couldn't bind to port %d. Exiting.\n", (int)port);
199  return 1;
200  }
201 
202  {
203  /* Extract and display the address we're listening on. */
204  struct sockaddr_storage ss = {};
205  evutil_socket_t fd;
206  ev_socklen_t socklen = sizeof(ss);
207  char addrbuf[128];
208  void *inaddr;
209  const char *addr;
210  int got_port = -1;
211  fd = evhttp_bound_socket_get_fd(handle);
212  memset(&ss, 0, sizeof(ss));
213  if (getsockname(fd, (struct sockaddr *)&ss, &socklen)) {
214  perror("getsockname() failed");
215  return 1;
216  }
217  if (ss.ss_family == AF_INET) {
218  got_port = ntohs(((struct sockaddr_in *)&ss)->sin_port);
219  inaddr = &((struct sockaddr_in *)&ss)->sin_addr;
220  } else if (ss.ss_family == AF_INET6) {
221  got_port = ntohs(((struct sockaddr_in6 *)&ss)->sin6_port);
222  inaddr = &((struct sockaddr_in6 *)&ss)->sin6_addr;
223  } else {
224  fprintf(stderr, "Weird address family %d\n", ss.ss_family);
225  return 1;
226  }
227  addr = evutil_inet_ntop(ss.ss_family, inaddr, addrbuf, sizeof(addrbuf));
228  if (addr) {
229  printf("Listening on %s:%d\n", addr, got_port);
230  evutil_snprintf(uri_root, sizeof(uri_root), "http://%s:%d", addr,
231  got_port);
232  } else {
233  fprintf(stderr, "evutil_inet_ntop failed\n");
234  return 1;
235  }
236  }
237 
238  event_base_dispatch(base);
239 
240  return 0;
241 }