mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2024-11-30 05:19:57 +00:00
Network performance optimizations for node.js (#5436)
This commit is contained in:
parent
e699cf1fe8
commit
f4d7b2c7f7
@ -30,6 +30,7 @@ All parameters are optional with safe defaults, and can be specified in any orde
|
|||||||
* ''tls-key'' - pathname of TLS key file (relative to wiki folder)
|
* ''tls-key'' - pathname of TLS key file (relative to wiki folder)
|
||||||
* ''debug-level'' - optional debug level; set to "debug" to view request details (defaults to "none")
|
* ''debug-level'' - optional debug level; set to "debug" to view request details (defaults to "none")
|
||||||
* ''gzip'' - set to "yes" to enable gzip compression for some http endpoints (defaults to "no")
|
* ''gzip'' - set to "yes" to enable gzip compression for some http endpoints (defaults to "no")
|
||||||
|
* ''use-browser-cache'' - set to "yes" to allow the browser to cache responses to save bandwith (defaults to "no")
|
||||||
|
|
||||||
For information on opening up your instance to the entire local network, and possible security concerns, see the WebServer tiddler at TiddlyWiki.com.
|
For information on opening up your instance to the entire local network, and possible security concerns, see the WebServer tiddler at TiddlyWiki.com.
|
||||||
|
|
||||||
|
@ -17,9 +17,8 @@ exports.method = "GET";
|
|||||||
exports.path = /^\/favicon.ico$/;
|
exports.path = /^\/favicon.ico$/;
|
||||||
|
|
||||||
exports.handler = function(request,response,state) {
|
exports.handler = function(request,response,state) {
|
||||||
response.writeHead(200, {"Content-Type": "image/x-icon"});
|
|
||||||
var buffer = state.wiki.getTiddlerText("$:/favicon.ico","");
|
var buffer = state.wiki.getTiddlerText("$:/favicon.ico","");
|
||||||
response.end(buffer,"base64");
|
state.sendResponse(200,{"Content-Type": "image/x-icon"},buffer,"base64");
|
||||||
};
|
};
|
||||||
|
|
||||||
}());
|
}());
|
||||||
|
@ -34,10 +34,7 @@ exports.handler = function(request,response,state) {
|
|||||||
content = content;
|
content = content;
|
||||||
type = ($tw.config.fileExtensionInfo[extension] ? $tw.config.fileExtensionInfo[extension].type : "application/octet-stream");
|
type = ($tw.config.fileExtensionInfo[extension] ? $tw.config.fileExtensionInfo[extension].type : "application/octet-stream");
|
||||||
}
|
}
|
||||||
response.writeHead(status,{
|
state.sendResponse(status,{"Content-Type": type},content);
|
||||||
"Content-Type": type
|
|
||||||
});
|
|
||||||
response.end(content);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -12,38 +12,16 @@ GET /
|
|||||||
/*global $tw: false */
|
/*global $tw: false */
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var zlib = require("zlib");
|
|
||||||
|
|
||||||
exports.method = "GET";
|
exports.method = "GET";
|
||||||
|
|
||||||
exports.path = /^\/$/;
|
exports.path = /^\/$/;
|
||||||
|
|
||||||
exports.handler = function(request,response,state) {
|
exports.handler = function(request,response,state) {
|
||||||
var acceptEncoding = request.headers["accept-encoding"];
|
|
||||||
if(!acceptEncoding) {
|
|
||||||
acceptEncoding = "";
|
|
||||||
}
|
|
||||||
var text = state.wiki.renderTiddler(state.server.get("root-render-type"),state.server.get("root-tiddler")),
|
var text = state.wiki.renderTiddler(state.server.get("root-render-type"),state.server.get("root-tiddler")),
|
||||||
responseHeaders = {
|
responseHeaders = {
|
||||||
"Content-Type": state.server.get("root-serve-type")
|
"Content-Type": state.server.get("root-serve-type")
|
||||||
};
|
};
|
||||||
/*
|
state.sendResponse(200,responseHeaders,text);
|
||||||
If the gzip=yes flag for `listen` is set, check if the user agent permits
|
|
||||||
compression. If so, compress our response. Note that we use the synchronous
|
|
||||||
functions from zlib to stay in the imperative style. The current `Server`
|
|
||||||
doesn't depend on this, and we may just as well use the async versions.
|
|
||||||
*/
|
|
||||||
if(state.server.enableGzip) {
|
|
||||||
if (/\bdeflate\b/.test(acceptEncoding)) {
|
|
||||||
responseHeaders["Content-Encoding"] = "deflate";
|
|
||||||
text = zlib.deflateSync(text);
|
|
||||||
} else if (/\bgzip\b/.test(acceptEncoding)) {
|
|
||||||
responseHeaders["Content-Encoding"] = "gzip";
|
|
||||||
text = zlib.gzipSync(text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
response.writeHead(200,responseHeaders);
|
|
||||||
response.end(text);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}());
|
}());
|
||||||
|
@ -17,7 +17,6 @@ exports.method = "GET";
|
|||||||
exports.path = /^\/status$/;
|
exports.path = /^\/status$/;
|
||||||
|
|
||||||
exports.handler = function(request,response,state) {
|
exports.handler = function(request,response,state) {
|
||||||
response.writeHead(200, {"Content-Type": "application/json"});
|
|
||||||
var text = JSON.stringify({
|
var text = JSON.stringify({
|
||||||
username: state.authenticatedUsername || state.server.get("anon-username") || "",
|
username: state.authenticatedUsername || state.server.get("anon-username") || "",
|
||||||
anonymous: !state.authenticatedUsername,
|
anonymous: !state.authenticatedUsername,
|
||||||
@ -28,7 +27,7 @@ exports.handler = function(request,response,state) {
|
|||||||
},
|
},
|
||||||
tiddlywiki_version: $tw.version
|
tiddlywiki_version: $tw.version
|
||||||
});
|
});
|
||||||
response.end(text,"utf8");
|
state.sendResponse(200,{"Content-Type": "application/json"},text,"utf8");
|
||||||
};
|
};
|
||||||
|
|
||||||
}());
|
}());
|
||||||
|
@ -32,9 +32,9 @@ exports.handler = function(request,response,state) {
|
|||||||
renderTemplate = renderTemplate || state.server.get("tiddler-render-template");
|
renderTemplate = renderTemplate || state.server.get("tiddler-render-template");
|
||||||
}
|
}
|
||||||
var text = state.wiki.renderTiddler(renderType,renderTemplate,{parseAsInline: true, variables: {currentTiddler: title}});
|
var text = state.wiki.renderTiddler(renderType,renderTemplate,{parseAsInline: true, variables: {currentTiddler: title}});
|
||||||
|
|
||||||
// Naughty not to set a content-type, but it's the easiest way to ensure the browser will see HTML pages as HTML, and accept plain text tiddlers as CSS or JS
|
// Naughty not to set a content-type, but it's the easiest way to ensure the browser will see HTML pages as HTML, and accept plain text tiddlers as CSS or JS
|
||||||
response.writeHead(200);
|
state.sendResponse(200,{},text,"utf8");
|
||||||
response.end(text,"utf8");
|
|
||||||
} else {
|
} else {
|
||||||
response.writeHead(404);
|
response.writeHead(404);
|
||||||
response.end();
|
response.end();
|
||||||
|
@ -36,8 +36,7 @@ exports.handler = function(request,response,state) {
|
|||||||
tiddlerFields.revision = state.wiki.getChangeCount(title);
|
tiddlerFields.revision = state.wiki.getChangeCount(title);
|
||||||
tiddlerFields.bag = "default";
|
tiddlerFields.bag = "default";
|
||||||
tiddlerFields.type = tiddlerFields.type || "text/vnd.tiddlywiki";
|
tiddlerFields.type = tiddlerFields.type || "text/vnd.tiddlywiki";
|
||||||
response.writeHead(200, {"Content-Type": "application/json"});
|
state.sendResponse(200,{"Content-Type": "application/json"},JSON.stringify(tiddlerFields),"utf8");
|
||||||
response.end(JSON.stringify(tiddlerFields),"utf8");
|
|
||||||
} else {
|
} else {
|
||||||
response.writeHead(404);
|
response.writeHead(404);
|
||||||
response.end();
|
response.end();
|
||||||
|
@ -33,7 +33,6 @@ exports.handler = function(request,response,state) {
|
|||||||
}
|
}
|
||||||
var excludeFields = (state.queryParameters.exclude || "text").split(","),
|
var excludeFields = (state.queryParameters.exclude || "text").split(","),
|
||||||
titles = state.wiki.filterTiddlers(filter);
|
titles = state.wiki.filterTiddlers(filter);
|
||||||
response.writeHead(200, {"Content-Type": "application/json"});
|
|
||||||
var tiddlers = [];
|
var tiddlers = [];
|
||||||
$tw.utils.each(titles,function(title) {
|
$tw.utils.each(titles,function(title) {
|
||||||
var tiddler = state.wiki.getTiddler(title);
|
var tiddler = state.wiki.getTiddler(title);
|
||||||
@ -45,7 +44,7 @@ exports.handler = function(request,response,state) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
var text = JSON.stringify(tiddlers);
|
var text = JSON.stringify(tiddlers);
|
||||||
response.end(text,"utf8");
|
state.sendResponse(200,{"Content-Type": "application/json"},text,"utf8");
|
||||||
};
|
};
|
||||||
|
|
||||||
}());
|
}());
|
||||||
|
@ -17,7 +17,9 @@ if($tw.node) {
|
|||||||
fs = require("fs"),
|
fs = require("fs"),
|
||||||
url = require("url"),
|
url = require("url"),
|
||||||
path = require("path"),
|
path = require("path"),
|
||||||
querystring = require("querystring");
|
querystring = require("querystring"),
|
||||||
|
crypto = require("crypto"),
|
||||||
|
zlib = require("zlib");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -47,6 +49,8 @@ function Server(options) {
|
|||||||
this.csrfDisable = this.get("csrf-disable") === "yes";
|
this.csrfDisable = this.get("csrf-disable") === "yes";
|
||||||
// Initialize Gzip compression
|
// Initialize Gzip compression
|
||||||
this.enableGzip = this.get("gzip") === "yes";
|
this.enableGzip = this.get("gzip") === "yes";
|
||||||
|
// Initialize browser-caching
|
||||||
|
this.enableBrowserCache = this.get("use-browser-cache") === "yes";
|
||||||
// Initialise authorization
|
// Initialise authorization
|
||||||
var authorizedUserName = (this.get("username") && this.get("password")) ? this.get("username") : "(anon)";
|
var authorizedUserName = (this.get("username") && this.get("password")) ? this.get("username") : "(anon)";
|
||||||
this.authorizationPrincipals = {
|
this.authorizationPrincipals = {
|
||||||
@ -78,6 +82,71 @@ function Server(options) {
|
|||||||
this.transport = require(this.protocol);
|
this.transport = require(this.protocol);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Send a response to the client. This method checks if the response must be sent
|
||||||
|
or if the client alrady has the data cached. If that's the case only a 304
|
||||||
|
response will be transmitted and the browser will use the cached data.
|
||||||
|
Only requests with status code 200 are considdered for caching.
|
||||||
|
request: request instance passed to the handler
|
||||||
|
response: response instance passed to the handler
|
||||||
|
statusCode: stauts code to send to the browser
|
||||||
|
headers: response headers (they will be augmented with an `Etag` header)
|
||||||
|
data: the data to send (passed to the end method of the response instance)
|
||||||
|
encoding: the encoding of the data to send (passed to the end method of the response instance)
|
||||||
|
*/
|
||||||
|
function sendResponse(request,response,statusCode,headers,data,encoding) {
|
||||||
|
if(this.enableBrowserCache && (statusCode == 200)) {
|
||||||
|
var hash = crypto.createHash('md5');
|
||||||
|
// Put everything into the hash that could change and invalidate the data that
|
||||||
|
// the browser already stored. The headers the data and the encoding.
|
||||||
|
hash.update(data);
|
||||||
|
hash.update(JSON.stringify(headers));
|
||||||
|
if(encoding) {
|
||||||
|
hash.update(encoding);
|
||||||
|
}
|
||||||
|
var contentDigest = hash.digest("hex");
|
||||||
|
// RFC 7232 section 2.3 mandates for the etag to be enclosed in quotes
|
||||||
|
headers["Etag"] = '"' + contentDigest + '"';
|
||||||
|
headers["Cache-Control"] = "max-age=0, must-revalidate";
|
||||||
|
// Check if any of the hashes contained within the if-none-match header
|
||||||
|
// matches the current hash.
|
||||||
|
// If one matches, do not send the data but tell the browser to use the
|
||||||
|
// cached data.
|
||||||
|
// We do not implement "*" as it makes no sense here.
|
||||||
|
var ifNoneMatch = request.headers["if-none-match"];
|
||||||
|
if(ifNoneMatch) {
|
||||||
|
var matchParts = ifNoneMatch.split(",").map(function(etag) {
|
||||||
|
return etag.replace(/^[ "]+|[ "]+$/g, "");
|
||||||
|
});
|
||||||
|
if(matchParts.indexOf(contentDigest) != -1) {
|
||||||
|
response.writeHead(304,headers);
|
||||||
|
response.end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
If the gzip=yes is set, check if the user agent permits compression. If so,
|
||||||
|
compress our response if the raw data is bigger than 2k. Compressing less
|
||||||
|
data is inefficient. Note that we use the synchronous functions from zlib
|
||||||
|
to stay in the imperative style. The current `Server` doesn't depend on
|
||||||
|
this, and we may just as well use the async versions.
|
||||||
|
*/
|
||||||
|
if(this.enableGzip && (data.length > 2048)) {
|
||||||
|
var acceptEncoding = request.headers["accept-encoding"] || "";
|
||||||
|
if(/\bdeflate\b/.test(acceptEncoding)) {
|
||||||
|
headers["Content-Encoding"] = "deflate";
|
||||||
|
data = zlib.deflateSync(data);
|
||||||
|
} else if(/\bgzip\b/.test(acceptEncoding)) {
|
||||||
|
headers["Content-Encoding"] = "gzip";
|
||||||
|
data = zlib.gzipSync(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
response.writeHead(statusCode,headers);
|
||||||
|
response.end(data,encoding);
|
||||||
|
}
|
||||||
|
|
||||||
Server.prototype.defaultVariables = {
|
Server.prototype.defaultVariables = {
|
||||||
port: "8080",
|
port: "8080",
|
||||||
host: "127.0.0.1",
|
host: "127.0.0.1",
|
||||||
@ -89,7 +158,8 @@ Server.prototype.defaultVariables = {
|
|||||||
"system-tiddler-render-type": "text/plain",
|
"system-tiddler-render-type": "text/plain",
|
||||||
"system-tiddler-render-template": "$:/core/templates/wikified-tiddler",
|
"system-tiddler-render-template": "$:/core/templates/wikified-tiddler",
|
||||||
"debug-level": "none",
|
"debug-level": "none",
|
||||||
"gzip": "no"
|
"gzip": "no",
|
||||||
|
"use-browser-cache": "no"
|
||||||
};
|
};
|
||||||
|
|
||||||
Server.prototype.get = function(name) {
|
Server.prototype.get = function(name) {
|
||||||
@ -167,6 +237,7 @@ Server.prototype.requestHandler = function(request,response,options) {
|
|||||||
state.urlInfo = url.parse(request.url);
|
state.urlInfo = url.parse(request.url);
|
||||||
state.queryParameters = querystring.parse(state.urlInfo.query);
|
state.queryParameters = querystring.parse(state.urlInfo.query);
|
||||||
state.pathPrefix = options.pathPrefix || this.get("path-prefix") || "";
|
state.pathPrefix = options.pathPrefix || this.get("path-prefix") || "";
|
||||||
|
state.sendResponse = sendResponse.bind(self,request,response);
|
||||||
// Get the principals authorized to access this resource
|
// Get the principals authorized to access this resource
|
||||||
var authorizationType = this.methodMappings[request.method] || "readers";
|
var authorizationType = this.methodMappings[request.method] || "readers";
|
||||||
// Check for the CSRF header if this is a write
|
// Check for the CSRF header if this is a write
|
||||||
|
@ -31,5 +31,6 @@ Mögliche Parameter:
|
|||||||
* ''tls-key'' - Pfad zur "TLS key" Datei (relativ zum Wiki Verzeichnis)
|
* ''tls-key'' - Pfad zur "TLS key" Datei (relativ zum Wiki Verzeichnis)
|
||||||
* ''debug-level'' - "debug" bewikt eine detailierte Anzeige der HTTP Anfrage-Parameter. (Standard: "none")
|
* ''debug-level'' - "debug" bewikt eine detailierte Anzeige der HTTP Anfrage-Parameter. (Standard: "none")
|
||||||
* ''gzip'' - Wenn auf "yes" gesetzt, dann wird gzip Kompression aktiviert. (Standard: "no")
|
* ''gzip'' - Wenn auf "yes" gesetzt, dann wird gzip Kompression aktiviert. (Standard: "no")
|
||||||
|
* ''use-browser-cache'' - Ist dieser Parameter auf "yes" gesetzt kann der Browser Inhalte zwischenspeichern um Übertragungsbandbreite zu sparen. (Standard: "no")
|
||||||
|
|
||||||
Für weitere Sicherheitshinweise und Informationen für die Verwendung in lokalen Netzwerken siehe: WebServer auf TiddlyWiki.com
|
Für weitere Sicherheitshinweise und Informationen für die Verwendung in lokalen Netzwerken siehe: WebServer auf TiddlyWiki.com
|
||||||
|
Loading…
Reference in New Issue
Block a user