Giter Club home page Giter Club logo

send's Introduction

NPM version Build Status Test coverage License Downloads

Koa static file serving middleware.

Install

NPM

$ npm i @koa/send

Options

  • maxage Browser cache max-age in milliseconds. (defaults to 0).
  • immutable Tell the browser the resource is immutable and can be cached indefinitely. (defaults to false).
  • hidden Allow transfer of hidden files. (defaults to false).
  • root Root directory to restrict file access.
  • index Name of the index file to serve automatically when visiting the root location. (defaults to none).
  • gzip Try to serve the gzipped version of a file automatically when gzip is supported by a client and if the requested file with .gz extension exists. (defaults to true).
  • brotli Try to serve the brotli version of a file automatically when brotli is supported by a client and if the requested file with .br extension exists. (defaults to true).
  • format If not false (defaults to true), format the path to serve static file servers and not require a trailing slash for directories, so that you can do both /directory and /directory/.
  • setHeaders Function to set custom headers on response.
  • extensions Try to match extensions from passed array to search for file when no extension is sufficed in URL. First found is served. (defaults to false)

Root path

Note that root is required, defaults to '' and will be resolved, removing the leading / to make the path relative and this path must not contain "..", protecting developers from concatenating user input. If you plan on serving files based on user input supply a root directory from which to serve from.

For example to serve files from ./public:

app.use(async (ctx) => {
  await send(ctx, ctx.path, { root: __dirname + "/public" });
});

To serve developer specified files:

app.use(async (ctx) => {
  await send(ctx, "path/to/my.js");
});

setHeaders

The function is called as fn(res, path, stats), where the arguments are:

  • res: the response object.
  • path: the resolved file path that is being sent.
  • stats: the stats object of the file that is being sent.

You should only use the setHeaders option when you wish to edit the Cache-Control or Last-Modified headers, because doing it before is useless (it's overwritten by send), and doing it after is too late because the headers are already sent.

If you want to edit any other header, simply set them before calling send.

Example

const send = require("@koa/send");
const Koa = require("koa");
const app = new Koa();

// $ GET /package.json
// $ GET /

app.use(async (ctx) => {
  if ("/" == ctx.path) return (ctx.body = "Try GET /package.json");
  await send(ctx, ctx.path);
});

app.listen(3000);
console.log("listening on port 3000");

License

MIT

send's People

Contributors

3imed-jaberi avatar 9point6 avatar adenflorian avatar bredthau avatar dzcpy avatar felixfbecker avatar greengremlin avatar haoxins avatar ide avatar jiasm avatar jonathanong avatar jtmthf avatar kjendrzyca avatar mako-taco avatar mysticatea avatar necromos avatar niftylettuce avatar niieani avatar olov avatar pepkin88 avatar pierreburgy avatar queckezz avatar sinewyk avatar tejasmanohar avatar tj avatar trysound avatar xpepermint avatar yorkie avatar zacanger avatar zhangruizhuo avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

send's Issues

send fails every other run.

Appologize for the gif. However a gif is worth 1000 words. 😎

The following code is running from snuggsi ツ:

const
  root   = 'mixins'
, mime   = /^\*\/\*$/
, filter = /^\/mixins\/(.*\.es)*$/g

, send   = require ('koa-send')


module.exports = async (context, next) => {

  const
    match = filter.test (context.path)
  , resource = './storable.es'

  , settings = [context, resource, { root }]

  , header = ['Content-Type', 'application/ecmascript']
  , response = match && await send ( ... settings )

  void (response && context.set ( ... header ))
    || await next ()

  console.log ('response', context.response, response)
}

By the way it's a little wonky to change headers AFTER send is called. (which just returns a file path anyways). Let me know where I can help. PR. etc.

Thanks in advance @tj
koa

Explanation of examples

In your example, you use this function:

send(this, this.path, { root: __dirname + '/public' });

But you don't explain what the first and second arguments are? What do those do?

v4

  • use async/await, remove co
  • throw error when file does not exist #43

Does this work with async/await?

Koa supports async functions, which is very useful for route definitions if you want to fetch some stuff from a database etc. But you have to use await inside the function. Since this doesn't return a promise, I can't await it... Why don't you make it return a promise? People could still yield it inside generator function middleware as they are automatically wrapped with co.
Example:

router.get('/pictures/:id', async function (next) {
  let files = await glob(this.request.params.id + '.*')
  if (files.length === 0) {
    throw new HttpError(404)
  }
  await sendFile(this, PATH + '/' + files[0])
})

Does not throw when file does not exist

If a file does not exist there is no error. The promise resolves because and returns undefined on the yield fs.stat here:

    try {
      var stats = yield fs.stat(path);
      // nothing gets executed later on...

Not sure if that's a bug in mz or with the usage by koa-send.

It seems that it cannot handle 304.

I set Cache-Control to no-cache, and set Last-Modified to a UTC time string in the setHeaders,
but the status code doesn't change in the browser, still 200.
I read the source and found that it did not handle the 304.

Do you need to provide this feature?
If so , I'd love to PR

Doesn't work with absolute paths

Assuming there is a file named 'index.html' in the directory, accessing the server at localhost:3000 returns a 404 Not Found with the code below. Note that the file does exist and the path is correct, as demonstrated by the fs.statSync call.

const app = require('koa')();
const send = require('koa-send');
const fs = require('fs');

const INDEX_FILE = `${__dirname}/index.html`;
// This string is an absolute path, e.g. in my case "/home/miguel/Programming/koa-send-test/index.html"

app.use(function*() {
    console.log(fs.statSync(INDEX_FILE)); //Returns a valid object
    yield send(this, INDEX_FILE); //Does nothing
});

app.listen(3000);

However if INDEX_FILE is set to a relative path, the code works fine and returns index.html when accessing localhost:3000

const INDEX_FILE = `index.html`;

Directories without a trailing slash

Would be nice to mimic most static file servers and not require a trailing slash for directories, so that you could do both:

/directory
/directory/
Without having to care (since users aren't likely to type the trailing slash when sharing URLs to things) and they would both work.

acceptsEncodings throws under certain conditions

Under certain (probably rare) circumstances, when send tries to run this.acceptsEncodings() it will throw the following error:

TypeError: this.acceptsEncodings is not a function

This only happens when send is being called, of course, from outside the Koa context, but there are circumstances where sometimes you have to run functions that aren't normal Koa middleware for BC reasons. In my case, this occurred when I called send in a custom callback from a koa-passport OAuth authentication strategy (Github, specifically), like follows:

r.get('/auth/github/callback', function *() {
    let ctx = this;
    yield passport.authenticate('github', function *(err, user, info) {
      // ...
      yield send(ctx, returnPath, {root: path.resolve(__dirname, publicFolder)});
   }).call(this, next);
});

I have to assign this to ctx in the outer function because the context is lost during the authentication.

Because send is trying to call acceptsEncodings from this rather than the context that's passed in through the first argument, if the context of this is no longer Koa (as it will be in instances like the above) it will throw. I can use .apply(ctx) on send to fix the problem, but it's more streamlined (and, in my opinion, more fail-safe) to change line 48 of index.js from this:

var encoding = this.acceptsEncodings('gzip', 'deflate', 'identity');

To this:

var encoding = ctx.acceptsEncodings('gzip', 'deflate', 'identity');

Since this is the only call to this that I know if in the module, I would assume changing it to use ctx ensures that the module will work under any circumstance, providing the developer passes in the right context as the first argument (which is necessary anyway).

I forked and branched the fix which you can see at https://github.com/raphaelvalerio/send/tree/fix-acceptsEncodings. If you want I can run a PR with it.

Tests fail on Windows

Wanted to work on async/await support and did nothing but git clone, npm install, npm test.

send(ctx, file)
    √ should set the Content-Type (38ms)
    √ should set the Content-Length

  Error: boom
      at Object.<anonymous> (C:\Users\felix\git\send\test\index.js:448:33)
      at next (native)
      at Object.<anonymous> (C:\Users\felix\git\send\node_modules\koa-compose\index.js:29:12)
      at next (native)
      at onFulfilled (C:\Users\felix\git\send\node_modules\co\index.js:65:19)

    √ should cleanup on socket error
    with no .root
      when the path is absolute
        1) should 404
      when the path is relative
        √ should 200
      when the path contains ..
        √ should 403
    with .root
      when the path is absolute
        2) should 404
      when the path is relative and exists
        √ should serve the file
      when the path is relative and does not exist
        √ should 404
      when the path resolves above the root
        √ should 403
      when the path resolves within root
        √ should 403
    with .index
      when the index file is present
        √ should serve it
        √ should serve it
    when path is not a file
      √ should 404
      √ should return undefined
    when path is a directory
      √ should 404
    when path does not finish with slash and format is disabled
      √ should 404
      √ should 404
    when path does not finish with slash and format is enabled
      3) should 200
double callback!
    when path is malformed
      √ should 400
    when path is a file

  AssertionError: 'C:\\Users\\felix\\git\\send\\test\\fixtures\\user.json' == 'C:\\Users\\felix\\git\\send\\test/fixtures/user.json'
      at Object.<anonymous> (C:\Users\felix\git\send\test\index.js:269:16)
      at next (native)
      at Object.<anonymous> (C:\Users\felix\git\send\node_modules\koa-compose\index.js:29:12)
      at next (native)
      at onFulfilled (C:\Users\felix\git\send\node_modules\co\index.js:65:19)

      4) should return the path
      or .gz version when requested and if possible
        √ should return path
        √ should return .gz path (gzip option defaults to true)
        √ should return .gz path when gzip option is turned on
        √ should not return .gz path when gzip option is false
      and max age is specified

  AssertionError: 'C:\\Users\\felix\\git\\send\\test\\fixtures\\user.json' == 'C:\\Users\\felix\\git\\send\\test/fixtures/user.json'
      at Object.<anonymous> (C:\Users\felix\git\send\test\index.js:346:18)
      at next (native)
      at Object.<anonymous> (C:\Users\felix\git\send\node_modules\koa-compose\index.js:29:12)
      at next (native)
      at onFulfilled (C:\Users\felix\git\send\node_modules\co\index.js:65:19)

        5) should set max-age in seconds

  AssertionError: 'C:\\Users\\felix\\git\\send\\test\\fixtures\\user.json' == 'C:\\Users\\felix\\git\\send\\test/fixtures/user.json'
      at Object.<anonymous> (C:\Users\felix\git\send\test\index.js:361:18)
      at next (native)
      at Object.<anonymous> (C:\Users\felix\git\send\node_modules\koa-compose\index.js:29:12)
      at next (native)
      at onFulfilled (C:\Users\felix\git\send\node_modules\co\index.js:65:19)

        6) should truncate fractional values for max-age
    .hidden option
      when trying to get a hidden file
        7) should 404
      when trying to get a file from a hidden directory
        8) should 404
      when trying to get a hidden file and .hidden check is turned off
        √ should 200


  22 passing (234ms)
  8 failing

  1) send(ctx, file) with no .root when the path is absolute should 404:
     Error: expected 404 "Not Found", got 400 "Bad Request"
      at Test.assert (C:\Users\felix\git\send\node_modules\supertest\lib\test.js:205:15)
      at assert (C:\Users\felix\git\send\node_modules\supertest\lib\test.js:132:12)
      at C:\Users\felix\git\send\node_modules\supertest\lib\test.js:129:5
      at Test.Request.callback (C:\Users\felix\git\send\node_modules\superagent\lib\node\index.js:746:30)
      at Test.<anonymous> (C:\Users\felix\git\send\node_modules\superagent\lib\node\index.js:135:10)
      at IncomingMessage.<anonymous> (C:\Users\felix\git\send\node_modules\superagent\lib\node\index.js:938:12)
      at endReadableNT (_stream_readable.js:893:12)

  2) send(ctx, file) with .root when the path is absolute should 404:
     Error: expected 404 "Not Found", got 400 "Bad Request"
      at Test.assert (C:\Users\felix\git\send\node_modules\supertest\lib\test.js:205:15)
      at assert (C:\Users\felix\git\send\node_modules\supertest\lib\test.js:132:12)
      at C:\Users\felix\git\send\node_modules\supertest\lib\test.js:129:5
      at Test.Request.callback (C:\Users\felix\git\send\node_modules\superagent\lib\node\index.js:746:30)
      at Test.<anonymous> (C:\Users\felix\git\send\node_modules\superagent\lib\node\index.js:135:10)
      at IncomingMessage.<anonymous> (C:\Users\felix\git\send\node_modules\superagent\lib\node\index.js:938:12)
      at endReadableNT (_stream_readable.js:893:12)

  3) send(ctx, file) when path does not finish with slash and format is enabled should 200:
     Error: Parse Error
      at Error (native)
      at Socket.socketOnData (_http_client.js:305:20)
      at readableAddChunk (_stream_readable.js:146:16)
      at Socket.Readable.push (_stream_readable.js:110:10)
      at TCP.onread (net.js:523:20)

  4) send(ctx, file) when path is a file should return the path:
     Error: expected 200 "OK", got 500 "Internal Server Error"
      at Test.assert (C:\Users\felix\git\send\node_modules\supertest\lib\test.js:205:15)
      at assert (C:\Users\felix\git\send\node_modules\supertest\lib\test.js:132:12)
      at C:\Users\felix\git\send\node_modules\supertest\lib\test.js:129:5
      at Test.Request.callback (C:\Users\felix\git\send\node_modules\superagent\lib\node\index.js:746:30)
      at Test.<anonymous> (C:\Users\felix\git\send\node_modules\superagent\lib\node\index.js:135:10)
      at IncomingMessage.<anonymous> (C:\Users\felix\git\send\node_modules\superagent\lib\node\index.js:938:12)
      at endReadableNT (_stream_readable.js:893:12)

  5) send(ctx, file) when path is a file and max age is specified should set max-age in seconds:
     Error: expected "Cache-Control" header field
      at Test.assert (C:\Users\felix\git\send\node_modules\supertest\lib\test.js:190:35)
      at assert (C:\Users\felix\git\send\node_modules\supertest\lib\test.js:132:12)
      at C:\Users\felix\git\send\node_modules\supertest\lib\test.js:129:5
      at Test.Request.callback (C:\Users\felix\git\send\node_modules\superagent\lib\node\index.js:746:30)
      at Test.<anonymous> (C:\Users\felix\git\send\node_modules\superagent\lib\node\index.js:135:10)
      at IncomingMessage.<anonymous> (C:\Users\felix\git\send\node_modules\superagent\lib\node\index.js:938:12)
      at endReadableNT (_stream_readable.js:893:12)

  6) send(ctx, file) when path is a file and max age is specified should truncate fractional values for max-age:
     Error: expected "Cache-Control" header field
      at Test.assert (C:\Users\felix\git\send\node_modules\supertest\lib\test.js:190:35)
      at assert (C:\Users\felix\git\send\node_modules\supertest\lib\test.js:132:12)
      at C:\Users\felix\git\send\node_modules\supertest\lib\test.js:129:5
      at Test.Request.callback (C:\Users\felix\git\send\node_modules\superagent\lib\node\index.js:746:30)
      at Test.<anonymous> (C:\Users\felix\git\send\node_modules\superagent\lib\node\index.js:135:10)
      at IncomingMessage.<anonymous> (C:\Users\felix\git\send\node_modules\superagent\lib\node\index.js:938:12)
      at endReadableNT (_stream_readable.js:893:12)

  7) send(ctx, file) .hidden option when trying to get a hidden file should 404:
     Error: expected 404 "Not Found", got 200 "OK"
      at Test.assert (C:\Users\felix\git\send\node_modules\supertest\lib\test.js:205:15)
      at assert (C:\Users\felix\git\send\node_modules\supertest\lib\test.js:132:12)
      at C:\Users\felix\git\send\node_modules\supertest\lib\test.js:129:5
      at Test.Request.callback (C:\Users\felix\git\send\node_modules\superagent\lib\node\index.js:746:30)
      at Test.<anonymous> (C:\Users\felix\git\send\node_modules\superagent\lib\node\index.js:135:10)
      at IncomingMessage.<anonymous> (C:\Users\felix\git\send\node_modules\superagent\lib\node\index.js:938:12)
      at endReadableNT (_stream_readable.js:893:12)

  8) send(ctx, file) .hidden option when trying to get a file from a hidden directory should 404:
     Error: expected 404 "Not Found", got 200 "OK"
      at Test.assert (C:\Users\felix\git\send\node_modules\supertest\lib\test.js:205:15)
      at assert (C:\Users\felix\git\send\node_modules\supertest\lib\test.js:132:12)
      at C:\Users\felix\git\send\node_modules\supertest\lib\test.js:129:5
      at Test.Request.callback (C:\Users\felix\git\send\node_modules\superagent\lib\node\index.js:746:30)
      at Test.<anonymous> (C:\Users\felix\git\send\node_modules\superagent\lib\node\index.js:135:10)
      at IncomingMessage.<anonymous> (C:\Users\felix\git\send\node_modules\superagent\lib\node\index.js:938:12)
      at endReadableNT (_stream_readable.js:893:12)

extract sanitization

koa-send does a good job to analyze the path and sanitize it (decode, '..',, normalize).

this is something we probably want done even if there is a cache miss.

nevertheless, when there is a cache miss, we only get either an error or an empty path so we have to redo the same things.

what do you think would be the better way to handle this ? extract these parts in another middleware ? copy them ? how could this code be shared with koa-send without duplication (of code and of execution time) ?

can not send absolute path

"koa-send": "^3.3.0",

var filePath=path.join(__dirname,'file');
.........
await send(ctx,filePath);

But It do not work。
And i just use it as follow:

await send(ctx,'./file')

Should the path be normalized?

I'm using koa-send in my Koa server and I have the problem that I get Malicious Path from resolve-path when the request path is something along the lines of http://localhost:3000//some/path/image.jpg.

Would it be a bad idea to path.normalize the path in koa-send before processing it further? This could for instance be done here https://github.com/koajs/send/blob/master/index.js#L46 before doing substring on the path.

Triggering a download in browser from Koa server?

I'm trying to send a POST request from the client to the server. Upon receiving that POST request, the server should send the browser a file to be downloaded. I read the Koa docs, and it says that this.attachment should be used to prompt the client for download, but it doesn't seem to be doing anything.

Here is what I've tried:

client.js

const { fetch } = window;

fetch('/download', { method: 'POST' });

server.js

const Koa = require('koa');
const Router = require('koa-router');
const serve = require('koa-static');
const send = require('koa-send');

const app = new Koa();
const router = new Router();

router.post('/download', function * () {
  console.log('THIS IS RUNNING'); // This runs

  const path = `${__dirname}/blah.txt`;
  this.attachment('blah.txt');
  send(this, path);
});

app.use(serve(`${__dirname}/public`));
app.use(router.routes());
app.use(router.allowedMethods());

app.listen(3000);

However, nothing seems to happen, the file doesn't download. I've also tried changing send(this, path); to this.body = send(this, path); and that didn't do anything either (although it stopped the 404 error from coming since a body is now being sent as a response).

Am I understanding something wrong?

how to send dotfiles?

I can't send a file or directory that begins with a dot

await send(ctx, '/a/xx'); // exist, 200
await send(ctx, '/.a/xx'); // exist, but 404 Not Found
await send(ctx, '/a/.xx'); // exist, but 404 Not Found

No setting in docs. How can I do?

Can it support custom `Content-Type`?

using setHeaders: ctx => ctx.set('Content-Type', 'application/octet-stream') is unsupported when i send mp3 file, the response header is audio/mpeg still.

if i put
if (setHeaders) setHeaders(ctx.res, path, stats)
after
ctx.type = type(path, encodingExt), it can works?

Format option defaults to true

What do you think about setting the format option as true by default (to serve static file servers without requiring a trailing slash) ?

koa-send and heroku

koa-send is not working into heroku environment because it is calculating wrong path

Test "when path does not finish with slash and format is enabled should 200" fails

1) send(ctx, file) when path does not finish with slash and format is enabled should 200:
     Error: Parse Error
      at Error (native)
      at Socket.socketOnData (_http_client.js:305:20)
      at readableAddChunk (_stream_readable.js:146:16)
      at Socket.Readable.push (_stream_readable.js:110:10)
      at TCP.onread (net.js:523:20)

The issue is that in index.js:73 fs.stat() is called, checked for a directory and then the index is appended to the path. However, fs.stat() isn't called again, so stat.size is 0, which means Content-Length is set wrong and therefor a Parse Error is emitted. PR incoming.

Unexpected token function

use koa-views and koa-view use koa-sen
Start File:

require('babel-core/register');
require('babel-polyfill');
const app = require('../app.js');

.babelrc File

{
  "presets": [
    "es2015-node",
    "stage-3"
  ],
  "plugins": [
    "add-module-exports"
  ]
}

app File

import Koa from 'koa';
import views from 'koa-views';
const app = new Koa();

Error

/Volumes/Mac/Projects/Node/koa-test/node_modules/koa-send/index.js:37
async function send (ctx, path, opts = {}) {
^^^^^^^^
SyntaxError: Unexpected token function
at Object.exports.runInThisContext (vm.js:76:16)
at Module._compile (module.js:542:28)
at Module._extensions..js (module.js:579:10)
at Object.require.extensions.(anonymous function) [as .js] (/Volumes/Mac/Projects/Node/koa-test/node_modules/babel-register/lib/node.js:152:7)
at Module.load (module.js:487:32)
at tryModuleLoad (module.js:446:12)
at Function.Module._load (module.js:438:3)
at Module.require (module.js:497:17)
at require (internal/module.js:20:19)
at Object. (/Volumes/Mac/Projects/Node/koa-test/node_modules/koa-views/src/index.js:8:14)
at Module._compile (module.js:570:32)
at Module._extensions..js (module.js:579:10)
at Object.require.extensions.(anonymous function) [as .js] (/Volumes/Mac/Projects/Node/koa-test/node_modules/babel-register/lib/node.js:152:7)
at Module.load (module.js:487:32)
at tryModuleLoad (module.js:446:12)
at Function.Module._load (module.js:438:3)

Warning: a promise was created in a handler but was not returned from it

I am receiving this warning:
Warning: a promise was created in a handler but was not returned from it

the file is served properly though.

I used koa-convert to use koa-static with koa alpha 3:

app.use(convert(serve(__dirname + '../../../dist', { maxage: config.cacheTime })));

npm versions:

"koa": "^2.0.0-alpha.3",
"koa-send": "^3.1.0",
"koa-static": "^1.5.2",
"koa-convert": "^1.2.0",
koa-send send "/" {"maxage":604800000,"root":"/home/max/projects/kask/dist","index":"index.html"} +12s
Warning: a promise was created in a handler but was not returned from it
    at Object.eval (eval at <anonymous> (/home/max/projects/kask/node_modules/thenify/index.js:30:15), <anonymous>:9:8)
    at Object.<anonymous> (/home/max/projects/kask/node_modules/koa-static/node_modules/koa-send/index.js:72:28)
    at [object Generator].next (native)
    at onFulfilled (/home/max/projects/kask/node_modules/co/index.js:65:19)
    at processImmediate [as _immediateCallback] (timers.js:383:17)
From previous event:
    at next (/home/max/projects/kask/node_modules/co/index.js:100:51)
    at onFulfilled (/home/max/projects/kask/node_modules/co/index.js:69:7)
    at /home/max/projects/kask/node_modules/co/index.js:54:5
    at Object.co (/home/max/projects/kask/node_modules/co/index.js:50:10)
    at Object.toPromise (/home/max/projects/kask/node_modules/co/index.js:118:63)
    at next (/home/max/projects/kask/node_modules/co/index.js:99:29)
    at onFulfilled (/home/max/projects/kask/node_modules/co/index.js:69:7)
    at /home/max/projects/kask/node_modules/co/index.js:54:5
    at Object.co (/home/max/projects/kask/node_modules/co/index.js:50:10)
    at converted (/home/max/projects/kask/node_modules/koa-convert/index.js:17:15)
    at dispatch (/home/max/projects/kask/node_modules/koa/node_modules/koa-compose/index.js:41:32)
    at next (/home/max/projects/kask/node_modules/koa/node_modules/koa-compose/index.js:42:18)
    at /home/max/projects/kask/node_modules/koa-bodyparser/index.js:60:14
    at process._tickCallback (node.js:377:9)

Should we allow customizing the cache-control header ?

I want to add must-revalidate to the cache-control but I can't.

Read #39 but it doesn't help me if I want to set Cache-Control because it is overwritten here

send/index.js

Line 100 in 0e2fe76

ctx.set('Cache-Control', 'max-age=' + (maxage / 1000 | 0));

Could this module allow passing a cacheControl option of some kind to just take control of the header entirely (in this case maxAge default and option is simply ignored) ?

Byte-Range Support?

Are there plans to add byte-range support to this? Similar to the send module that express uses?

It's not enough just have max-age in header to cache the static files.

When I use iOS/OS X Safari to access static files (serve by koa-send).
The response headers has the max-age=2628000.
BUT when I press CMD + R or tap the iOS Safari refresh button...
Safari will set the request headers to max-age=0.
And then the cache is broken...

What if we set both of max-age and expires?
It will be better to add last-modified to koa-send.

Extensions option breaks if local path contains a dot anywhere

The regex in this line will match paths with a dot anywhere. In particular if I'm developing in the local folder /home/razorx/web/example.com and try to use the extensions option to serve /foo then path = /home/razorx/web/example.com/foo and will match the regex thus skipping the if block.

 if (extensions && !/\..*$/.exec(path)) {

add '//' patterns as malicious ?

I wonder what you think about adding a sequence of consecutive slashes as potentially malicious.

maybe finding '//' in the path should throw ?

Page type not set and path messed up

Hi koa-send developers,
Currently am i working on a project and i am trying to implement koa-send.
https://github.com/projecthammer/hammer
But I am only running into a problem:

when i am trying to send a html file back to the client do i always have to define the root option:
https://github.com/projecthammer/hammer/blob/master/core/http/index.js#L52
If i am not doing this does koa-send mess up the path when it tries to resolve the path:
https://github.com/koajs/send/blob/master/index.js#L68
for some reason does it add root of the file again to the beginning of the path which causes that the path becomes invalid.
Is this a bug or do i always have to define the root option?

Also when the file is send back to the client is the 'type' header for some reason not set:
https://github.com/koajs/send/blob/master/index.js#L108
the type function nicely returns 'text/html' but for some reason is it not set in the response.

HTTP/1.1 404 Not Found
Content-Length: 138
Last-Modified: Fri, 14 Oct 2016 13:18:10 GMT
Cache-Control: max-age=0
Content-Type: application/json; charset=utf-8
Date: Fri, 14 Oct 2016 13:35:52 GMT
Connection: keep-alive

Trailing slash redirect

There should be a way to redirect /foo/bar requests to /foo/bar/ path to properly handle relative links.

Example: <img src='hello.png'> placed in /foo/bar/index.html won't load when you open /foo/bar. The request goes to /foo/hello.png instead of /foo/bar/hello.png.

Prefer Brotli over Gzip

I see that Brotli was released in v4.1.0 - awesome, thanks for adding!

There's one small issue. Chrome's typical Accept-Encoding header looks like this:

Accept-Encoding:gzip, deflate, sdch, br

It seems that koa-send interprets the order as a preference, and if it sees gzip, serves that first. That means if both .gz and .br files are available, the Brotli variants will never get sent:

screen shot 2017-05-30 at 13 20 14

In instances where more than one compression algorithm is accepted, I think it'd be better to automatically send the smallest file.

`fs.exists` causes a code-breaking hang.

Took me a while to find the source of this.
In the context of koa-static, I have some routes that looks like this:

app.use(route.post('/upload', routes.upload));
// ... other routes here
app.use(serve(__dirname + '/build'));

Which works great at first and the serve route serves my build directory statically.
However, once the post route is successfully used once, the serve generator never returns if it is called again. Instead the debug outputs:

 <-- GET /
  koa-send send "/" {"root":".../build","index":"index.html"} +2m

but it never bubbles back up the middleware chain.

After digging into the source, I found that it's hanging on this line in koa-send.
Breaking this apart more, I found that whatever happens after a file upload route is used causes yield fs.exists(path + '.gz') to hang and never return, which blocks koa-send from returning.

I frankensteined this block code to use fs.exists as a callback (ie, not the mz/fs wrapper), and that works fine.

I also updated koa-send's mz/fs dependency to 2.0.0, but it didn't solve the issue.

Notes:

  • All other routes continue to work fine, as they don't rely on koa-send
  • I changed the router and upload route a few times, to use different styles of handling the upload with different dependencies. All of them cause this problem.
  • I also changed the koa-static route to serve files in different ways and with different dependencies, but all the effective ways of serving static files rely on koa-send.
  • I can see this being an issue to report with mz/fs, but I want to report it here first as this code should not block server routes.

Using in typescript

Cannot install typings

typings install dt~koa-send --global --save
typings ERR! message Unable to find "koa-send" ("dt") in the registry.

Update mz dependency to 2.0

Not the most pressing issue, but anytime I use npm my console gets polluted with an npm WARN unmet dependency message

koa-send requires mz@'1.0.1 ' but will load ... version 2.0.0

I've been using it without any problems. Is there a reason you're continuing to use the old version?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.