From 8a06826aba27a3be0f6a597e95d416bda695da7c Mon Sep 17 00:00:00 2001 From: Vincent Wang Date: Mon, 20 May 2019 08:46:38 +0800 Subject: [PATCH] make StaticFileWeblet to be concurrency safe A weblet instance can be run multiple times at the same time, so payload can not be stored in weblet instance for potential concurrency issue. to fix the issue, precreate a number of payload, as many as web handlers. --- src/kits/communityWebServer/Payload.sedona | 31 +++++ .../StaticFileWeblet.sedona | 107 +++++++++++------- 2 files changed, 98 insertions(+), 40 deletions(-) create mode 100644 src/kits/communityWebServer/Payload.sedona diff --git a/src/kits/communityWebServer/Payload.sedona b/src/kits/communityWebServer/Payload.sedona new file mode 100644 index 0000000..420e7af --- /dev/null +++ b/src/kits/communityWebServer/Payload.sedona @@ -0,0 +1,31 @@ +internal class Payload +{ + void lock() + { + locked = true + } + + bool isFree() + { + return !locked + } + + void unlock() + { + locked = false + } + + void reset() + { + filePath.set(0, 0) + } + + define int bufLen = 1024 + define int pathStrLen = 256 + + private bool locked = false + + internal inline byte[bufLen] readBuf + internal inline Str(pathStrLen) filePath // buffer for file's path + internal inline File file +} diff --git a/src/kits/communityWebServer/StaticFileWeblet.sedona b/src/kits/communityWebServer/StaticFileWeblet.sedona index dc90c08..d8a12ab 100644 --- a/src/kits/communityWebServer/StaticFileWeblet.sedona +++ b/src/kits/communityWebServer/StaticFileWeblet.sedona @@ -22,6 +22,11 @@ ** To save space on sedona, gzipped file is supported and I recommend to use it ** since that will be more efficient(less bytes to be loaded and transferred) ** +** Warning: since it is impossible to create local payload for a request, we +** have to precreate a list of payloads, in theory we need to create the same +** number of payloads as the number of web handlers. The space cost for a +** payload is about 1.5KB. +** class StaticFileWeblet extends Weblet { @config @asStr property Buf(12) urlPrefix = "statics" @@ -71,67 +76,71 @@ class StaticFileWeblet extends Weblet ** override void get(WebReq req, WebRes res) { - reset() + Payload p = borrowPayload() + if (p == null) + { + res.writeStatus(HttpCode.serviceUnavailable) + res.finishHeaders() + return + } + + p.reset() - prepareFile(req) + prepareFile(p, req) - if (isDir(filePath)) { - tryAppendIndex(filePath) + if (isDir(p.filePath)) { + tryAppendIndex(p.filePath) } - serveFile(res) + serveFile(p, res) + returnPayload(p) } - internal void reset() + internal void serveFile(Payload p, WebRes res) { - filePath.set(0, 0) - } - - internal void serveFile(WebRes res) - { - if (file.name==null) { + if (p.file.name==null) { res.writeStatus(HttpCode.notFound).finishHeaders() return } - if (!file.exists()) { + if (!p.file.exists()) { //try gzipped content - appendStr(file.name, ".gz", pathStrLen) + appendStr(p.file.name, ".gz", Payload.pathStrLen) - if (!file.exists()) { + if (!p.file.exists()) { res.writeStatus(HttpCode.notFound).finishHeaders() return } } - if (!file.open("r")) { + if (!p.file.open("r")) { res.writeStatus(HttpCode.internalError).finishHeaders() return } - WebService.log.trace("Serving File: " + file.name); + WebService.log.trace("Serving File: " + p.file.name); res.writeStatusOk() //write headers - if (endsWith(file.name, ".gz", 0)) + if (endsWith(p.file.name, ".gz", 0)) res.writeHeader("Content-Encoding", "gzip"); - res.writeContentType(getContentType(file.name)) + res.writeContentType(getContentType(p.file.name)) .writeHeader("Cache-Control", "max-age=604800") .writeHeader("Server", "Sedona Web Server") - .writeHeader("Content-Length", Sys.intStr(file.size())) + .writeHeader("Content-Length", Sys.intStr(p.file.size())) .finishHeaders() - file.seek(0) + p.file.seek(0) while(true) { - int readed = file.in.readBytes(readBuf, 0, bufLen) + int readed = p.file.in.readBytes(p.readBuf, 0, Payload.bufLen) if (readed <= 0) break - res.writeBytes(readBuf, 0, readed) + res.writeBytes(p.readBuf, 0, readed) } - file.close() + p.file.close() } bool isDir(Str fileName) { @@ -155,14 +164,14 @@ class StaticFileWeblet extends Weblet ** bool tryAppendIndex(Str fileName) { int len = fileName.length() - if (len <= 0 || len+1>=pathStrLen) + if (len <= 0 || len+1>=Payload.pathStrLen) return false //try to append index.html to path end if (!endsWith(fileName, "/", 0)) - appendStr(fileName, "/", pathStrLen) + appendStr(fileName, "/", Payload.pathStrLen) - return appendStr(fileName, "index.html", pathStrLen) + return appendStr(fileName, "index.html", Payload.pathStrLen) } ** @@ -227,26 +236,26 @@ class StaticFileWeblet extends Weblet return "text/plain" } - internal void prepareFile(WebReq req) + internal void prepareFile(Payload p, WebReq req) { if (req.path.size < 1) return else { //TODO: process path to avoid security issues - filePath.copyFromStr(".", pathStrLen); - int index = filePath.length() - byte[] fileBuf = filePath.toBytes() - for(int i=0; i