Roxen.git
/
server
/
modules
/
tags
/
static_resource.pike
version
»
Context lines:
10
20
40
80
file
none
3
Roxen.git/server/modules/tags/static_resource.pike:1:
+
inherit "module";
+
#include <module.h>
-
+
constant module_unique = 1;
+
constant module_name = "Tags: Static Resource Server";
+
string module_doc =
+
#"
+
<p>This module can help you improve browser caching of semi-static
+
resources (Javascript files, CSS, images etc. that may change every
+
once in a while) by:
+
<ol>
+
<li>Setting a far-future Expires HTTP header, as described by <a href=\"http://developer.yahoo.com/performance/rules.html#expires\">Yahoo!</a></li>
+
<li>Injecting a variable part in the resource URL that changes whenever the linked resource file changes</li>
+
</ol>
+
</p>
+
+
<p>These two features combined will make sure that the resource files are browser-cached for as long as possible while avoiding overcaching.</p>
+
";
+
+
constant module_type = MODULE_FILTER | MODULE_TAG;
+
+
constant expire_time = 86400*365; // One year.
+
+
constant default_process_tags = ([ "link" : "href",
+
"script" : "src" ]);
+
+
void create(Configuration conf)
+
{
+
defvar("process_tags",
+
Variable.Mapping(default_process_tags, 0,
+
"Tags to process",
+
"The tags to process and the corresponding "
+
"attribute that refers an external resource."));
+
}
+
+
class TagServeStaticResources
+
{
+
inherit RXML.Tag;
+
constant name = "serve-static-resources";
+
+
string mangle_resource_urls(string s, RequestID id)
+
{
+
mapping process_tags = query("process_tags");
+
Parser.HTML parser = Parser.HTML();
+
+
function process_tag = lambda(Parser.HTML p, mapping args)
+
{
+
string tag_name = p->tag_name();
+
string attr_name = process_tags[tag_name];
+
string link = args[attr_name];
+
if(link && link[0] == '/') {
+
array(int)|Stdio.Stat st =
+
id->conf->try_stat_file(link, id);
+
+
if(st) {
+
if(arrayp(st))
+
st = Stdio.Stat(st);
+
+
string varystr = sprintf("mtime=%d", st->mtime);
+
+
args[attr_name] =
+
Roxen.add_pre_state(link, (< "cache-forever", varystr >));
+
return ({ Roxen.make_tag(tag_name, args, 1, 1) });
+
}
+
}
+
return 0;
+
};
+
+
foreach(process_tags; string tag_name; string attr_name)
+
parser->add_tag(tag_name, process_tag);
+
+
string res = parser->finish(s)->read();
+
parser = 0;
+
process_tag = 0;
+
return res;
+
};
+
+
class Frame
+
{
+
inherit RXML.Frame;
+
array do_return(RequestID id)
+
{
+
result = mangle_resource_urls(content || "", id);
+
}
+
}
+
}
+
+
mapping|void filter(mapping res, RequestID id)
+
{
+
if (!res) return;
+
+
if(id->prestate["cache-forever"]) {
+
if (res->extra_heads) {
+
m_delete(res->extra_heads, "cache-control");
+
m_delete(res->extra_heads, "Cache-Control");
+
m_delete(res->extra_heads, "expires");
+
m_delete(res->extra_heads, "Expires");
+
}
+
+
RAISE_CACHE(expire_time);
+
+
id->misc->vary = (<>);
+
return res;
+
}
+
}
+
+
TAGDOCUMENTATION;
+
#ifdef manual
+
constant tagdoc = ([
+
"serve-static-resources":#"<desc type='tag'><p><short>Specifies a block of tags referring static resources.</short> Wrap this tag around your block of static resource referring tags.</p>
+
<ex-box>
+
<serve-static-resources>
+
<link rel=\"stylesheet\" href=\"/index.css\"/>
+
<script type=\"text/javascript\" src=\"/index.js\"/>
+
</serve-static-resources>
+
</ex-box>
+
<p>Note: Only local absolute paths will be processed, i.e. they have to begin with a '/'.</p>
+
</desc>"
+
]);
+
#endif // manual