summaryrefslogtreecommitdiffstats
path: root/src/nghttpd.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/nghttpd.cc')
-rw-r--r--src/nghttpd.cc508
1 files changed, 508 insertions, 0 deletions
diff --git a/src/nghttpd.cc b/src/nghttpd.cc
new file mode 100644
index 0000000..3c5f5b4
--- /dev/null
+++ b/src/nghttpd.cc
@@ -0,0 +1,508 @@
+/*
+ * nghttp2 - HTTP/2 C Library
+ *
+ * Copyright (c) 2012 Tatsuhiro Tsujikawa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp2_config.h"
+
+#ifdef __sgi
+# define daemon _daemonize
+#endif
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif // HAVE_UNISTD_H
+#include <signal.h>
+#include <getopt.h>
+
+#include <cstdlib>
+#include <cstring>
+#include <cassert>
+#include <string>
+#include <iostream>
+#include <string>
+
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <nghttp2/nghttp2.h>
+
+#include "app_helper.h"
+#include "HttpServer.h"
+#include "util.h"
+#include "tls.h"
+
+namespace nghttp2 {
+
+namespace {
+int parse_push_config(Config &config, const char *optarg) {
+ const char *eq = strchr(optarg, '=');
+ if (eq == nullptr) {
+ return -1;
+ }
+ auto &paths = config.push[std::string(optarg, eq)];
+ auto optarg_end = optarg + strlen(optarg);
+ auto i = eq + 1;
+ for (;;) {
+ const char *j = strchr(i, ',');
+ if (j == nullptr) {
+ j = optarg_end;
+ }
+ paths.emplace_back(i, j);
+ if (j == optarg_end) {
+ break;
+ }
+ i = j;
+ ++i;
+ }
+
+ return 0;
+}
+} // namespace
+
+namespace {
+void print_version(std::ostream &out) {
+ out << "nghttpd nghttp2/" NGHTTP2_VERSION << std::endl;
+}
+} // namespace
+
+namespace {
+void print_usage(std::ostream &out) {
+ out << "Usage: nghttpd [OPTION]... <PORT> [<PRIVATE_KEY> <CERT>]\n"
+ << "HTTP/2 server" << std::endl;
+}
+} // namespace
+
+namespace {
+void print_help(std::ostream &out) {
+ Config config;
+ print_usage(out);
+ out << R"(
+ <PORT> Specify listening port number.
+ <PRIVATE_KEY>
+ Set path to server's private key. Required unless
+ --no-tls is specified.
+ <CERT> Set path to server's certificate. Required unless
+ --no-tls is specified.
+Options:
+ -a, --address=<ADDR>
+ The address to bind to. If not specified the default IP
+ address determined by getaddrinfo is used.
+ -D, --daemon
+ Run in a background. If -D is used, the current working
+ directory is changed to '/'. Therefore if this option
+ is used, -d option must be specified.
+ -V, --verify-client
+ The server sends a client certificate request. If the
+ client did not return a certificate, the handshake is
+ terminated. Currently, this option just requests a
+ client certificate and does not verify it.
+ -d, --htdocs=<PATH>
+ Specify document root. If this option is not specified,
+ the document root is the current working directory.
+ -v, --verbose
+ Print debug information such as reception/ transmission
+ of frames and name/value pairs.
+ --no-tls Disable SSL/TLS.
+ -c, --header-table-size=<SIZE>
+ Specify decoder header table size.
+ --encoder-header-table-size=<SIZE>
+ Specify encoder header table size. The decoder (client)
+ specifies the maximum dynamic table size it accepts.
+ Then the negotiated dynamic table size is the minimum of
+ this option value and the value which client specified.
+ --color Force colored log output.
+ -p, --push=<PATH>=<PUSH_PATH,...>
+ Push resources <PUSH_PATH>s when <PATH> is requested.
+ This option can be used repeatedly to specify multiple
+ push configurations. <PATH> and <PUSH_PATH>s are
+ relative to document root. See --htdocs option.
+ Example: -p/=/foo.png -p/doc=/bar.css
+ -b, --padding=<N>
+ Add at most <N> bytes to a frame payload as padding.
+ Specify 0 to disable padding.
+ -m, --max-concurrent-streams=<N>
+ Set the maximum number of the concurrent streams in one
+ HTTP/2 session.
+ Default: )"
+ << config.max_concurrent_streams << R"(
+ -n, --workers=<N>
+ Set the number of worker threads.
+ Default: 1
+ -e, --error-gzip
+ Make error response gzipped.
+ -w, --window-bits=<N>
+ Sets the stream level initial window size to 2**<N>-1.
+ -W, --connection-window-bits=<N>
+ Sets the connection level initial window size to
+ 2**<N>-1.
+ --dh-param-file=<PATH>
+ Path to file that contains DH parameters in PEM format.
+ Without this option, DHE cipher suites are not
+ available.
+ --early-response
+ Start sending response when request HEADERS is received,
+ rather than complete request is received.
+ --trailer=<HEADER>
+ Add a trailer header to a response. <HEADER> must not
+ include pseudo header field (header field name starting
+ with ':'). The trailer is sent only if a response has
+ body part. Example: --trailer 'foo: bar'.
+ --hexdump Display the incoming traffic in hexadecimal (Canonical
+ hex+ASCII display). If SSL/TLS is used, decrypted data
+ are used.
+ --echo-upload
+ Send back uploaded content if method is POST or PUT.
+ --mime-types-file=<PATH>
+ Path to file that contains MIME media types and the
+ extensions that represent them.
+ Default: )"
+ << config.mime_types_file << R"(
+ --no-content-length
+ Don't send content-length header field.
+ --ktls Enable ktls.
+ --no-rfc7540-pri
+ Disable RFC7540 priorities.
+ --version Display version information and exit.
+ -h, --help Display this help and exit.
+
+--
+
+ The <SIZE> argument is an integer and an optional unit (e.g., 10K is
+ 10 * 1024). Units are K, M and G (powers of 1024).)"
+ << std::endl;
+}
+} // namespace
+
+int main(int argc, char **argv) {
+ tls::libssl_init();
+
+#ifndef NOTHREADS
+ tls::LibsslGlobalLock lock;
+#endif // NOTHREADS
+
+ Config config;
+ bool color = false;
+ auto mime_types_file_set_manually = false;
+
+ while (1) {
+ static int flag = 0;
+ constexpr static option long_options[] = {
+ {"address", required_argument, nullptr, 'a'},
+ {"daemon", no_argument, nullptr, 'D'},
+ {"htdocs", required_argument, nullptr, 'd'},
+ {"help", no_argument, nullptr, 'h'},
+ {"verbose", no_argument, nullptr, 'v'},
+ {"verify-client", no_argument, nullptr, 'V'},
+ {"header-table-size", required_argument, nullptr, 'c'},
+ {"push", required_argument, nullptr, 'p'},
+ {"padding", required_argument, nullptr, 'b'},
+ {"max-concurrent-streams", required_argument, nullptr, 'm'},
+ {"workers", required_argument, nullptr, 'n'},
+ {"error-gzip", no_argument, nullptr, 'e'},
+ {"window-bits", required_argument, nullptr, 'w'},
+ {"connection-window-bits", required_argument, nullptr, 'W'},
+ {"no-tls", no_argument, &flag, 1},
+ {"color", no_argument, &flag, 2},
+ {"version", no_argument, &flag, 3},
+ {"dh-param-file", required_argument, &flag, 4},
+ {"early-response", no_argument, &flag, 5},
+ {"trailer", required_argument, &flag, 6},
+ {"hexdump", no_argument, &flag, 7},
+ {"echo-upload", no_argument, &flag, 8},
+ {"mime-types-file", required_argument, &flag, 9},
+ {"no-content-length", no_argument, &flag, 10},
+ {"encoder-header-table-size", required_argument, &flag, 11},
+ {"ktls", no_argument, &flag, 12},
+ {"no-rfc7540-pri", no_argument, &flag, 13},
+ {nullptr, 0, nullptr, 0}};
+ int option_index = 0;
+ int c = getopt_long(argc, argv, "DVb:c:d:ehm:n:p:va:w:W:", long_options,
+ &option_index);
+ if (c == -1) {
+ break;
+ }
+ switch (c) {
+ case 'a':
+ config.address = optarg;
+ break;
+ case 'D':
+ config.daemon = true;
+ break;
+ case 'V':
+ config.verify_client = true;
+ break;
+ case 'b': {
+ auto n = util::parse_uint(optarg);
+ if (n == -1) {
+ std::cerr << "-b: Bad option value: " << optarg << std::endl;
+ exit(EXIT_FAILURE);
+ }
+ config.padding = n;
+ break;
+ }
+ case 'd':
+ config.htdocs = optarg;
+ break;
+ case 'e':
+ config.error_gzip = true;
+ break;
+ case 'm': {
+ // max-concurrent-streams option
+ auto n = util::parse_uint(optarg);
+ if (n == -1) {
+ std::cerr << "-m: invalid argument: " << optarg << std::endl;
+ exit(EXIT_FAILURE);
+ }
+ config.max_concurrent_streams = n;
+ break;
+ }
+ case 'n': {
+#ifdef NOTHREADS
+ std::cerr << "-n: WARNING: Threading disabled at build time, "
+ << "no threads created." << std::endl;
+#else
+ auto n = util::parse_uint(optarg);
+ if (n == -1) {
+ std::cerr << "-n: Bad option value: " << optarg << std::endl;
+ exit(EXIT_FAILURE);
+ }
+ config.num_worker = n;
+#endif // NOTHREADS
+ break;
+ }
+ case 'h':
+ print_help(std::cout);
+ exit(EXIT_SUCCESS);
+ case 'v':
+ config.verbose = true;
+ break;
+ case 'c': {
+ auto n = util::parse_uint_with_unit(optarg);
+ if (n == -1) {
+ std::cerr << "-c: Bad option value: " << optarg << std::endl;
+ exit(EXIT_FAILURE);
+ }
+ if (n > std::numeric_limits<uint32_t>::max()) {
+ std::cerr << "-c: Value too large. It should be less than or equal to "
+ << std::numeric_limits<uint32_t>::max() << std::endl;
+ exit(EXIT_FAILURE);
+ }
+ config.header_table_size = n;
+ break;
+ }
+ case 'p':
+ if (parse_push_config(config, optarg) != 0) {
+ std::cerr << "-p: Bad option value: " << optarg << std::endl;
+ }
+ break;
+ case 'w':
+ case 'W': {
+ auto n = util::parse_uint(optarg);
+ if (n == -1 || n > 30) {
+ std::cerr << "-" << static_cast<char>(c)
+ << ": specify the integer in the range [0, 30], inclusive"
+ << std::endl;
+ exit(EXIT_FAILURE);
+ }
+
+ if (c == 'w') {
+ config.window_bits = n;
+ } else {
+ config.connection_window_bits = n;
+ }
+
+ break;
+ }
+ case '?':
+ util::show_candidates(argv[optind - 1], long_options);
+ exit(EXIT_FAILURE);
+ case 0:
+ switch (flag) {
+ case 1:
+ // no-tls option
+ config.no_tls = true;
+ break;
+ case 2:
+ // color option
+ color = true;
+ break;
+ case 3:
+ // version
+ print_version(std::cout);
+ exit(EXIT_SUCCESS);
+ case 4:
+ // dh-param-file
+ config.dh_param_file = optarg;
+ break;
+ case 5:
+ // early-response
+ config.early_response = true;
+ break;
+ case 6: {
+ // trailer option
+ auto header = optarg;
+ auto value = strchr(optarg, ':');
+ if (!value) {
+ std::cerr << "--trailer: invalid header: " << optarg << std::endl;
+ exit(EXIT_FAILURE);
+ }
+ *value = 0;
+ value++;
+ while (isspace(*value)) {
+ value++;
+ }
+ if (*value == 0) {
+ // This could also be a valid case for suppressing a header
+ // similar to curl
+ std::cerr << "--trailer: invalid header - value missing: " << optarg
+ << std::endl;
+ exit(EXIT_FAILURE);
+ }
+ config.trailer.emplace_back(header, value, false);
+ util::inp_strlower(config.trailer.back().name);
+ break;
+ }
+ case 7:
+ // hexdump option
+ config.hexdump = true;
+ break;
+ case 8:
+ // echo-upload option
+ config.echo_upload = true;
+ break;
+ case 9:
+ // mime-types-file option
+ mime_types_file_set_manually = true;
+ config.mime_types_file = optarg;
+ break;
+ case 10:
+ // no-content-length option
+ config.no_content_length = true;
+ break;
+ case 11: {
+ // encoder-header-table-size option
+ auto n = util::parse_uint_with_unit(optarg);
+ if (n == -1) {
+ std::cerr << "--encoder-header-table-size: Bad option value: "
+ << optarg << std::endl;
+ exit(EXIT_FAILURE);
+ }
+ if (n > std::numeric_limits<uint32_t>::max()) {
+ std::cerr << "--encoder-header-table-size: Value too large. It "
+ "should be less than or equal to "
+ << std::numeric_limits<uint32_t>::max() << std::endl;
+ exit(EXIT_FAILURE);
+ }
+ config.encoder_header_table_size = n;
+ break;
+ }
+ case 12:
+ // tls option
+ config.ktls = true;
+ break;
+ case 13:
+ // no-rfc7540-pri option
+ config.no_rfc7540_pri = true;
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ if (argc - optind < (config.no_tls ? 1 : 3)) {
+ print_usage(std::cerr);
+ std::cerr << "Too few arguments" << std::endl;
+ exit(EXIT_FAILURE);
+ }
+
+ {
+ auto portStr = argv[optind++];
+ auto n = util::parse_uint(portStr);
+ if (n == -1 || n > std::numeric_limits<uint16_t>::max()) {
+ std::cerr << "<PORT>: Bad value: " << portStr << std::endl;
+ exit(EXIT_FAILURE);
+ }
+ config.port = n;
+ }
+
+ if (!config.no_tls) {
+ config.private_key_file = argv[optind++];
+ config.cert_file = argv[optind++];
+ }
+
+ if (config.daemon) {
+ if (config.htdocs.empty()) {
+ print_usage(std::cerr);
+ std::cerr << "-d option must be specified when -D is used." << std::endl;
+ exit(EXIT_FAILURE);
+ }
+#ifdef __sgi
+ if (daemon(0, 0, 0, 0) == -1) {
+#else
+ if (util::daemonize(0, 0) == -1) {
+#endif
+ perror("daemon");
+ exit(EXIT_FAILURE);
+ }
+ }
+ if (config.htdocs.empty()) {
+ config.htdocs = "./";
+ }
+
+ if (util::read_mime_types(config.mime_types,
+ config.mime_types_file.c_str()) != 0) {
+ if (mime_types_file_set_manually) {
+ std::cerr << "--mime-types-file: Could not open mime types file: "
+ << config.mime_types_file << std::endl;
+ }
+ }
+
+ auto &trailer_names = config.trailer_names;
+ for (auto &h : config.trailer) {
+ trailer_names += h.name;
+ trailer_names += ", ";
+ }
+ if (trailer_names.size() >= 2) {
+ trailer_names.resize(trailer_names.size() - 2);
+ }
+
+ set_color_output(color || isatty(fileno(stdout)));
+
+ struct sigaction act {};
+ act.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &act, nullptr);
+
+ reset_timer();
+
+ HttpServer server(&config);
+ if (server.run() != 0) {
+ exit(EXIT_FAILURE);
+ }
+ return 0;
+}
+
+} // namespace nghttp2
+
+int main(int argc, char **argv) {
+ return nghttp2::run_app(nghttp2::main, argc, argv);
+}