@@ -99,6 +99,33 @@ public function __construct( Request $request )
9999 $ this ->request = $ request ;
100100 }
101101
102+ /**
103+ * Safely decode a JSON response from the looking glass backend.
104+ *
105+ * Birdseye/birdwatcher can return garbage (HTML error pages, empty bodies,
106+ * control characters) when the daemon is restarting or hits an internal
107+ * error. Without this guard, json_decode throws and we 500 the user.
108+ *
109+ * Returns an object with an empty "routes" array as a safe default so the
110+ * template always has something iterable.
111+ */
112+ private function safeJsonDecode ( ?string $ raw ): object
113+ {
114+ if ( $ raw === null || $ raw === '' ) {
115+ return (object )['routes ' => []];
116+ }
117+ try {
118+ $ decoded = json_decode ( $ raw , false , 512 , JSON_THROW_ON_ERROR );
119+ return is_object ( $ decoded ) ? $ decoded : (object )['routes ' => []];
120+ } catch ( \Throwable $ e ) {
121+ \Log::warning ( '[LookingGlass] Invalid JSON from backend ' , [
122+ 'error ' => $ e ->getMessage (),
123+ 'sample ' => substr ( (string )$ raw , 0 , 200 ),
124+ ] );
125+ return (object )['routes ' => []];
126+ }
127+ }
128+
102129 /**
103130 * Looking glass accessor
104131 *
@@ -142,7 +169,7 @@ private function addCommonParams( View $view ): View
142169 $ cust = Auth::check () ? Customer::find ( Auth::getUser ()->custid ) : null ;
143170 $ user = Auth::check () ? User::find ( Auth::id () ) : null ;
144171
145- $ view ->with ( 'status ' , json_decode ( $ this ->lg ()->status (), false , 512 , JSON_THROW_ON_ERROR ));
172+ $ view ->with ( 'status ' , $ this -> safeJsonDecode ( $ this ->lg ()->status () ));
146173 $ view ->with ( 'lg ' , $ this ->lg () );
147174 $ view ->with ( 'routers ' , RouterAggregator::forDropdown ( $ cust , $ user ) );
148175 $ view ->with ( 'tabRouters ' , RouterAggregator::forTab ( $ cust , $ user ) );
@@ -212,7 +239,7 @@ public function bgpSummary(string $handle ): View
212239 {
213240 // get bgp protocol summary
214241 $ view = view ('services/lg/bgp-summary ' )->with ([
215- 'content ' => json_decode ( $ this ->lg ()->bgpSummary (), false , 512 , JSON_THROW_ON_ERROR ),
242+ 'content ' => $ this -> safeJsonDecode ( $ this ->lg ()->bgpSummary () ),
216243 ]);
217244
218245 return $ this ->addCommonParams ( $ view );
@@ -246,7 +273,7 @@ public function routesForTable( string $handle, string $table ): RedirectRespons
246273 }
247274
248275 $ view = view ('services/lg/routes ' )->with ([
249- 'content ' => json_decode ( $ routes, false , 512 , JSON_THROW_ON_ERROR ),
276+ 'content ' => $ this -> safeJsonDecode ( $ routes ),
250277 'source ' => 'table ' , 'name ' => $ table ,
251278 'peerName ' => null ,
252279 ]);
@@ -265,7 +292,7 @@ public function routesForProtocol( string $handle, string $protocol ): RedirectR
265292 try {
266293 // get bgp protocol summary
267294 $ view = view ('services/lg/routes ' )->with ([
268- 'content ' => json_decode ( $ this ->lg ()->routesForProtocol ( $ protocol ), false , 512 , JSON_THROW_ON_ERROR ),
295+ 'content ' => $ this -> safeJsonDecode ( $ this ->lg ()->routesForProtocol ( $ protocol ) ),
269296 'source ' => 'protocol ' , 'name ' => $ protocol ,
270297 'peerName ' => $ this ->peerName ( $ protocol ),
271298 ]);
@@ -288,7 +315,7 @@ public function routesForExport( string $handle, string $protocol ): View
288315 {
289316 // get bgp protocol summary
290317 $ view = view ('services/lg/routes ' )->with ([
291- 'content ' => json_decode ( $ this ->lg ()->routesForExport ( $ protocol ), false , 512 , JSON_THROW_ON_ERROR ),
318+ 'content ' => $ this -> safeJsonDecode ( $ this ->lg ()->routesForExport ( $ protocol ) ),
292319 'source ' => 'export to protocol ' ,
293320 'name ' => $ protocol ,
294321 'peerName ' => $ this ->peerName ( $ protocol ),
@@ -308,11 +335,8 @@ public function routesForExport( string $handle, string $protocol ): View
308335 */
309336 public function routeProtocol ( string $ handle , string $ network , string $ mask , string $ protocol ): View
310337 {
311- $ raw = $ this ->lg ()->protocolRoute ($ protocol , $ network , (int ) $ mask );
312- $ content = json_decode ($ raw , false );
313-
314338 return view ('services/lg/route ' )->with ([
315- 'content ' => $ content ?: ( object )[ ' routes ' => []] ,
339+ 'content ' => $ this -> safeJsonDecode ( $ this -> lg ()-> protocolRoute ( $ protocol , $ network , ( int ) $ mask ) ) ,
316340 'source ' => 'protocol ' ,
317341 'name ' => $ protocol ,
318342 'lg ' => $ this ->lg (),
@@ -332,11 +356,8 @@ public function routeProtocol( string $handle, string $network, string $mask, st
332356 */
333357 public function routeTable ( string $ handle , string $ network , string $ mask , string $ table ): View
334358 {
335- $ raw = $ this ->lg ()->protocolTable ( $ table , $ network , (int )$ mask );
336- $ content = json_decode ( $ raw , false );
337-
338359 return view ('services/lg/route ' )->with ( [
339- 'content ' => $ content ?: ( object )[ ' routes ' => []] ,
360+ 'content ' => $ this -> safeJsonDecode ( $ this -> lg ()-> protocolTable ( $ table , $ network , ( int ) $ mask ) ) ,
340361 'source ' => 'table ' ,
341362 'name ' => $ table ,
342363 'lg ' => $ this ->lg (),
@@ -357,7 +378,7 @@ public function routeTable( string $handle, string $network, string $mask, strin
357378 public function routeExport ( string $ handle , string $ network , string $ mask , string $ protocol ): View
358379 {
359380 return view ('services/lg/route ' )->with ([
360- 'content ' => json_decode ( $ this ->lg ()->exportRoute ( $ protocol , $ network , (int )$ mask ), false ),
381+ 'content ' => $ this -> safeJsonDecode ( $ this ->lg ()->exportRoute ( $ protocol , $ network , (int )$ mask ) ),
361382 'source ' => 'export ' ,
362383 'name ' => $ protocol ,
363384 'lg ' => $ this ->lg (),
@@ -503,7 +524,7 @@ public function routesFiltered( string $handle, string $protocol ): RedirectResp
503524 try {
504525 $ routes = $ this ->getFilteredRoutes ( $ protocol );
505526 $ view = view ('services/lg/routes ' )->with ([
506- 'content ' => json_decode ( $ routes, false , 512 , JSON_THROW_ON_ERROR ),
527+ 'content ' => $ this -> safeJsonDecode ( $ routes ),
507528 'source ' => 'filtered from protocol ' ,
508529 'name ' => $ protocol ,
509530 'peerName ' => $ this ->peerName ( $ protocol ),
@@ -523,7 +544,7 @@ public function routesNotExported( string $handle, string $protocol ): RedirectR
523544 try {
524545 $ routes = $ this ->getNotExportedRoutes ( $ protocol );
525546 $ view = view ('services/lg/routes ' )->with ([
526- 'content ' => json_decode ( $ routes, false , 512 , JSON_THROW_ON_ERROR ),
547+ 'content ' => $ this -> safeJsonDecode ( $ routes ),
527548 'source ' => 'not exported to protocol ' ,
528549 'name ' => $ protocol ,
529550 'peerName ' => $ this ->peerName ( $ protocol ),
0 commit comments