5 Copyright (c) 2014-2018, Regents of the University of California,
6 Arizona Board of Regents,
7 Colorado State University,
8 University Pierre & Marie Curie, Sorbonne University,
9 Washington University in St. Louis,
10 Beijing Institute of Technology,
11 The University of Memphis.
13 This file is part of NFD (Named Data Networking Forwarding Daemon).
14 See AUTHORS.md for complete list of NFD authors and contributors.
16 NFD is free software: you can redistribute it and/or modify it under the terms
17 of the GNU General Public License as published by the Free Software Foundation,
18 either version 3 of the License, or (at your option) any later version.
20 NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
21 without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
22 PURPOSE. See the GNU General Public License for more details.
24 You should have received a copy of the GNU General Public License along with
25 NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
28 from http.server
import HTTPServer, SimpleHTTPRequestHandler
29 from socketserver
import ThreadingMixIn
30 from urllib.parse
import urlsplit
31 import argparse, ipaddress, os, socket, subprocess
35 """ The handler class to handle requests """
37 path = urlsplit(self.path).path
40 elif path ==
"/robots.txt" and self.server.allowRobots:
45 def __serveReport(self):
46 """ Obtain XML-formatted NFD status report and send it back as response body """
49 output = subprocess.check_output([
"nfdc",
"status",
"report",
"xml"], universal_newlines=
True)
50 except OSError
as err:
51 super().
log_message(
"error invoking nfdc: {}".format(err))
53 except subprocess.CalledProcessError
as err:
54 super().
log_message(
"error invoking nfdc: command exited with status {}".format(err.returncode))
55 self.send_error(504,
"Cannot connect to NFD (code {})".format(err.returncode))
59 pos = output.index(
">") + 1
61 +
'<?xml-stylesheet type="text/xsl" href="nfd-status.xsl"?>'\
63 self.send_response(200)
64 self.send_header(
"Content-Type",
"text/xml; charset=UTF-8")
66 self.wfile.write(xml.encode())
70 if self.server.verbose:
75 """ Handle requests using threads """
76 def __init__(self, bindAddr, port, handler, allowRobots=False, verbose=False):
79 if bindAddr.version == 6:
83 super().
__init__((str(bindAddr), port), handler)
88 """ Validate IP address """
90 value = ipaddress.ip_address(arg)
92 raise argparse.ArgumentTypeError(
"{!r} is not a valid IP address".format(arg))
96 """ Validate port number """
101 if value < 0
or value > 65535:
102 raise argparse.ArgumentTypeError(
"{!r} is not a valid port number".format(arg))
105 parser = argparse.ArgumentParser(description=
"Serves NFD status page via HTTP")
106 parser.add_argument(
"-V",
"--version", action=
"version", version=
"@VERSION@")
107 parser.add_argument(
"-a",
"--address", default=
"127.0.0.1", type=ipAddress, metavar=
"ADDR",
108 help=
"bind to this IP address (default: %(default)s)")
109 parser.add_argument(
"-p",
"--port", default=8080, type=portNumber,
110 help=
"bind to this port number (default: %(default)s)")
111 parser.add_argument(
"-f",
"--workdir", default=
"@DATAROOTDIR@/ndn", metavar=
"DIR",
112 help=
"server's working directory (default: %(default)s)")
113 parser.add_argument(
"-r",
"--robots", action=
"store_true",
114 help=
"allow crawlers and other HTTP bots")
115 parser.add_argument(
"-v",
"--verbose", action=
"store_true",
116 help=
"turn on verbose logging")
117 args = parser.parse_args()
119 os.chdir(args.workdir)
122 allowRobots=args.robots, verbose=args.verbose)
124 if httpd.address_family == socket.AF_INET6:
125 url =
"http://[{}]:{}"
128 print(
"Server started at", url.format(*httpd.server_address))
131 httpd.serve_forever()
132 except KeyboardInterrupt:
137 if __name__ ==
"__main__":
def log_message(self, *args)
def __init__(self, bindAddr, port, handler, allowRobots=False, verbose=False)