[HN Gopher] Chrome Returns 206 when the Server Returns 403
       ___________________________________________________________________
        
       Chrome Returns 206 when the Server Returns 403
        
       Author : aoli-al
       Score  : 81 points
       Date   : 2025-03-03 18:00 UTC (4 hours ago)
        
 (HTM) web link (aoli.al)
 (TXT) w3m dump (aoli.al)
        
       | Something1234 wrote:
       | This is super weird and needs a bit of editing but it seems like
       | an actual bug. Shouldn't a 403 invalidate whatever was cached?
       | 
       | As in it should bubble the error up to the user.
        
         | ajross wrote:
         | Should it? You can return a partial result for the request,
         | there's no reason it couldn't be a subset of a previous partial
         | request. Why is the browser required to make a network request
         | at all when it can serve a valid (but incomplete) response out
         | of the cache? There's space for argument for what the "best"
         | way to handle this is, but I have a hard time seeing a valid
         | response as "incorrect" or a "bug".
         | 
         | Honestly, this genre of "big tech company refused to fix my
         | very obscure edge case and that confirms all my priors about
         | them" post is getting a little tiresome. There are like three
         | of them coming through the front page every day.
        
           | mananaysiempre wrote:
           | Whether it should or not depends on whether you understand a
           | 403 as a refusal to let you do the given method against the
           | given resource at all, or as a refusal to do this one
           | specific request. The HTTP spec (as I've just learned) does
           | support the narrower interpretation if the server wishes it:
           | the description for 403 is just that "[t]he server understood
           | the request, but is refusing to fulfill it", with no
           | implications regarding other requests for this resource.
        
             | ajross wrote:
             | Again, it's a range request though. What if the browser
             | simply didn't send a network request at all and just
             | synchronously returned the partial result from the cache.
             | You agree that would be correct (if arguably not very
             | useful), right? The point is that the 403 isn't required to
             | be seen, at all. You can't require the browser return a
             | value that the browser doesn't know about.
             | 
             | It's a cache consistency bug at its root. The value was
             | there, and now it's not. The reporter says "the browser is
             | responsible for cache coherency" (call this the "MESI
             | camp"). The Chrome folks say "the app is responsible for
             | cache coherency" (the "unsnooped incoherent" gang). Neither
             | is wrong. And the problem remains obscure regardless.
        
               | aoli-al wrote:
               | I'm the author of the post.
               | 
               | I'm not sure Chrome's current caching behavior is helpful
               | because the second response does not indicate which part
               | of the data is returned. So, the application has no
               | choice but to discard the data.
               | 
               | But thank you for your comments. This helped me to
               | crystalize why I think this is a bug.
        
               | mananaysiempre wrote:
               | Yeah, if there's no way to tell from the request which
               | range has actually been returned that seems like a deal-
               | breaker. The spec's allowance for a partial response is
               | explicitly motivated by the response being self-
               | describing, and if after Chrome's creative
               | reinterpretation it is not, then it's not clear what the
               | client could even do.
        
               | ajross wrote:
               | There's no clear way to define "correct" in this case
               | regardless. The whole premise behind a range request is
               | that the data is immutable (because otherwise it wouldn't
               | make sense to be able to fetch it piecewise), and it's
               | mutating here by disappearing! What are you supposed to
               | do, really? The answer is always going to be app-
               | dependent, the browser can't get it right because the
               | server is being obtuse and confusing.
               | 
               | When we handle this in the hardware world it's via
               | algorithms that _know_ about the mutability of the cached
               | data and operate on top of primitives like  "flush" and
               | "invalidate" that can restore the inconsistent memory
               | system to a known state. HTTP didn't spec that stuff, but
               | the closest analog is "fetch it again", which is exactly
               | what the suggested workaround is in the bug.
        
           | ForTheKidz wrote:
           | > Honestly, this genre of "big tech company refused to fix my
           | very obscure edge case and that confirms all my priors about
           | them" post is getting a little tiresome.
           | 
           | Ahh, let's just wait for the startup to fix it then.
        
         | YetAnotherNick wrote:
         | I think merging two requests opens up whole can of worms.
         | 200+403 merged translates to 206? There is also content length
         | merging. Wondering what would the rest of the headers
         | translates to. If I respond with a header saying that the
         | stream is EOF in the second call, would that be preserved.
        
       | nemothekid wrote:
       | I'm assuming that the OP is using a signed request and the fact
       | that Chrome rewrites the request is what is causing the 403.
       | 
       | I'm interested in what kind of application depends on this
       | behavior - if an application gets partial data from the server,
       | especially one that doesn't match the content-length header, that
       | should always be an error to me.
        
         | gwd wrote:
         | Some authentication schemes have a short-lived "authorization
         | token", that is valid for like 5 minutes, and a longer-lived
         | "refresh token", which is valid for like a week or two; and
         | return a 403 when the authorization token expires to prompt the
         | client to refresh the token. (See say, supertokens.com .) If
         | your auth token expired in the middle of a multi-part download,
         | it could cause this situation that triggered this bug.
        
       | mdaniel wrote:
       | > The Netlog looks scary because it not only contains the traffic
       | while I reproduced the bug but also 1) all traffic from the
       | Chrome plugins and 2) many websites that I have browsed before
       | but haven't visited during the recording
       | 
       | Isn't that a fantastic use for a 2nd Chrome Profile or even just
       | downloading a Chromium build[1] and using that, showing the
       | behavior in a bleeding edge build?
       | 
       | 1: https://download-chromium.appspot.com/
        
         | nightpool wrote:
         | I was also pretty surprised when the OP said "the Chromium team
         | refused to use my server to reproduce the bug", when the actual
         | comments of the ticket were "clone this repo and run my giant
         | node app" and the tester's response was "It seems a bit
         | difficult to set up an build environment to run the static
         | server, could you provide a more minimal repro case?". OP's
         | description of the tester's reasonable concerns seems very
         | unfair.
         | 
         | Even just having a web-accessible endpoint that reproduced the
         | issue would have made the process a lot smoother I think.
         | Apparently in response to OP's request for an easier test case,
         | OP asked for GCP cloud credits(?) to host their server with?.
         | You probably used more bandwidth & CPU loading the new Chromium
         | issue tracker page then you would have just setting up a simple
         | vps to reproduce the issue
        
       | mnot wrote:
       | Chrome's cache is indeed acting correctly. Effectively, it is
       | acting as an intermediary here - your application made a partial
       | content request, and it can satisfy it (partially), so it sends
       | you a 206.
       | 
       | HTTP partial content responses need to be evaluated (like any
       | other response) according to their metadata: servers are not
       | required to send you exactly the ranges you request, so you need
       | to pay attention to Content-Range and process accordingly
       | (potentially issuing more requests).
       | 
       | See: https://httpwg.org/specs/rfc9110.html#status.206
        
         | Ajedi32 wrote:
         | Shouldn't the response header returned by Chrome say "4-138724"
         | then though, and not "4-1943507"? The synthesized response body
         | doesn't include bytes "138725-1943507".
        
           | mnot wrote:
           | Ah - I need to remember to coffee before posting in the AM.
           | 
           | Yes, the mismatch between the response headers and the
           | content is a problem. Unfortunately, IME browsers often do
           | "fix ups" of headers that make them less than reliable, this
           | might be one of them -- it's effectively rewriting the
           | response but failing to update all of the metadata.
           | 
           | The bug summary says "Chrome returns wrong status code while
           | using range header with caches." That's indeed not a bug. I
           | think the most concerning thing here is that the Content-
           | Range header is obviously incorrect, so Chrome should either
           | be updating it or producing a clear error to alert you --
           | which it looks like the Chrome dev acknowledges when they say
           | "it is probably a bug that there is no AbortError exception
           | on the read".
           | 
           | I might try to add some tests for this to https://cache-
           | tests.fyi/#partial
        
         | nightpool wrote:
         | But the Content-Range header and the Content-Length header both
         | indicated the "expected" number of bytes e.g. the number of
         | bytes that would have been returned if the server had given a
         | 206 or a 200, not the truncated number of bytes that the
         | response actually contained. Is that expected?
         | 
         | The latest response from the Chromium team
         | (https://issues.chromium.org/issues/390229583#comment20) seems
         | to take a different approach from your comment, and says that
         | you should think of it as a streaming response where the
         | connection failed partway through, which feels reasonable to
         | me, except for the fact that `await`ing the response doesn't
         | seem to trigger any errors:
         | https://issues.chromium.org/issues/390229583#comment21
        
       ___________________________________________________________________
       (page generated 2025-03-03 23:00 UTC)