1 # Copyright (c) 2011 Google Inc. All rights reserved.
3 # Redistribution and use in source and binary forms, with or without
4 # modification, are permitted provided that the following conditions are
7 # * Redistributions of source code must retain the above copyright
8 # notice, this list of conditions and the following disclaimer.
9 # * Redistributions in binary form must reproduce the above
10 # copyright notice, this list of conditions and the following disclaimer
11 # in the documentation and/or other materials provided with the
13 # * Neither the name of Google Inc. nor the names of its
14 # contributors may be used to endorse or promote products derived from
15 # this software without specific prior written permission.
17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 from __future__ import with_statement
34 # python 2.5 compatibility
35 import webkitpy.thirdparty.simplejson as json
53 class ReflectionHandler(BaseHTTPServer.BaseHTTPRequestHandler):
54 # Subclasses should override.
55 STATIC_FILE_NAMES = None
56 STATIC_FILE_DIRECTORY = None
58 # Setting this flag to True causes the server to send
59 # Access-Control-Allow-Origin: *
60 # with every response.
61 allow_cross_origin_requests = False
64 self._handle_request()
67 self._handle_request()
69 def _read_entity_body(self):
70 length = int(self.headers.getheader('content-length'))
71 return self.rfile.read(length)
73 def _read_entity_body_as_json(self):
74 return json.loads(self._read_entity_body())
76 def _handle_request(self):
78 path, query_string = self.path.split("?", 1)
79 self.query = cgi.parse_qs(query_string)
83 function_or_file_name = path[1:] or "index.html"
85 if function_or_file_name in self.STATIC_FILE_NAMES:
86 self._serve_static_file(function_or_file_name)
89 function_name = function_or_file_name.replace(".", "_")
90 if not hasattr(self, function_name):
91 self.send_error(404, "Unknown function %s" % function_name)
93 if function_name[0] == "_":
94 self.send_error(401, "Not allowed to invoke private or protected methods")
96 function = getattr(self, function_name)
99 def _serve_static_file(self, static_path):
100 self._serve_file(os.path.join(self.STATIC_FILE_DIRECTORY, static_path))
102 def quitquitquit(self):
103 self._serve_text("Server quit.\n")
104 # Shutdown has to happen on another thread from the server's thread,
105 # otherwise there's a deadlock
106 threading.Thread(target=lambda: self.server.shutdown()).start()
108 def _send_access_control_header(self):
109 if self.allow_cross_origin_requests:
110 self.send_header('Access-Control-Allow-Origin', '*')
112 def _serve_text(self, text):
113 self.send_response(200)
114 self._send_access_control_header()
115 self.send_header("Content-type", "text/plain")
117 self.wfile.write(text)
119 def _serve_json(self, json_object):
120 self.send_response(200)
121 self._send_access_control_header()
122 self.send_header('Content-type', 'application/json')
124 json.dump(json_object, self.wfile)
126 def _serve_file(self, file_path, cacheable_seconds=0):
127 if not os.path.exists(file_path):
128 self.send_error(404, "File not found")
130 with codecs.open(file_path, "rb") as static_file:
131 self.send_response(200)
132 self._send_access_control_header()
133 self.send_header("Content-Length", os.path.getsize(file_path))
134 mime_type, encoding = mimetypes.guess_type(file_path)
136 self.send_header("Content-type", mime_type)
138 if cacheable_seconds:
139 expires_time = (datetime.datetime.now() +
140 datetime.timedelta(0, cacheable_seconds))
141 expires_formatted = format_date_time(
142 time.mktime(expires_time.timetuple()))
143 self.send_header("Expires", expires_formatted)
146 shutil.copyfileobj(static_file, self.wfile)