Unable to retrieve responseStatus.statusCode or a response header when request is intercepted at gateway

Unable to retrieve responseStatus.statusCode or a response header when request is intercepted at gateway

Hello,
I am using ServiceStack 8.4.0 on the server side (C#) and @servicestack/client 2.1.5 on the client.

In between the client and the services, we have an application gateway governed by a WAF that implements an OWASP ruleset. We are encountering a situation where a PUT request is being intercepted at the gateway. The gateway returns a 403 status code, along with a response “Server” header indicating it was the gateway that provided the response.

We have a class that derives from JsonServiceClient that takes care of routine stuff like attaching the bearer token and handling simple common errors from our services, but handling an error from a gateway is proving more challenging.

I want to be able to handle this situation by detecting the 403 status code and the response header, and then take an appropriate action. Inside the derived class, I set up this code prior to the request, in order to capture the response header I need:

private responseServer: string;
// further down...
    private prepareForRequest() {
        this.bearerToken = someBearerToken;
        this.responseFilter = res => {
            this.responseServer = res.headers.get("Server");
        }
    }

// for clarity, here's what the PUT looks like.
    put<T>(request: IReturn<T>, args?: any): Promise<T> {
        this.prepareForRequest();

        let promise = new Promise<T>((resolve, reject) => {
            super.put(request)
                .then(res => {
                    this.handleSuccessfulResponse();
                    resolve(res);
                }, msg => {
                    this.handleCompletion();
                    this.handleRejection(msg);
                    reject(msg);
                })
                .catch(ex => this.handleCompletion(ex))
        });

        return promise;
    }

In the service, I added this code to allow this header to be available:

        options.AddPolicy("CorsPolicy",
            builder => builder
                .SetIsOriginAllowed(CorsHelper.IsOriginAllowed)
                .RedactedStuff() 
                .WithExposedHeaders(new string[] { "Server" }));
        });

This works and I can view the response header, whenever the response is coming from the service.

On the client, the handleRejection function just takes a single parameter of any, but when the response comes from the service, it is an object that is populated with a responseStatus property which contains an error code. But, when the request is intercepted in the gateway, due to one of the WAF rules getting triggered, the response comes directly from the gateway and the service method is never invoked. In this case, the responseFilter function is never executed, and the handleRejection parameter is as shown below:

handleRejection: msg: TypeError: Failed to fetch
    at zone.js:1518:36
    at proto.<computed> (zone.js:973:24)
    at JsonServiceClientAuth.sendRequest (servicestack-client.mjs:1034:16)
    at JsonServiceClientAuth.fetch (servicestack-client.mjs:1003:21)
    at JsonServiceClientAuth.put (servicestack-client.mjs:799:21)
    at json.service.client.auth.ts:79:19
    at new ZoneAwarePromise (zone.js:1429:21)
    at JsonServiceClientAuth.put (json.service.client.auth.ts:78:23)
    at api.service.ts:986:25
    at _ZoneDelegate.invoke (zone.js:372:26)

I understand that what I’m trying to do is somewhat outside of the norm, I’m just trying to figure out if there is a way I can access both the status code and the response header when the response is coming directly from the gateway instead of the service.

Thanks.

If the error information doesn’t contain what you’re looking it may not be accessible, since you’re using a modified client anyway you may as well use a local modified JsonServiceClient so you have direct access to the fetch response object directly which may have what you need.

@servicestack/client is maintained within a single TypeScript file without any dependencies so it should be easy to include and use in your TypeScript project: