Imported Upstream version 1.0.0
[platform/upstream/nghttp2.git] / src / nghttpd.cc
1 /*
2  * nghttp2 - HTTP/2 C Library
3  *
4  * Copyright (c) 2012 Tatsuhiro Tsujikawa
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sublicense, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be
15  * included in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  */
25 #include "nghttp2_config.h"
26
27 #ifdef HAVE_UNISTD_H
28 #include <unistd.h>
29 #endif // HAVE_UNISTD_H
30 #include <signal.h>
31 #include <getopt.h>
32
33 #include <cstdlib>
34 #include <cstring>
35 #include <cassert>
36 #include <string>
37 #include <iostream>
38 #include <string>
39
40 #include <openssl/ssl.h>
41 #include <openssl/err.h>
42 #include <openssl/conf.h>
43 #include <nghttp2/nghttp2.h>
44
45 #include "app_helper.h"
46 #include "HttpServer.h"
47 #include "util.h"
48 #include "ssl.h"
49
50 namespace nghttp2 {
51
52 namespace {
53 int parse_push_config(Config &config, const char *optarg) {
54   const char *eq = strchr(optarg, '=');
55   if (eq == NULL) {
56     return -1;
57   }
58   auto &paths = config.push[std::string(optarg, eq)];
59   auto optarg_end = optarg + strlen(optarg);
60   auto i = eq + 1;
61   for (;;) {
62     const char *j = strchr(i, ',');
63     if (j == NULL) {
64       j = optarg_end;
65     }
66     paths.emplace_back(i, j);
67     if (j == optarg_end) {
68       break;
69     }
70     i = j;
71     ++i;
72   }
73
74   return 0;
75 }
76 } // namespace
77
78 namespace {
79 void print_version(std::ostream &out) {
80   out << "nghttpd nghttp2/" NGHTTP2_VERSION << std::endl;
81 }
82 } // namespace
83
84 namespace {
85 void print_usage(std::ostream &out) {
86   out << "Usage: nghttpd [OPTION]... <PORT> [<PRIVATE_KEY> <CERT>]\n"
87       << "HTTP/2 experimental server" << std::endl;
88 }
89 } // namespace
90
91 namespace {
92 void print_help(std::ostream &out) {
93   Config config;
94   print_usage(out);
95   out << R"(
96   <PORT>      Specify listening port number.
97   <PRIVATE_KEY>
98               Set  path  to  server's private  key.   Required  unless
99               --no-tls is specified.
100   <CERT>      Set  path  to  server's  certificate.   Required  unless
101               --no-tls is specified.
102 Options:
103   -a, --address=<ADDR>
104               The address to bind to.  If not specified the default IP
105               address determined by getaddrinfo is used.
106   -D, --daemon
107               Run in a background.  If -D is used, the current working
108               directory is  changed to '/'.  Therefore  if this option
109               is used, -d option must be specified.
110   -V, --verify-client
111               The server  sends a client certificate  request.  If the
112               client did  not return  a certificate, the  handshake is
113               terminated.   Currently,  this  option just  requests  a
114               client certificate and does not verify it.
115   -d, --htdocs=<PATH>
116               Specify document root.  If this option is not specified,
117               the document root is the current working directory.
118   -v, --verbose
119               Print debug information  such as reception/ transmission
120               of frames and name/value pairs.
121   --no-tls    Disable SSL/TLS.
122   -c, --header-table-size=<SIZE>
123               Specify decoder header table size.
124   --color     Force colored log output.
125   -p, --push=<PATH>=<PUSH_PATH,...>
126               Push  resources <PUSH_PATH>s  when <PATH>  is requested.
127               This option  can be used repeatedly  to specify multiple
128               push  configurations.    <PATH>  and   <PUSH_PATH>s  are
129               relative  to   document  root.   See   --htdocs  option.
130               Example: -p/=/foo.png -p/doc=/bar.css
131   -b, --padding=<N>
132               Add at  most <N>  bytes to a  frame payload  as padding.
133               Specify 0 to disable padding.
134   -m, --max-concurrent-streams=<N>
135               Set the maximum number of  the concurrent streams in one
136               HTTP/2 session.
137               Default: )" << config.max_concurrent_streams << R"(
138   -n, --workers=<N>
139               Set the number of worker threads.
140               Default: 1
141   -e, --error-gzip
142               Make error response gzipped.
143   --dh-param-file=<PATH>
144               Path to file that contains  DH parameters in PEM format.
145               Without  this   option,  DHE   cipher  suites   are  not
146               available.
147   --early-response
148               Start sending response when request HEADERS is received,
149               rather than complete request is received.
150   --trailer=<HEADER>
151               Add a trailer  header to a response.   <HEADER> must not
152               include pseudo header field  (header field name starting
153               with ':').  The  trailer is sent only if  a response has
154               body part.  Example: --trailer 'foo: bar'.
155   --hexdump   Display the  incoming traffic in  hexadecimal (Canonical
156               hex+ASCII display).  If SSL/TLS  is used, decrypted data
157               are used.
158   --echo-upload
159               Send back uploaded content if method is POST or PUT.
160   --version   Display version information and exit.
161   -h, --help  Display this help and exit.
162
163 --
164
165   The <SIZE> argument is an integer and an optional unit (e.g., 10K is
166   10 * 1024).  Units are K, M and G (powers of 1024).)" << std::endl;
167 }
168 } // namespace
169
170 int main(int argc, char **argv) {
171   Config config;
172   bool color = false;
173   while (1) {
174     static int flag = 0;
175     static option long_options[] = {
176         {"address", required_argument, nullptr, 'a'},
177         {"daemon", no_argument, nullptr, 'D'},
178         {"htdocs", required_argument, nullptr, 'd'},
179         {"help", no_argument, nullptr, 'h'},
180         {"verbose", no_argument, nullptr, 'v'},
181         {"verify-client", no_argument, nullptr, 'V'},
182         {"header-table-size", required_argument, nullptr, 'c'},
183         {"push", required_argument, nullptr, 'p'},
184         {"padding", required_argument, nullptr, 'b'},
185         {"max-concurrent-streams", required_argument, nullptr, 'm'},
186         {"workers", required_argument, nullptr, 'n'},
187         {"error-gzip", no_argument, nullptr, 'e'},
188         {"no-tls", no_argument, &flag, 1},
189         {"color", no_argument, &flag, 2},
190         {"version", no_argument, &flag, 3},
191         {"dh-param-file", required_argument, &flag, 4},
192         {"early-response", no_argument, &flag, 5},
193         {"trailer", required_argument, &flag, 6},
194         {"hexdump", no_argument, &flag, 7},
195         {"echo-upload", no_argument, &flag, 8},
196         {nullptr, 0, nullptr, 0}};
197     int option_index = 0;
198     int c = getopt_long(argc, argv, "DVb:c:d:ehm:n:p:va:", long_options,
199                         &option_index);
200     char *end;
201     if (c == -1) {
202       break;
203     }
204     switch (c) {
205     case 'a':
206       config.address = optarg;
207       break;
208     case 'D':
209       config.daemon = true;
210       break;
211     case 'V':
212       config.verify_client = true;
213       break;
214     case 'b':
215       config.padding = strtol(optarg, nullptr, 10);
216       break;
217     case 'd':
218       config.htdocs = optarg;
219       break;
220     case 'e':
221       config.error_gzip = true;
222       break;
223     case 'm': {
224       // max-concurrent-streams option
225       auto n = util::parse_uint(optarg);
226       if (n == -1) {
227         std::cerr << "-m: invalid argument: " << optarg << std::endl;
228         exit(EXIT_FAILURE);
229       }
230       config.max_concurrent_streams = n;
231       break;
232     }
233     case 'n':
234 #ifdef NOTHREADS
235       std::cerr << "-n: WARNING: Threading disabled at build time, "
236                 << "no threads created." << std::endl;
237 #else
238       errno = 0;
239       config.num_worker = strtoul(optarg, &end, 10);
240       if (errno == ERANGE || *end != '\0' || config.num_worker == 0) {
241         std::cerr << "-n: Bad option value: " << optarg << std::endl;
242         exit(EXIT_FAILURE);
243       }
244 #endif // NOTHREADS
245       break;
246     case 'h':
247       print_help(std::cout);
248       exit(EXIT_SUCCESS);
249     case 'v':
250       config.verbose = true;
251       break;
252     case 'c':
253       errno = 0;
254       config.header_table_size = util::parse_uint_with_unit(optarg);
255       if (config.header_table_size == -1) {
256         std::cerr << "-c: Bad option value: " << optarg << std::endl;
257         exit(EXIT_FAILURE);
258       }
259       break;
260     case 'p':
261       if (parse_push_config(config, optarg) != 0) {
262         std::cerr << "-p: Bad option value: " << optarg << std::endl;
263       }
264       break;
265     case '?':
266       util::show_candidates(argv[optind - 1], long_options);
267       exit(EXIT_FAILURE);
268     case 0:
269       switch (flag) {
270       case 1:
271         // no-tls option
272         config.no_tls = true;
273         break;
274       case 2:
275         // color option
276         color = true;
277         break;
278       case 3:
279         // version
280         print_version(std::cout);
281         exit(EXIT_SUCCESS);
282       case 4:
283         // dh-param-file
284         config.dh_param_file = optarg;
285         break;
286       case 5:
287         // early-response
288         config.early_response = true;
289         break;
290       case 6: {
291         // trailer option
292         auto header = optarg;
293         auto value = strchr(optarg, ':');
294         if (!value) {
295           std::cerr << "--trailer: invalid header: " << optarg << std::endl;
296           exit(EXIT_FAILURE);
297         }
298         *value = 0;
299         value++;
300         while (isspace(*value)) {
301           value++;
302         }
303         if (*value == 0) {
304           // This could also be a valid case for suppressing a header
305           // similar to curl
306           std::cerr << "--trailer: invalid header - value missing: " << optarg
307                     << std::endl;
308           exit(EXIT_FAILURE);
309         }
310         config.trailer.emplace_back(header, value, false);
311         util::inp_strlower(config.trailer.back().name);
312         break;
313       }
314       case 7:
315         // hexdump option
316         config.hexdump = true;
317         break;
318       case 8:
319         // echo-upload option
320         config.echo_upload = true;
321         break;
322       }
323       break;
324     default:
325       break;
326     }
327   }
328   if (argc - optind < (config.no_tls ? 1 : 3)) {
329     print_usage(std::cerr);
330     std::cerr << "Too few arguments" << std::endl;
331     exit(EXIT_FAILURE);
332   }
333
334   config.port = strtol(argv[optind++], nullptr, 10);
335
336   if (!config.no_tls) {
337     config.private_key_file = argv[optind++];
338     config.cert_file = argv[optind++];
339   }
340
341   if (config.daemon) {
342     if (config.htdocs.empty()) {
343       print_usage(std::cerr);
344       std::cerr << "-d option must be specified when -D is used." << std::endl;
345       exit(EXIT_FAILURE);
346     }
347     if (daemon(0, 0) == -1) {
348       perror("daemon");
349       exit(EXIT_FAILURE);
350     }
351   }
352   if (config.htdocs.empty()) {
353     config.htdocs = "./";
354   }
355
356   set_color_output(color || isatty(fileno(stdout)));
357
358   struct sigaction act;
359   memset(&act, 0, sizeof(struct sigaction));
360   act.sa_handler = SIG_IGN;
361   sigaction(SIGPIPE, &act, nullptr);
362   OPENSSL_config(nullptr);
363   OpenSSL_add_all_algorithms();
364   SSL_load_error_strings();
365   SSL_library_init();
366 #ifndef NOTHREADS
367   ssl::LibsslGlobalLock lock;
368 #endif // NOTHREADS
369
370   reset_timer();
371
372   HttpServer server(&config);
373   if (server.run() != 0) {
374     exit(EXIT_FAILURE);
375   }
376   return 0;
377 }
378
379 } // namespace nghttp2
380
381 int main(int argc, char **argv) { return nghttp2::main(argc, argv); }