Giter Club home page Giter Club logo

fresh's Introduction

fresh

NPM Version NPM Downloads Node.js Version Build Status Test Coverage

HTTP response freshness testing

Installation

This is a Node.js module available through the npm registry. Installation is done using the npm install command:

$ npm install fresh

API

var fresh = require('fresh')

fresh(reqHeaders, resHeaders)

Check freshness of the response using request and response headers.

When the response is still "fresh" in the client's cache true is returned, otherwise false is returned to indicate that the client cache is now stale and the full response should be sent.

When a client sends the Cache-Control: no-cache request header to indicate an end-to-end reload request, this module will return false to make handling these requests transparent.

Known Issues

This module is designed to only follow the HTTP specifications, not to work-around all kinda of client bugs (especially since this module typically does not receive enough information to understand what the client actually is).

There is a known issue that in certain versions of Safari, Safari will incorrectly make a request that allows this module to validate freshness of the resource even when Safari does not have a representation of the resource in the cache. The module jumanji can be used in an Express application to work-around this issue and also provides links to further reading on this Safari bug.

Example

API usage

var reqHeaders = { 'if-none-match': '"foo"' }
var resHeaders = { etag: '"bar"' }
fresh(reqHeaders, resHeaders)
// => false

var reqHeaders = { 'if-none-match': '"foo"' }
var resHeaders = { etag: '"foo"' }
fresh(reqHeaders, resHeaders)
// => true

Using with Node.js http server

var fresh = require('fresh')
var http = require('http')

var server = http.createServer(function (req, res) {
  // perform server logic
  // ... including adding ETag / Last-Modified response headers

  if (isFresh(req, res)) {
    // client has a fresh copy of resource
    res.statusCode = 304
    res.end()
    return
  }

  // send the resource
  res.statusCode = 200
  res.end('hello, world!')
})

function isFresh (req, res) {
  return fresh(req.headers, {
    etag: res.getHeader('ETag'),
    'last-modified': res.getHeader('Last-Modified')
  })
}

server.listen(3000)

License

MIT

fresh's People

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  avatar  avatar  avatar  avatar

fresh's Issues

nginx transforms ETag to Weak Etag on gzip => module doesn't work

I explored a bit trying to find out why this module doesn't work for me.

The first issue I found was weak etags. The module does not support them.

The second was kind of astonishing for me cause I saw: return !! (etagMatches && notModified); at the end.

ETag and Last-Modified are two different mechanisms. If any of them is satisfied, the request cache is "fresh". Not both of them. So there must be return !! (etagMatches || notModified); unless you point out the sane reason :/

Eventually support IncomingMessage/OutgoingMessage

Right now the signature is fresh(reqHeaders, resHeaders), which is of course nice for being agnostic, but slightly lame. I don't think we should remove this "agnostic-ness" per-se, but it would be nice to pass in IncomigMessage and OutgoingMessage objects so this lib can just handle weirdness like expressjs/express#2468

May i ask a question?

hi,Why do you write this function like this? What's the difference between ascill and normal characters?

function parseTokenList (str) {
  var end = 0
  var list = []
  var start = 0

  // gather tokens
  for (var i = 0, len = str.length; i < len; i++) {
    switch (str.charCodeAt(i)) {
      case 0x20: /*   */
        if (start === end) {
          start = end = i + 1
        }
        break
      case 0x2c: /* , */
        list.push(str.substring(start, end))
        start = end = i + 1
        break
      default:
        end = i + 1
        break
    }
  }

  // final token
  list.push(str.substring(start, end))

  return list
}

emmmm,

str.replace(/\s/g,'').split(',');

Vulnerability in fresh

From snyk.io, we have Regular Expression Denial of Service (ReDoS) on this package

TL;DR:

Affected versions of this package are vulnerable to Regular expression Denial of Service (ReDoS) attacks. A Regular Expression (/ *, */) was used for parsing HTTP headers and take about 2 seconds matching time for 50k characters.

Please take a look and check, if true --> please fix it asap, because many packages are based on it

Source: https://snyk.io/vuln/npm:fresh:20170908

Getting "TypeError: Cannot read property 'last-modified' of undefined" when running gulp-connect

[12:13:53] Running server
/Users/.../node_modules/gulp-connect/node_modules/fresh/index.js:29
  var lastModified = res['last-modified'];
                        ^

TypeError: Cannot read property 'last-modified' of undefined
    at fresh (/Users/.../node_modules/gulp-connect/node_modules/fresh/index.js:29:25)
    at SendStream.isFresh (/Users/.../node_modules/gulp-connect/node_modules/send/index.js:372:10)
    at SendStream.send (/Users/.../node_modules/gulp-connect/node_modules/send/index.js:539:13)
    at onstat (/Users/.../node_modules/gulp-connect/node_modules/send/index.js:624:10)
    at FSReqWrap.oncomplete (fs.js:167:5)

gulp-connect setup:
connect.server({ livereload: false, root: config.getDir() + '/', fallback: config.getDir() + '/index.html', https: true, host: '0.0.0.0', });
Mostly happens when running with https:true and accessing the dev env from outside the network (I've opened a port to be forward from WLAN to my dev computer, for debugging). Anyway, only then this problem happened.

Fresh version: 0.3.0

Why check cache-control: no-cache?

Hi,
If a request contains both header cache-control: no-cache and a fresh etag, fresh will mark the request not fresh. I don't understand why. According to w3.org's explanation about no-cache: If the no-cache directive does not specify a field-name, then a cache MUST NOT use the response to satisfy a subsequent request without successful revalidation with the origin server. This directive only tells a proxy to revalidate with the server. It shouldn't be used by origin server to determine a cached content is stale, which is the job of etag and last modified timestamp header.

I encountered this problem when using IE XDomainRequest. IE added cache-control: no-cache automatically if previous response has header Cache-Control: max-age=0 and there is no way to disable the request header.

fresh is not ignoring empty http tokens

It appears that fresh has copy-pasted logic from send related to etag handling.

This commit should probably be copied in it's entirety over to fresh, right? :
pillarjs/send@aeb69c6

It does raise the question if it might be possible to package up in a more re-usable way since there is some overlap between parts of the internals of fresh (req, res) and send's isPreconditionFailure. Or perhaps they are just different enough to warrant the duplication.

Anyway, if you agree that this commit should be copied over from send happy to send a PR.

thanks for these 2 fantastic modules by the way! ❤️ I'm working on improving koa's range support and send, fresh have both been incredibly rich sources towards better compliance with rfc7233, rfc 7232

latest change for max-age=0

I think the latest change to look for max-age=0 in the request and decides if the cache is fresh or not, is not a valid assumptions.

the code I am talking about is in this line.

if (cc && (cc.indexOf('no-cache') !== -1 || cc.indexOf('max-age=0') !== -1)) return false;

The max-age=0 in the request is sent by the browser to validate if the cache information in the request is current or not. With this change the server will never return 304 for any request contains (max-age=0). The response will always return the content (200).

please read the meaning of max-age=0
from http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html

14.9.4 Cache Revalidation and Reload Controls

Specific end-to-end revalidation
The request includes a "max-age=0" cache-control directive, which forces each cache along the path to the origin server to revalidate its own entry, if any, with the next cache or server. The initial request includes a cache-validating conditional with the client's current validator.

max-age
When an intermediate cache is forced, by means of a max-age=0 directive, to revalidate its own cache entry, and the client has supplied its own validator in the request, the supplied validator might differ from the validator currently stored with the cache entry. In this case, the cache MAY use either validator in making its own request without affecting semantic transparency.
However, the choice of validator might affect performance. The best approach is for the intermediate cache to use its own validator when making its request. If the server replies with 304 (Not Modified), then the cache can return its now validated copy to the client with a 200 (OK) response. If the server replies with a new entity and cache validator, however, the intermediate cache can compare the returned validator with the one provided in the client's request, using the strong comparison function. If the client's validator is equal to the origin server's, then the intermediate cache simply returns 304 (Not Modified). Otherwise, it returns the new entity with a 200 (OK) response.
If a request includes the no-cache directive, it SHOULD NOT include min-fresh, max-stale, or max-age.

noneMatch.split is not a function

Hello everyone, i have encountered an issue when using fresh in my web server based on express. I suspect that express automatically installed fresh. Here is the error:

TypeError: noneMatch.split is not a function
    at fresh (/server/web/main/node_modules/fresh/index.js:61:40)
    at IncomingMessage.fresh (/server/web/main/node_modules/express/lib/request.js:469:12)
    at ServerResponse.send (/server/web/main/node_modules/express/lib/response.js:196:10)
    at ServerResponse.json (/server/web/main/node_modules/express/lib/response.js:256:15)
    at ServerResponse.send (/server/web/main/node_modules/express/lib/response.js:158:21)
    at app.get (/server/web/main/main.js:23:6)
    at Layer.handle [as handle_request] (/server/web/main/node_modules/express/lib/router/layer.js:95:5)
    at next (/server/web/main/node_modules/express/lib/router/route.js:137:13)
    at Route.dispatch (/server/web/main/node_modules/express/lib/router/route.js:112:3)
    at Layer.handle [as handle_request] (/server/web/main/node_modules/express/lib/router/layer.js:95:5)

Does anyone know why this happens? I am behind an NGINX proxy, if that is important.

Inadvertent reverse of 'no-cache' and 'no-store'

As stated in the MDN docs the no-cache directive is used indicate that the client has a copy in the cache but needs to validate it with each request. no-store on the other hand will disable all caches.

no-cache
Forces caches to submit the request to the origin server for validation before releasing a cached copy.

This means that fresh should give back true inconjuction with theses headers:
var reqHeaders = {
"if-none-match": "foo",
"cache-control" : "no-cache"
};

var resHeaders = {
"etag" : "foo"
};

correct me if I'm wrong;

If-Modified-Since is not ignored if request contains If-None-Match

According to the latest HTTP RFC,

A recipient MUST ignore If-Modified-Since if the request contains an If-None-Match header field; the condition in If-None-Match is considered to be a more accurate replacement for the condition in If-Modified-Since, and the two are only combined for the sake of interoperating with older intermediaries that might not implement If-None-Match.

fresh does not ignore If-Modified-Since, it returns false if the If-Modified-Since is before last-modified. I would expect it to return true immediately if If-None-Match matches the ETag header.

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.