55#include < ydb/core/grpc_services/base/base.h>
66#include < ydb/core/base/ticket_parser.h>
77
8+ #include < library/cpp/json/json_writer.h>
89#include < library/cpp/lwtrace/all.h>
910#include < library/cpp/lwtrace/mon/mon_lwtrace.h>
1011#include < ydb/library/actors/core/probes.h>
@@ -145,6 +146,11 @@ class THttpMonRequest : public NMonitoring::IMonHttpRequest {
145146 return {};
146147 }
147148
149+ bool AcceptsJsonResponse () {
150+ TStringBuf acceptHeader = GetHeader (" Accept" );
151+ return acceptHeader.find (TStringBuf (" application/json" )) != TStringBuf::npos;
152+ }
153+
148154 virtual TStringBuf GetCookie (TStringBuf name) const override {
149155 NHttp::TCookies cookies (GetHeader (" Cookie" ));
150156 return cookies.Get (name);
@@ -247,10 +253,15 @@ class THttpMonLegacyActorRequest : public TActorBootstrapped<THttpMonLegacyActor
247253 PassAway ();
248254 }
249255
256+ bool CredentialsProvided () {
257+ return Container.GetCookie (" ydb_session_id" ) || Container.GetHeader (" Authorization" );
258+ }
259+
250260 TString YdbToHttpError (Ydb::StatusIds::StatusCode status) {
251261 switch (status) {
252262 case Ydb::StatusIds::UNAUTHORIZED:
253- return " 401 Unauthorized" ;
263+ // YDB status UNAUTHORIZED is used for both access denied case and if no credentials were provided.
264+ return CredentialsProvided () ? " 403 Forbidden" : " 401 Unauthorized" ;
254265 case Ydb::StatusIds::INTERNAL_ERROR:
255266 return " 500 Internal Server Error" ;
256267 case Ydb::StatusIds::UNAVAILABLE:
@@ -267,26 +278,45 @@ class THttpMonLegacyActorRequest : public TActorBootstrapped<THttpMonLegacyActor
267278 }
268279
269280 void ReplyErrorAndPassAway (const NKikimr::NGRpcService::TEvRequestAuthAndCheckResult& result) {
281+ ReplyErrorAndPassAway (result.Status , result.Issues , true );
282+ }
283+
284+ void ReplyErrorAndPassAway (Ydb::StatusIds::StatusCode status, const NYql::TIssues& issues, bool addAccessControlHeaders) {
270285 NHttp::THttpIncomingRequestPtr request = Event->Get ()->Request ;
271- NHttp::THeaders headers (request->Headers );
272286 TStringBuilder response;
273287 TStringBuilder body;
274- const TString httpError = YdbToHttpError (result.Status );
275- body << " <html><body><h1>" << httpError << " </h1>" ;
276- if (result.Issues ) {
277- body << " <p>" << result.Issues .ToString () << " </p>" ;
278- }
279- body << " </body></html>" ;
280- TString origin = TString (headers[" Origin" ]);
281- if (origin.empty ()) {
282- origin = " *" ;
288+ TStringBuf contentType;
289+ const TString httpError = YdbToHttpError (status);
290+
291+ if (Container.AcceptsJsonResponse ()) {
292+ contentType = " application/json" ;
293+ NJson::TJsonValue json;
294+ TString message;
295+ MakeJsonErrorReply (json, message, issues, NYdb::EStatus (status));
296+ NJson::WriteJson (&body.Out , &json);
297+ } else {
298+ contentType = " text/html" ;
299+ body << " <html><body><h1>" << httpError << " </h1>" ;
300+ if (issues) {
301+ body << " <p>" << issues.ToString () << " </p>" ;
302+ }
303+ body << " </body></html>" ;
283304 }
305+
284306 response << " HTTP/1.1 " << httpError << " \r\n " ;
285- response << " Access-Control-Allow-Origin: " << origin << " \r\n " ;
286- response << " Access-Control-Allow-Credentials: true\r\n " ;
287- response << " Access-Control-Allow-Headers: Content-Type,Authorization,Origin,Accept\r\n " ;
288- response << " Access-Control-Allow-Methods: OPTIONS, GET, POST, PUT, DELETE\r\n " ;
289- response << " Content-Type: text/html\r\n " ;
307+ if (addAccessControlHeaders) {
308+ NHttp::THeaders headers (request->Headers );
309+ TString origin = TString (headers[" Origin" ]);
310+ if (origin.empty ()) {
311+ origin = " *" ;
312+ }
313+ response << " Access-Control-Allow-Origin: " << origin << " \r\n " ;
314+ response << " Access-Control-Allow-Credentials: true\r\n " ;
315+ response << " Access-Control-Allow-Headers: Content-Type,Authorization,Origin,Accept\r\n " ;
316+ response << " Access-Control-Allow-Methods: OPTIONS, GET, POST, PUT, DELETE\r\n " ;
317+ }
318+
319+ response << " Content-Type: " << contentType << " \r\n " ;
290320 response << " Content-Length: " << body.Size () << " \r\n " ;
291321 response << " \r\n " ;
292322 response << body;
@@ -295,21 +325,9 @@ class THttpMonLegacyActorRequest : public TActorBootstrapped<THttpMonLegacyActor
295325 }
296326
297327 void ReplyForbiddenAndPassAway (const TString& error = {}) {
298- NHttp::THttpIncomingRequestPtr request = Event->Get ()->Request ;
299- TStringBuilder response;
300- TStringBuilder body;
301- body << " <html><body><h1>403 Forbidden</h1>" ;
302- if (!error.empty ()) {
303- body << " <p>" << error << " </p>" ;
304- }
305- body << " </body></html>" ;
306- response << " HTTP/1.1 403 Forbidden\r\n " ;
307- response << " Content-Type: text/html\r\n " ;
308- response << " Content-Length: " << body.Size () << " \r\n " ;
309- response << " \r\n " ;
310- response << body;
311- ReplyWith (request->CreateResponseString (response));
312- PassAway ();
328+ NYql::TIssues issues;
329+ issues.AddIssue (error);
330+ ReplyErrorAndPassAway (Ydb::StatusIds::UNAUTHORIZED, issues, false );
313331 }
314332
315333 void SendRequest (const NKikimr::NGRpcService::TEvRequestAuthAndCheckResult* result = nullptr ) {
0 commit comments