add luci-lib-httpprotoutils

This commit is contained in:
LEAN-ESX 2019-09-22 08:33:04 -07:00
parent 3a2a6186ed
commit 411f655970
7 changed files with 454 additions and 0 deletions

View File

@ -0,0 +1,14 @@
#
# Copyright (C) 2018 The LuCI Team <luci@lists.subsignal.org>
#
# This is free software, licensed under the Apache License, Version 2.0 .
#
include $(TOPDIR)/rules.mk
LUCI_TITLE:=HTTP protocol utility functions
LUCI_DEPENDS:=+luci-base
include ../../luci.mk
# call BuildPackage - OpenWrt buildroot signature

View File

@ -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

View File

@ -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
]]

View File

@ -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

View File

@ -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
]]

View File

@ -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

View File

@ -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
]]