Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
jsinspector: Support UTF-8 responses to CDP's IO.read
Summary: The initial implementation of `Network.loadNetworkResource` and the accompanying `IO.read` (D54202854) base64-encodes all data as if it is binary. This is the more general case, and we'll continue to base64-encode non-text resources. In the common case of text resources (particularly JS and JSON), it'd be preferable to do as Chrome does and send UTF-8 over the wire directly. This has a few performance benefits: - Less CPU and RAM footprint on device (UTF-8 truncation is constant-time, fast, and in-place), similarly less decoding for the frontend. - 25% less data per chunk (base64 encodes 3 bytes as 4 characters), implies up to 25% fewer network round trips for large resources. It also has the benefit of being human-readable in the CDP protocol inspector. ## Determining whether data is text We use exactly Chromium's heuristic for this (code pointers in comments), which is based only on the `Content-Type` header, and assuming any text mime type is UTF-8. ## UTF-8 truncation The slight implementation complexity here is that `IO.read` requests may specify a maximum number of bytes, and so we must slice a raw buffer up into valid UTF-8 sequences. This turns out to be fairly simple and cheap: 1. Naively truncate the buffer, inspect the last byte 2. If the last byte has topmost bit =0, it's ASCII (single byte) and we're done. 3. Otherwise, look back at most 3 bytes to find the first byte of the code point (topmost bits 11), counting the number of "continuationBytes" at the end of our buffer. If we don't find one within 3 bytes then the string isn't UTF-8 - throw. 4. Read the code point length, which is encoded into the first byte. 5. Resize to remove the last code point fragment, unless it terminates correctly exactly at the end of our buffer. ## Edge cases + divergence from Chrome Chrome's behaviour here in at least one case is questionable and we intentionally differ: - If a response has header "content-type: text/plain" but content eg`0x80` (not valid UTF-8), Chrome will respond to an `IO.read` with `{ "data": "", "base64Encoded": false, "eof": false }`, ie an empty string, but will move its internal pointer such that the next or some subsequent `IO.read` will have `"eof": true`. To the client, this is indistinguishable from a successfully received resource, when in fact it is effectively corrupted. - Instead, we respond with a CDP error to the `IO.read`. We do not immediately cancel the request or discard data, since not all `IO.read` errors are necessarily fatal. I've verified that CDT sends `IO.close` after an error, so we'll clean up that way (this isn't strictly guaranteed by any spec, but nor is `IO.close` after a resource is successfully consumed). Changelog: [General] Debugger: Support text responses to CDP `IO.read` requests Differential Revision: D58323790
- Loading branch information