blob: 6e0f1675fb67495f60a8d91c86ebe88cc35731e2 [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
27from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
28from SocketServer import ThreadingMixIn
29import sys
30import commands
31import StringIO
32import urlparse
33import logging
34import cgi
35import argparse
Chengyu Fane25b0f02014-04-05 21:42:40 -060036import socket
Chengyu Fanb07788a2014-03-31 12:15:36 -060037
38
39class StatusHandler(BaseHTTPRequestHandler):
40 """ The handler class to handle requests."""
41 def do_GET(self):
42 # get the url info to decide how to respond
43 parsedPath = urlparse.urlparse(self.path)
44 resultMessage = ""
45 if parsedPath.path == "/robots.txt":
46 if self.server.robots == False:
47 # return User-agent: * Disallow: / to disallow robots
48 resultMessage = "User-agent: * \nDisallow: /\n"
49 self.send_response(200)
50 self.send_header("Content-type", "text/plain")
51 elif parsedPath.path == "/":
52 # get current nfd status, and use it as result message
53 resultMessage = self.getNfdStatus()
54 self.send_response(200)
55 self.send_header("Content-type", "text/html; charset=UTF-8")
56 else:
57 # non-existing content
58 resultMessage = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 '\
59 + 'Transitional//EN" '\
60 + '"http://www.w3.org/TR/html4/loose.dtd">\n'\
61 + '<html><head><title>Object not '\
62 + 'found!</title><meta http-equiv="Content-type" ' \
63 + 'content="text/html;charset=UTF-8"></head>\n'\
64 + '<body><h1>Object not found!</h1>'\
65 + '<p>The requested URL was not found on this server.</p>'\
66 + '<h2>Error 404</h2>'\
67 + '</body></html>'
68 self.send_response(404)
69 self.send_header("Content-type", "text/html; charset=UTF-8")
70
71 self.end_headers()
72 self.wfile.write(resultMessage)
73
Chengyu Fanb07788a2014-03-31 12:15:36 -060074 def log_message(self, format, *args):
75 if self.server.verbose:
Chengyu Fane25b0f02014-04-05 21:42:40 -060076 logging.info("%s - %s\n" % (self.address_string(),
77 format % args))
Chengyu Fanb07788a2014-03-31 12:15:36 -060078
79 def getNfdStatus(self):
80 """
81 This function tries to call nfd-status command
82 to get nfd's current status, after convert it
83 to html format, return it to the caller
84 """
85 (res, output) = commands.getstatusoutput('nfd-status')
86
87 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>'
92 if res == 0:
93 # parse the output
94 buf = StringIO.StringIO(output)
95 firstLine = 0
96 for line in buf:
97 if line.endswith(":\n"):
98 if firstLine != 0:
99 htmlStr += "</ul>\n"
100 firstLine += 1
101 htmlStr += "<b>" + cgi.escape(line.strip()) + "</b><br>\n"\
102 + "<ul>\n"
103 continue
104 line = line.strip()
105 htmlStr += "<li>" + cgi.escape(line) + "</li>\n"
106 buf.close()
107 htmlStr += "</ul>\n"
108 else:
109 # return connection error code
110 htmlStr = htmlStr + "<p>Cannot connect to NFD,"\
111 + " Code = " + str(res) + "</p>\n"
112 htmlStr = htmlStr + "</body></html>"
113 return htmlStr
114
115
Chengyu Fane25b0f02014-04-05 21:42:40 -0600116class ThreadHttpServer(ThreadingMixIn, HTTPServer):
117 """ Handle requests using threads """
Chengyu Fanb07788a2014-03-31 12:15:36 -0600118 def __init__(self, server, handler, verbose=False, robots=False):
Chengyu Fane25b0f02014-04-05 21:42:40 -0600119 serverAddr = server[0]
120 # socket.AF_UNSPEC is not supported, check whether it is v6 or v4
121 ipType = self.getIpType(serverAddr)
122 if ipType == socket.AF_INET6:
123 self.address_family = socket.AF_INET6
124 elif ipType == socket.AF_INET:
125 self.address_family == socket.AF_INET
126 else:
127 logging.error("The input IP address is neither IPv6 nor IPv4")
128 sys.exit(2)
129
Chengyu Fanb07788a2014-03-31 12:15:36 -0600130 try:
131 HTTPServer.__init__(self, server, handler)
132 except Exception as e:
133 logging.error(str(e))
134 sys.exit(2)
135 self.verbose = verbose
136 self.robots = robots
137
Chengyu Fane25b0f02014-04-05 21:42:40 -0600138 def getIpType(self, ipAddr):
139 """ Get ipAddr's address type """
140 # if ipAddr is an IPv6 addr, return AF_INET6
141 try:
142 socket.inet_pton(socket.AF_INET6, ipAddr)
143 return socket.AF_INET6
144 except socket.error:
145 pass
146 # if ipAddr is an IPv4 addr return AF_INET, if not, return None
147 try:
148 socket.inet_pton(socket.AF_INET, ipAddr)
149 return socket.AF_INET
150 except socket.error:
151 return None
152
Chengyu Fanb07788a2014-03-31 12:15:36 -0600153
154# main function to start
155def httpServer():
156 parser = argparse.ArgumentParser()
157 parser.add_argument("-p", type=int, metavar="port number",
158 help="Specify the HTTP server port number, default is 8080.",
159 dest="port", default=8080)
Chengyu Fane25b0f02014-04-05 21:42:40 -0600160 # if address is not specified, use 127.0.0.1
161 parser.add_argument("-a", default="127.0.0.1", metavar="IP address", dest="addr",
Chengyu Fanb07788a2014-03-31 12:15:36 -0600162 help="Specify the HTTP server IP address.")
163 parser.add_argument("-r", default=False, dest="robots", action="store_true",
164 help="Enable HTTP robots to crawl; disabled by default.")
165 parser.add_argument("-v", default=False, dest="verbose", action="store_true",
166 help="Verbose mode.")
Alexander Afanasyevb47d5382014-05-05 14:35:03 -0700167 parser.add_argument("--version", default=False, dest="version", action="store_true",
168 help="Show version and exit")
Chengyu Fanb07788a2014-03-31 12:15:36 -0600169
170 args = vars(parser.parse_args())
Alexander Afanasyevb47d5382014-05-05 14:35:03 -0700171
172 if args['version']:
173 print "@VERSION@"
174 return
175
Chengyu Fanb07788a2014-03-31 12:15:36 -0600176 localPort = args["port"]
177 localAddr = args["addr"]
178 verbose = args["verbose"]
179 robots = args["robots"]
180
181 # setting log message format
182 logging.basicConfig(format='%(asctime)s [%(levelname)s] %(message)s',
183 level=logging.INFO)
184
Chengyu Fane25b0f02014-04-05 21:42:40 -0600185 # if port is invalid, exit
Chengyu Fanb07788a2014-03-31 12:15:36 -0600186 if localPort <= 0 or localPort > 65535:
187 logging.error("Specified port number is invalid")
188 sys.exit(2)
189
Chengyu Fane25b0f02014-04-05 21:42:40 -0600190 httpd = ThreadHttpServer((localAddr, localPort), StatusHandler,
Chengyu Fanb07788a2014-03-31 12:15:36 -0600191 verbose, robots)
Chengyu Fane25b0f02014-04-05 21:42:40 -0600192 httpServerAddr = ""
193 if httpd.address_family == socket.AF_INET6:
194 httpServerAddr = "http://[%s]:%s" % (httpd.server_address[0],
195 httpd.server_address[1])
196 else:
197 httpServerAddr = "http://%s:%s" % (httpd.server_address[0],
198 httpd.server_address[1])
Chengyu Fanb07788a2014-03-31 12:15:36 -0600199
Chengyu Fane25b0f02014-04-05 21:42:40 -0600200 logging.info("Server started - at %s" % httpServerAddr)
Chengyu Fanb07788a2014-03-31 12:15:36 -0600201
202 try:
203 httpd.serve_forever()
204 except KeyboardInterrupt:
205 pass
206
Chengyu Fanb07788a2014-03-31 12:15:36 -0600207 httpd.server_close()
208
Chengyu Fane25b0f02014-04-05 21:42:40 -0600209 logging.info("Server stopped")
Chengyu Fanb07788a2014-03-31 12:15:36 -0600210
211
212if __name__ == '__main__':
213 httpServer()