Can't do a websocket handshake using an APIM on azure - azure

I have some issues doing a handshake to a websocket endpoint through an Azure APIM. This error doesn't happen if I use the app service endpoint, it is only when the request is sent through the APIM.
The main problem, and as far I can see, is that the APIM is not sending the header Connection: Update, it overrides the header and sets it to keep-alive. I have tried to create a policy to set that header to Upgrade but the APIM returns the following error.
Error in element 'set-header' Header name is invalid or restricted
from modification
The policy I tried to use is
<policies>
<inbound>
<base />
<set-header name="Connection" exists-action="override">
<value>Upgrade</value>
</set-header>
</inbound>
...
</policies>
I opened an issue on the MS documentation github (https://github.com/MicrosoftDocs/azure-docs/issues/96878) asking this and they told me to try using some extra headers
Connection: Upgrade
Upgrade: Websocket
Sec-WebSocket-Version: 13
And the response is the same in any case...
{
"statusCode": 400,
"message": "Invalid websocket upgrade request"
}
Postman request and response
Also, there is another parameter negotiateVersion that is set to 1 that can't be seen on the picture.
Any way to solve this? Thanks in advance.

Related

Why is Azure APIM preflight response so slow?

I have an app service which is accessed via APIM.
Intermittently (about 1/5), HTTP responses for GET requests are taking >1 minute.
I noticed the preflight request is taking 1.4 minutes, which makes me think there is nothing wrong with the application itself, but rather with APIM.
I've confirmed that there is nothing DB related that is causing the slowness.
I have this policy set up in the API.
Does this mean the preflight request is being responded to by APIM, and not the backend web service?
<inbound>
<cors>
<allowed-origins>
<origin>*</origin>
</allowed-origins>
<allowed-methods>
<method>*</method>
</allowed-methods>
<allowed-headers>
<header>*</header>
</allowed-headers>
</cors>
<base />
</inbound>
Here is the preflight request:
And the timing:
Here's the actual request:
And the timing:
I've replicated this slowness using Chrome with disabled security settings:
"C:\Program Files\Google\Chrome\Application\chrome.exe" --disable-web-security --ignore-certificate-errors --user-data-dir="C:/ChromeDevSession"
in order to avoid the preflight request, however the response still takes about 1.4 minutes.
So the preflight request itself is not causing slowness.
I've tested the exact same request through the APIM test tab and am unable to replicate the slowness.
We have other app services running which are not behind APIM and they run fine, which is why I believe the issue is APIM.
However I'm very confused as to why I can't replicate the issue within the APIM test tab in the Portal (I've tried 100 times).
The only difference between the requests is that I've left out the Connection: keep-alive header in APIM because it was causing a 500 response.
Here's the pricing tier info:
Why is APIM intermittently slow and why is it only experienced through the web browser and not through the APIM test tab in the Portal?

Why don't I see any results when I run my test on Azure API Management

I signed up for an API that retrieves a zip file and downloads it. For this, I received a bearer token.
I have created an apim instance and was looking to test the API I had subscribed to which downloads the file.
When I tested it, the apim instance kept loading and got stuck.
I tested this on Postman and it works perfectly. I got the "200 OK response", but still got weird characters like "�vt�" in my body, but that's probably because I'm retrieving a zip file. But why doesn't it work also within Azure's apim? Are there any extra policies I need to add?
Kind regards
You got the "200 OK response", but still facing the issue, you can try to troubleshoot by following approach:
1. If Backend section is taking most of the time (approx. 5 seconds), which means there is some slowness or long running operation is taking place at the backend.
Once you have isolated that the slowness is at the backend, you need to investigate the backend application code of the Web API application. For scenarios where you don't have access to the backend, you can implement caching at APIM level like below. Read about how you can implement caching policies to improve performance in Azure API Management.
<?xml version="1.0" encoding="UTF-8"?>
<policies>
<inbound>
<base />
<cache-lookup vary-by-developer="true" vary-by-developer-groups="true" must-revalidate="true" downstream-caching-type="public" />
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
<cache-store duration="60" />
</outbound>
<on-error>
<base />
</on-error>
</policies>
You can refer to REST API service when called from azure APIM returning empty response body with Status code 200,
Azure API Management returning 200 status without response body, gzip Compression, and Azure API management gives 200 response code,

Azure API Management forward-request Not Honoring follow-redirects

I have an Azure API Management with the following backend policy:
<backend>
<forward-request timeout="10" follow-redirects="true" />
</backend>
When testing in Postman, I'm still receiving a 308 Permanent Redirect. I thought that follow-redirects followed a backend redirect response. Why is it getting passed back to the client?
follow-redirects:Specifies whether redirects from the backend service are followed by the gateway or returned to the caller.
As the article said, when you follow-redirects as true, it will returned to the client.

Azure API Management caching policy

In my Azure API Managemenent I'm defining a header based caching policy at API level.
The policy is quite simple:
<policies>
<inbound>
<check-header name="token" failed-check-httpcode="400" failed-check-error-message="Token header is missing" ignore-case="true" />
<cache-lookup vary-by-developer="false" vary-by-developer-groups="false" downstream-caching-type="none">
<vary-by-header>token</vary-by-header>
</cache-lookup>
<base />
</inbound>
<backend>
<forward-request />
</backend>
<outbound>
<cache-store duration="3600" />
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
This works ok in the case my downstream returns a 200 with a body - next request with same header token will hit the cache and the response will be returned from the API Management cache.
However is a error code is returned by the downstream (eg: 401 Unauthorized) that response is not cached by the API Management (confirmed by the tracing I've enabled on the API Management).
I was under the impression that whole responses are cached, but this doesn't seem to be the case...
Can somebody let me know if it's possible to cache responses also in case of unsuccessful http codes and if yes point me to some doc - I've been googling all day yesterday, but was unable to find more.
Thanks in advance!
This is by design. If you look into traces you should see message there "Backend service responed with the status code of 401 rather than 200 OK. Cache Store policy was not applied." The reasoning is that at APIM level we assume that non 200 responses are more transient than 200.
Say that client gets 401, goes on to do whatever to make sure that token is allowed to do the operation and retries the call. And still gets 401 from cache until cache expires.
That certainly may be added as an extra configuration option on a policy: http://aka.ms/apimwish
You could workaround that by using cache-lookup-value and cache-store-value. I.e. in the outbound section if you get 401 store some value in cache using cache-store-value to keyed with token. And in the inbound before cache-lookup do cache-lookup-value with token and see if you get value stored earlier. If you do you can generate 401 response right in place.

CORS Preflight request not working with Azure API Management

I have 2 Azure Websites (ASP.NET MVC 5 and ASP.NET WebApi 2). The MVC website has some jQuery which tries to post CORS request to the WebApi. It works just fine if it connects directly to the WebApi. However it doesn't work when trying to connect through the API Management.
The error I got in Chrome is:
XMLHttpRequest cannot load https://XXXXXX.azure-api.net/api/search. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://YYYYYY.azurewebsites.net' is therefore not allowed access.
I ruled out the problem being with the WebApi config, because as I said it works directly.
Below is my policy:
<policies>
<inbound>
<cors>
<allowed-origins>
<origin>*</origin>
<!-- allow any -->
</allowed-origins>
<allowed-headers>
<header>accept</header>
<header>accept-encoding</header>
<header>access-control-request-headers</header>
<header>access-control-request-method</header>
<header>connection</header>
<header>content-type</header>
<header>host</header>
<header>origin</header>
<header>referer</header>
<header>user-agent</header>
</allowed-headers>
<expose-headers>
<header>access-control-allow-headers</header>
<header>access-control-allow-origin</header>
<header>cache-control</header>
<header>content-length</header>
<header>date</header>
<header>expires</header>
<header>pragma</header>
<header>server</header>
<header>set-cookie</header>
<header>x-aspnet-version</header>
<header>x-powered-by</header>
</expose-headers>
</cors>
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
</policies>
Any ideas?
CORS policy intended use is for the cases when your backend does not support CORS. In that case you can put this policy in and it will reply to OPTION requests without forwarding them to your backend. And you can use this policy to decide which origins/headers/methods to process for CORS.
Alternatively, if your backend already supports CORS and you see no benefit in handling CORS flow on APIM level you could just proxy the entire flow. For that to happen you should remove CORS policy and create a new operation in your API in APIM with OPTIONS method, so that OPTIONS requests would be forwarded to backend normally.
After spending many weeks fighting this issue, and coming up empty, I (with the help of Microsoft Support) discovered a bug in Azure API Management. If you have multiple APIs and one of them does not have an URL suffix (even if it's not the one that's failing), check it's CORS policy. The first API we put in Azure API Management didn't have any methods other than GET, so we set a CORS policy to only allow GET and OPTION. However, the bug that exists seems to make all inbound pre-flight requests route to the API without an URL suffix. So I was able to fix my issue by added a PUT (and POST for good measure) to the CORS policy of an unrelated API.
TL;DR: Check to make sure the CORS policy of the API with no URL suffix allows the method that's failing.
Hope this saves someone's sanity.
I was getting below preflight error from APIM and found that issue is that my policy <method>*</method> is actually not working. Changing the policy to explicitly add all the HTTP Verbs like below has resolved my issue.
<allowed-methods preflight-result-max-age="300">
<method>OPTIONS</method>
<method>GET</method>
<method>POST</method>
<method>PUT</method>
<method>PATCH</method>
<method>DELETE</method>
</allowed-methods>

Resources