blob: c46ad8867c975aef6cedd477d8261a04a50747ce [file] [log] [blame]
Zsolt Haraszti034db372016-10-03 22:26:41 -07001#!/usr/bin/env python
2#
3# Copyright 2016 the original author or authors.
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17
18import os
19
20from klein import Klein
21from simplejson import dumps, load
22from structlog import get_logger
23from twisted.internet import reactor, endpoints
24from twisted.internet.defer import inlineCallbacks, returnValue
25from twisted.internet.tcp import Port
26from twisted.web.server import Site
27from twisted.web.static import File
Zsolt Harasztid4226ed2016-10-05 17:49:27 -070028from werkzeug.exceptions import BadRequest
Zsolt Haraszti034db372016-10-03 22:26:41 -070029
30log = get_logger()
31
Zsolt Haraszti034db372016-10-03 22:26:41 -070032
33class WebServer(object):
34
35 app = Klein()
36
37 def __init__(self, port, work_dir, grpc_client):
38 self.port = port
39 self.site = None
40 self.work_dir = work_dir
41 self.grpc_client = grpc_client
42
43 self.swagger_ui_root_dir = os.path.abspath(
44 os.path.join(os.path.dirname(__file__), '../swagger_ui'))
45
46 self.tcp_port = None
Zsolt Harasztid4226ed2016-10-05 17:49:27 -070047 self.shutting_down = False
Zsolt Haraszti034db372016-10-03 22:26:41 -070048
49 @inlineCallbacks
50 def run(self):
51 yield self._open_endpoint()
Zsolt Haraszti034db372016-10-03 22:26:41 -070052 returnValue(self)
53
Zsolt Haraszti034db372016-10-03 22:26:41 -070054 @inlineCallbacks
55 def _open_endpoint(self):
56 endpoint = endpoints.TCP4ServerEndpoint(reactor, self.port)
57 self.site = Site(self.app.resource())
58 self.tcp_port = yield endpoint.listen(self.site)
59 log.info('web-server-started', port=self.port)
60 self.endpoint = endpoint
61
62 @inlineCallbacks
63 def shutdown(self):
Zsolt Harasztid4226ed2016-10-05 17:49:27 -070064 self.shutting_down = True
65 if self.tcp_port is not None:
Zsolt Haraszti034db372016-10-03 22:26:41 -070066 assert isinstance(self.tcp_port, Port)
67 yield self.tcp_port.socket.close()
68
Zsolt Harasztid4226ed2016-10-05 17:49:27 -070069 def reload_generated_routes(self):
70 for fname in os.listdir(self.work_dir):
71 if fname.endswith('_gw.py'):
72 module_name = fname.replace('.py', '')
73 m = __import__(module_name)
74 assert hasattr(m, 'add_routes')
75 m.add_routes(self.app, self.grpc_client)
76 log.info('routes-loaded', module=module_name)
77
Zsolt Haraszti034db372016-10-03 22:26:41 -070078 # static swagger_ui website as landing page (for now)
79
80 @app.route('/', branch=True)
81 def static(self, request):
82 try:
83 log.debug(request=request)
84 return File(self.swagger_ui_root_dir)
85 except Exception, e:
86 log.exception('file-not-found', request=request)
87
88 # static swagger.json file to serve the schema
89
90 @app.route('/v1/swagger.json')
91 def swagger_json(self, request):
92 try:
93 return File(os.path.join(self.work_dir, 'swagger.json'))
94 except Exception, e:
95 log.exception('file-not-found', request=request)