blob: f127d403a12a2fd00fd6cb6bdcf16d35b0a9505a [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 """
Chengyu Fan30aa2072014-07-20 13:52:32 -060076 sp = subprocess.Popen(['nfd-status', '-x'], stdout=subprocess.PIPE)
77 res = sp.wait()
Chengyu Fanb07788a2014-03-31 12:15:36 -060078 if res == 0:
Chengyu Fan45d1a762014-07-08 14:21:32 -060079 # add the xml-stylesheet processing instruction after the 1st '>' symbol
Chengyu Fan30aa2072014-07-20 13:52:32 -060080 output = sp.stdout.read()
Chengyu Fan45d1a762014-07-08 14:21:32 -060081 newLineIndex = output.index('>') + 1
82 resultStr = output[:newLineIndex]\
83 + "<?xml-stylesheet type=\"text/xsl\" href=\"nfd-status.xsl\"?>"\
84 + output[newLineIndex:]
85 return (res, resultStr)
Chengyu Fanb07788a2014-03-31 12:15:36 -060086 else:
Chengyu Fan45d1a762014-07-08 14:21:32 -060087 htmlStr = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional'\
88 + '//EN" "http://www.w3.org/TR/html4/loose.dtd">\n'\
89 + '<html><head><title>NFD status</title>'\
90 + '<meta http-equiv="Content-type" content="text/html;'\
91 + 'charset=UTF-8"></head>\n<body>'
Chengyu Fanb07788a2014-03-31 12:15:36 -060092 # return connection error code
93 htmlStr = htmlStr + "<p>Cannot connect to NFD,"\
94 + " Code = " + str(res) + "</p>\n"
Chengyu Fan45d1a762014-07-08 14:21:32 -060095 htmlStr = htmlStr + "</body></html>"
96 return (res, htmlStr)
Chengyu Fanb07788a2014-03-31 12:15:36 -060097
Chengyu Fane25b0f02014-04-05 21:42:40 -060098class ThreadHttpServer(ThreadingMixIn, HTTPServer):
99 """ Handle requests using threads """
Chengyu Fanb07788a2014-03-31 12:15:36 -0600100 def __init__(self, server, handler, verbose=False, robots=False):
Chengyu Fane25b0f02014-04-05 21:42:40 -0600101 serverAddr = server[0]
102 # socket.AF_UNSPEC is not supported, check whether it is v6 or v4
103 ipType = self.getIpType(serverAddr)
104 if ipType == socket.AF_INET6:
105 self.address_family = socket.AF_INET6
106 elif ipType == socket.AF_INET:
107 self.address_family == socket.AF_INET
108 else:
109 logging.error("The input IP address is neither IPv6 nor IPv4")
110 sys.exit(2)
111
Chengyu Fanb07788a2014-03-31 12:15:36 -0600112 try:
113 HTTPServer.__init__(self, server, handler)
114 except Exception as e:
115 logging.error(str(e))
116 sys.exit(2)
117 self.verbose = verbose
118 self.robots = robots
119
Chengyu Fane25b0f02014-04-05 21:42:40 -0600120 def getIpType(self, ipAddr):
121 """ Get ipAddr's address type """
122 # if ipAddr is an IPv6 addr, return AF_INET6
123 try:
124 socket.inet_pton(socket.AF_INET6, ipAddr)
125 return socket.AF_INET6
126 except socket.error:
127 pass
128 # if ipAddr is an IPv4 addr return AF_INET, if not, return None
129 try:
130 socket.inet_pton(socket.AF_INET, ipAddr)
131 return socket.AF_INET
132 except socket.error:
133 return None
134
Chengyu Fanb07788a2014-03-31 12:15:36 -0600135
136# main function to start
137def httpServer():
138 parser = argparse.ArgumentParser()
139 parser.add_argument("-p", type=int, metavar="port number",
140 help="Specify the HTTP server port number, default is 8080.",
141 dest="port", default=8080)
Chengyu Fane25b0f02014-04-05 21:42:40 -0600142 # if address is not specified, use 127.0.0.1
143 parser.add_argument("-a", default="127.0.0.1", metavar="IP address", dest="addr",
Chengyu Fanb07788a2014-03-31 12:15:36 -0600144 help="Specify the HTTP server IP address.")
145 parser.add_argument("-r", default=False, dest="robots", action="store_true",
146 help="Enable HTTP robots to crawl; disabled by default.")
Alexander Afanasyev8a093762014-07-16 18:43:09 -0700147 parser.add_argument("-f", default="@DATAROOTDIR@/ndn", metavar="Server Directory", dest="serverDir",
148 help="Specify the working directory of nfd-status-http-server, default is @DATAROOTDIR@/ndn.")
Chengyu Fanb07788a2014-03-31 12:15:36 -0600149 parser.add_argument("-v", default=False, dest="verbose", action="store_true",
150 help="Verbose mode.")
Alexander Afanasyevb47d5382014-05-05 14:35:03 -0700151 parser.add_argument("--version", default=False, dest="version", action="store_true",
152 help="Show version and exit")
Chengyu Fanb07788a2014-03-31 12:15:36 -0600153
154 args = vars(parser.parse_args())
Alexander Afanasyevb47d5382014-05-05 14:35:03 -0700155
156 if args['version']:
157 print "@VERSION@"
158 return
159
Chengyu Fanb07788a2014-03-31 12:15:36 -0600160 localPort = args["port"]
161 localAddr = args["addr"]
162 verbose = args["verbose"]
163 robots = args["robots"]
Chengyu Fan45d1a762014-07-08 14:21:32 -0600164 serverDirectory = args["serverDir"]
165
166 os.chdir(serverDirectory)
Chengyu Fanb07788a2014-03-31 12:15:36 -0600167
168 # setting log message format
169 logging.basicConfig(format='%(asctime)s [%(levelname)s] %(message)s',
170 level=logging.INFO)
171
Chengyu Fane25b0f02014-04-05 21:42:40 -0600172 # if port is invalid, exit
Chengyu Fanb07788a2014-03-31 12:15:36 -0600173 if localPort <= 0 or localPort > 65535:
174 logging.error("Specified port number is invalid")
175 sys.exit(2)
176
Chengyu Fane25b0f02014-04-05 21:42:40 -0600177 httpd = ThreadHttpServer((localAddr, localPort), StatusHandler,
Chengyu Fanb07788a2014-03-31 12:15:36 -0600178 verbose, robots)
Chengyu Fane25b0f02014-04-05 21:42:40 -0600179 httpServerAddr = ""
180 if httpd.address_family == socket.AF_INET6:
181 httpServerAddr = "http://[%s]:%s" % (httpd.server_address[0],
182 httpd.server_address[1])
183 else:
184 httpServerAddr = "http://%s:%s" % (httpd.server_address[0],
185 httpd.server_address[1])
Chengyu Fanb07788a2014-03-31 12:15:36 -0600186
Chengyu Fane25b0f02014-04-05 21:42:40 -0600187 logging.info("Server started - at %s" % httpServerAddr)
Chengyu Fanb07788a2014-03-31 12:15:36 -0600188
189 try:
190 httpd.serve_forever()
191 except KeyboardInterrupt:
192 pass
193
Chengyu Fanb07788a2014-03-31 12:15:36 -0600194 httpd.server_close()
195
Chengyu Fane25b0f02014-04-05 21:42:40 -0600196 logging.info("Server stopped")
Chengyu Fanb07788a2014-03-31 12:15:36 -0600197
198
199if __name__ == '__main__':
200 httpServer()