diff options
author | Jo-Philipp Wich <jo@mein.io> | 2018-04-18 15:49:26 +0200 |
---|---|---|
committer | Jo-Philipp Wich <jo@mein.io> | 2018-04-18 16:21:27 +0200 |
commit | b4040aacb04ce38b7eae315d37b2a61a38aea4b1 (patch) | |
tree | e8b38cd68d04f5a2bcac5d55294cd591558e349a /libs/luci-lib-httpprotoutils/luasrc | |
parent | eb4571c6dc7e9f99133de1d7df23024ba6d31d9e (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')
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 000000000..86b4db29c --- /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 000000000..7ce0b5ebe --- /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 000000000..72f1bdb57 --- /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 000000000..6028fb483 --- /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 000000000..0bcff8a36 --- /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 000000000..7751e2baf --- /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 +]] + |