Azure App Service not closing keep-alive connections on SSE (server sent events) endpoint

Waldemar Barbe 0 Reputation points
2023-08-11T12:24:53.73+00:00

Hello,

I have a weird problem. I'm using Node.js & Express to serve a Server-Sent-Events endpoint. It does something like this:

router.get("/", async (request, response, next) => {
  response.writeHead(200, {
    "Content-Type": "text/event-stream",
    Connection: "keep-alive",
    "Cache-Control": "no-cache",
  });

  // save response object, use it later to send events

  request.on("close", () => {
    console.log(`${clientId} Connection closed`);
    delete clients[clientId];
  });

I have an endpoint to give me the number of connected clients. Locally, as soon as I disconnect from the endpoint the "close" event is called and the client is removed.

On Azure App Service, for some reason, the network connections are never closed. I tried executing "lsof" on the containers, and the connections are really there and are not closed for hours. Anyone got any idea why this could be? I suspect the app-service load balancer is responsible for this.

Here are some of the settings:

{
    "type": "Microsoft.Web/sites",
    "kind": "app,linux,container",
    "properties": {
        "state": "Running",
        "owner": null,
        "usageState": 0,
        "enabled": true,
        "adminEnabled": true,
        "siteProperties": {
            "metadata": null,
            "properties": [
                {
                    "name": "LinuxFxVersion",
                    "value": "DOCKER|..."
                },
                {
                    "name": "WindowsFxVersion",
                    "value": null
                }
            ],
            "appSettings": null
        },
        "availabilityState": 0,
        "sslCertificates": null,
        "csrs": [],
        "cers": null,
        "siteMode": null,
        "computeMode": null,
        "serverFarm": null,
        "reserved": true,
        "isXenon": false,
        "hyperV": false,
        "lastModifiedTimeUtc": "2023-08-11T11:53:53.9866667",
        "storageRecoveryDefaultState": "Running",
        "contentAvailabilityState": 0,
        "runtimeAvailabilityState": 0,
        "dnsConfiguration": {},
        "vnetRouteAllEnabled": false,
        "containerAllocationSubnet": null,
        "useContainerLocalhostBindings": null,
        "vnetImagePullEnabled": false,
        "vnetContentShareEnabled": false,
        "siteConfig": {
            "numberOfWorkers": 1,
            "defaultDocuments": null,
            "netFrameworkVersion": null,
            "phpVersion": null,
            "pythonVersion": null,
            "nodeVersion": null,
            "powerShellVersion": null,
            "linuxFxVersion": "DOCKER|...",
            "windowsFxVersion": null,
            "windowsConfiguredStacks": null,
            "requestTracingEnabled": null,
            "remoteDebuggingEnabled": null,
            "remoteDebuggingVersion": null,
            "httpLoggingEnabled": null,
            "azureMonitorLogCategories": null,
            "acrUseManagedIdentityCreds": true,
            "acrUserManagedIdentityID": null,
            "logsDirectorySizeLimit": null,
            "detailedErrorLoggingEnabled": null,
            "publishingUsername": null,
            "publishingPassword": null,
            "appSettings": null,
            "metadata": null,
            "connectionStrings": null,
            "machineKey": null,
            "handlerMappings": null,
            "documentRoot": null,
            "scmType": null,
            "use32BitWorkerProcess": null,
            "webSocketsEnabled": null,
            "alwaysOn": true,
            "javaVersion": null,
            "javaContainer": null,
            "javaContainerVersion": null,
            "appCommandLine": null,
            "managedPipelineMode": null,
            "virtualApplications": null,
            "winAuthAdminState": null,
            "winAuthTenantState": null,
            "customAppPoolIdentityAdminState": null,
            "customAppPoolIdentityTenantState": null,
            "runtimeADUser": null,
            "runtimeADUserPassword": null,
            "loadBalancing": null,
            "routingRules": null,
            "experiments": null,
            "limits": null,
            "autoHealEnabled": null,
            "autoHealRules": null,
            "tracingOptions": null,
            "vnetName": null,
            "vnetRouteAllEnabled": null,
            "vnetPrivatePortsCount": null,
            "publicNetworkAccess": null,
            "cors": null,
            "push": null,
            "apiDefinition": null,
            "apiManagementConfig": null,
            "autoSwapSlotName": null,
            "localMySqlEnabled": null,
            "managedServiceIdentityId": null,
            "xManagedServiceIdentityId": null,
            "keyVaultReferenceIdentity": null,
            "ipSecurityRestrictions": null,
            "ipSecurityRestrictionsDefaultAction": null,
            "scmIpSecurityRestrictions": null,
            "scmIpSecurityRestrictionsDefaultAction": null,
            "scmIpSecurityRestrictionsUseMain": null,
            "http20Enabled": false,
            "minTlsVersion": null,
            "minTlsCipherSuite": null,
            "supportedTlsCipherSuites": null,
            "scmMinTlsVersion": null,
            "ftpsState": null,
            "preWarmedInstanceCount": null,
            "functionAppScaleLimit": 0,
            "elasticWebAppScaleLimit": null,
            "healthCheckPath": null,
            "fileChangeAuditEnabled": null,
            "functionsRuntimeScaleMonitoringEnabled": null,
            "websiteTimeZone": null,
            "minimumElasticInstanceCount": 0,
            "azureStorageAccounts": null,
            "http20ProxyFlag": null,
            "sitePort": null,
            "antivirusScanEnabled": null,
            "storageType": null
        },
        "slotName": null,
        "trafficManagerHostNames": null,
        "sku": "Basic",
        "scmSiteAlsoStopped": false,
        "targetSwapSlot": null,
        "hostingEnvironment": null,
        "hostingEnvironmentProfile": null,
        "clientAffinityEnabled": false,
        "clientCertEnabled": false,
        "clientCertMode": 0,
        "clientCertExclusionPaths": null,
        "hostNamesDisabled": false,
        "vnetBackupRestoreEnabled": false,
        "domainVerificationIdentifiers": null,
        "kind": "app,linux,container",
        "managedEnvironmentId": null,
        "containerSize": 0,
        "dailyMemoryTimeQuota": 0,
        "suspendedTill": null,
        "siteDisabledReason": 0,
        "functionExecutionUnitsCache": null,
        "maxNumberOfWorkers": null,
        "cloningInfo": null,
        "hostingEnvironmentId": null,
        "slotSwapStatus": null,
        "httpsOnly": true,
        "endToEndEncryptionEnabled": false,
        "redundancyMode": 0,
        "inProgressOperationId": null,
        "geoDistributions": null,
        "privateEndpointConnections": [],
        "publicNetworkAccess": null,
        "buildVersion": null,
        "targetBuildVersion": null,
        "migrationState": null,
        "eligibleLogCategories": "AppServiceAppLogs,AppServiceAuditLogs,AppServiceConsoleLogs,AppServiceHTTPLogs,AppServiceIPSecAuditLogs,AppServicePlatformLogs,ScanLogs",
        "inFlightFeatures": [],
        "storageAccountRequired": false,
        "virtualNetworkSubnetId": null,
        "keyVaultReferenceIdentity": "SystemAssigned",
        "defaultHostNameScope": 0,
        "privateLinkIdentifiers": null
    },
}

The lsof connections look like this:

{
  "fd": 27,
  "type": "IP",
  "access": "u",
  "lock": " ",
  "name": "169.254.129.2:8080->169.254.129.4:36050",
  "protocol": "TCP",
  "version": 6,
  "from": {
    "address": "169.254.129.2",
    "port": 8080
  },
  "to": {
    "address": "169.254.129.4",
    "port": 36050
  }
}
Azure App Service
Azure App Service
Azure App Service is a service used to create and deploy scalable, mission-critical web apps.
8,974 questions
{count} votes

1 answer

Sort by: Most helpful
  1. SnehaAgrawal-MSFT 22,706 Reputation points Moderator
    2023-08-16T17:41:55.4833333+00:00

    @Waldemar Barbe My sincere apology for late response here!

    It appears that in Node.js, you need to manually terminate the underlying HTTP connection.

    See this informative article at https://auth0.com/blog/developing-real-time-web-applications-with-server-sent-events/. About halfway through the article, there's an illustrative example that demonstrates how to manage the server-side closure using an explicit close function in conjunction with response.end().

    User's image

    Please let us know if further query or issue remains.

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.