blob: 33adb48f153e397ea238d18b19ddd21f45c8c5dc [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
31import commands
32import 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 Fan45d1a762014-07-08 14:21:32 -060076 (res, output) = commands.getstatusoutput('nfd-status -x')
Chengyu Fanb07788a2014-03-31 12:15:36 -060077 if res == 0:
Chengyu Fan45d1a762014-07-08 14:21:32 -060078 # add the xml-stylesheet processing instruction after the 1st '>' symbol
79 newLineIndex = output.index('>') + 1
80 resultStr = output[:newLineIndex]\
81 + "<?xml-stylesheet type=\"text/xsl\" href=\"nfd-status.xsl\"?>"\
82 + output[newLineIndex:]
83 return (res, resultStr)
Chengyu Fanb07788a2014-03-31 12:15:36 -060084 else:
Chengyu Fan45d1a762014-07-08 14:21:32 -060085 htmlStr = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional'\
86 + '//EN" "http://www.w3.org/TR/html4/loose.dtd">\n'\
87 + '<html><head><title>NFD status</title>'\
88 + '<meta http-equiv="Content-type" content="text/html;'\
89 + 'charset=UTF-8"></head>\n<body>'
Chengyu Fanb07788a2014-03-31 12:15:36 -060090 # return connection error code
91 htmlStr = htmlStr + "<p>Cannot connect to NFD,"\
92 + " Code = " + str(res) + "</p>\n"
Chengyu Fan45d1a762014-07-08 14:21:32 -060093 htmlStr = htmlStr + "</body></html>"
94 return (res, htmlStr)
Chengyu Fanb07788a2014-03-31 12:15:36 -060095
Chengyu Fane25b0f02014-04-05 21:42:40 -060096class ThreadHttpServer(ThreadingMixIn, HTTPServer):
97 """ Handle requests using threads """
Chengyu Fanb07788a2014-03-31 12:15:36 -060098 def __init__(self, server, handler, verbose=False, robots=False):
Chengyu Fane25b0f02014-04-05 21:42:40 -060099 serverAddr = server[0]
100 # socket.AF_UNSPEC is not supported, check whether it is v6 or v4
101 ipType = self.getIpType(serverAddr)
102 if ipType == socket.AF_INET6:
103 self.address_family = socket.AF_INET6
104 elif ipType == socket.AF_INET:
105 self.address_family == socket.AF_INET
106 else:
107 logging.error("The input IP address is neither IPv6 nor IPv4")
108 sys.exit(2)
109
Chengyu Fanb07788a2014-03-31 12:15:36 -0600110 try:
111 HTTPServer.__init__(self, server, handler)
112 except Exception as e:
113 logging.error(str(e))
114 sys.exit(2)
115 self.verbose = verbose
116 self.robots = robots
117
Chengyu Fane25b0f02014-04-05 21:42:40 -0600118 def getIpType(self, ipAddr):
119 """ Get ipAddr's address type """
120 # if ipAddr is an IPv6 addr, return AF_INET6
121 try:
122 socket.inet_pton(socket.AF_INET6, ipAddr)
123 return socket.AF_INET6
124 except socket.error:
125 pass
126 # if ipAddr is an IPv4 addr return AF_INET, if not, return None
127 try:
128 socket.inet_pton(socket.AF_INET, ipAddr)
129 return socket.AF_INET
130 except socket.error:
131 return None
132
Chengyu Fanb07788a2014-03-31 12:15:36 -0600133
134# main function to start
135def httpServer():
136 parser = argparse.ArgumentParser()
137 parser.add_argument("-p", type=int, metavar="port number",
138 help="Specify the HTTP server port number, default is 8080.",
139 dest="port", default=8080)
Chengyu Fane25b0f02014-04-05 21:42:40 -0600140 # if address is not specified, use 127.0.0.1
141 parser.add_argument("-a", default="127.0.0.1", metavar="IP address", dest="addr",
Chengyu Fanb07788a2014-03-31 12:15:36 -0600142 help="Specify the HTTP server IP address.")
143 parser.add_argument("-r", default=False, dest="robots", action="store_true",
144 help="Enable HTTP robots to crawl; disabled by default.")
Chengyu Fan45d1a762014-07-08 14:21:32 -0600145 parser.add_argument("-f", default="/usr/local/share/ndn/", metavar="Server Directory", dest="serverDir",
146 help="Specify the working directory of nfd-status-http-server, default is /usr/local/share/ndn.")
Chengyu Fanb07788a2014-03-31 12:15:36 -0600147 parser.add_argument("-v", default=False, dest="verbose", action="store_true",
148 help="Verbose mode.")
Alexander Afanasyevb47d5382014-05-05 14:35:03 -0700149 parser.add_argument("--version", default=False, dest="version", action="store_true",
150 help="Show version and exit")
Chengyu Fanb07788a2014-03-31 12:15:36 -0600151
152 args = vars(parser.parse_args())
Alexander Afanasyevb47d5382014-05-05 14:35:03 -0700153
154 if args['version']:
155 print "@VERSION@"
156 return
157
Chengyu Fanb07788a2014-03-31 12:15:36 -0600158 localPort = args["port"]
159 localAddr = args["addr"]
160 verbose = args["verbose"]
161 robots = args["robots"]
Chengyu Fan45d1a762014-07-08 14:21:32 -0600162 serverDirectory = args["serverDir"]
163
164 os.chdir(serverDirectory)
Chengyu Fanb07788a2014-03-31 12:15:36 -0600165
166 # setting log message format
167 logging.basicConfig(format='%(asctime)s [%(levelname)s] %(message)s',
168 level=logging.INFO)
169
Chengyu Fane25b0f02014-04-05 21:42:40 -0600170 # if port is invalid, exit
Chengyu Fanb07788a2014-03-31 12:15:36 -0600171 if localPort <= 0 or localPort > 65535:
172 logging.error("Specified port number is invalid")
173 sys.exit(2)
174
Chengyu Fane25b0f02014-04-05 21:42:40 -0600175 httpd = ThreadHttpServer((localAddr, localPort), StatusHandler,
Chengyu Fanb07788a2014-03-31 12:15:36 -0600176 verbose, robots)
Chengyu Fane25b0f02014-04-05 21:42:40 -0600177 httpServerAddr = ""
178 if httpd.address_family == socket.AF_INET6:
179 httpServerAddr = "http://[%s]:%s" % (httpd.server_address[0],
180 httpd.server_address[1])
181 else:
182 httpServerAddr = "http://%s:%s" % (httpd.server_address[0],
183 httpd.server_address[1])
Chengyu Fanb07788a2014-03-31 12:15:36 -0600184
Chengyu Fane25b0f02014-04-05 21:42:40 -0600185 logging.info("Server started - at %s" % httpServerAddr)
Chengyu Fanb07788a2014-03-31 12:15:36 -0600186
187 try:
188 httpd.serve_forever()
189 except KeyboardInterrupt:
190 pass
191
Chengyu Fanb07788a2014-03-31 12:15:36 -0600192 httpd.server_close()
193
Chengyu Fane25b0f02014-04-05 21:42:40 -0600194 logging.info("Server stopped")
Chengyu Fanb07788a2014-03-31 12:15:36 -0600195
196
197if __name__ == '__main__':
198 httpServer()