UNPKG

3.48 kBJavaScriptView Raw
1/**
2 * Get the expiry time of an XMLHttpRequest response
3 * returns a Date object for when the request expires
4 * returns null if there is valid data that says not to cache
5 * returns undefined if there is no caching information
6 **/
7
8function getCacheExpiry(xhr){
9 var expiresHeader = xhr.getResponseHeader('Expires');
10 var cacheControlHeader = xhr.getResponseHeader('Cache-Control');
11
12 var expiry = parseCacheControlHeader(cacheControlHeader);
13 if(expiry === undefined) {
14 expiry = parseExpiresHeader(expiresHeader);
15 }
16 expiry = nullifyInvalidExpiration(expiry);
17 return expiry;
18}
19
20/**
21 * Parse the data in the Cache-Control header for information
22 * returns Date object if the Cache-Control header is valid
23 * returns null if there is a valid Cache-Control header that says not to cache
24 * returns undefined if there is no valid Cache-Control header
25 **/
26function parseCacheControlHeader(cacheControlHeader){
27 if(cacheControlHeader === null || cacheControlHeader === undefined){
28 return undefined;
29 }
30 var headerData = cacheControlHeader.split(",");
31 var expiry = undefined;
32 var keyword;
33 for(var i=0; i<headerData.length; i++){
34 keyword = headerData[i].trim();
35 if(keyword.indexOf("max-age") > -1){
36 expiry = parseCacheControlAge(keyword, expiry);
37 }else{
38 expiry = parseCacheControlKeyword(keyword, expiry);
39 }
40 }
41 return expiry;
42}
43
44/**
45 * Parse the max-age value from the Cache-Control header data
46 * returns Date object if the max-age value is valid
47 * returns undefined if there is no valid data
48 **/
49function parseCacheControlAge(maxAge, expiry){
50 if(expiry === null){
51 return expiry;
52 }
53 var seconds = maxAge.split('=')[1].trim();
54 seconds = parseInt(seconds);
55 if(isNaN(seconds)){
56 return undefined;
57 }else{
58 return nowPlusSeconds(seconds);
59 }
60}
61
62/**
63 * Parse non-max-age keywords in the Cache-Control header
64 * returns expiry Date, undefined, or null depending on the keyword behavior
65 **/
66function parseCacheControlKeyword(keyword, expiry){
67 if(keyword.indexOf("public") > -1 || keyword.indexOf("private") > -1){
68 return expiry;
69 }
70 if(keyword.indexOf("no-cache") > -1 || keyword.indexOf("no-store") > -1){
71 return null;
72 }
73 if(keyword.indexOf("must-revalidate") > -1 || keyword.indexOf("proxy-revalidate") > -1){
74 return expiry; // Noop
75 }
76 if(keyword.indexOf("s-maxage") > -1){
77 return expiry; // Noop
78 }
79 return expiry; // Unknown keyword, Noop
80}
81
82/**
83 * Parse the data in the Expires header for information
84 * returns Date object if the Expires header is valid
85 * returns null if there is a valid Expires header that says not to cache
86 * returns undefined if there is no valid Cache-Control header
87 **/
88function parseExpiresHeader(expiresHeader){
89 if(expiresHeader === null || expiresHeader === undefined) {
90 return undefined;
91 }
92 expires = new Date(expiresHeader);
93 if(expires == "Invalid Date"){
94 return null;
95 }
96 return expires;
97}
98
99/**
100 * Nullify any invalid expiration Date objects (if they're in the past)
101 **/
102function nullifyInvalidExpiration(expiration){
103 if(expiration instanceof Date && expiration < new Date()){
104 return null;
105 }
106 return expiration;
107}
108
109/**
110 * Returns the current time plus the given number of seconds into the future
111 **/
112function nowPlusSeconds(seconds){
113 var now = new Date();
114 now.setTime(now.getTime() + seconds * 1000);
115 return now;
116}
117
118module.exports.getCacheExpiry = getCacheExpiry;
119module.exports.nowPlusSeconds = nowPlusSeconds;