Blame SOURCES/bz1783106-02-send-request-from-python-to-ruby-more-directly.patch

bc4e95
From 770252b476bc342ea08da2bc5b83de713463d14a Mon Sep 17 00:00:00 2001
bc4e95
From: Ivan Devat <idevat@redhat.com>
bc4e95
Date: Thu, 12 Mar 2020 15:32:31 +0100
bc4e95
Subject: [PATCH 1/2] send request from python to ruby more directly
bc4e95
bc4e95
Rack protection middleware is launched before
bc4e95
TornadoCommunicationMiddleware. When request parts are unpacked in
bc4e95
TornadoCommunicationMiddleware they are not checked by rack protection.
bc4e95
bc4e95
This commit changes communication between python and ruby - request is
bc4e95
sent to ruby more directly (without need to unpack request in sinatra
bc4e95
middleware).
bc4e95
---
bc4e95
 pcs/daemon/ruby_pcsd.py                   | 217 ++++++++++++++--------
bc4e95
 pcs_test/tier0/daemon/app/fixtures_app.py |   7 +-
bc4e95
 pcs_test/tier0/daemon/test_ruby_pcsd.py   |  61 ++----
bc4e95
 pcsd/rserver.rb                           |  39 ++--
bc4e95
 4 files changed, 175 insertions(+), 149 deletions(-)
bc4e95
bc4e95
diff --git a/pcs/daemon/ruby_pcsd.py b/pcs/daemon/ruby_pcsd.py
bc4e95
index e612f8da..53c53eaf 100644
bc4e95
--- a/pcs/daemon/ruby_pcsd.py
bc4e95
+++ b/pcs/daemon/ruby_pcsd.py
bc4e95
@@ -7,8 +7,8 @@ from time import time as now
bc4e95
 import pycurl
bc4e95
 from tornado.gen import convert_yielded
bc4e95
 from tornado.web import HTTPError
bc4e95
-from tornado.httputil import split_host_and_port, HTTPServerRequest
bc4e95
-from tornado.httpclient import AsyncHTTPClient
bc4e95
+from tornado.httputil import HTTPServerRequest, HTTPHeaders
bc4e95
+from tornado.httpclient import AsyncHTTPClient, HTTPClientError
bc4e95
 from tornado.curl_httpclient import CurlError
bc4e95
 
bc4e95
 
bc4e95
@@ -29,6 +29,11 @@ RUBY_LOG_LEVEL_MAP = {
bc4e95
     "DEBUG": logging.DEBUG,
bc4e95
 }
bc4e95
 
bc4e95
+__id_dict = {"id": 0}
bc4e95
+def get_request_id():
bc4e95
+    __id_dict["id"] += 1
bc4e95
+    return __id_dict["id"]
bc4e95
+
bc4e95
 class SinatraResult(namedtuple("SinatraResult", "headers, status, body")):
bc4e95
     @classmethod
bc4e95
     def from_response(cls, response):
bc4e95
@@ -60,6 +65,59 @@ def process_response_logs(rb_log_list):
bc4e95
             group_id=group_id
bc4e95
         )
bc4e95
 
bc4e95
+class RubyDaemonRequest(namedtuple(
bc4e95
+    "RubyDaemonRequest",
bc4e95
+    "request_type, path, query, headers, method, body"
bc4e95
+)):
bc4e95
+    def __new__(
bc4e95
+        cls,
bc4e95
+        request_type,
bc4e95
+        http_request: HTTPServerRequest = None,
bc4e95
+        payload=None,
bc4e95
+    ):
bc4e95
+        headers = http_request.headers if http_request else HTTPHeaders()
bc4e95
+        headers.add("X-Pcsd-Type", request_type)
bc4e95
+        if payload:
bc4e95
+            headers.add(
bc4e95
+                "X-Pcsd-Payload",
bc4e95
+                b64encode(json.dumps(payload).encode()).decode()
bc4e95
+            )
bc4e95
+        return super(RubyDaemonRequest, cls).__new__(
bc4e95
+            cls,
bc4e95
+            request_type,
bc4e95
+            http_request.path if http_request else "",
bc4e95
+            http_request.query if http_request else "",
bc4e95
+            headers,
bc4e95
+            http_request.method if http_request else "GET",
bc4e95
+            http_request.body if http_request else None,
bc4e95
+        )
bc4e95
+
bc4e95
+    @property
bc4e95
+    def url(self):
bc4e95
+        # We do not need location for communication with ruby itself since we
bc4e95
+        # communicate via unix socket. But it is required by AsyncHTTPClient so
bc4e95
+        # "localhost" is used.
bc4e95
+        query = f"?{self.query}" if self.query else ""
bc4e95
+        return f"localhost/{self.path}{query}"
bc4e95
+
bc4e95
+    @property
bc4e95
+    def is_get(self):
bc4e95
+        return self.method.upper() == "GET"
bc4e95
+
bc4e95
+    @property
bc4e95
+    def has_http_request_detail(self):
bc4e95
+        return self.path or self.query or self.method != "GET" or self.body
bc4e95
+
bc4e95
+def log_ruby_daemon_request(label, request: RubyDaemonRequest):
bc4e95
+    log.pcsd.debug("%s type: '%s'", label, request.request_type)
bc4e95
+    if request.has_http_request_detail:
bc4e95
+        log.pcsd.debug("%s path: '%s'", label, request.path)
bc4e95
+        if request.query:
bc4e95
+            log.pcsd.debug("%s query: '%s'", label, request.query)
bc4e95
+        log.pcsd.debug("%s method: '%s'", label, request.method)
bc4e95
+        if request.body:
bc4e95
+            log.pcsd.debug("%s body: '%s'", label, request.body)
bc4e95
+
bc4e95
 class Wrapper:
bc4e95
     def __init__(self, pcsd_ruby_socket, debug=False):
bc4e95
         self.__debug = debug
bc4e95
@@ -67,74 +125,87 @@ class Wrapper:
bc4e95
         self.__client = AsyncHTTPClient()
bc4e95
         self.__pcsd_ruby_socket = pcsd_ruby_socket
bc4e95
 
bc4e95
-    @staticmethod
bc4e95
-    def get_sinatra_request(request: HTTPServerRequest):
bc4e95
-        host, port = split_host_and_port(request.host)
bc4e95
-        return {"env": {
bc4e95
-            "PATH_INFO": request.path,
bc4e95
-            "QUERY_STRING": request.query,
bc4e95
-            "REMOTE_ADDR": request.remote_ip,
bc4e95
-            "REMOTE_HOST": request.host,
bc4e95
-            "REQUEST_METHOD": request.method,
bc4e95
-            "REQUEST_URI": f"{request.protocol}://{request.host}{request.uri}",
bc4e95
-            "SCRIPT_NAME": "",
bc4e95
-            "SERVER_NAME": host,
bc4e95
-            "SERVER_PORT": port,
bc4e95
-            "SERVER_PROTOCOL": request.version,
bc4e95
-            "HTTP_HOST": request.host,
bc4e95
-            "HTTP_ACCEPT": "*/*",
bc4e95
-            "HTTP_COOKIE": ";".join([
bc4e95
-                v.OutputString() for v in request.cookies.values()
bc4e95
-            ]),
bc4e95
-            "HTTPS": "on" if request.protocol == "https" else "off",
bc4e95
-            "HTTP_VERSION": request.version,
bc4e95
-            "REQUEST_PATH": request.path,
bc4e95
-            "rack.input": request.body.decode("utf8"),
bc4e95
-        }}
bc4e95
-
bc4e95
     def prepare_curl_callback(self, curl):
bc4e95
         curl.setopt(pycurl.UNIX_SOCKET_PATH, self.__pcsd_ruby_socket)
bc4e95
         curl.setopt(pycurl.TIMEOUT, 70)
bc4e95
 
bc4e95
-    async def send_to_ruby(self, request_json):
bc4e95
-        # We do not need location for communication with ruby itself since we
bc4e95
-        # communicate via unix socket. But it is required by AsyncHTTPClient so
bc4e95
-        # "localhost" is used.
bc4e95
-        tornado_request = b64encode(request_json.encode()).decode()
bc4e95
-        return (await self.__client.fetch(
bc4e95
-            "localhost",
bc4e95
-            method="POST",
bc4e95
-            body=f"TORNADO_REQUEST={tornado_request}",
bc4e95
-            prepare_curl_callback=self.prepare_curl_callback,
bc4e95
-        )).body
bc4e95
-
bc4e95
-    async def run_ruby(self, request_type, request=None):
bc4e95
-        """
bc4e95
-        request_type: SINATRA_GUI|SINATRA_REMOTE|SYNC_CONFIGS
bc4e95
-        request: result of get_sinatra_request|None
bc4e95
-            i.e. it has structure returned by get_sinatra_request if the request
bc4e95
-            is not None - so we can get SERVER_NAME and  SERVER_PORT
bc4e95
-        """
bc4e95
-        request = request or {}
bc4e95
-        request.update({"type": request_type})
bc4e95
-        request_json = json.dumps(request)
bc4e95
-
bc4e95
-        if self.__debug:
bc4e95
-            log.pcsd.debug("Ruby daemon request: '%s'", request_json)
bc4e95
+    async def send_to_ruby(self, request: RubyDaemonRequest):
bc4e95
         try:
bc4e95
-            ruby_response = await self.send_to_ruby(request_json)
bc4e95
+            return (await self.__client.fetch(
bc4e95
+                request.url,
bc4e95
+                headers=request.headers,
bc4e95
+                method=request.method,
bc4e95
+                # Tornado enforces body=None for GET method:
bc4e95
+                # Even with `allow_nonstandard_methods` we disallow GET with a
bc4e95
+                # body (because libcurl doesn't allow it unless we use
bc4e95
+                # CUSTOMREQUEST).  While the spec doesn't forbid clients from
bc4e95
+                # sending a body, it arguably disallows the server from doing
bc4e95
+                # anything with them.
bc4e95
+                body=(request.body if not request.is_get else None),
bc4e95
+                prepare_curl_callback=self.prepare_curl_callback,
bc4e95
+            )).body
bc4e95
         except CurlError as e:
bc4e95
+            # This error we can get e.g. when ruby daemon is down.
bc4e95
             log.pcsd.error(
bc4e95
                 "Cannot connect to ruby daemon (message: '%s'). Is it running?",
bc4e95
                 e
bc4e95
             )
bc4e95
             raise HTTPError(500)
bc4e95
+        except HTTPClientError as e:
bc4e95
+            # This error we can get e.g. when rack protection raises exception.
bc4e95
+            log.pcsd.error(
bc4e95
+                (
bc4e95
+                    "Got error from ruby daemon (message: '%s')."
bc4e95
+                    " Try checking system logs (e.g. journal, systemctl status"
bc4e95
+                    " pcsd.service) for more information.."
bc4e95
+                ),
bc4e95
+                e
bc4e95
+            )
bc4e95
+            raise HTTPError(500)
bc4e95
+
bc4e95
+    async def run_ruby(
bc4e95
+        self,
bc4e95
+        request_type,
bc4e95
+        http_request: HTTPServerRequest = None,
bc4e95
+        payload=None,
bc4e95
+    ):
bc4e95
+        request = RubyDaemonRequest(request_type, http_request, payload)
bc4e95
+        request_id = get_request_id()
bc4e95
+
bc4e95
+        def log_request():
bc4e95
+            log_ruby_daemon_request(
bc4e95
+                f"Ruby daemon request (id: {request_id})",
bc4e95
+                request,
bc4e95
+            )
bc4e95
+
bc4e95
+        if self.__debug:
bc4e95
+            log_request()
bc4e95
+
bc4e95
+        return self.process_ruby_response(
bc4e95
+            f"Ruby daemon response (id: {request_id})",
bc4e95
+            log_request,
bc4e95
+            await self.send_to_ruby(request),
bc4e95
+        )
bc4e95
+
bc4e95
+    def process_ruby_response(self, label, log_request, ruby_response):
bc4e95
+        """
bc4e95
+        Return relevant part of unpacked ruby response. As a side effect
bc4e95
+        relevant logs are writen.
bc4e95
 
bc4e95
+        string label -- is used as a log prefix
bc4e95
+        callable log_request -- is used to log request when some errors happen;
bc4e95
+            we want to log request before error even if there is not debug mode
bc4e95
+        string ruby_response -- body of response from ruby; it should contain
bc4e95
+            json with dictionary with response specific keys
bc4e95
+        """
bc4e95
         try:
bc4e95
             response = json.loads(ruby_response)
bc4e95
             if "error" in response:
bc4e95
+                if not self.__debug:
bc4e95
+                    log_request()
bc4e95
                 log.pcsd.error(
bc4e95
-                    "Ruby daemon response contains an error: '%s'",
bc4e95
+                    "%s contains an error: '%s'",
bc4e95
+                    label,
bc4e95
                     json.dumps(response)
bc4e95
                 )
bc4e95
                 raise HTTPError(500)
bc4e95
@@ -144,56 +215,52 @@ class Wrapper:
bc4e95
                 body = b64decode(response.pop("body"))
bc4e95
                 if self.__debug:
bc4e95
                     log.pcsd.debug(
bc4e95
-                        "Ruby daemon response (without logs and body): '%s'",
bc4e95
+                        "%s (without logs and body): '%s'",
bc4e95
+                        label,
bc4e95
                         json.dumps(response)
bc4e95
                     )
bc4e95
-                    log.pcsd.debug("Ruby daemon response body: '%s'", body)
bc4e95
+                    log.pcsd.debug("%s body: '%s'", label, body)
bc4e95
                 response["body"] = body
bc4e95
 
bc4e95
             elif self.__debug:
bc4e95
                 log.pcsd.debug(
bc4e95
-                    "Ruby daemon response (without logs): '%s'",
bc4e95
+                    "%s (without logs): '%s'",
bc4e95
+                    label,
bc4e95
                     json.dumps(response)
bc4e95
                 )
bc4e95
             process_response_logs(logs)
bc4e95
             return response
bc4e95
         except (json.JSONDecodeError, binascii.Error) as e:
bc4e95
             if self.__debug:
bc4e95
-                log.pcsd.debug("Ruby daemon response: '%s'", ruby_response)
bc4e95
+                log.pcsd.debug("%s: '%s'", label, ruby_response)
bc4e95
+            else:
bc4e95
+                log_request()
bc4e95
+
bc4e95
             log.pcsd.error("Cannot decode json from ruby pcsd wrapper: '%s'", e)
bc4e95
             raise HTTPError(500)
bc4e95
 
bc4e95
     async def request_gui(
bc4e95
         self, request: HTTPServerRequest, user, groups, is_authenticated
bc4e95
     ) -> SinatraResult:
bc4e95
-        sinatra_request = self.get_sinatra_request(request)
bc4e95
         # Sessions handling was removed from ruby. However, some session
bc4e95
         # information is needed for ruby code (e.g. rendering some parts of
bc4e95
         # templates). So this information must be sent to ruby by another way.
bc4e95
-        sinatra_request.update({
bc4e95
-            "session": {
bc4e95
+        return SinatraResult.from_response(
bc4e95
+            await convert_yielded(self.run_ruby(SINATRA_GUI, request, {
bc4e95
                 "username": user,
bc4e95
                 "groups": groups,
bc4e95
                 "is_authenticated": is_authenticated,
bc4e95
-            }
bc4e95
-        })
bc4e95
-        response = await convert_yielded(self.run_ruby(
bc4e95
-            SINATRA_GUI,
bc4e95
-            sinatra_request
bc4e95
-        ))
bc4e95
-        return SinatraResult.from_response(response)
bc4e95
+            }))
bc4e95
+        )
bc4e95
 
bc4e95
     async def request_remote(self, request: HTTPServerRequest) -> SinatraResult:
bc4e95
-        response = await convert_yielded(self.run_ruby(
bc4e95
-            SINATRA_REMOTE,
bc4e95
-            self.get_sinatra_request(request)
bc4e95
-        ))
bc4e95
-        return SinatraResult.from_response(response)
bc4e95
+        return SinatraResult.from_response(
bc4e95
+            await convert_yielded(self.run_ruby(SINATRA_REMOTE, request))
bc4e95
+        )
bc4e95
 
bc4e95
     async def sync_configs(self):
bc4e95
         try:
bc4e95
-            response = await convert_yielded(self.run_ruby(SYNC_CONFIGS))
bc4e95
-            return response["next"]
bc4e95
+            return (await convert_yielded(self.run_ruby(SYNC_CONFIGS)))["next"]
bc4e95
         except HTTPError:
bc4e95
             log.pcsd.error("Config synchronization failed")
bc4e95
             return int(now()) + DEFAULT_SYNC_CONFIG_DELAY
bc4e95
diff --git a/pcs_test/tier0/daemon/app/fixtures_app.py b/pcs_test/tier0/daemon/app/fixtures_app.py
bc4e95
index 8d5b8f4c..590203b4 100644
bc4e95
--- a/pcs_test/tier0/daemon/app/fixtures_app.py
bc4e95
+++ b/pcs_test/tier0/daemon/app/fixtures_app.py
bc4e95
@@ -20,7 +20,12 @@ class RubyPcsdWrapper(ruby_pcsd.Wrapper):
bc4e95
         self.headers = {"Some": "value"}
bc4e95
         self.body = b"Success action"
bc4e95
 
bc4e95
-    async def run_ruby(self, request_type, request=None):
bc4e95
+    async def run_ruby(
bc4e95
+        self,
bc4e95
+        request_type,
bc4e95
+        http_request=None,
bc4e95
+        payload=None,
bc4e95
+    ):
bc4e95
         if request_type != self.request_type:
bc4e95
             raise AssertionError(
bc4e95
                 f"Wrong request type: expected '{self.request_type}'"
bc4e95
diff --git a/pcs_test/tier0/daemon/test_ruby_pcsd.py b/pcs_test/tier0/daemon/test_ruby_pcsd.py
bc4e95
index 28f14c87..32eb74cc 100644
bc4e95
--- a/pcs_test/tier0/daemon/test_ruby_pcsd.py
bc4e95
+++ b/pcs_test/tier0/daemon/test_ruby_pcsd.py
bc4e95
@@ -4,7 +4,7 @@ from base64 import b64encode
bc4e95
 from unittest import TestCase, mock
bc4e95
 from urllib.parse import urlencode
bc4e95
 
bc4e95
-from tornado.httputil import HTTPServerRequest
bc4e95
+from tornado.httputil import HTTPServerRequest, HTTPHeaders
bc4e95
 from tornado.testing import AsyncTestCase, gen_test
bc4e95
 from tornado.web import HTTPError
bc4e95
 
bc4e95
@@ -22,46 +22,17 @@ def create_http_request():
bc4e95
     return HTTPServerRequest(
bc4e95
         method="POST",
bc4e95
         uri="/pcsd/uri",
bc4e95
-        headers={"Cookie": "cookie1=first;cookie2=second"},
bc4e95
+        headers=HTTPHeaders({"Cookie": "cookie1=first;cookie2=second"}),
bc4e95
         body=str.encode(urlencode({"post-key": "post-value"})),
bc4e95
         host="pcsd-host:2224"
bc4e95
     )
bc4e95
 
bc4e95
-class GetSinatraRequest(TestCase):
bc4e95
-    def test_translate_request(self):
bc4e95
-        # pylint: disable=invalid-name
bc4e95
-        self.maxDiff = None
bc4e95
-        self.assertEqual(
bc4e95
-            create_wrapper().get_sinatra_request(create_http_request()),
bc4e95
-            {
bc4e95
-                'env': {
bc4e95
-                    'HTTPS': 'off',
bc4e95
-                    'HTTP_ACCEPT': '*/*',
bc4e95
-                    'HTTP_COOKIE': 'cookie1=first;cookie2=second',
bc4e95
-                    'HTTP_HOST': 'pcsd-host:2224',
bc4e95
-                    'HTTP_VERSION': 'HTTP/1.0',
bc4e95
-                    'PATH_INFO': '/pcsd/uri',
bc4e95
-                    'QUERY_STRING': '',
bc4e95
-                    'REMOTE_ADDR': None, # It requires complicated request args
bc4e95
-                    'REMOTE_HOST': 'pcsd-host:2224',
bc4e95
-                    'REQUEST_METHOD': 'POST',
bc4e95
-                    'REQUEST_PATH': '/pcsd/uri',
bc4e95
-                    'REQUEST_URI': 'http://pcsd-host:2224/pcsd/uri',
bc4e95
-                    'SCRIPT_NAME': '',
bc4e95
-                    'SERVER_NAME': 'pcsd-host',
bc4e95
-                    'SERVER_PORT': 2224,
bc4e95
-                    'SERVER_PROTOCOL': 'HTTP/1.0',
bc4e95
-                    'rack.input': 'post-key=post-value'
bc4e95
-                }
bc4e95
-            }
bc4e95
-        )
bc4e95
-
bc4e95
 patch_ruby_pcsd = create_patcher(ruby_pcsd)
bc4e95
 
bc4e95
 class RunRuby(AsyncTestCase):
bc4e95
     def setUp(self):
bc4e95
         self.ruby_response = ""
bc4e95
-        self.request = self.create_request()
bc4e95
+        self.request = ruby_pcsd.RubyDaemonRequest(ruby_pcsd.SYNC_CONFIGS)
bc4e95
         self.wrapper = create_wrapper()
bc4e95
         patcher = mock.patch.object(
bc4e95
             self.wrapper,
bc4e95
@@ -72,14 +43,10 @@ class RunRuby(AsyncTestCase):
bc4e95
         patcher.start()
bc4e95
         super().setUp()
bc4e95
 
bc4e95
-    async def send_to_ruby(self, request_json):
bc4e95
-        self.assertEqual(json.loads(request_json), self.request)
bc4e95
+    async def send_to_ruby(self, ruby_request):
bc4e95
+        self.assertEqual(ruby_request, self.request)
bc4e95
         return self.ruby_response
bc4e95
 
bc4e95
-    @staticmethod
bc4e95
-    def create_request(_type=ruby_pcsd.SYNC_CONFIGS):
bc4e95
-        return {"type": _type}
bc4e95
-
bc4e95
     def set_run_result(self, run_result):
bc4e95
         self.ruby_response = json.dumps({**run_result, "logs": []})
bc4e95
 
bc4e95
@@ -125,10 +92,10 @@ class RunRuby(AsyncTestCase):
bc4e95
             "body": b64encode(str.encode(body)).decode(),
bc4e95
         })
bc4e95
         http_request = create_http_request()
bc4e95
-        self.request = {
bc4e95
-            **self.create_request(ruby_pcsd.SINATRA_REMOTE),
bc4e95
-            **self.wrapper.get_sinatra_request(http_request),
bc4e95
-        }
bc4e95
+        self.request = ruby_pcsd.RubyDaemonRequest(
bc4e95
+            ruby_pcsd.SINATRA_REMOTE,
bc4e95
+            http_request,
bc4e95
+        )
bc4e95
         result = yield self.wrapper.request_remote(http_request)
bc4e95
         self.assert_sinatra_result(result, headers, status, body)
bc4e95
 
bc4e95
@@ -148,15 +115,15 @@ class RunRuby(AsyncTestCase):
bc4e95
             "body": b64encode(str.encode(body)).decode(),
bc4e95
         })
bc4e95
         http_request = create_http_request()
bc4e95
-        self.request = {
bc4e95
-            **self.create_request(ruby_pcsd.SINATRA_GUI),
bc4e95
-            **self.wrapper.get_sinatra_request(http_request),
bc4e95
-            "session": {
bc4e95
+        self.request = ruby_pcsd.RubyDaemonRequest(
bc4e95
+            ruby_pcsd.SINATRA_GUI,
bc4e95
+            http_request,
bc4e95
+            {
bc4e95
                 "username": user,
bc4e95
                 "groups": groups,
bc4e95
                 "is_authenticated": is_authenticated,
bc4e95
             }
bc4e95
-        }
bc4e95
+        )
bc4e95
         result = yield self.wrapper.request_gui(
bc4e95
             http_request,
bc4e95
             user=user,
bc4e95
diff --git a/pcsd/rserver.rb b/pcsd/rserver.rb
bc4e95
index 6002a73c..4b58f252 100644
bc4e95
--- a/pcsd/rserver.rb
bc4e95
+++ b/pcsd/rserver.rb
bc4e95
@@ -11,42 +11,25 @@ def pack_response(response)
bc4e95
   return [200, {}, [response.to_json.to_str]]
bc4e95
 end
bc4e95
 
bc4e95
-def unpack_request(transport_env)
bc4e95
-  return JSON.parse(Base64.strict_decode64(
bc4e95
-    transport_env["rack.request.form_hash"]["TORNADO_REQUEST"]
bc4e95
-  ))
bc4e95
-end
bc4e95
-
bc4e95
 class TornadoCommunicationMiddleware
bc4e95
   def initialize(app)
bc4e95
     @app = app
bc4e95
   end
bc4e95
 
bc4e95
-  def call(transport_env)
bc4e95
+  def call(env)
bc4e95
     Thread.current[:pcsd_logger_container] = []
bc4e95
     begin
bc4e95
-      request = unpack_request(transport_env)
bc4e95
+      type = env["HTTP_X_PCSD_TYPE"]
bc4e95
 
bc4e95
-      if ["sinatra_gui", "sinatra_remote"].include?(request["type"])
bc4e95
-        if request["type"] == "sinatra_gui"
bc4e95
-          session = request["session"]
bc4e95
+      if ["sinatra_gui", "sinatra_remote"].include?(type)
bc4e95
+        if type == "sinatra_gui"
bc4e95
+          session = JSON.parse(Base64.strict_decode64(env["HTTP_X_PCSD_PAYLOAD"]))
bc4e95
           Thread.current[:tornado_username] = session["username"]
bc4e95
           Thread.current[:tornado_groups] = session["groups"]
bc4e95
           Thread.current[:tornado_is_authenticated] = session["is_authenticated"]
bc4e95
         end
bc4e95
 
bc4e95
-        # Keys rack.input and rack.errors are required. We make sure they are
bc4e95
-        # there.
bc4e95
-        request_env = request["env"]
bc4e95
-        request_env["rack.input"] = StringIO.new(request_env["rack.input"])
bc4e95
-        request_env["rack.errors"] = StringIO.new()
bc4e95
-
bc4e95
-        status, headers, body = @app.call(request_env)
bc4e95
-
bc4e95
-        rack_errors = request_env['rack.errors'].string()
bc4e95
-        if not rack_errors.empty?()
bc4e95
-          $logger.error(rack_errors)
bc4e95
-        end
bc4e95
+        status, headers, body = @app.call(env)
bc4e95
 
bc4e95
         return pack_response({
bc4e95
           :status => status,
bc4e95
@@ -56,16 +39,20 @@ class TornadoCommunicationMiddleware
bc4e95
         })
bc4e95
       end
bc4e95
 
bc4e95
-      if request["type"] == "sync_configs"
bc4e95
+      if type == "sync_configs"
bc4e95
         return pack_response({
bc4e95
           :next => Time.now.to_i + run_cfgsync(),
bc4e95
           :logs => Thread.current[:pcsd_logger_container],
bc4e95
         })
bc4e95
       end
bc4e95
 
bc4e95
-      raise "Unexpected value for key 'type': '#{request['type']}'"
bc4e95
+      return pack_response({
bc4e95
+        :error => "Unexpected value for key 'type': '#{type}'"
bc4e95
+      })
bc4e95
     rescue => e
bc4e95
-      return pack_response({:error => "Processing request error: '#{e}'"})
bc4e95
+      return pack_response({
bc4e95
+        :error => "Processing request error: '#{e}' '#{e.backtrace}'"
bc4e95
+      })
bc4e95
     end
bc4e95
   end
bc4e95
 end
bc4e95
-- 
bc4e95
2.21.1
bc4e95