@@ -71,9 +71,6 @@ size_t read_body(Func func, request_rec* r) {
7171 return total_size;
7272}
7373
74- #define PTABLE (s, table ) print_table(s, #table, table)
75- #define PSTR (str ) ap_log_error(APLOG_MARK , APLOG_INFO , 0 , r->server, " %s = %s" , #str, str);
76-
7774void print_table (server_rec* s, const char * name, apr_table_t * table) {
7875 auto *fields = apr_table_elts (table);
7976 auto *e = (apr_table_entry_t *)fields->elts ;
@@ -94,10 +91,8 @@ class grpc_connection_provider {
9491 std::map<std::string, con_entry> channels;
9592public:
9693 grpc_connection_provider () {
97- printf (" ConProvider create\n " );
9894 }
9995 ~grpc_connection_provider () {
100- printf (" ConProvider destroy\n " );
10196 }
10297
10398 std::unique_ptr<::thalhammer::http::Handler::Stub> getStub (const char * host, int64_t timeout) {
@@ -137,7 +132,12 @@ class grpc_connection_provider {
137132
138133static grpc_connection_provider con_provider;
139134
140- int handle_request (request_rec* r, const grpcbackend_config_t * config) {
135+ http_handler::http_handler (request_rec* r) {
136+ this ->r = r;
137+ this ->config = static_cast <grpcbackend_config_t *>(ap_get_module_config (r->per_dir_config , &grpcbackend_module));
138+ }
139+
140+ int http_handler::handle_request () {
141141 if (config->host == nullptr )
142142 return HTTP_INTERNAL_SERVER_ERROR ;
143143 auto stub = con_provider.getStub (config->host , config->connect_timeout_ms );
@@ -183,26 +183,6 @@ int handle_request(request_rec* r, const grpcbackend_config_t* config) {
183183 }
184184 }
185185
186- /*
187- // Debug helpers
188- PSTR(r->uri);
189- PSTR(r->unparsed_uri);
190- PSTR(r->parsed_uri.scheme);
191- PSTR(r->parsed_uri.hostinfo);
192- PSTR(r->parsed_uri.user);
193- PSTR(r->parsed_uri.password);
194- PSTR(r->parsed_uri.hostname);
195- PSTR(r->parsed_uri.port_str);
196- PSTR(r->parsed_uri.path);
197- PSTR(r->parsed_uri.query);
198- PSTR(r->parsed_uri.fragment);
199- PSTR(r->filename);
200- PSTR(r->canonical_filename);
201- PSTR(r->path_info);
202- PSTR(r->args);
203- PSTR(r->hostname);
204- */
205-
206186 bool failed = false ;
207187 read_body ([&stream, &failed](const char * data, size_t len){
208188 ::thalhammer::http::HandleRequest req;
@@ -254,3 +234,134 @@ int handle_request(request_rec* r, const grpcbackend_config_t* config) {
254234
255235 return DONE ;
256236}
237+
238+ void websocket_handler::send (int type, const uint8_t * buffer, size_t buffer_size)
239+ {
240+ _server->send (_server, type, buffer, buffer_size);
241+ }
242+
243+ websocket_handler::websocket_handler (const WebSocketServer* server)
244+ : _server(server)
245+ {
246+ auto * r = server->request (server);
247+ auto * config = static_cast <grpcbackend_config_t *>(ap_get_module_config (r->per_dir_config , &grpcbackend_module));
248+ if (!config->host )
249+ throw std::runtime_error (" Missing grpc host" );
250+ _stub = con_provider.getStub (config->host , config->connect_timeout_ms );
251+
252+ if (!_stub) {
253+ throw std::runtime_error (" GRPC Backend timeout" );
254+ }
255+
256+ _stream = _stub->HandleWebSocket (&_call_context);
257+
258+ {
259+ ::thalhammer::http::HandleWebSocketRequest req;
260+ auto * client = req.mutable_request ()->mutable_client ();
261+ auto * con = r->connection ;
262+ client->set_local_port (con->local_addr ->port );
263+ client->set_local_ip (apr_addr_to_string (con->local_addr ));
264+ client->set_remote_port (con->client_addr ->port );
265+ client->set_remote_ip (apr_addr_to_string (con->client_addr ));
266+ client->set_encrypted (!strcmp (ap_http_scheme (r), " https" ));
267+ auto * request = req.mutable_request ();
268+ request->set_method (r->method );
269+ request->set_protocol (r->protocol );
270+ request->set_resource (r->unparsed_uri );
271+
272+ auto *fields = apr_table_elts (r->headers_in );
273+ auto *e = (apr_table_entry_t *)fields->elts ;
274+ for (int i = 0 ; i < fields->nelts ; i++) {
275+ auto * header = request->add_headers ();
276+ header->set_key (e[i].key );
277+ header->set_value (e[i].val );
278+ }
279+
280+ if (!_stream->Write (req)) {
281+ con_provider.reset_cache (config->host );
282+ throw std::runtime_error (" Failed to write initial grpc request" );
283+ }
284+ }
285+
286+ {
287+ ::thalhammer::http::HandleWebSocketResponse resp;
288+ if (!_stream->Read (&resp)) {
289+ con_provider.reset_cache (config->host );
290+ throw std::runtime_error (" Failed to read initial grpc response" );
291+ } else {
292+ for (auto & header : resp.response ().headers ()) {
293+ apr_table_setn (r->headers_out , apr_pstrdup (r->pool , header.key ().c_str ()), apr_pstrdup (r->pool , header.value ().c_str ()));
294+ std::string key = header.key ();
295+ std::transform (key.begin (), key.end (), key.begin (), ::tolower);
296+ }
297+ }
298+ }
299+
300+ _recv_thread = std::thread ([this ,r](){
301+ try {
302+ ::thalhammer::http::HandleWebSocketResponse resp;
303+ while (!_recv_shutdown && _stream->Read (&resp)) {
304+ auto & msg = resp.message ();
305+ int mtype = MESSAGE_TYPE_INVALID ;
306+ switch (msg.type ()) {
307+ case ::thalhammer::http::WebSocketMessage::TEXT :
308+ mtype = MESSAGE_TYPE_TEXT ; break ;
309+ case ::thalhammer::http::WebSocketMessage::BINARY :
310+ mtype = MESSAGE_TYPE_BINARY ; break ;
311+ case ::thalhammer::http::WebSocketMessage::CLOSE :
312+ _server->close (_server);
313+ _recv_shutdown = true ;
314+ break ;
315+ default :
316+ break ;
317+ }
318+ if (mtype != MESSAGE_TYPE_INVALID ) {
319+ auto & content = msg.content ();
320+ this ->send (mtype, (const uint8_t *)content.data (), content.size ());
321+ }
322+ }
323+ } catch (...) {
324+ ap_log_error (APLOG_MARK , APLOG_ERR , 0 , r->server , " Exception in read thread" );
325+ }
326+ });
327+ }
328+
329+ websocket_handler::~websocket_handler ()
330+ {
331+ }
332+
333+ void websocket_handler::on_message (int type, const uint8_t * buffer, size_t buffer_size)
334+ {
335+ ::thalhammer::http::HandleWebSocketRequest req;
336+ auto * msg = req.mutable_message ();
337+ switch (type) {
338+ case MESSAGE_TYPE_TEXT :
339+ msg->set_type (::thalhammer::http::WebSocketMessage::TEXT ); break ;
340+ case MESSAGE_TYPE_BINARY :
341+ msg->set_type (::thalhammer::http::WebSocketMessage::BINARY ); break ;
342+ }
343+ msg->set_content ((const char *)buffer, buffer_size);
344+
345+ if (!_stream->Write (req))
346+ {
347+ ap_log_error (APLOG_MARK , APLOG_ERR , 0 , _server->request (_server)->server , " Failed to send websocket message to backend" );
348+ }
349+ }
350+
351+ void websocket_handler::on_disconnect ()
352+ {
353+ ::thalhammer::http::HandleWebSocketRequest req;
354+ auto * msg = req.mutable_message ();
355+ msg->set_type (::thalhammer::http::WebSocketMessage::CLOSE );
356+ msg->set_content (" " );
357+ _stream->WriteLast (req, ::grpc::WriteOptions ());
358+
359+ _recv_shutdown = true ;
360+ ::grpc::Status s = _stream->Finish ();
361+ if (!s.ok ()) {
362+ ap_log_error (APLOG_MARK , APLOG_ERR , 0 , _server->request (_server)->server , " Failed to execute rpc: %s" , s.error_message ().c_str ());
363+ }
364+ if (_recv_thread.joinable ())
365+ _recv_thread.join ();
366+ }
367+
0 commit comments