summaryrefslogtreecommitdiff
path: root/service/test/support/dispatcher/proxy.py
blob: d4cb95ee5a65fa70501857698595c3a0b464dfe1 (plain)
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
#
# Copyright (c) 2014 ThoughtWorks Deutschland GmbH
#
# Pixelated is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Pixelated is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Pixelated. If not, see <http://www.gnu.org/licenses/>.

from __future__ import print_function
import multiprocessing
import traceback
import sys
import time

from tornado.httpclient import AsyncHTTPClient
from tornado.httpserver import HTTPServer
import tornado.ioloop
import tornado.web
import tornado.escape
from tornado import gen


class MainHandler(tornado.web.RequestHandler):
    __slots__ = '_app_port'

    def initialize(self, app_port):
        self._app_port = app_port

    @tornado.web.asynchronous
    @gen.engine
    def get(self):
        self.forward(self._app_port, '127.0.0.1')

    @tornado.web.asynchronous
    @gen.engine
    def post(self):
        self.get()

    @tornado.web.asynchronous
    @gen.engine
    def put(self):
        self.get()

    @tornado.web.asynchronous
    @gen.engine
    def delete(self):
        self.get()

    def handle_response(self, response):
        if response.error and not isinstance(response.error, tornado.httpclient.HTTPError):
            print(self.request.uri)
            print(response.error)
            self.set_status(500)
            self.write("Internal server error:\n" + str(response.error))
            self.finish()
        else:
            self.set_status(response.code)
            for header in ("Date", "Cache-Control", "Server", "Content-Type", "Location"):
                v = response.headers.get(header)
                if v:
                    self.set_header(header, v)
            if response.body:
                self.write(response.body)
            self.finish()

    def forward(self, port=None, host=None):
        url = "%s://%s:%s%s" % (
            'http', host or "127.0.0.1", port or 80, self.request.uri)
        try:
            tornado.httpclient.AsyncHTTPClient().fetch(
                tornado.httpclient.HTTPRequest(
                    url=url,
                    method=self.request.method,
                    body=None if not self.request.body else self.request.body,
                    headers=self.request.headers,
                    follow_redirects=False,
                    request_timeout=10),
                self.handle_response)
        except tornado.httpclient.HTTPError, x:
            if hasattr(x, 'response') and x.response:
                self.handle_response(x.response)
        except Exception, e:
            self.set_status(500)
            self.write("Internal server error:\n" + ''.join(traceback.format_exception(*sys.exc_info())))
            self.finish()


class Proxy:

    def __init__(self, proxy_port, app_port):
        self._proxy_port = proxy_port
        self._app_port = app_port

    def _create_app(self):
        app = tornado.web.Application(
            [
                (r"/.*", MainHandler, dict(app_port=self._app_port))
            ],
            xsrf_cookies=False,
            debug=True)
        return app

    def serve_forever(self):
        app = self._create_app()
        self._server = HTTPServer(app)
        self._server.listen(port=self._proxy_port, address='127.0.0.1')
        self._ioloop = tornado.ioloop.IOLoop.instance()
        self._ioloop.start()  # this is a blocking call, server has stopped on next line
        self._ioloop = None

    def shutdown(self):
        if self._ioloop:
            self._server.stop()
            self._ioloop.stop()

    def run_on_a_thread(self):
        process = multiprocessing.Process(target=self.serve_forever)
        process.start()
        time.sleep(1)  # just let it start
        return lambda: process.terminate()