microsoft / microsoft/azure-container-apps
Feature Request: Websockets and gRPC support are mutually exclusive (ingress transport 'auto' is 'http')
@cachai2 is already working on this.
Since Mar 6, 2023.
- Dominant language
- Bicep
- Stars
- 445
- Forks
- 54
- Avg merge
- 5h 14m
- Merged PRs (30d)
- 3
Description
Please provide us with the following information:
This issue is a: (mark with an x)
- bug report -> please search issues before submitting
- documentation issue or request
- regression (a behavior that used to work and stopped in a new release)
Issue description
We want to accept WebSocket connections and gRPC requests, on one container app. This is currently impossible, because it seems that transport: auto does not automatically detect HTTP/1 or HTTP/2. transport: auto seems to be equal to transport: http.
Steps to reproduce
Spin up a container app with transport: auto with Websocket and gRPC handlers.
Expected behavior
Setting transport: auto detects HTTP/2 for gRPC and HTTP/1 for Websockets.
Actual behavior
Setting transport: auto or http means only Websockets work and setting transport: http2 means only gRPC works.
Additional context
Docs
The docs here explain the possible values for ingress.transport:
httpfor HTTP/1,http2for HTTP/2,autoto automatically detect HTTP/1 or HTTP/2 (default),tcpfor TCP.
Those docs also indicate that
With HTTPS ingress enabled, your container app … supports WebSocket and gRPC.
Related issues
It is well documented that gRPC requires transport: http2. This issue for example: https://github.com/microsoft/azure-container-apps/issues/38.
It is also the case that Websocket requires transport: auto or http. See our tests below and this issue: https://github.com/microsoft/azure-container-apps/issues/280.
In fact, as @ahmelsayed suggests here and here, it seems that auto just means http.
Our own testing
We ran some of our own testing, to reproduce the behaviour described here: https://github.com/microsoft/azure-container-apps/issues/280.
This involved deploying a minimal websockets server in Go and changing the ingress transport settings.
We found that it indeed worked with either transport: auto or http.
But with transport: http2, the client gives the error failed to WebSocket dial: expected handshake response status code 101 but got 503.
Of course, that makes sense because our toy websockets handler doesn't accept http2, but is a problem because our real app needs transport: http2 for gRPC.
If it helps, we could write a minimal server that accepts both Websockets and HTTP/2 (e.g. gRPC), to show that only HTTP/1 works with auto.
Our code
main.bicep
resource containerApp 'Microsoft.App/containerApps@2022-01-01-preview' = {
name: appName
location: location
identity: {
type: 'SystemAssigned'
}
properties: {
managedEnvironmentId: environment.id
configuration: {
ingress: {
external: true
targetPort: 9090
transport: 'http2' // doesn't work
//transport: 'http' // works
//transport: 'auto' // works
}
secrets: [
{
name: 'container-registry-password'
value: registry.listCredentials().passwords[0].value
}
]
registries: [
{
server: '${registryName}.azurecr.io'
username: registry.listCredentials().username
passwordSecretRef: 'container-registry-password'
}
]
}
template: {
containers: [
{
image: containerImage
name: appName
resources: {
cpu: json('0.5') // https://github.com/Azure/bicep/issues/5993
memory: '1Gi'
}
}
]
}
}
}
server.go
package main
import (
"context"
"fmt"
"log"
"net/http"
"time"
"nhooyr.io/websocket"
"nhooyr.io/websocket/wsjson"
)
func main() {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
c, err := websocket.Accept(w, r, nil)
if err != nil {
panic(fmt.Sprintf("Accept failed: %v", err))
}
defer c.Close(websocket.StatusInternalError, "Internal error")
ctx, cancel := context.WithTimeout(r.Context(), time.Second*10)
defer cancel()
var v interface{}
err = wsjson.Read(ctx, c, &v)
if err != nil {
panic(fmt.Sprintf("JSON read failed: %v", err))
}
log.Printf("received: %v", v)
c.Close(websocket.StatusNormalClosure, "")
})
port := "9090"
addr := fmt.Sprintf("0.0.0.0:%s", port)
log.Printf("Listening on port %s", port)
err := http.ListenAndServe(addr, handler)
panic(fmt.Sprintf("Listening error: %v", err))
}
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.