HttpService: Zombie connection on 1XX response

When HttpService receives a 1XX response (possibly others), the request blocks indefinitely. This connection persists between instances, blocking subsequent requests within the current process.

  1. Open fresh Studio session.
  2. Create new place.
  3. Run the following in the command bar:
game.HttpService.HttpEnabled = true
-- Three to max out connection limit.
for i = 1, 3 do
    spawn(function()
        -- Generates a response with the given status code.
        game.HttpService:GetAsync("https://httpbin.org/status/100")
        print("RESPONSE", i)
    end)
end
  1. Wait a minute to observe that the requests never time out.
  2. Close the place.
  3. Within the same Studio session, create a new place.
  4. Run the following in the command bar:
game.HttpService.HttpEnabled = true
game.HttpService:GetAsync("https://example.com")
print("RESPONSE")
  1. Wait a minute to observe that the request also blocks indefinitely.

The persistence could be considered a separate issue, where long-running requests within an instance aren’t canceled when the instance shuts down.

Is this a long-standing issue or a recent regression?

1 Like

No clue. Just discovered it today while testing HttpService behavior.

Hi @Anaminus

I just looked into this and this behavior is expected. The 1xx status codes are not request-ending responses, they are informational / in progress messages and a further response is expected. You should eventually get a time out error message if you wait long enough (120 seconds). I tried running your script:

> print err;
10:32:57.440 - print err;:1: Expected '=', got 'err'
> for i = 1, 3 do
spawn(function()
    -- Generates a response with the given status code.
    game.HttpService:GetAsync("https://httpbin.org/status/100")
    print("RESPONSE", i)
end)
end
10:35:01.920 - HttpError: Timedout
10:35:01.926 - Stack Begin
10:35:01.927 - Script 'for i = 1, 3 do
10:35:01.927 -     spawn(function()
10:35:01.927 -         -- Generates a response with the given status code.
10:35:01.928 -         game.HttpService:GetAsync("https://httpbin.org/status/100")
10:35:01.928 -         print("RESPONSE", i)
10:35:01.928 -     end)
10:35:01.928 - end', Line 4
10:35:01.929 - Stack End
10:35:01.930 - HttpError: Timedout
10:35:01.931 - Stack Begin
10:35:01.931 - Script 'for i = 1, 3 do
10:35:01.931 -     spawn(function()
10:35:01.932 -         -- Generates a response with the given status code.
10:35:01.932 -         game.HttpService:GetAsync("https://httpbin.org/status/100")
10:35:01.932 -         print("RESPONSE", i)
10:35:01.933 -     end)
10:35:01.935 - end', Line 4
10:35:01.935 - Stack End
10:35:01.935 - HttpError: Timedout
10:35:01.936 - Stack Begin
10:35:01.936 - Script 'for i = 1, 3 do
10:35:01.936 -     spawn(function()
10:35:01.936 -         -- Generates a response with the given status code.
10:35:01.937 -         game.HttpService:GetAsync("https://httpbin.org/status/100")
10:35:01.937 -         print("RESPONSE", i)
10:35:01.937 -     end)
10:35:01.938 - end', Line 4
10:35:01.938 - Stack End

If you just wait a bit longer do you get the same behavior? I ran this a few times with the same results, but let me know if you do wait indefinitely and I can investigate more.

I see. My impression was that the timeout was shorter. It must have been increased recently.

I still think the connections persisting between instances is a problem though, if less important (it’s really only relevant in Studio). I can make a separate post, if needed.

Deciding on the right timeout value is kind of tough. Some developers have legitimate requests that can take a significant amount of time if there is a lot to transfer or process, and there are others that would prefer a shorter timeout and to handle the error case.

I agree that the requests sticking around after switching instances is not intuitive, I’ll look into this. If I need more info I will get back to you.

Thanks for bringing this to our attention by the way!

2 Likes

Would be cool if https://developer.roblox.com/en-us/api-reference/function/HttpService/RequestAsync accepted an additional parameter “Timeout” in the dictionary that could be used to set the timeout in seconds (and defaults to 120 or whatever other engine value you guys provide), so that developers can get the right timeout for their use case themselves.

6 Likes