blob: 6a3c0ee7e0de28886acf63447752a42436504719 [file] [log] [blame]
Chengyu Fanb07788a2014-03-31 12:15:36 -06001#!/usr/bin/env python2.7
Alexander Afanasyev9bcbc7c2014-04-06 19:37:37 -07002# -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
3
4"""
Junxiao Shie2733332016-11-24 14:11:40 +00005Copyright (c) 2014-2016, 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.
Alexander Afanasyev9bcbc7c2014-04-06 19:37:37 -070012
13This file is part of NFD (Named Data Networking Forwarding Daemon).
14See AUTHORS.md for complete list of NFD authors and contributors.
15
16NFD is free software: you can redistribute it and/or modify it under the terms
17of the GNU General Public License as published by the Free Software Foundation,
18either version 3 of the License, or (at your option) any later version.
19
20NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
21without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
22PURPOSE. See the GNU General Public License for more details.
23
24You should have received a copy of the GNU General Public License along with
25NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
26"""
Chengyu Fanb07788a2014-03-31 12:15:36 -060027
Chengyu Fan45d1a762014-07-08 14:21:32 -060028from BaseHTTPServer import HTTPServer
29from SimpleHTTPServer import SimpleHTTPRequestHandler
Chengyu Fanb07788a2014-03-31 12:15:36 -060030from SocketServer import ThreadingMixIn
31import sys
Chengyu Fan30aa2072014-07-20 13:52:32 -060032import subprocess
Chengyu Fanb07788a2014-03-31 12:15:36 -060033import urlparse
34import logging
Chengyu Fanb07788a2014-03-31 12:15:36 -060035import argparse
Chengyu Fane25b0f02014-04-05 21:42:40 -060036import socket
Chengyu Fan45d1a762014-07-08 14:21:32 -060037import os
Chengyu Fanb07788a2014-03-31 12:15:36 -060038
39
Chengyu Fan45d1a762014-07-08 14:21:32 -060040class StatusHandler(SimpleHTTPRequestHandler):
Chengyu Fanb07788a2014-03-31 12:15:36 -060041 """ The handler class to handle requests."""
42 def do_GET(self):
43 # get the url info to decide how to respond
44 parsedPath = urlparse.urlparse(self.path)
Chengyu Fan45d1a762014-07-08 14:21:32 -060045 if parsedPath.path == "/":
46 # get current nfd status, and use it as result message
47 (res, resultMessage) = self.getNfdStatus()
48 self.send_response(200)
49 if res == 0:
50 self.send_header("Content-type", "text/xml; charset=UTF-8")
51 else:
52 self.send_header("Content-type", "text/html; charset=UTF-8")
53
54 self.end_headers()
55 self.wfile.write(resultMessage)
56 elif parsedPath.path == "/robots.txt" and self.server.robots == True:
57 resultMessage = ""
Chengyu Fanb07788a2014-03-31 12:15:36 -060058 self.send_response(200)
59 self.send_header("Content-type", "text/plain")
Chengyu Fan45d1a762014-07-08 14:21:32 -060060 self.end_headers()
61 self.wfile.write(resultMessage)
Chengyu Fanb07788a2014-03-31 12:15:36 -060062 else:
Chengyu Fan45d1a762014-07-08 14:21:32 -060063 SimpleHTTPRequestHandler.do_GET(self)
Chengyu Fanb07788a2014-03-31 12:15:36 -060064
Chengyu Fanb07788a2014-03-31 12:15:36 -060065 def log_message(self, format, *args):
66 if self.server.verbose:
Chengyu Fane25b0f02014-04-05 21:42:40 -060067 logging.info("%s - %s\n" % (self.address_string(),
Chengyu Fan45d1a762014-07-08 14:21:32 -060068 format % args))
Chengyu Fanb07788a2014-03-31 12:15:36 -060069
70 def getNfdStatus(self):
Junxiao Shie2733332016-11-24 14:11:40 +000071 """ Obtain XML-formatted NFD status report """
72 sp = subprocess.Popen(['nfdc', 'status', 'report', 'xml'], stdout=subprocess.PIPE, close_fds=True)
Alexander Afanasyevb4bac922014-11-03 13:56:01 -080073 output = sp.communicate()[0]
74 if sp.returncode == 0:
Chengyu Fan45d1a762014-07-08 14:21:32 -060075 # add the xml-stylesheet processing instruction after the 1st '>' symbol
76 newLineIndex = output.index('>') + 1
77 resultStr = output[:newLineIndex]\
78 + "<?xml-stylesheet type=\"text/xsl\" href=\"nfd-status.xsl\"?>"\
79 + output[newLineIndex:]
Alexander Afanasyevb4bac922014-11-03 13:56:01 -080080 return (sp.returncode, resultStr)
Chengyu Fanb07788a2014-03-31 12:15:36 -060081 else:
Chengyu Fan45d1a762014-07-08 14:21:32 -060082 htmlStr = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional'\
83 + '//EN" "http://www.w3.org/TR/html4/loose.dtd">\n'\
84 + '<html><head><title>NFD status</title>'\
85 + '<meta http-equiv="Content-type" content="text/html;'\
86 + 'charset=UTF-8"></head>\n<body>'
Chengyu Fanb07788a2014-03-31 12:15:36 -060087 # return connection error code
88 htmlStr = htmlStr + "<p>Cannot connect to NFD,"\
Alexander Afanasyevb4bac922014-11-03 13:56:01 -080089 + " Code = " + str(sp.returncode) + "</p>\n"
Chengyu Fan45d1a762014-07-08 14:21:32 -060090 htmlStr = htmlStr + "</body></html>"
Alexander Afanasyevb4bac922014-11-03 13:56:01 -080091 return (sp.returncode, htmlStr)
Chengyu Fanb07788a2014-03-31 12:15:36 -060092
Chengyu Fane25b0f02014-04-05 21:42:40 -060093class ThreadHttpServer(ThreadingMixIn, HTTPServer):
94 """ Handle requests using threads """
Chengyu Fanb07788a2014-03-31 12:15:36 -060095 def __init__(self, server, handler, verbose=False, robots=False):
Chengyu Fane25b0f02014-04-05 21:42:40 -060096 serverAddr = server[0]
97 # socket.AF_UNSPEC is not supported, check whether it is v6 or v4
98 ipType = self.getIpType(serverAddr)
99 if ipType == socket.AF_INET6:
100 self.address_family = socket.AF_INET6
101 elif ipType == socket.AF_INET:
102 self.address_family == socket.AF_INET
103 else:
104 logging.error("The input IP address is neither IPv6 nor IPv4")
105 sys.exit(2)
106
Chengyu Fanb07788a2014-03-31 12:15:36 -0600107 try:
108 HTTPServer.__init__(self, server, handler)
109 except Exception as e:
110 logging.error(str(e))
111 sys.exit(2)
112 self.verbose = verbose
113 self.robots = robots
114
Chengyu Fane25b0f02014-04-05 21:42:40 -0600115 def getIpType(self, ipAddr):
116 """ Get ipAddr's address type """
117 # if ipAddr is an IPv6 addr, return AF_INET6
118 try:
119 socket.inet_pton(socket.AF_INET6, ipAddr)
120 return socket.AF_INET6
121 except socket.error:
122 pass
123 # if ipAddr is an IPv4 addr return AF_INET, if not, return None
124 try:
125 socket.inet_pton(socket.AF_INET, ipAddr)
126 return socket.AF_INET
127 except socket.error:
128 return None
129
Chengyu Fanb07788a2014-03-31 12:15:36 -0600130
131# main function to start
132def httpServer():
133 parser = argparse.ArgumentParser()
134 parser.add_argument("-p", type=int, metavar="port number",
135 help="Specify the HTTP server port number, default is 8080.",
136 dest="port", default=8080)
Chengyu Fane25b0f02014-04-05 21:42:40 -0600137 # if address is not specified, use 127.0.0.1
138 parser.add_argument("-a", default="127.0.0.1", metavar="IP address", dest="addr",
Chengyu Fanb07788a2014-03-31 12:15:36 -0600139 help="Specify the HTTP server IP address.")
140 parser.add_argument("-r", default=False, dest="robots", action="store_true",
141 help="Enable HTTP robots to crawl; disabled by default.")
Alexander Afanasyev8a093762014-07-16 18:43:09 -0700142 parser.add_argument("-f", default="@DATAROOTDIR@/ndn", metavar="Server Directory", dest="serverDir",
143 help="Specify the working directory of nfd-status-http-server, default is @DATAROOTDIR@/ndn.")
Chengyu Fanb07788a2014-03-31 12:15:36 -0600144 parser.add_argument("-v", default=False, dest="verbose", action="store_true",
145 help="Verbose mode.")
Alexander Afanasyevb47d5382014-05-05 14:35:03 -0700146 parser.add_argument("--version", default=False, dest="version", action="store_true",
147 help="Show version and exit")
Chengyu Fanb07788a2014-03-31 12:15:36 -0600148
149 args = vars(parser.parse_args())
Alexander Afanasyevb47d5382014-05-05 14:35:03 -0700150
151 if args['version']:
152 print "@VERSION@"
153 return
154
Chengyu Fanb07788a2014-03-31 12:15:36 -0600155 localPort = args["port"]
156 localAddr = args["addr"]
157 verbose = args["verbose"]
158 robots = args["robots"]
Chengyu Fan45d1a762014-07-08 14:21:32 -0600159 serverDirectory = args["serverDir"]
160
161 os.chdir(serverDirectory)
Chengyu Fanb07788a2014-03-31 12:15:36 -0600162
163 # setting log message format
164 logging.basicConfig(format='%(asctime)s [%(levelname)s] %(message)s',
165 level=logging.INFO)
166
Chengyu Fane25b0f02014-04-05 21:42:40 -0600167 # if port is invalid, exit
Chengyu Fanb07788a2014-03-31 12:15:36 -0600168 if localPort <= 0 or localPort > 65535:
169 logging.error("Specified port number is invalid")
170 sys.exit(2)
171
Chengyu Fane25b0f02014-04-05 21:42:40 -0600172 httpd = ThreadHttpServer((localAddr, localPort), StatusHandler,
Chengyu Fanb07788a2014-03-31 12:15:36 -0600173 verbose, robots)
Chengyu Fane25b0f02014-04-05 21:42:40 -0600174 httpServerAddr = ""
175 if httpd.address_family == socket.AF_INET6:
176 httpServerAddr = "http://[%s]:%s" % (httpd.server_address[0],
177 httpd.server_address[1])
178 else:
179 httpServerAddr = "http://%s:%s" % (httpd.server_address[0],
180 httpd.server_address[1])
Chengyu Fanb07788a2014-03-31 12:15:36 -0600181
Chengyu Fane25b0f02014-04-05 21:42:40 -0600182 logging.info("Server started - at %s" % httpServerAddr)
Chengyu Fanb07788a2014-03-31 12:15:36 -0600183
184 try:
185 httpd.serve_forever()
186 except KeyboardInterrupt:
187 pass
188
Chengyu Fanb07788a2014-03-31 12:15:36 -0600189 httpd.server_close()
190
Chengyu Fane25b0f02014-04-05 21:42:40 -0600191 logging.info("Server stopped")
Chengyu Fanb07788a2014-03-31 12:15:36 -0600192
193
194if __name__ == '__main__':
195 httpServer()