Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Way to access the response body size in the hook #5749

Closed
acarl005 opened this issue Jul 28, 2022 · 5 comments · Fixed by #8371
Closed

Way to access the response body size in the hook #5749

acarl005 opened this issue Jul 28, 2022 · 5 comments · Fixed by #8371
Labels
feature / enhancement New feature or request low hanging fruit p3-edge-case SvelteKit cannot be used in an uncommon way
Milestone

Comments

@acarl005
Copy link

acarl005 commented Jul 28, 2022

Describe the problem

I want to set up logging in my sveltekit hook. One of the things I want to log is the number of bytes in the response body. It would be nice if the Response exposed the body length so that we can access it. Something like this...

export async function handle({ event, resolve }) {
    response = await resolve(event)
    // these don't actually work, they are undefined. But something LIKE this would be nice
    console.log(response.body.length)
    console.log(response.headers.get("content-length"))
    return response
}

I notice that the length is somewhere on the object if I just console.log(response) when returning JSON...

Response {
  [Symbol(realm)]: { settingsObject: {} },
  [Symbol(state)]: {
    aborted: false,
    rangeRequested: false,
    timingAllowPassed: false,
    requestIncludesCredentials: false,
    type: 'default',
    status: 200,
    timingInfo: null,
    cacheState: '',
    statusText: '',
    headersList: HeadersList {
      [Symbol(headers map)]: [Map],
      [Symbol(headers map sorted)]: null
    },
    urlList: [],
    body: {
      stream: undefined,
      source: '{"result":{"rows":[["to become more autonomous in data analysis"],["to better structure your thoughts"],["to develop a useful professional skill"]],"columns":[{"name":"reason","type":"VARCHAR"}],"moreRows":false}}',
      length: 213
    }
  },
  [Symbol(headers)]: HeadersList {
    [Symbol(headers map)]: Map(2) {
      'content-type' => 'application/json; charset=utf-8',
      'etag' => '"1ba7x9x"'
    },
    [Symbol(headers map sorted)]: null
  }
}

...but that piece of data is private (attached to a symbol) and I can't access it.

In my opinion, being able to observe information like this in my hook is important to building debuggable, observable web services. Response payload size is an important metric to monitor. Putting that logging logic in the hook seems to be the only way to make sure it is reliably logged.

Describe the proposed solution

The workaround I have right now is await response.clone().text(), but that is not ideal because I have to consume the stream. This is wasteful especially if I only want the length.

Alternatives considered

I suppose I could do something with Object.getOwnPropertySymbols(), but I think that's frowned upon?

Importance

would make my life easier

Additional Information

As I understand it (may be wrong), the content length isn't necessarily always known without reading the stream, but there are many cases where the length is known at the time of the response instantiation, like when JSON or HTML are being returned from a string in memory. Sveltekit should be able to set the length in cases where length is known.

Here is a simple case where content length is known but not exposed: https://stackblitz.com/edit/sveltejs-kit-template-default-9ydsbu?file=src%2Fhooks.js&terminal=dev

@benmccann
Copy link
Member

Response is not an API we've defined: https://developer.mozilla.org/en-US/docs/Web/API/Response

@acarl005
Copy link
Author

Thanks for the response @benmccann (see what I did there 😂). Anyways, what if Sveltekit set the content-length header? That wouldn't require alteration of the standard Response API. Is that feasible to do efficiently?

@CaptainCodeman
Copy link
Contributor

CaptainCodeman commented Jul 30, 2022

This is terrible and you shouldn't use it, but it can go in the hooks handle (really what you already mentioned, I realize after re-reading):

const bytes = await response.clone().arrayBuffer()
console.log('response size:', bytes.byteLength)

You might be able to avoid the clone impact somewhat by returning your own streaming response that you pipe the current response to, counting the size as you do.

@Rich-Harris
Copy link
Member

In the case where you're rendering a page, you can do this:

let length = 0;

const response = await resolve(event, {
  transformPageChunk({ html }) => {
    length += html.length;
    return html;
  }
});

(In future if we add a stream option you'd need to wait until the markup was rendered before reading the length, but for now length will be accurate as soon as you have the response.)

As of #5748, non-page responses must be generated by you, the app author — we don't have a way to inspect the Response object to determine the length.

@acarl005
Copy link
Author

acarl005 commented Aug 3, 2022

@Rich-Harris Thanks for looking into this. I suppose there are 3 cases here:

  1. pages
  2. page endpoints
  3. standalone endpoints

Getting the content size for (1) pages is taken care of by transformPageChunk as you suggested. As for (3) standalone endpoints, since I am responsible for creating the whole Response, I can calculate content-length and set it myself inside the endpoint handler. However, I don't see a way to do it in (2) page endpoints, since only a POJO is returned.

As a tangent, I'm grateful for the thoughtful changes in #5748. I like the new distinction between standalone endpoints (returning a Response) and page endpoints (returning only data). I definitely had been conflating the 2 up to this point. Overall the changes are nice and I'm excited to upgrade.

@benmccann benmccann added the p3-edge-case SvelteKit cannot be used in an uncommon way label Aug 3, 2022
@Rich-Harris Rich-Harris added the feature / enhancement New feature or request label Sep 6, 2022
@Rich-Harris Rich-Harris added this to the post-1.0 milestone Sep 6, 2022
Rich-Harris added a commit that referenced this issue Jan 7, 2023
dummdidumm added a commit that referenced this issue Jan 11, 2023
Rich-Harris added a commit that referenced this issue Jan 19, 2023
…t(...)` helper (#8371)

* add content-length headers to generated responses, via new `text(...)` helper - closes #5749

* correctly determine length

* fix

* Update packages/kit/types/index.d.ts

* Update .changeset/nine-walls-listen.md

* Update .changeset/old-weeks-reflect.md

Co-authored-by: Simon H <[email protected]>
Co-authored-by: Ben McCann <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature / enhancement New feature or request low hanging fruit p3-edge-case SvelteKit cannot be used in an uncommon way
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants