How to get original HTTP 405 response body from target when Apigee throws another error processing the response?

My goal is to make an Apigee API proxy to retranslate all types of responses from target endpoint to client. There is a problem though: the target endpoint in some cases returns HTTP 405 without Allow header (which must be present by RFC 2616) and Apigee throws a new HTTP 502 error "Received 405 Response without Allow Header" (protocol.http.Response405WithoutAllowHeader).

For the client that works with the API proxy it is OK not to have the Allow header and no change to the target endpoint is allowed at the moment, so Apigee the only place where the problem could be solved. The ultimate goal is to ignore what Apigee throws (it would be ideal to suppress that error from Apigee ) and return the original HTTP 405 response from the target endpoint.

My first approach was to modify success.codes adding 405 to that target endpoint so, hopefully, Apigee ignores that 405 response from the target endpoint:

<Property name="success.codes">1xx,3xx,405</Property>

did not work, the protocol.http.Response405WithoutAllowHeader error is thrown by Apigee anyway (it seems like success.codes mostly used for API traffic statistics purposes).

The second approach to use Apigee FaultRule to provide response from a target endpoint when the target endpoint throws HTTP 405. I use RasieFault policy to re-rise 405 :

    <FaultRules>
        <FaultRule name="ErrorHandling-405">
            <Step>
                <Name>raise-fault-405</Name>
            </Step>
            <Condition>(response.status.code = 405)</Condition>
        </FaultRule>
    </FaultRules>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<RaiseFault async="false" continueOnError="false" enabled="true" name="raise-fault-405">
    <DisplayName>raise-fault-405</DisplayName>
    <Properties/>
    <FaultResponse>
        <Set>
            <Headers/>
            <Payload contentType="application/json">{ "meta":{ "code":"405", "msg":"IP address does not match" }, "data":{} }</Payload>
            <StatusCode>405</StatusCode>
            <ReasonPhrase>IP address does not match</ReasonPhrase>
        </Set>
    </FaultResponse>
    <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
</RaiseFault>

This approach works but has huge disadvantages: the response payload is hardcoded and if target endpoint changes the payload I will need to reconfigure the RaiseFault policy.

The much better way would be to have the payload copied from the response, like

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<RaiseFault async="false" continueOnError="false" enabled="true" name="raise-fault-405">
    <DisplayName>raise-fault-405</DisplayName>
    <Properties/>
    <FaultResponse>
        <Set>
            <Headers>
                <Header name="Allow">GET</Header>
            </Headers>
            <StatusCode>405</StatusCode>
        </Set>
        <Copy source="response">
            <Headers/>
            <ReasonPhrase/>
            <Payload/>
        </Copy>
    </FaultResponse>
    <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
</RaiseFault>

But it doesn't work, the response object (as well as error object) I tried to use as a source in the Copy node already contain paload that was thrown by Apigee "Received 405 Response without Allow Header" not the original response from the target endpoint.

How could I get the original response from the target endpoint in this configuration?

Ideally I would just tell Apigee not to validate the HTTP 405 response, leaving it all on me, something like

    <FaultRules>
        <FaultRule name="ErrorHandling-405">
            <!-- This empty fault rule was added to suppress the default Apigee validation saying stay off, Apigee, I know what is going on here, just send the original error from the target endpoint -->
            <Condition>(response.status.code = 405)</Condition>
        </FaultRule>
    </FaultRules>

But that approach doesn't work either. How could I suppress that Apigee validation that throws the protocol.http.Response405WithoutAllowHeader error?

Solved Solved
0 9 2,059
1 ACCEPTED SOLUTION

I don't know of a way to "capture" the payload response that the backend returns, before Apigee overwrites it. That's sort of unfortunate. Probably we should take a feature request, to allow the proxy to access that information when a fault occurs.

There may be a way for you to avoid the situation.

Are you using Apigee On-premises?

If so, in http.properties, you can set "HTTP.ignore.allow_header.for.405" via CWC

Follow along here.

Are you using Apigee cloud?

There may be a way for you to ask Apigee support to modify your configuration similarly. (refer support to this ticket: b/133631868)

Also, I think it would nice if there were a configurable Property in the HTTPTargetConnection, telling Apigee to not fail when the target returns a 405, and no allow header. This behavior violates the HTTP Specification, but it would be nice if Apigee could be more flexible about it.

I am imagining something like this:

<TargetEndpoint name="default">
  <HTTPTargetConnection>
    <URL>https://mytarget.example.net</URL>
    <Properties>
      <!-- this is not implemented, does not actually work -->
      <Property name="ignore.allow_header.for.405">true</Property>
    </Properties>
  </HTTPTargetConnection>
</TargetEndpoint>

I raised a feature request for this. refer to b/169173326 . But there's no commitment to implement this change, and no timeline.

View solution in original post

9 REPLIES 9

Not applicable

You have specified "Received 405 Response without Allow Header" you are seeing. You can make this as condition and in the fault rule can add one assign message with the response you want.

Thanks for the quick reply, Priyadarshi! Could you, please, elaborate more? I want to get the original response from the target endpoint and retranslate to the to the proxy endpoint. How could I get that response using AssingMessage policy? As I understand the AssignMessage policy would have the same Copy block I have in the RaiseFault policy but which object should be used as a source? Working with RaiseFault I tried both response and error objects as sources and both objects have the generated by Apigee "Received 405 Response without Allow Header" payload, not the original payload that was returned by target endpoint.

What is the original payload you are getting? Is this a fixed one or changing one?

The original payload (response payload from the target endpoint) could change. Is there a way to avoid Apigee validation (the code that throws protocol.http.Response405WithoutAllowHeader error)?

No, that cannot be changed. You can just customize the error code and response using Apigee policies.

Ok, if it cannot be changed and the protocol.http.Response405WithoutAllowHeader is thrown anyway, is there a way to get the original target endpoint response, before Apigee validation?

I don't know of a way to "capture" the payload response that the backend returns, before Apigee overwrites it. That's sort of unfortunate. Probably we should take a feature request, to allow the proxy to access that information when a fault occurs.

There may be a way for you to avoid the situation.

Are you using Apigee On-premises?

If so, in http.properties, you can set "HTTP.ignore.allow_header.for.405" via CWC

Follow along here.

Are you using Apigee cloud?

There may be a way for you to ask Apigee support to modify your configuration similarly. (refer support to this ticket: b/133631868)

Also, I think it would nice if there were a configurable Property in the HTTPTargetConnection, telling Apigee to not fail when the target returns a 405, and no allow header. This behavior violates the HTTP Specification, but it would be nice if Apigee could be more flexible about it.

I am imagining something like this:

<TargetEndpoint name="default">
  <HTTPTargetConnection>
    <URL>https://mytarget.example.net</URL>
    <Properties>
      <!-- this is not implemented, does not actually work -->
      <Property name="ignore.allow_header.for.405">true</Property>
    </Properties>
  </HTTPTargetConnection>
</TargetEndpoint>

I raised a feature request for this. refer to b/169173326 . But there's no commitment to implement this change, and no timeline.

Thanks a lot, yeah, we use Apigee cloud and will reach Apigee support to modify our configuration (we will refer support the ticket b/133631868)! That's probably the most canonical way to get what we need.

Not applicable

You have few more options which may give you the actual response from backend.

You can use javascript or java callout to call to your backend and get the original response from the backend. You can try and let us known.