Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | ETags now working for the /uv page. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | etags-cache-control |
Files: | files | file ages | folders |
SHA3-256: |
5b84cab0d6d0dd8ac6820d68e7c76e55 |
User & Date: | drh 2018-02-24 03:38:07.398 |
Context
2018-02-24
| ||
03:47 | Optimizations to the ETag implementation. ... (check-in: 2588d447 user: drh tags: etags-cache-control) | |
03:38 | ETags now working for the /uv page. ... (check-in: 5b84cab0 user: drh tags: etags-cache-control) | |
00:39 | First cut at supporting ETags: and If-None-Match: for cache control. ... (check-in: 94c0b8ec user: drh tags: etags-cache-control) | |
Changes
Changes to src/doc.c.
︙ | ︙ | |||
637 638 639 640 641 642 643 | goto doc_not_found; } }else{ goto doc_not_found; } } if( isUV ){ | | > > > | < | | > | 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 | goto doc_not_found; } }else{ goto doc_not_found; } } if( isUV ){ if( db_table_exists("repository","unversioned") ){ char *zHash; zHash = db_text(0, "SELECT hash FROM unversioned WHERE name=%Q",zName); etag_require_hash(zHash); if( unversioned_content(zName, &filebody)==0 ){ rid = 1; zDfltTitle = zName; } } }else if( fossil_strcmp(zCheckin,"ckout")==0 ){ /* Read from the local checkout */ char *zFullpath; db_must_be_within_tree(); zFullpath = mprintf("%s/%s", g.zLocalRoot, zName); if( file_isfile(zFullpath, RepoFILE) |
︙ | ︙ |
Changes to src/etag.c.
︙ | ︙ | |||
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | ** if( database-has-changed ) return; ** if( display-cookie-"n"-attribute-has-changes ) return; ** Output "304 Not Modified" message and abort; ** ** In other words, if all conditions specified by the ETag are met, then ** Fossil will return a 304 and avoid doing all the work, and all of the ** bandwidth, associating with regenerating the whole page. */ #include "config.h" #include "etag.h" #if INTERFACE /* ** Things to monitor */ #define ETAG_CONST 0x00 /* Output is independent of database or parameters */ #define ETAG_CONFIG 0x01 /* Output depends on the configuration */ #define ETAG_DATA 0x02 /* Output depends on 'event' table */ #define ETAG_COOKIE 0x04 /* Output depends on a display cookie value */ | > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > < > > | > > > | < < < < < < < < < < < < < < < < < < < < < < | 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 | ** if( database-has-changed ) return; ** if( display-cookie-"n"-attribute-has-changes ) return; ** Output "304 Not Modified" message and abort; ** ** In other words, if all conditions specified by the ETag are met, then ** Fossil will return a 304 and avoid doing all the work, and all of the ** bandwidth, associating with regenerating the whole page. ** ** To make use of this feature, page generators can invoke the ** etag_require() interface with mask of ETAG_CONST, ETAG_CONFIG, ** ETAG_DATA, and/or ETAG_COOKIE. Or it can invoke etag_require_hash() ** with some kind of text hash. ** ** Or, in the WEBPAGE: line for the page generator, extra arguments ** can be added. "const", "config", "data", and/or "cookie" ** ** ETAG_CONST const The reply is always the same for the same ** build of the fossil binary. The content ** is independent of the repository. ** ** ETAG_CONFIG config The reply is the same as long as the repository ** config is constant. ** ** ETAG_DATA data The reply is the same as long as no new artifacts ** are added to the repository ** ** ETAG_COOKIE cookie The reply is the same as long as the display ** cookie is unchanged. ** ** Page generator routines can also invoke etag_require_hash(HASH) where ** HASH is some string. In that case, the reply is the same as long as ** the hash is the same. */ #include "config.h" #include "etag.h" #if INTERFACE /* ** Things to monitor */ #define ETAG_CONST 0x00 /* Output is independent of database or parameters */ #define ETAG_CONFIG 0x01 /* Output depends on the configuration */ #define ETAG_DATA 0x02 /* Output depends on 'event' table */ #define ETAG_COOKIE 0x04 /* Output depends on a display cookie value */ #define ETAG_HASH 0x08 /* Output depends on a hash */ #define ETAG_DYNAMIC 0x10 /* Output is always different */ #endif /* Set of all etag requirements */ static int mEtag = 0; /* Mask of requirements */ static const char *zEHash = 0; /* Hash value if ETAG_HASH is set */ /* Check an ETag to see if all conditions are valid. If all conditions are ** valid, then return true. If any condition is false, return false. */ static int etag_valid(const char *zTag){ int iKey; char *zCk; int rc; int nTag; if( zTag==0 || zTag[0]<=0 ) return 0; nTag = (int)strlen(zTag); if( zTag[0]=='"' && zTag[nTag-1]=='"' ){ zTag++; nTag -= 2; } iKey = zTag[0] - '0'; zCk = etag_generate(iKey); rc = nTag==(int)strlen(zCk) && strncmp(zCk, zTag, nTag)==0; fossil_free(zCk); if( rc ) mEtag = iKey; return rc; } /* ** Check to see if there is an If-None-Match: header that ** matches the current etag settings. If there is, then ** generate a 304 Not Modified reply. ** ** This routine exits and does not return if the 304 Not Modified ** reply is generated. ** ** If the etag does not match, the routine returns normally. */ static void etag_check(void){ const char *zETag = P("HTTP_IF_NONE_MATCH"); if( zETag==0 ) return; if( !etag_valid(zETag) ) return; /* If we get this far, it means that the content has ** not changed and we can do a 304 reply */ cgi_reset_content(); cgi_set_status(304, "Not Modified"); cgi_reply(); fossil_exit(0); } /* Add one or more new etag requirements. ** ** Page generator logic invokes one or both of these methods to signal ** under what conditions page generation can be skipped ** ** After each call to these routines, the HTTP_IF_NONE_MATCH cookie ** is checked, and if it contains a compatible ETag, then a ** 304 Not Modified return is generated and execution aborts. This ** routine does not return if the 304 is generated. */ void etag_require(int code){ mEtag |= code; etag_check(); } void etag_require_hash(const char *zHash){ if( zHash ){ zEHash = zHash; mEtag = ETAG_HASH; etag_check(); } } /* Return an appropriate max-age. */ int etag_maxage(void){ if( mEtag ) return 1; return 3600*24; } /* Generate an appropriate ETags value that captures all requirements. ** Space is obtained from fossil_malloc(). ** ** The argument is the mask of attributes to include in the ETag. ** If the argument is -1 then whatever mask is found from prior ** calls to etag_require() and etag_require_hash() is used. ** ** Format: ** ** <mask><exec-mtime>/<data-or-config-key>/<cookie>/<hash> ** ** The <mask> is a single-character decimal number that is the mask of ** all required attributes: ** ** ETAG_CONFIG: 1 ** ETAG_DATA: 2 ** ETAG_COOKIE: 4 ** ETAG_HASH: 8 ** ** If ETAG_HASH is present, the others are omitted, so the number is ** never greater than 8. ** ** The <exec-mtime> is the mtime of the Fossil executable. Since this ** is part of the ETag, it means that recompiling or just "touch"-ing the ** fossil binary is sufficient to invalidate all prior caches. ** ** The other elements are only present if the appropriate mask bits ** appear in the first character. */ char *etag_generate(int m){ Blob x = BLOB_INITIALIZER; int mtime; if( m<0 ) m = mEtag; if( m & ETAG_DYNAMIC ) return 0; mtime = file_mtime(g.nameOfExe, ExtFILE); blob_appendf(&x,"%d%x", m, mtime); if( m & ETAG_HASH ){ blob_appendf(&x, "/%s", zEHash); }else if( m & ETAG_DATA ){ int iKey = db_int(0, "SELECT max(rcvid) FROM rcvfrom"); blob_appendf(&x, "/%x", iKey); }else if( m & ETAG_CONFIG ){ int iKey = db_int(0, "SELECT value FROM config WHERE name='cfgcnt'"); blob_appendf(&x, "/%x", iKey); } if( m & ETAG_COOKIE ){ blob_appendf(&x, "/%s", P(DISPLAY_SETTINGS_COOKIE)); } return blob_str(&x); } /* ** COMMAND: test-etag ** ** Usage: fossil test-etag -key KEY-NUMBER -hash HASH ** ** Generate an etag given a KEY-NUMBER and/or a HASH. ** ** KEY-NUMBER is some combination of: ** ** 1 ETAG_CONFIG The config table version number ** 2 ETAG_DATA The event table version number ** 4 ETAG_COOKIE The display cookie */ void test_etag_cmd(void){ char *zTag; const char *zHash; const char *zKey; db_find_and_open_repository(0, 0); zKey = find_option("key",0,1); zHash = find_option("hash",0,1); if( zKey ) etag_require(atoi(zKey)); if( zHash ) etag_require_hash(zHash); zTag = etag_generate(mEtag); fossil_print("%s\n", zTag); fossil_free(zTag); } |
Changes to src/main.c.
︙ | ︙ | |||
1627 1628 1629 1630 1631 1632 1633 | strncmp(g.zContentType, "application/x-fossil", 20)==0 ){ /* Special case: If the content mimetype shows that it is "fossil sync" ** payload, then pretend that the PATH_INFO is /xfer so that we always ** invoke the sync page. */ zPathInfo = "/xfer"; } | < < < < < < < < < | 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 | strncmp(g.zContentType, "application/x-fossil", 20)==0 ){ /* Special case: If the content mimetype shows that it is "fossil sync" ** payload, then pretend that the PATH_INFO is /xfer so that we always ** invoke the sync page. */ zPathInfo = "/xfer"; } /* Use the first element of PATH_INFO as the page name ** and deliver the appropriate page back to the user. */ set_base_url(0); if( zPathInfo==0 || zPathInfo[0]==0 || (zPathInfo[0]=='/' && zPathInfo[1]==0) ){ /* Second special case: If the PATH_INFO is blank, issue a redirect to |
︙ | ︙ |
Changes to src/mkindex.c.
︙ | ︙ | |||
78 79 80 81 82 83 84 | #include <assert.h> #include <string.h> /*************************************************************************** ** These macros must match similar macros in dispatch.c. ** ** Allowed values for CmdOrPage.eCmdFlags. */ | | | | | | | | | | | | | > | | | 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | #include <assert.h> #include <string.h> /*************************************************************************** ** These macros must match similar macros in dispatch.c. ** ** Allowed values for CmdOrPage.eCmdFlags. */ #define CMDFLAG_1ST_TIER 0x00001 /* Most important commands */ #define CMDFLAG_2ND_TIER 0x00002 /* Obscure and seldom used commands */ #define CMDFLAG_TEST 0x00004 /* Commands for testing only */ #define CMDFLAG_WEBPAGE 0x00008 /* Web pages */ #define CMDFLAG_COMMAND 0x00010 /* A command */ #define CMDFLAG_SETTING 0x00020 /* A setting */ #define CMDFLAG_VERSIONABLE 0x00040 /* A versionable setting */ #define CMDFLAG_BLOCKTEXT 0x00080 /* Multi-line text setting */ #define CMDFLAG_BOOLEAN 0x00100 /* A boolean setting */ #define CMDFLAG_CONST 0x00000 /* ETAG_CONST */ #define CMDFLAG_CONFIG 0x01000 /* ETAG_CONFIG */ #define CMDFLAG_DATA 0x02000 /* ETAG_DATA */ #define CMDFLAG_COOKIE 0x04000 /* ETAG_COOKIE */ #define CMDFLAG_DYNAMIC 0x10000 /* ETAG_DYNAMIC - on by default */ #define CMDFLAG_ETAG 0x1f000 /* Mask of all ETAG entries */ #define CMDFLAG_TO_ETAG(X) ((X)>>12) /**************************************************************************/ /* ** Each entry looks like this: */ typedef struct Entry { |
︙ | ︙ | |||
249 250 251 252 253 254 255 256 257 258 259 260 261 262 | aEntry[nUsed].eType |= CMDFLAG_CONST; }else if( j==6 && strncmp(&zLine[i], "config", j)==0 ){ aEntry[nUsed].eType &= ~CMDFLAG_ETAG; aEntry[nUsed].eType |= CMDFLAG_CONFIG; }else if( j==4 && strncmp(&zLine[i], "data", j)==0 ){ aEntry[nUsed].eType &= ~CMDFLAG_ETAG; aEntry[nUsed].eType |= CMDFLAG_DATA; }else if( j==7 && strncmp(&zLine[i], "boolean", j)==0 ){ aEntry[nUsed].eType &= ~(CMDFLAG_BLOCKTEXT); aEntry[nUsed].iWidth = 0; aEntry[nUsed].eType |= CMDFLAG_BOOLEAN; }else if( j==10 && strncmp(&zLine[i], "block-text", j)==0 ){ aEntry[nUsed].eType &= ~(CMDFLAG_BOOLEAN); aEntry[nUsed].eType |= CMDFLAG_BLOCKTEXT; | > > > | 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 | aEntry[nUsed].eType |= CMDFLAG_CONST; }else if( j==6 && strncmp(&zLine[i], "config", j)==0 ){ aEntry[nUsed].eType &= ~CMDFLAG_ETAG; aEntry[nUsed].eType |= CMDFLAG_CONFIG; }else if( j==4 && strncmp(&zLine[i], "data", j)==0 ){ aEntry[nUsed].eType &= ~CMDFLAG_ETAG; aEntry[nUsed].eType |= CMDFLAG_DATA; }else if( j==4 && strncmp(&zLine[i], "cookie", j)==0 ){ aEntry[nUsed].eType &= ~CMDFLAG_ETAG; aEntry[nUsed].eType |= CMDFLAG_COOKIE; }else if( j==7 && strncmp(&zLine[i], "boolean", j)==0 ){ aEntry[nUsed].eType &= ~(CMDFLAG_BLOCKTEXT); aEntry[nUsed].iWidth = 0; aEntry[nUsed].eType |= CMDFLAG_BOOLEAN; }else if( j==10 && strncmp(&zLine[i], "block-text", j)==0 ){ aEntry[nUsed].eType &= ~(CMDFLAG_BOOLEAN); aEntry[nUsed].eType |= CMDFLAG_BLOCKTEXT; |
︙ | ︙ |
Changes to src/unversioned.c.
︙ | ︙ | |||
151 152 153 154 155 156 157 | ** 0: zName does not exist in the unversioned table. ** 1: zName exists and should be replaced by the mtime/zHash remote. ** 2: zName exists and is the same as zHash but has a older mtime ** 3: zName exists and is identical to mtime/zHash in all respects. ** 4: zName exists and is the same as zHash but has a newer mtime. ** 5: zName exists and should override the mtime/zHash remote. */ | | > > > > | 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 | ** 0: zName does not exist in the unversioned table. ** 1: zName exists and should be replaced by the mtime/zHash remote. ** 2: zName exists and is the same as zHash but has a older mtime ** 3: zName exists and is identical to mtime/zHash in all respects. ** 4: zName exists and is the same as zHash but has a newer mtime. ** 5: zName exists and should override the mtime/zHash remote. */ int unversioned_status( const char *zName, sqlite3_int64 mtime, const char *zHash ){ int iStatus = 0; Stmt q; db_prepare(&q, "SELECT mtime, hash FROM unversioned WHERE name=%Q", zName); if( db_step(&q)==SQLITE_ROW ){ const char *zLocalHash = db_column_text(&q, 1); int hashCmp; sqlite3_int64 iLocalMtime = db_column_int64(&q, 0); |
︙ | ︙ |