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

Auto generate ETag & freshness check #92

Merged
merged 8 commits into from
Dec 1, 2018
Merged

Auto generate ETag & freshness check #92

merged 8 commits into from
Dec 1, 2018

Conversation

tiaod
Copy link
Contributor

@tiaod tiaod commented Dec 1, 2018

Fix #91

@tiaod
Copy link
Contributor Author

tiaod commented Dec 1, 2018

Freshness check

Return 304 if fresh.

module.exports = {
  name: "rss",
  actions: {
    feed(ctx) {
      ctx.meta.$responseHeaders = {
        "Last-Modified": "Tue, 24 Feb 2018 08:01:04 GMT"
      }
      return rssFeed
    },
    async getItem(ctx){
      let item = await getRssItem(ctx.params.id)
      ctx.meta.$responseHeaders = {
        "ETag": item.hash
      }
      return item
    }
  }
}

image

Auto generate etag

Service settings:

settings: {
  etag: true
}

Will only generate ETag for none stream response. If you want to stream your response, you should generate the etag by yourself.

module.exports = {
  name: "export",
  actions: {
    // Download response as a file in the browser
    downloadCSV(ctx) {
      ctx.meta.$responseType = "text/csv";
      ctx.meta.$responseHeaders = {
        "Content-Disposition": `attachment; filename="data-${ctx.params.id}.csv"`,
        "ETag": '<your etag here>'
      };
      return csvFileStream;
    }
}

@icebob
Copy link
Member

icebob commented Dec 1, 2018

Great, thank you! Please cover this ETag functionality with tests.

@coveralls
Copy link

coveralls commented Dec 1, 2018

Pull Request Test Coverage Report for Build 360

  • 29 of 29 (100.0%) changed or added relevant lines in 2 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage increased (+0.3%) to 93.822%

Totals Coverage Status
Change from base Build 356: 0.3%
Covered Lines: 572
Relevant Lines: 587

💛 - Coveralls

@icebob icebob merged commit 0526fd9 into moleculerjs:master Dec 1, 2018
@icebob
Copy link
Member

icebob commented Dec 1, 2018

Thanks @tiaod,
I've made some changes:

  • moved the etag setting to route level instead of service level.
  • change the default to false because hash calculation is a time-consuming operation and breaks the performance.
  • removed weak: true ETag option, because weak works only with fs.Stats entity but API Gateway never use it, so it always will use strong ETags

@tiaod
Copy link
Contributor Author

tiaod commented Dec 1, 2018

@icebob I‘m just trying to do what express do by default.

See doc

  1. Express return weak etag by default.
  2. The default setting of etag is true.
    image
    I think maybe it's better to make the etag setting act like express?
//Boolean
settings: {
  routes: [{
    path: "/",
    etag: true
  }]
},

//String
settings: {
  routes: [{
    path: "/",
    etag: "weak" // or "strong"
  }]
},

// Function
settings: {
  routes: [{
    path: "/",
    etag: function(body, encoding) {
      return generateHash(body, encoding); // consider the function is defined
    }
  }]
}

Btw: HTTP ETag strong and weak validation from wikipedia:

A strongly validating ETag match indicates that the content of the two resource representations is byte-for-byte identical and that all other entity fields (such as Content-Language) are also unchanged. Strong ETags permit the caching and reassembly of partial responses, as with byte-range requests.

A weakly validating ETag match only indicates that the two representations are semantically equivalent, meaning that for practical purposes they are interchangeable and that cached copies can be used. However the resource representations are not necessarily byte-for-byte identical, and thus weak ETags are not suitable for byte-range requests. Weak ETags may be useful for cases in which strong ETags are impractical for a web server to generate, such as with dynamically-generated content.

@icebob
Copy link
Member

icebob commented Dec 1, 2018

Ok, we can change it to express-like, but if you check the source of etag, you will see, weak mode works only if you pass an FsStat instance for the generator function:
https://github.com/jshttp/etag/blob/4664b6e53c85a56521076f9c5004dd9626ae10c8/index.js#L76-L79
So with API Gateway it never use weak etags. Use strong hashes just put W/ before the hash :)
But the custom generator function is good idea.

@icebob
Copy link
Member

icebob commented Dec 1, 2018

I've updated. So now it has the following values:

  • etag: true, - weak hashing (Express-like)
  • etag: false, - disabled (default)
  • etag: "weak", - weak hashing
  • etag: "strong", - strong hashing
  • etag: body => hash(body), - custom hash function.

And you can set it in service settings and route setting as well. Route setting overwrites service setting.

@tiaod
Copy link
Contributor Author

tiaod commented Dec 2, 2018

I'm living in GMT+8 and good morning! 😄

I think the weak mode of etag is not only for fs.state, It certainly has other uses. The etag docs says that:

etag(entity, [options])
Generate a strong ETag for the given entity. This should be the complete body of the entity. Strings, Buffers, and fs.Stats are accepted. By default, a strong ETag is generated except for fs.Stats, which will generate a weak ETag (this can be overwritten by options.weak).

After some search, I found the difference between the strong and weak ETag:

  • Strong ETag indicates that resource content is same for response body and the response headers.
  • Weak ETag indicates that the two representations are semantically equivalent. It compares only the response body.

But I am still very confused which should I use. 😅 Finally, I decided to follow the express, to use the weak etag by default. Do what express do is right!

@HighSoftWare96
Copy link

Excuse me guys but why no Cache-Control header is attached when using the Etag?

@icebob
Copy link
Member

icebob commented Apr 8, 2020

Cache-Control & ETag are two different caching solutions. Using ETag, no need Cache-Control headers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Feature Request] Cache Control
4 participants