koajs / koa

Expressive middleware for node.js using ES2017 async functions
https://koajs.com
MIT License
35.07k stars 3.22k forks source link

[fix] Send response as buffer #1782

Closed AlcaYezzz closed 7 months ago

AlcaYezzz commented 10 months ago

Describe the bug

Node.js version: v18.12.1

OS version: Ubuntu 22

Description: Send response as buffer isn't working unless wrapped in object

Actual behavior

provided the following code:

const fooFunction = async (ctx: Context) => {
  ctx.response.set("Content-Type", "application/octet-stream");
  ctx.response.set(
    "Content-disposition",
    `attachment;filename=foo.bar`
  );
  ctx.body = {
    data: Buffer.from("test")
  }
  return ctx
}

The http request give me the following response:

{
  "data":{
    "type":"Buffer",
    "data":[116,101,115,116]
  }
}

If I change the object affected to body:

const fooFunction = async (ctx: Context) => {
  ctx.response.set("Content-Type", "application/octet-stream");
  ctx.response.set(
    "Content-disposition",
    `attachment;filename=foo.bar`
  );
  ctx.body = Buffer.from("test")
  return ctx
}

the http response contains

test

Expected behavior

The response to contain the buffer set to the body

{
  "type":"Buffer",
  "data":[116,101,115,116]
}

Checklist

iwanofski commented 10 months ago

Just to be clear, you expect ctx.body = Buffer.from(...) to result in a response body as:

{
  "type":"Buffer",
  "data":[116,101,115,116]
}

Because that's super opinionated? Koa doesn't do anything special when body is set to a buffer. This is how node:http works.

adebayohountondji commented 9 months ago

Hello @AlcaYezzz,

Koa doesn't do anything special, you should try this to get the behavior you're hoping for:

const fooFunction = async (ctx: Context) => {
  // ...
  ctx.body = Buffer.from('test').toJSON()
  return ctx
}
lagden commented 9 months ago

@AlcaYezzz

I can only show you the door, you're the one who have to walk through it.

import {PassThrough, Readable} from 'node:stream'

function * _generate() {
    yield 'test'
}

async function _stream(ctx) {
    const stream = Readable.from(_generate())
    ctx.type = 'application/octet-stream'
    ctx.response.set('content-disposition', 'attachment; filename="via_stream.txt"')
    ctx.body = stream.pipe(new PassThrough())
}

async function _json(ctx) {
    ctx.response.set('content-disposition', 'attachment; filename="via_json.json"')
    const _data = []
    for (const data of _generate()) {
        _data.push(data)
    }
    ctx.body = {
        type: 'Uint8Array',
        data: [...(new globalThis.TextEncoder().encode(_data.join('')))],
    }
}

Using @koa/router:

GET /json

curl -vJO 'http://localhost:5001/json
cat via_json.json
// => {"type":"Uint8Array","data":[116,101,115,116]}

GET /

curl -vJO 'http://localhost:5001/
cat via_stream.txt
// => test
siakc commented 7 months ago

Koa does something special in case body is set to a buffer. Changes the content type to bin.