1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
#!/usr/bin/env python
import inspect
import json
import logging
import schema
import jsonschema
from concurrent.futures import Future
import tornado
import tornado.gen
import tornado.web
from tornado.web import RequestHandler, StaticFileHandler
import errors
class BaseHandler(RequestHandler):
def data_received(self, chunk):
raise NotImplementedError()
class IndexPageHandler(BaseHandler):
def get(self):
self.render(MAIN_INDEX, status="Ready", info="Retrieving data ...")
class StaticTextFileHandler(StaticFileHandler):
def set_default_headers(self):
self.set_header('Content-Type', 'text/plain')
class DevStaticFileHandler(StaticFileHandler):
def set_extra_headers(self, path):
# Disable cache
self.set_header(
'Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
logger = logging.getLogger(__name__)
logger.addHandler(logging.NullHandler())
class CommandHandler(tornado.web.RequestHandler):
"""REST Command Handler
Routes json command in the form
{'name' : 'apiHandler.function', 'parameters' : {}}
to the right apiHandler instance function
(see https://developers.google.com/streetview/open-spherical-camera/
for an overview of the protocol)
This handler supports synchronous and asyncronous calls. Asynchronous API
methods must be decorated with @run_on_executor and the enclosing class
must provide an 'executor' ThreadPoolExecutor class instance.
Example:::
class MyAPIHandler(APIHandler):
executor = ThreadPoolExecutor(1)
@run_on_executor
def asynchronousAPI (self, parameters) :
return
def synchronousAPI (self, parameters) :
return
"""
def initialize(self, apiHandlers, extra):
self.apiHandlers = apiHandlers
self.extra = extra
self.verbose = extra["verbose"]
def set_default_headers(self):
self.set_header("Access-Control-Allow-Origin", "*")
self.set_header("Access-Control-Allow-Headers", "x-requested-with")
self.set_header('Access-Control-Allow-Methods', 'POST')
@tornado.gen.coroutine
def post(self):
try:
data = json.loads(self.request.body)
except ValueError:
raise tornado.web.HTTPError(400, "Ill-formed message command")
# Split command name
command_name = data.get("name")
if command_name is None:
raise tornado.web.HTTPError(400, "No command name")
if command_name.count(".") != 1:
raise tornado.web.HTTPError(400, "Invalid command name")
(class_name, function_name) = command_name.split(".")
# Create a handler instance
class_instance = self.apiHandlers.get(class_name)
if class_instance is None:
raise tornado.web.HTTPError(400, "Unknown handler " + class_name)
instance = class_instance(self.extra)
# Validate parameters
validate = data.get("validate", True)
log = data.get("log", True)
try:
parameters = data.get("parameters")
if self.verbose and log:
logger.info(
"> " + class_name + "." + function_name + "(" + \
json.dumps(parameters) + ")")
if validate:
schema.api.validate_parameters(command_name, parameters)
except jsonschema.ValidationError as ve:
e = errors.InvalidParameter(str(ve)).payload
m = {'error': e}
logger.error(command_name + ", invalid parameter:\n" + str(ve))
else:
# Call instance method
if not hasattr(instance, function_name):
raise tornado.web.HTTPError(
400, "Unknown function " + command_name)
# call method
function = getattr(instance, function_name)
future = function(parameters)
try:
if isinstance(future, Future) or isinstance(
future, tornado.concurrent.Future):
result = yield future
else:
f = Future()
f.set_result(future)
result = yield f
except errors.VSError as e:
m = {'error': e.payload}
else:
# Validate result
try:
if validate:
schema.api.validate_result(command_name, result)
except jsonschema.ValidationError as ve:
e = errors.InvalidReturnValue(str(ve)).payload
m = {'error': e}
logger.error(command_name + ", invalid return value:\n" + str(ve))
else:
m = {'results': result}
if self.verbose and log:
logger.info("< " + json.dumps(m))
self.write(m)
self.finish()
class APIHandler(object):
"""Base class for APIHandlers
"""
def __init__(self, extra):
pass
def get_methods(self):
"""Get the list of exposed methods for the API
Returns:
A list of methods available.
"""
members = []
for member in inspect.getmembers(self, predicate=inspect.ismethod):
if member[0] not in ["__init__", "ok", "error"]:
members.append(member[0])
return members