blob: 3d34d7381f0217f9ceafd0eb3fdba6167b1a912b [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"""
5Copyright (c) 2014 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
12This file is part of NFD (Named Data Networking Forwarding Daemon).
13See AUTHORS.md for complete list of NFD authors and contributors.
14
15NFD is free software: you can redistribute it and/or modify it under the terms
16of the GNU General Public License as published by the Free Software Foundation,
17either version 3 of the License, or (at your option) any later version.
18
19NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
20without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
21PURPOSE. See the GNU General Public License for more details.
22
23You should have received a copy of the GNU General Public License along with
24NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
25"""
Chengyu Fanb07788a2014-03-31 12:15:36 -060026
Chengyu Fan45d1a762014-07-08 14:21:32 -060027from BaseHTTPServer import HTTPServer
28from SimpleHTTPServer import SimpleHTTPRequestHandler
Chengyu Fanb07788a2014-03-31 12:15:36 -060029from SocketServer import ThreadingMixIn
30import sys
Chengyu Fan30aa2072014-07-20 13:52:32 -060031import subprocess
Chengyu Fanb07788a2014-03-31 12:15:36 -060032import StringIO
33import urlparse
34import logging
35import cgi
36import argparse
Chengyu Fane25b0f02014-04-05 21:42:40 -060037import socket
Chengyu Fan45d1a762014-07-08 14:21:32 -060038import os
Chengyu Fanb07788a2014-03-31 12:15:36 -060039
40
Chengyu Fan45d1a762014-07-08 14:21:32 -060041class StatusHandler(SimpleHTTPRequestHandler):
Chengyu Fanb07788a2014-03-31 12:15:36 -060042 """ The handler class to handle requests."""
43 def do_GET(self):
44 # get the url info to decide how to respond
45 parsedPath = urlparse.urlparse(self.path)
Chengyu Fan45d1a762014-07-08 14:21:32 -060046 if parsedPath.path == "/":
47 # get current nfd status, and use it as result message
48 (res, resultMessage) = self.getNfdStatus()
49 self.send_response(200)
50 if res == 0:
51 self.send_header("Content-type", "text/xml; charset=UTF-8")
52 else:
53 self.send_header("Content-type", "text/html; charset=UTF-8")
54
55 self.end_headers()
56 self.wfile.write(resultMessage)
57 elif parsedPath.path == "/robots.txt" and self.server.robots == True:
58 resultMessage = ""
Chengyu Fanb07788a2014-03-31 12:15:36 -060059 self.send_response(200)
60 self.send_header("Content-type", "text/plain")
Chengyu Fan45d1a762014-07-08 14:21:32 -060061 self.end_headers()
62 self.wfile.write(resultMessage)
Chengyu Fanb07788a2014-03-31 12:15:36 -060063 else:
Chengyu Fan45d1a762014-07-08 14:21:32 -060064 SimpleHTTPRequestHandler.do_GET(self)
Chengyu Fanb07788a2014-03-31 12:15:36 -060065
Chengyu Fanb07788a2014-03-31 12:15:36 -060066 def log_message(self, format, *args):
67 if self.server.verbose:
Chengyu Fane25b0f02014-04-05 21:42:40 -060068 logging.info("%s - %s\n" % (self.address_string(),
Chengyu Fan45d1a762014-07-08 14:21:32 -060069 format % args))
Chengyu Fanb07788a2014-03-31 12:15:36 -060070
71 def getNfdStatus(self):
72 """
Chengyu Fan45d1a762014-07-08 14:21:32 -060073 This function is to call nfd-status command
74 to get xml format output
Chengyu Fanb07788a2014-03-31 12:15:36 -060075 """
Alexander Afanasyevb4bac9252014-11-03 13:56:01 -080076 sp = subprocess.Popen(['nfd-status', '-x'], stdout=subprocess.PIPE, close_fds=True)
77 output = sp.communicate()[0]
78 if sp.returncode == 0:
Chengyu Fan45d1a762014-07-08 14:21:32 -060079 # add the xml-stylesheet processing instruction after the 1st '>' symbol
80 newLineIndex = output.index('>') + 1
81 resultStr = output[:newLineIndex]\
82 + "<?xml-stylesheet type=\"text/xsl\" href=\"nfd-status.xsl\"?>"\
83 + output[newLineIndex:]
Alexander Afanasyevb4bac9252014-11-03 13:56:01 -080084 return (sp.returncode, resultStr)
Chengyu Fanb07788a2014-03-31 12:15:36 -060085 else:
Chengyu Fan45d1a762014-07-08 14:21:32 -060086 htmlStr = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional'\
87 + '//EN" "http://www.w3.org/TR/html4/loose.dtd">\n'\
88 + '<html><head><title>NFD status</title>'\
89 + '<meta http-equiv="Content-type" content="text/html;'\
90 + 'charset=UTF-8"></head>\n<body>'
Chengyu Fanb07788a2014-03-31 12:15:36 -060091 # return connection error code
92 htmlStr = htmlStr + "<p>Cannot connect to NFD,"\
Alexander Afanasyevb4bac9252014-11-03 13:56:01 -080093 + " Code = " + str(sp.returncode) + "</p>\n"
Chengyu Fan45d1a762014-07-08 14:21:32 -060094 htmlStr = htmlStr + "</body></html>"
Alexander Afanasyevb4bac9252014-11-03 13:56:01 -080095 return (sp.returncode, htmlStr)
Chengyu Fanb07788a2014-03-31 12:15:36 -060096
Chengyu Fane25b0f02014-04-05 21:42:40 -060097class ThreadHttpServer(ThreadingMixIn, HTTPServer):
98 """ Handle requests using threads """
Chengyu Fanb07788a2014-03-31 12:15:36 -060099 def __init__(self, server, handler, verbose=False, robots=False):
Chengyu Fane25b0f02014-04-05 21:42:40 -0600100 serverAddr = server[0]
101 # socket.AF_UNSPEC is not supported, check whether it is v6 or v4
102 ipType = self.getIpType(serverAddr)
103 if ipType == socket.AF_INET6:
104 self.address_family = socket.AF_INET6
105 elif ipType == socket.AF_INET:
106 self.address_family == socket.AF_INET
107 else:
108 logging.error("The input IP address is neither IPv6 nor IPv4")
109 sys.exit(2)
110
Chengyu Fanb07788a2014-03-31 12:15:36 -0600111 try:
112 HTTPServer.__init__(self, server, handler)
113 except Exception as e:
114 logging.error(str(e))
115 sys.exit(2)
116 self.verbose = verbose
117 self.robots = robots
118
Chengyu Fane25b0f02014-04-05 21:42:40 -0600119 def getIpType(self, ipAddr):
120 """ Get ipAddr's address type """
121 # if ipAddr is an IPv6 addr, return AF_INET6
122 try:
123 socket.inet_pton(socket.AF_INET6, ipAddr)
124 return socket.AF_INET6
125 except socket.error:
126 pass
127 # if ipAddr is an IPv4 addr return AF_INET, if not, return None
128 try:
129 socket.inet_pton(socket.AF_INET, ipAddr)
130 return socket.AF_INET
131 except socket.error:
132 return None
133
Chengyu Fanb07788a2014-03-31 12:15:36 -0600134
135# main function to start
136def httpServer():
137 parser = argparse.ArgumentParser()
138 parser.add_argument("-p", type=int, metavar="port number",
139 help="Specify the HTTP server port number, default is 8080.",
140 dest="port", default=8080)
Chengyu Fane25b0f02014-04-05 21:42:40 -0600141 # if address is not specified, use 127.0.0.1
142 parser.add_argument("-a", default="127.0.0.1", metavar="IP address", dest="addr",
Chengyu Fanb07788a2014-03-31 12:15:36 -0600143 help="Specify the HTTP server IP address.")
144 parser.add_argument("-r", default=False, dest="robots", action="store_true",
145 help="Enable HTTP robots to crawl; disabled by default.")
Alexander Afanasyev8a093762014-07-16 18:43:09 -0700146 parser.add_argument("-f", default="@DATAROOTDIR@/ndn", metavar="Server Directory", dest="serverDir",
147 help="Specify the working directory of nfd-status-http-server, default is @DATAROOTDIR@/ndn.")
Chengyu Fanb07788a2014-03-31 12:15:36 -0600148 parser.add_argument("-v", default=False, dest="verbose", action="store_true",
149 help="Verbose mode.")
Alexander Afanasyevb47d5382014-05-05 14:35:03 -0700150 parser.add_argument("--version", default=False, dest="version", action="store_true",
151 help="Show version and exit")
Chengyu Fanb07788a2014-03-31 12:15:36 -0600152
153 args = vars(parser.parse_args())
Alexander Afanasyevb47d5382014-05-05 14:35:03 -0700154
155 if args['version']:
156 print "@VERSION@"
157 return
158
Chengyu Fanb07788a2014-03-31 12:15:36 -0600159 localPort = args["port"]
160 localAddr = args["addr"]
161 verbose = args["verbose"]
162 robots = args["robots"]
Chengyu Fan45d1a762014-07-08 14:21:32 -0600163 serverDirectory = args["serverDir"]
164
165 os.chdir(serverDirectory)
Chengyu Fanb07788a2014-03-31 12:15:36 -0600166
167 # setting log message format
168 logging.basicConfig(format='%(asctime)s [%(levelname)s] %(message)s',
169 level=logging.INFO)
170
Chengyu Fane25b0f02014-04-05 21:42:40 -0600171 # if port is invalid, exit
Chengyu Fanb07788a2014-03-31 12:15:36 -0600172 if localPort <= 0 or localPort > 65535:
173 logging.error("Specified port number is invalid")
174 sys.exit(2)
175
Chengyu Fane25b0f02014-04-05 21:42:40 -0600176 httpd = ThreadHttpServer((localAddr, localPort), StatusHandler,
Chengyu Fanb07788a2014-03-31 12:15:36 -0600177 verbose, robots)
Chengyu Fane25b0f02014-04-05 21:42:40 -0600178 httpServerAddr = ""
179 if httpd.address_family == socket.AF_INET6:
180 httpServerAddr = "http://[%s]:%s" % (httpd.server_address[0],
181 httpd.server_address[1])
182 else:
183 httpServerAddr = "http://%s:%s" % (httpd.server_address[0],
184 httpd.server_address[1])
Chengyu Fanb07788a2014-03-31 12:15:36 -0600185
Chengyu Fane25b0f02014-04-05 21:42:40 -0600186 logging.info("Server started - at %s" % httpServerAddr)
Chengyu Fanb07788a2014-03-31 12:15:36 -0600187
188 try:
189 httpd.serve_forever()
190 except KeyboardInterrupt:
191 pass
192
Chengyu Fanb07788a2014-03-31 12:15:36 -0600193 httpd.server_close()
194
Chengyu Fane25b0f02014-04-05 21:42:40 -0600195 logging.info("Server stopped")
Chengyu Fanb07788a2014-03-31 12:15:36 -0600196
197
198if __name__ == '__main__':
199 httpServer()