1818from http .server import HTTPServer , BaseHTTPRequestHandler
1919from os import path
2020from urllib .parse import urlparse
21+ import errno
2122import socket
2223import sys
2324
3738"""
3839
3940
40- class NamedPipe (object ):
41+ class BackendError (Exception ):
42+ pass
43+
44+
45+ class UnixSocketConnector (object ):
46+ """Unix Domain Socket connector. Connects to socket lazily."""
47+
48+ def __init__ (self , socket_path ):
49+ self .socket_path = socket_path
50+ self .socket = None
51+
52+ @staticmethod
53+ def _get_error_message (os_error_number ):
54+ if os_error_number == errno .ENOENT :
55+ return "Unix Domain Socket '{}' does not exist"
56+ if os_error_number == errno .ECONNREFUSED :
57+ return "Connection to '{}' refused"
58+ return "Unknown error when connecting to '{}'"
59+
60+ def _connect (self ):
61+ if self .socket is None :
62+ try :
63+ s = socket .socket (socket .AF_UNIX , socket .SOCK_STREAM )
64+ s .connect (self .socket_path )
65+ s .settimeout (1 )
66+ # Assign last, to keep it None in case of exception.
67+ self .socket = s
68+ except OSError as ex :
69+ msg = self ._get_error_message (ex .errno )
70+ err = BackendError (msg .format (self .socket_path ))
71+ raise err from ex
72+
73+ def _reconnect (self ):
74+ self .socket .shutdown (socket .SHUT_RDWR )
75+ self .socket .close ()
76+ self .socket = None
77+ self ._connect ()
78+
79+ def recv (self , max_length ):
80+ self ._connect ()
81+ return self .socket .recv (max_length )
82+
83+ def sendall (self , data ):
84+ self ._connect ()
85+ try :
86+ return self .socket .sendall (data )
87+ except OSError as ex :
88+ if ex .errno == errno .EPIPE :
89+ # The connection was terminated by the backend.
90+ self ._reconnect ()
91+ return self .socket .sendall (data )
92+ else :
93+ raise
94+
95+
96+ class NamedPipeConnector (object ):
4197 """Windows named pipe simulating socket."""
4298
4399 def __init__ (self , ipc_path ):
@@ -61,19 +117,20 @@ def close(self):
61117 self .handle .close ()
62118
63119
64- def get_ipc_socket (ipc_path , timeout = 1 ):
120+ def get_ipc_connector (ipc_path ):
65121 if sys .platform == 'win32' :
66- return NamedPipe (ipc_path )
67-
68- sock = socket .socket (socket .AF_UNIX , socket .SOCK_STREAM )
69- sock .connect (ipc_path )
70- sock .settimeout (timeout )
71- return sock
122+ return NamedPipeConnector (ipc_path )
123+ return UnixSocketConnector (ipc_path )
72124
73125
74126class HTTPRequestHandler (BaseHTTPRequestHandler ):
75127
76128 def do_GET (self ):
129+ if self .path != '/' :
130+ self .send_response (404 )
131+ self .end_headers ()
132+ return
133+
77134 self .send_response (200 )
78135 self .send_header ("Content-type" , "text/plain" )
79136 self .addCORS ()
@@ -96,15 +153,27 @@ def do_POST(self):
96153 # self.log_message("Headers: {}".format(self.headers))
97154 # self.log_message("Request: {}".format(request_content))
98155
99- response_content = self .server .process (request_content )
100- # self.log_message("Response: {}".format(response_content))
101-
102- self .send_response (200 )
103- self .send_header ("Content-type" , "application/json" )
104- self .send_header ("Content-length" , len (response_content ))
105- self .addCORS ()
106- self .end_headers ()
107- self .wfile .write (response_content )
156+ try :
157+ response_content = self .server .process (request_content )
158+ # self.log_message("Response: {}".format(response_content))
159+
160+ self .send_response (200 )
161+ self .send_header ("Content-type" , "application/json" )
162+ self .send_header ("Content-length" , len (response_content ))
163+ self .addCORS ()
164+ self .end_headers ()
165+ self .wfile .write (response_content )
166+ except BackendError as err :
167+ self .send_response (502 )
168+ error_msg = str (err ).encode ('utf-8' )
169+ # TODO: Send as JSON-RPC response
170+ self .send_header ("Content-type" , "text/plain" )
171+ self .send_header ("Content-length" , len (error_msg ))
172+ self .end_headers ()
173+ self .wfile .write (error_msg )
174+ self .log_message ("Backend Error: {}" .format (err ))
175+
176+ # TODO: Handle other excaptions as error 500.
108177
109178 def addCORS (self ):
110179 self .send_header ("Access-Control-Allow-Origin" , "*" )
@@ -123,14 +192,14 @@ def __init__(self, proxy_url, backend_path):
123192 super (Proxy , self ).__init__ (proxy_address , HTTPRequestHandler )
124193
125194 self .backend_address = path .expanduser (backend_path )
126- self .sock = get_ipc_socket (self .backend_address )
195+ self .conn = get_ipc_connector (self .backend_address )
127196
128197 def process (self , request ):
129- self .sock .sendall (request )
198+ self .conn .sendall (request )
130199
131200 response = b''
132201 while True :
133- r = self .sock .recv (BUFSIZE )
202+ r = self .conn .recv (BUFSIZE )
134203 if not r :
135204 break
136205 if r [- 1 ] == DELIMITER :
0 commit comments