summaryrefslogtreecommitdiffhomepage
path: root/libs/luci-lib-httpprotoutils/luasrc/http
diff options
context:
space:
mode:
authorJo-Philipp Wich <jo@mein.io>2018-04-18 15:49:26 +0200
committerJo-Philipp Wich <jo@mein.io>2018-04-18 16:21:27 +0200
commitb4040aacb04ce38b7eae315d37b2a61a38aea4b1 (patch)
treee8b38cd68d04f5a2bcac5d55294cd591558e349a /libs/luci-lib-httpprotoutils/luasrc/http
parenteb4571c6dc7e9f99133de1d7df23024ba6d31d9e (diff)
libs: move http.protocol.{date,mime,conditionals} to luci-lib-httpprotoutils
Also adjust the dependencies of components depending on these classes and flatten the namespace from luci.http.protocol.* to luci.http.* Signed-off-by: Jo-Philipp Wich <jo@mein.io>
Diffstat (limited to 'libs/luci-lib-httpprotoutils/luasrc/http')
-rw-r--r--libs/luci-lib-httpprotoutils/luasrc/http/conditionals.lua110
-rw-r--r--libs/luci-lib-httpprotoutils/luasrc/http/conditionals.luadoc85
-rw-r--r--libs/luci-lib-httpprotoutils/luasrc/http/date.lua87
-rw-r--r--libs/luci-lib-httpprotoutils/luasrc/http/date.luadoc46
-rw-r--r--libs/luci-lib-httpprotoutils/luasrc/http/mime.lua78
-rw-r--r--libs/luci-lib-httpprotoutils/luasrc/http/mime.luadoc34
6 files changed, 440 insertions, 0 deletions
diff --git a/libs/luci-lib-httpprotoutils/luasrc/http/conditionals.lua b/libs/luci-lib-httpprotoutils/luasrc/http/conditionals.lua
new file mode 100644
index 0000000000..86b4db29cd
--- /dev/null
+++ b/libs/luci-lib-httpprotoutils/luasrc/http/conditionals.lua
@@ -0,0 +1,110 @@
+-- Copyright 2008 Freifunk Leipzig / Jo-Philipp Wich <jow@openwrt.org>
+-- Licensed to the public under the Apache License 2.0.
+
+-- This class provides basic ETag handling and implements most of the
+-- conditional HTTP/1.1 headers specified in RFC2616 Sct. 14.24 - 14.28 .
+module("luci.http.conditionals", package.seeall)
+
+local date = require("luci.http.date")
+
+
+function mk_etag( stat )
+ if stat ~= nil then
+ return string.format( '"%x-%x-%x"', stat.ino, stat.size, stat.mtime )
+ end
+end
+
+-- Test whether the given message object contains an "If-Match" header and
+-- compare it against the given stat object.
+function if_match( req, stat )
+ local h = req.headers
+ local etag = mk_etag( stat )
+
+ -- Check for matching resource
+ if type(h['If-Match']) == "string" then
+ for ent in h['If-Match']:gmatch("([^, ]+)") do
+ if ( ent == '*' or ent == etag ) and stat ~= nil then
+ return true
+ end
+ end
+
+ return false, 412
+ end
+
+ return true
+end
+
+-- Test whether the given message object contains an "If-Modified-Since" header
+-- and compare it against the given stat object.
+function if_modified_since( req, stat )
+ local h = req.headers
+
+ -- Compare mtimes
+ if type(h['If-Modified-Since']) == "string" then
+ local since = date.to_unix( h['If-Modified-Since'] )
+
+ if stat == nil or since < stat.mtime then
+ return true
+ end
+
+ return false, 304, {
+ ["ETag"] = mk_etag( stat );
+ ["Date"] = date.to_http( os.time() );
+ ["Last-Modified"] = date.to_http( stat.mtime )
+ }
+ end
+
+ return true
+end
+
+-- Test whether the given message object contains an "If-None-Match" header and
+-- compare it against the given stat object.
+function if_none_match( req, stat )
+ local h = req.headers
+ local etag = mk_etag( stat )
+ local method = req.env and req.env.REQUEST_METHOD or "GET"
+
+ -- Check for matching resource
+ if type(h['If-None-Match']) == "string" then
+ for ent in h['If-None-Match']:gmatch("([^, ]+)") do
+ if ( ent == '*' or ent == etag ) and stat ~= nil then
+ if method == "GET" or method == "HEAD" then
+ return false, 304, {
+ ["ETag"] = etag;
+ ["Date"] = date.to_http( os.time() );
+ ["Last-Modified"] = date.to_http( stat.mtime )
+ }
+ else
+ return false, 412
+ end
+ end
+ end
+ end
+
+ return true
+end
+
+-- The If-Range header is currently not implemented due to the lack of general
+-- byte range stuff in luci.http.protocol . This function will always return
+-- false, 412 to indicate a failed precondition.
+function if_range( req, stat )
+ -- Sorry, no subranges (yet)
+ return false, 412
+end
+
+-- Test whether the given message object contains an "If-Unmodified-Since"
+-- header and compare it against the given stat object.
+function if_unmodified_since( req, stat )
+ local h = req.headers
+
+ -- Compare mtimes
+ if type(h['If-Unmodified-Since']) == "string" then
+ local since = date.to_unix( h['If-Unmodified-Since'] )
+
+ if stat ~= nil and since <= stat.mtime then
+ return false, 412
+ end
+ end
+
+ return true
+end
diff --git a/libs/luci-lib-httpprotoutils/luasrc/http/conditionals.luadoc b/libs/luci-lib-httpprotoutils/luasrc/http/conditionals.luadoc
new file mode 100644
index 0000000000..7ce0b5ebe3
--- /dev/null
+++ b/libs/luci-lib-httpprotoutils/luasrc/http/conditionals.luadoc
@@ -0,0 +1,85 @@
+---[[
+LuCI http protocol implementation - HTTP/1.1 bits.
+
+This class provides basic ETag handling and implements most of the
+conditional HTTP/1.1 headers specified in RFC2616 Sct. 14.24 - 14.28 .
+]]
+module "luci.http.conditionals"
+
+---[[
+Implement 14.19 / ETag.
+
+@class function
+@name mk_etag
+@param stat A file.stat structure
+@return String containing the generated tag suitable for ETag headers
+]]
+
+---[[
+14.24 / If-Match
+
+Test whether the given message object contains an "If-Match" header and
+compare it against the given stat object.
+@class function
+@name if_match
+@param req HTTP request message object
+@param stat A file.stat object
+@return Boolean indicating whether the precondition is ok
+@return Alternative status code if the precondition failed
+]]
+
+---[[
+14.25 / If-Modified-Since
+
+Test whether the given message object contains an "If-Modified-Since" header
+and compare it against the given stat object.
+@class function
+@name if_modified_since
+@param req HTTP request message object
+@param stat A file.stat object
+@return Boolean indicating whether the precondition is ok
+@return Alternative status code if the precondition failed
+@return Table containing extra HTTP headers if the precondition failed
+]]
+
+---[[
+14.26 / If-None-Match
+
+Test whether the given message object contains an "If-None-Match" header and
+compare it against the given stat object.
+@class function
+@name if_none_match
+@param req HTTP request message object
+@param stat A file.stat object
+@return Boolean indicating whether the precondition is ok
+@return Alternative status code if the precondition failed
+@return Table containing extra HTTP headers if the precondition failed
+]]
+
+---[[
+14.27 / If-Range
+
+The If-Range header is currently not implemented due to the lack of general
+byte range stuff in luci.http.protocol . This function will always return
+false, 412 to indicate a failed precondition.
+@class function
+@name if_range
+@param req HTTP request message object
+@param stat A file.stat object
+@return Boolean indicating whether the precondition is ok
+@return Alternative status code if the precondition failed
+]]
+
+---[[
+14.28 / If-Unmodified-Since
+
+Test whether the given message object contains an "If-Unmodified-Since"
+header and compare it against the given stat object.
+@class function
+@name if_unmodified_since
+@param req HTTP request message object
+@param stat A file.stat object
+@return Boolean indicating whether the precondition is ok
+@return Alternative status code if the precondition failed
+]]
+
diff --git a/libs/luci-lib-httpprotoutils/luasrc/http/date.lua b/libs/luci-lib-httpprotoutils/luasrc/http/date.lua
new file mode 100644
index 0000000000..72f1bdb577
--- /dev/null
+++ b/libs/luci-lib-httpprotoutils/luasrc/http/date.lua
@@ -0,0 +1,87 @@
+-- Copyright 2008 Freifunk Leipzig / Jo-Philipp Wich <jow@openwrt.org>
+-- Licensed to the public under the Apache License 2.0.
+
+-- This class contains functions to parse, compare and format http dates.
+module("luci.http.date", package.seeall)
+
+require("luci.sys.zoneinfo")
+
+
+MONTHS = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug",
+ "Sep", "Oct", "Nov", "Dec"
+}
+
+function tz_offset(tz)
+
+ if type(tz) == "string" then
+
+ -- check for a numeric identifier
+ local s, v = tz:match("([%+%-])([0-9]+)")
+ if s == '+' then s = 1 else s = -1 end
+ if v then v = tonumber(v) end
+
+ if s and v then
+ return s * 60 * ( math.floor( v / 100 ) * 60 + ( v % 100 ) )
+
+ -- lookup symbolic tz
+ elseif luci.sys.zoneinfo.OFFSET[tz:lower()] then
+ return luci.sys.zoneinfo.OFFSET[tz:lower()]
+ end
+
+ end
+
+ -- bad luck
+ return 0
+end
+
+function to_unix(date)
+
+ local wd, day, mon, yr, hr, min, sec, tz = date:match(
+ "([A-Z][a-z][a-z]), ([0-9]+) " ..
+ "([A-Z][a-z][a-z]) ([0-9]+) " ..
+ "([0-9]+):([0-9]+):([0-9]+) " ..
+ "([A-Z0-9%+%-]+)"
+ )
+
+ if day and mon and yr and hr and min and sec then
+ -- find month
+ local month = 1
+ for i = 1, 12 do
+ if MONTHS[i] == mon then
+ month = i
+ break
+ end
+ end
+
+ -- convert to epoch time
+ return tz_offset(tz) + os.time( {
+ year = yr,
+ month = month,
+ day = day,
+ hour = hr,
+ min = min,
+ sec = sec
+ } )
+ end
+
+ return 0
+end
+
+function to_http(time)
+ return os.date( "%a, %d %b %Y %H:%M:%S GMT", time )
+end
+
+function compare(d1, d2)
+
+ if d1:match("[^0-9]") then d1 = to_unix(d1) end
+ if d2:match("[^0-9]") then d2 = to_unix(d2) end
+
+ if d1 == d2 then
+ return 0
+ elseif d1 < d2 then
+ return -1
+ else
+ return 1
+ end
+end
diff --git a/libs/luci-lib-httpprotoutils/luasrc/http/date.luadoc b/libs/luci-lib-httpprotoutils/luasrc/http/date.luadoc
new file mode 100644
index 0000000000..6028fb4837
--- /dev/null
+++ b/libs/luci-lib-httpprotoutils/luasrc/http/date.luadoc
@@ -0,0 +1,46 @@
+---[[
+LuCI http protocol implementation - date helper class.
+
+This class contains functions to parse, compare and format http dates.
+]]
+module "luci.http.date"
+
+---[[
+Return the time offset in seconds between the UTC and given time zone.
+
+@class function
+@name tz_offset
+@param tz Symbolic or numeric timezone specifier
+@return Time offset to UTC in seconds
+]]
+
+---[[
+Parse given HTTP date string and convert it to unix epoch time.
+
+@class function
+@name to_unix
+@param data String containing the date
+@return Unix epoch time
+]]
+
+---[[
+Convert the given unix epoch time to valid HTTP date string.
+
+@class function
+@name to_http
+@param time Unix epoch time
+@return String containing the formatted date
+]]
+
+---[[
+Compare two dates which can either be unix epoch times or HTTP date strings.
+
+@class function
+@name compare
+@param d1 The first date or epoch time to compare
+@param d2 The first date or epoch time to compare
+@return -1 - if d1 is lower then d2
+@return 0 - if both dates are equal
+@return 1 - if d1 is higher then d2
+]]
+
diff --git a/libs/luci-lib-httpprotoutils/luasrc/http/mime.lua b/libs/luci-lib-httpprotoutils/luasrc/http/mime.lua
new file mode 100644
index 0000000000..0bcff8a36b
--- /dev/null
+++ b/libs/luci-lib-httpprotoutils/luasrc/http/mime.lua
@@ -0,0 +1,78 @@
+-- Copyright 2008 Freifunk Leipzig / Jo-Philipp Wich <jow@openwrt.org>
+-- Licensed to the public under the Apache License 2.0.
+
+-- This class provides functions to guess mime types from file extensions and
+-- vice versa.
+module("luci.http.mime", package.seeall)
+
+require("luci.util")
+
+MIME_TYPES = {
+ ["txt"] = "text/plain";
+ ["js"] = "text/javascript";
+ ["css"] = "text/css";
+ ["htm"] = "text/html";
+ ["html"] = "text/html";
+ ["patch"] = "text/x-patch";
+ ["c"] = "text/x-csrc";
+ ["h"] = "text/x-chdr";
+ ["o"] = "text/x-object";
+ ["ko"] = "text/x-object";
+
+ ["bmp"] = "image/bmp";
+ ["gif"] = "image/gif";
+ ["png"] = "image/png";
+ ["jpg"] = "image/jpeg";
+ ["jpeg"] = "image/jpeg";
+ ["svg"] = "image/svg+xml";
+
+ ["zip"] = "application/zip";
+ ["pdf"] = "application/pdf";
+ ["xml"] = "application/xml";
+ ["xsl"] = "application/xml";
+ ["doc"] = "application/msword";
+ ["ppt"] = "application/vnd.ms-powerpoint";
+ ["xls"] = "application/vnd.ms-excel";
+ ["odt"] = "application/vnd.oasis.opendocument.text";
+ ["odp"] = "application/vnd.oasis.opendocument.presentation";
+ ["pl"] = "application/x-perl";
+ ["sh"] = "application/x-shellscript";
+ ["php"] = "application/x-php";
+ ["deb"] = "application/x-deb";
+ ["iso"] = "application/x-cd-image";
+ ["tgz"] = "application/x-compressed-tar";
+
+ ["mp3"] = "audio/mpeg";
+ ["ogg"] = "audio/x-vorbis+ogg";
+ ["wav"] = "audio/x-wav";
+
+ ["mpg"] = "video/mpeg";
+ ["mpeg"] = "video/mpeg";
+ ["avi"] = "video/x-msvideo";
+}
+
+-- "application/octet-stream" if the extension is unknown.
+function to_mime(filename)
+ if type(filename) == "string" then
+ local ext = filename:match("[^%.]+$")
+
+ if ext and MIME_TYPES[ext:lower()] then
+ return MIME_TYPES[ext:lower()]
+ end
+ end
+
+ return "application/octet-stream"
+end
+
+-- given mime-type is unknown.
+function to_ext(mimetype)
+ if type(mimetype) == "string" then
+ for ext, type in luci.util.kspairs( MIME_TYPES ) do
+ if type == mimetype then
+ return ext
+ end
+ end
+ end
+
+ return nil
+end
diff --git a/libs/luci-lib-httpprotoutils/luasrc/http/mime.luadoc b/libs/luci-lib-httpprotoutils/luasrc/http/mime.luadoc
new file mode 100644
index 0000000000..7751e2baf4
--- /dev/null
+++ b/libs/luci-lib-httpprotoutils/luasrc/http/mime.luadoc
@@ -0,0 +1,34 @@
+---[[
+LuCI http protocol implementation - mime helper class.
+
+This class provides functions to guess mime types from file extensions and
+vice versa.
+]]
+module "luci.http.mime"
+
+---[[
+MIME mapping table containg extension - mimetype relations.
+
+@class table
+]]
+
+---[[
+Extract extension from a filename and return corresponding mime-type or
+
+"application/octet-stream" if the extension is unknown.
+@class function
+@name to_mime
+@param filename The filename for which the mime type is guessed
+@return String containign the determined mime type
+]]
+
+---[[
+Return corresponding extension for a given mime type or nil if the
+
+given mime-type is unknown.
+@class function
+@name to_ext
+@param mimetype The mimetype to retrieve the extension from
+@return String with the extension or nil for unknown type
+]]
+