Merge Mainline

Signed-off-by: Tianling Shen <cnsztl@immortalwrt.org>
This commit is contained in:
Tianling Shen 2021-04-10 14:38:11 +08:00
commit 03c8db5bf6
No known key found for this signature in database
GPG Key ID: 6850B6345C862176
639 changed files with 20 additions and 222311 deletions

View File

@ -16,9 +16,9 @@ PKG_RELEASE:=1
PKG_SOURCE_URL:=https://github.com/aircrack-ng/rtl8812au.git
PKG_SOURCE_PROTO:=git
PKG_SOURCE_DATE:=2021-02-03
PKG_SOURCE_VERSION:=b65dcf4105641716d16f3a6c96507fdd9c1862b4
PKG_MIRROR_HASH:=5416d4a6e671c6b07f44f35fae54662baef71f448c4be4ade58bf7fc5ca4f192
PKG_SOURCE_DATE:=2021-03-27
PKG_SOURCE_VERSION:=c0ce81745eb3471a639f0efd4d556975153c666e
PKG_MIRROR_HASH:=7def015c5b4c089d1376f0d31c64b8d52cd4b3aa94448333edf3682d60f891cf
PKG_LICENSE:=GPL-2.0
PKG_LICENSE_FILES:=LICENSE

View File

@ -22,7 +22,7 @@ echo "${zt0}" > "/tmp/zt.nif"
iptables -I FORWARD -i "$i" -j ACCEPT
iptables -I FORWARD -o "$i" -j ACCEPT
iptables -t nat -I POSTROUTING -o "$i" -j MASQUERADE
ip_segment="$(ip route | grep "dev $i proto" | awk '{print $1}')"
ip_segment="$(ip route | grep "dev $i proto kernel" | awk '{print $1}')"
iptables -t nat -I POSTROUTING -s "${ip_segment}" -j MASQUERADE
done
}

View File

@ -12,8 +12,8 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=uugamebooster
PKG_VERSION:=v2.9.0
PKG_RELEASE:=7
PKG_VERSION:=v2.10.0
PKG_RELEASE:=8
include $(INCLUDE_DIR)/package.mk
@ -31,27 +31,27 @@ endef
ifeq ($(ARCH),x86_64)
UU_ARCH:=x86_64
PKG_MD5SUM:=136c6e63745b385ab986df60ff8b2ff0
PKG_MD5SUM:=a856aebe19b9c663bdc08b257a591b40
endif
ifeq ($(ARCH),mipsel)
UU_ARCH:=mipsel
PKG_MD5SUM:=7bec7f1df806d511f93e90e677aa830a
PKG_MD5SUM:=90e895012e5a03391a0c0f4759a4422c
endif
ifeq ($(ARCH),mips)
UU_ARCH:=mipsel
PKG_MD5SUM:=7bec7f1df806d511f93e90e677aa830a
PKG_MD5SUM:=90e895012e5a03391a0c0f4759a4422c
endif
ifeq ($(ARCH),arm)
UU_ARCH:=arm
PKG_MD5SUM:=ceefdf84f8ce3e1eb87d22200373900a
PKG_MD5SUM:=acc7e564b1718009cd2d94335ace1bcd
endif
ifeq ($(ARCH),aarch64)
UU_ARCH:=aarch64
PKG_MD5SUM:=a4c82d58416aa604c143053023aca71d
PKG_MD5SUM:=262cc28ba07d7cf84a3f487ff4fd146c
endif
PKG_SOURCE_URL:=http://uu.gdl.netease.com/openwrt-$(UU_ARCH)/$(PKG_VERSION)/uu.tar.gz?

View File

@ -9,13 +9,13 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=v2ray
PKG_VERSION:=4.37.1
PKG_VERSION:=4.37.2
PKG_RELEASE:=1
PKG_BUILD_DIR:=$(BUILD_DIR)/v2ray-core-$(PKG_VERSION)
PKG_SOURCE:=v2ray-core-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://codeload.github.com/v2fly/v2ray-core/tar.gz/v$(PKG_VERSION)?
PKG_HASH:=45b8e977477e586c5b3d54b2f7f92c013fd7e88a407237b8f9701a350b706aef
PKG_HASH:=ece74b2f0e04d9cd487d1b4d79010f0269b6ee0742aaeb147979e5b97639ffa3
PKG_LICENSE:=MIT
PKG_LICENSE_FILES:=LICENSE

View File

@ -1,56 +0,0 @@
#
# Copyright (C) 2015 OpenWrt-dist
# Copyright (C) 2015 Jian Chang <aa65535@live.com>
#
# Copyright (C) 2021 ImmortalWrt
# <https://immortalwrt.org>
#
# This is free software, licensed under the GNU General Public License v3.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
PKG_NAME:=dns-forwarder
PKG_VERSION:=1.2.1
PKG_RELEASE:=2
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/aa65535/hev-dns-forwarder.git
PKG_SOURCE_DATE:=2017-07-21
PKG_SOURCE_VERSION:=289e8c9c7167200668dff83b1e0cbce258665387
PKG_MIRROR_HASH:=a06f61c2b87f61cf68517d3b4a23439366f3c806dd736594a814ee0245adca43
PKG_LICENSE:=GPLv3
PKG_LICENSE_FILES:=LICENSE
PKG_MAINTAINER:=Jian Chang <aa65535@live.com>
PKG_BUILD_PARALLEL:=1
include $(INCLUDE_DIR)/package.mk
define Package/dns-forwarder
SECTION:=net
CATEGORY:=Network
TITLE:=Forwarding DNS queries on TCP transport
URL:=https://github.com/aa65535/hev-dns-forwarder
endef
define Package/dns-forwarder/description
Forwarding DNS queries on TCP transport.
endef
define Package/dns-forwarder/conffiles
/etc/config/dns-forwarder
endef
define Package/dns-forwarder/install
$(INSTALL_DIR) $(1)/usr/bin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/src/hev-dns-forwarder $(1)/usr/bin/dns-forwarder
$(INSTALL_DIR) $(1)/etc/config
$(INSTALL_DATA) ./files/dns-forwarder.config $(1)/etc/config/dns-forwarder
$(INSTALL_DIR) $(1)/etc/init.d
$(INSTALL_BIN) ./files/dns-forwarder.init $(1)/etc/init.d/dns-forwarder
endef
$(eval $(call BuildPackage,dns-forwarder))

View File

@ -1,6 +0,0 @@
config dns-forwarder
option enable '0'
option listen_addr '0.0.0.0'
option listen_port '5300'
option dns_servers '8.8.8.8'

View File

@ -1,46 +0,0 @@
#!/bin/sh /etc/rc.common
#
# Copyright (C) 2016 Jian Chang <aa65535@live.com>
#
# This is free software, licensed under the GNU General Public License v3.
# See /LICENSE for more information.
#
START=75
USE_PROCD=1
append_parm() {
local section="$1"
local option="$2"
local switch="$3"
local default="$4"
local _loctmp
config_get _loctmp "$section" "$option"
[ -n "$_loctmp" -o -n "$default" ] || return 0
procd_append_param command "$switch" "${_loctmp:-$default}"
}
start_instance() {
local enable
config_get_bool enable $1 enable
[ "$enable" = 1 ] || return 0
procd_open_instance
procd_set_param respawn
procd_set_param stderr 1
procd_set_param command /usr/bin/dns-forwarder
append_parm $1 listen_addr "-b"
append_parm $1 listen_port "-p"
append_parm $1 dns_servers "-s"
procd_close_instance
}
start_service() {
config_load dns-forwarder
config_foreach start_instance dns-forwarder
}
service_triggers() {
procd_add_reload_trigger "dns-forwarder"
}

View File

@ -1,57 +0,0 @@
#
# Copyright (C) 2021 ImmortalWrt
# <https://immortalwrt.org>
#
# This is free software, licensed under the GNU General Public License v3.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
PKG_NAME:=dns2socks
PKG_VERSION:=2.1
PKG_RELEASE:=2
PKG_SOURCE:=SourceCode.zip
PKG_SOURCE_URL:=@SF/dns2socks
PKG_SOURCE_DATE:=2020-02-18
PKG_HASH:=406b5003523577d39da66767adfe54f7af9b701374363729386f32f6a3a995f4
PKG_MAINTAINER:=ghostmaker
PKG_LICENSE:=BSD-3-Clause
PKG_LICENSE_FILE:=LICENSE
include $(INCLUDE_DIR)/package.mk
define Package/dns2socks
SECTION:=net
CATEGORY:=Network
SUBMENU:=IP Addresses and Names
TITLE:=The utility to resolve DNS requests via a SOCKS5 tunnel.
URL:=http://dns2socks.sourceforge.net/
MAINTAINER:=ghostmaker
DEPENDS:=+libpthread
endef
define Package/dns2socks/description
This is a utility to resolve DNS requests via a SOCKS5 tunnel and caches the answers.
endef
UNZIP_CMD:=unzip -q -d $(PKG_BUILD_DIR) $(DL_DIR)/$(PKG_SOURCE)
define Build/Compile
$(TARGET_CC) \
$(TARGET_CFLAGS) \
$(TARGET_CPPFLAGS) \
$(FPIC) \
-o $(PKG_BUILD_DIR)/DNS2SOCKS/dns2socks \
$(PKG_BUILD_DIR)/DNS2SOCKS/DNS2SOCKS.c \
$(TARGET_LDFLAGS) -pthread
endef
define Package/dns2socks/install
$(INSTALL_DIR) $(1)/usr/bin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/DNS2SOCKS/dns2socks $(1)/usr/bin/dns2socks
endef
$(eval $(call BuildPackage,dns2socks))

View File

@ -1,43 +0,0 @@
#
# Copyright (C) 2021 ImmortalWrt
# <https://immortalwrt.org>
#
# This is free software, licensed under the GNU General Public License v3.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
PKG_NAME:=ipt2socks
PKG_VERSION:=1.1.3
PKG_RELEASE:=2
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://codeload.github.com/zfl9/ipt2socks/tar.gz/v$(PKG_VERSION)?
PKG_HASH:=73a2498dc95934c225d358707e7f7d060b5ce81aa45260ada09cbd15207d27d1
PKG_BUILD_PARALLEL:=1
PKG_INSTALL:=1
PKG_LICENSE:=AGPL-3.0
PKG_LICENSE_FILE:=LICENSE
include $(INCLUDE_DIR)/package.mk
define Package/ipt2socks
SECTION:=net
CATEGORY:=Network
TITLE:=Utility for converting iptables (REDIRECT/TPROXY) to SOCKS5
URL:=https://github.com/zfl9/ipt2socks
DEPENDS:=+libpthread
endef
TARGET_CFLAGS += $(FPIC) -flto
TARGET_LDFLAGS += -flto
define Package/ipt2socks/install
$(INSTALL_DIR) $(1)/usr/bin
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/ipt2socks $(1)/usr/bin
endef
$(eval $(call BuildPackage,ipt2socks))

View File

@ -1,19 +0,0 @@
# Copyright (C) 2019-2020 Lienol <lawlienol@gmail.com>
#
# This is free software, licensed under the GNU General Public License v3.
#
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-brook-server
LUCI_TITLE:=LuCI support for Brook Server
LUCI_DEPENDS:=+brook
LUCI_PKGARCH:=all
PKG_VERSION:=1
PKG_RELEASE:=2-20200326
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature

View File

@ -1,72 +0,0 @@
-- Copyright 2019 Lienol <lawlienol@gmail.com>
module("luci.controller.brook_server", package.seeall)
local http = require "luci.http"
local brook = require "luci.model.cbi.brook_server.api.brook"
function index()
if not nixio.fs.access("/etc/config/brook_server") then return end
entry({"admin", "vpn"}, firstchild(), "VPN", 45).dependent = false
entry({"admin", "vpn", "brook_server"}, cbi("brook_server/index"),
_("Brook Server"), 3).dependent = true
entry({"admin", "vpn", "brook_server", "config"}, cbi("brook_server/config")).leaf =
true
entry({"admin", "vpn", "brook_server", "users_status"},
call("brook_users_status")).leaf = true
entry({"admin", "vpn", "brook_server", "check"}, call("brook_check")).leaf =
true
entry({"admin", "vpn", "brook_server", "update"}, call("brook_update")).leaf =
true
entry({"admin", "vpn", "brook_server", "get_log"}, call("get_log")).leaf =
true
entry({"admin", "vpn", "brook_server", "clear_log"}, call("clear_log")).leaf =
true
end
local function http_write_json(content)
http.prepare_content("application/json")
http.write_json(content or {code = 1})
end
function brook_users_status()
local e = {}
local index = luci.http.formvalue("index")
e.index = index
local protocol = luci.sys.exec("echo -n `uci get brook_server.@user[" ..
index .. "].protocol`")
local port = luci.sys.exec(
"echo -n `uci get brook_server.@user[" .. index ..
"].port`")
local password = luci.sys.exec("echo -n `uci get brook_server.@user[" ..
index .. "].password`")
e.status = luci.sys.call(
"ps -w | grep -v grep | grep 'brook " .. protocol .. " -l :" ..
port .. " -p " .. password .. "' >/dev/null") == 0
http_write_json(e)
end
function brook_check()
local json = brook.to_check("")
http_write_json(json)
end
function brook_update()
local json = nil
local task = http.formvalue("task")
if task == "move" then
json = brook.to_move(http.formvalue("file"))
else
json = brook.to_download(http.formvalue("url"))
end
http_write_json(json)
end
function get_log()
luci.http.write(luci.sys.exec(
"[ -f '/var/log/brook_server/app.log' ] && cat /var/log/brook_server/app.log"))
end
function clear_log() luci.sys.call("echo '' > /var/log/brook_server/app.log") end

View File

@ -1,338 +0,0 @@
module("luci.model.cbi.brook_server.api.brook", package.seeall)
local fs = require "nixio.fs"
local sys = require "luci.sys"
local uci = require"luci.model.uci".cursor()
local util = require "luci.util"
local i18n = require "luci.i18n"
local appname = "brook_server"
local brook_api =
"https://api.github.com/repos/txthinking/brook/releases/latest"
local wget = "/usr/bin/wget"
local wget_args = {
"--no-check-certificate", "--quiet", "--timeout=100", "--tries=3"
}
local command_timeout = 300
local LEDE_BOARD = nil
local DISTRIB_TARGET = nil
local is_armv7 = false
function uci_get_type(type, config, default)
value = uci:get(appname, "@" .. type .. "[0]", config) or sys.exec(
"echo -n `uci -q get " .. appname .. ".@" .. type .. "[0]." ..
config .. "`")
if (value == nil or value == "") and (default and default ~= "") then
value = default
end
return value
end
local function _unpack(t, i)
i = i or 1
if t[i] ~= nil then return t[i], _unpack(t, i + 1) end
end
local function exec(cmd, args, writer, timeout)
local os = require "os"
local nixio = require "nixio"
local fdi, fdo = nixio.pipe()
local pid = nixio.fork()
if pid > 0 then
fdo:close()
if writer or timeout then
local starttime = os.time()
while true do
if timeout and os.difftime(os.time(), starttime) >= timeout then
nixio.kill(pid, nixio.const.SIGTERM)
return 1
end
if writer then
local buffer = fdi:read(2048)
if buffer and #buffer > 0 then
writer(buffer)
end
end
local wpid, stat, code = nixio.waitpid(pid, "nohang")
if wpid and stat == "exited" then return code end
if not writer and timeout then nixio.nanosleep(1) end
end
else
local wpid, stat, code = nixio.waitpid(pid)
return wpid and stat == "exited" and code
end
elseif pid == 0 then
nixio.dup(fdo, nixio.stdout)
fdi:close()
fdo:close()
nixio.exece(cmd, args, nil)
nixio.stdout:close()
os.exit(1)
end
end
local function compare_versions(ver1, comp, ver2)
local table = table
local av1 = util.split(ver1, "[%.%-]", nil, true)
local av2 = util.split(ver2, "[%.%-]", nil, true)
local max = table.getn(av1)
local n2 = table.getn(av2)
if (max < n2) then max = n2 end
for i = 1, max, 1 do
local s1 = av1[i] or ""
local s2 = av2[i] or ""
if comp == "~=" and (s1 ~= s2) then return true end
if (comp == "<" or comp == "<=") and (s1 < s2) then return true end
if (comp == ">" or comp == ">=") and (s1 > s2) then return true end
if (s1 ~= s2) then return false end
end
return not (comp == "<" or comp == ">")
end
local function auto_get_arch()
local arch = nixio.uname().machine or ""
if fs.access("/usr/lib/os-release") then
LEDE_BOARD = sys.exec(
"echo -n `grep 'LEDE_BOARD' /usr/lib/os-release | awk -F '[\\042\\047]' '{print $2}'`")
end
if fs.access("/etc/openwrt_release") then
DISTRIB_TARGET = sys.exec(
"echo -n `grep 'DISTRIB_TARGET' /etc/openwrt_release | awk -F '[\\042\\047]' '{print $2}'`")
end
if arch == "mips" then
if LEDE_BOARD and LEDE_BOARD ~= "" then
if string.match(LEDE_BOARD, "ramips") == "ramips" then
arch = "ramips"
else
arch = sys.exec("echo '" .. LEDE_BOARD ..
"' | grep -oE 'ramips|ar71xx'")
end
elseif DISTRIB_TARGET and DISTRIB_TARGET ~= "" then
if string.match(DISTRIB_TARGET, "ramips") == "ramips" then
arch = "ramips"
else
arch = sys.exec("echo '" .. DISTRIB_TARGET ..
"' | grep -oE 'ramips|ar71xx'")
end
end
end
return util.trim(arch)
end
local function get_file_info(arch)
local file_tree = ""
local sub_version = ""
if arch == "x86_64" then
file_tree = "amd64"
elseif arch == "aarch64" then
file_tree = "arm64"
elseif arch == "ramips" then
file_tree = "mipsle"
elseif arch == "ar71xx" then
file_tree = "mips"
elseif arch:match("^i[%d]86$") then
file_tree = "386"
elseif arch:match("^armv[5-8]") then
file_tree = "arm"
sub_version = arch:match("[5-8]")
if LEDE_BOARD and string.match(LEDE_BOARD, "bcm53xx") == "bcm53xx" then
sub_version = "5"
elseif DISTRIB_TARGET and string.match(DISTRIB_TARGET, "bcm53xx") ==
"bcm53xx" then
sub_version = "5"
end
sub_version = "5"
end
return file_tree, sub_version
end
function get_api_json(url)
local jsonc = require "luci.jsonc"
local output = {}
-- exec(wget, { "-O-", url, _unpack(wget_args) },
-- function(chunk) output[#output + 1] = chunk end)
-- local json_content = util.trim(table.concat(output))
local json_content = luci.sys.exec(wget ..
" --no-check-certificate --timeout=10 -t 1 -O- " ..
url)
if json_content == "" then return {} end
return jsonc.parse(json_content) or {}
end
function get_brook_file_path()
return uci_get_type("global", "brook_path", "/usr/bin/brook")
end
function get_brook_version(file)
if file == nil then file = get_brook_file_path() end
if file and file ~= "" then
if not fs.access(file, "rwx", "rx", "rx") then
fs.chmod(file, 755)
end
local info = util.trim(sys.exec("%s -v 2>/dev/null" % file))
if info ~= "" then
local tb = util.split(info, "%s+", nil, true)
return tb[1] == "Brook" and tb[3] or ""
end
end
return ""
end
function to_check(arch)
if not arch or arch == "" then arch = auto_get_arch() end
local file_tree, sub_version = get_file_info(arch)
if file_tree == "" then
return {
code = 1,
error = i18n.translate(
"Can't determine ARCH, or ARCH not supported.")
}
end
file_tree = "_linux_" .. file_tree
if file_tree == "_linux_amd64" then file_tree = "" end
local json = get_api_json(brook_api)
if json.tag_name == nil then
return {
code = 1,
error = i18n.translate("Get remote version info failed.")
}
end
local remote_version = json.tag_name:match("[^v]+")
local client_file = get_brook_file_path()
local needs_update = compare_versions(get_brook_version(client_file), "<",
remote_version)
local html_url, download_url
if needs_update then
html_url = json.html_url
for _, v in ipairs(json.assets) do
if v.name and v.name:match(file_tree .. sub_version) then
download_url = v.browser_download_url
break
end
end
end
if needs_update and not download_url then
return {
code = 1,
now_version = get_brook_version(client_file),
version = remote_version,
html_url = html_url,
error = i18n.translate(
"New version found, but failed to get new version download url.")
}
end
return {
code = 0,
update = needs_update,
now_version = get_brook_version(client_file),
version = remote_version,
url = {html = html_url, download = download_url}
}
end
function to_download(url)
if not url or url == "" then
return {code = 1, error = i18n.translate("Download url is required.")}
end
sys.call("/bin/rm -f /tmp/brook_download.*")
local tmp_file = util.trim(util.exec("mktemp -u -t brook_download.XXXXXX"))
local result = exec(wget, {"-O", tmp_file, url, _unpack(wget_args)}, nil,
command_timeout) == 0
if not result then
exec("/bin/rm", {"-f", tmp_file})
return {
code = 1,
error = i18n.translatef("File download failed or timed out: %s", url)
}
end
return {code = 0, file = tmp_file}
end
function to_move(file)
if not file or file == "" or not fs.access(file) then
sys.call("/bin/rm -rf /tmp/brook_download.*")
return {code = 1, error = i18n.translate("Client file is required.")}
end
local version = get_brook_version(file)
if version == "" then
sys.call("/bin/rm -rf /tmp/brook_download.*")
return {
code = 1,
error = i18n.translate(
"The client file is not suitable for current device.")
}
end
local client_file = get_brook_file_path()
local client_file_bak
if fs.access(client_file) then
client_file_bak = client_file .. ".bak"
exec("/bin/mv", {"-f", client_file, client_file_bak})
end
local result = exec("/bin/mv", {"-f", file, client_file}, nil,
command_timeout) == 0
if not result or not fs.access(client_file) then
sys.call("/bin/rm -rf /tmp/brook_download.*")
if client_file_bak then
exec("/bin/mv", {"-f", client_file_bak, client_file})
end
return {
code = 1,
error = i18n.translatef("Can't move new file to path: %s",
client_file)
}
end
exec("/bin/chmod", {"755", client_file})
if client_file_bak then exec("/bin/rm", {"-f", client_file_bak}) end
sys.call("/bin/rm -rf /tmp/brook_download.*")
return {code = 0}
end

View File

@ -1,32 +0,0 @@
local app_name = "brook_server"
local d = require "luci.dispatcher"
map = Map(app_name, "Brook " .. translate("Server Config"))
map.redirect = d.build_url("admin", "vpn", "brook_server")
t = map:section(NamedSection, arg[1], "user", "")
t.addremove = false
t.dynamic = false
enable = t:option(Flag, "enable", translate("Enable"))
enable.default = "1"
enable.rmempty = false
remarks = t:option(Value, "remarks", translate("Remarks"))
remarks.default = translate("Remarks")
remarks.rmempty = false
port = t:option(Value, "port", translate("Port"))
port.datatype = "port"
port.rmempty = false
protocol = t:option(ListValue, "protocol", translate("Protocol"), translate(
"if shadowsocks server mode, fixed method is aes-256-cfb"))
protocol:value("server", translate("Brook"))
protocol:value("ssserver", translate("Shadowsocks"))
password = t:option(Value, "password", translate("Password"))
password.password = true
password.rmempty = false
return map

View File

@ -1,66 +0,0 @@
local i = require "luci.dispatcher"
local e = require "nixio.fs"
local e = require "luci.sys"
local e = luci.model.uci.cursor()
local o = "brook_server"
m = Map(o, translate("Brook Server"))
t = m:section(TypedSection, "global", translate("Global Settings"))
t.anonymous = true
t.addremove = false
e = t:option(Flag, "enable", translate("Enable"))
e.rmempty = false
t:append(Template("brook_server/brook"))
e = t:option(Value, "brook_path", translate("Brook Path"),
translate(
"if you want to run from memory, change the path, such as /tmp/brook, Then save the application and update it manually."))
e.default = "/usr/bin/brook"
e.rmempty = false
t = m:section(TypedSection, "user", translate("Users Manager"))
t.anonymous = true
t.addremove = true
t.template = "cbi/tblsection"
t.extedit = i.build_url("admin", "vpn", o, "config", "%s")
function t.create(t, e)
local e = TypedSection.create(t, e)
luci.http.redirect(i.build_url("admin", "vpn", o, "config", e))
end
function t.remove(t, a)
t.map.proceed = true
t.map:del(a)
luci.http.redirect(i.build_url("admin", "vpn", o))
end
e = t:option(Flag, "enable", translate("Enable"))
e.width = "5%"
e.rmempty = false
e = t:option(DummyValue, "status", translate("Status"))
e.template = "brook_server/users_status"
e.value = translate("Collecting data...")
e = t:option(DummyValue, "remarks", translate("Remarks"))
e.width = "20%"
e = t:option(DummyValue, "port", translate("Port"))
e.width = "20%"
e = t:option(DummyValue, "password", translate("Password"))
e.width = "30%"
e.cfgvalue = function(self, section)
local e = m:get(section, "password") or ""
local t = ""
if type(e) == "table" then
for a = 1, #e do t = t .. e[a] .. "," end
t = string.sub(t, 0, #t - 1)
else
t = e
end
return t
end
m:append(Template("brook_server/log"))
m:append(Template("brook_server/users_list_status"))
return m

View File

@ -1,168 +0,0 @@
<%
local brook_path = luci.sys.exec("echo -n `uci get brook_server.@global[0].brook_path`")
local brook_version = luci.sys.exec("[ -f '" .. brook_path .. "' ] && " .. brook_path .. " -v | awk '{print $3}'")
local dsp = require "luci.dispatcher"
-%>
<script type="text/javascript">
//<![CDATA[
var brookInfo;
var tokenStr = '<%=token%>';
var noUpdateText = '<%:已是最新版本%>';
var updateSuccessText = '<%:更新成功.%>';
var clickToUpdateText = '<%:点击更新%>';
var inProgressText = '<%:正在更新...%>';
var unexpectedErrorText = '<%:意外错误.%>';
var updateInProgressNotice = '<%:正在更新,你确认要关闭吗?%>';
window.onload = function() {
var brookCheckBtn = document.getElementById('_brook-check_btn');
var brookDetailElm = document.getElementById('_brook-check_btn-detail');
};
function addPageNotice_brook() {
window.onbeforeunload = function(e) {
e.returnValue = updateInProgressNotice;
return updateInProgressNotice;
};
}
function removePageNotice_brook() {
window.onbeforeunload = undefined;
}
function onUpdateSuccess_brook(btn) {
alert(updateSuccessText);
if(btn) {
btn.value = updateSuccessText;
btn.placeholder = updateSuccessText;
btn.disabled = true;
}
window.setTimeout(function() {
window.location.reload();
}, 1000);
}
function onRequestError_brook(btn, errorMessage) {
btn.disabled = false;
btn.value = btn.placeholder;
if(errorMessage) {
alert(errorMessage);
}
}
function doAjaxGet(url, data, onResult) {
new XHR().get(url, data, function(_, json) {
var resultJson = json || {
'code': 1,
'error': unexpectedErrorText
};
if(typeof onResult === 'function') {
onResult(resultJson);
}
})
}
function onBtnClick_brook(btn) {
if(brookInfo === undefined) {
checkUpdate_brook(btn);
} else {
doUpdate_brook(btn);
}
}
function checkUpdate_brook(btn) {
btn.disabled = true;
btn.value = inProgressText;
addPageNotice_brook();
var ckeckDetailElm = document.getElementById(btn.id + '-detail');
doAjaxGet('<%=dsp.build_url("admin/vpn/brook_server/check")%>', {
token: tokenStr,
arch: ''
}, function(json) {
removePageNotice_brook();
if(json.code) {
brookInfo = undefined;
onRequestError_brook(btn, json.error);
} else {
if(json.update) {
brookInfo = json;
btn.disabled = false;
btn.value = clickToUpdateText;
btn.placeholder = clickToUpdateText;
if(ckeckDetailElm) {
var urlNode = '';
if(json.version) {
urlNode = '<em style="color:red;">最新版本号:' + json.version + '</em>';
if(json.url && json.url.html) {
urlNode = '<a href="' + json.url.html + '" target="_blank">' + urlNode + '</a>';
}
}
ckeckDetailElm.innerHTML = urlNode;
}
} else {
btn.disabled = true;
btn.value = noUpdateText;
}
}
});
}
function doUpdate_brook(btn) {
btn.disabled = true;
btn.value = '<%:下载中...%>';
addPageNotice_brook();
var brookUpdateUrl = '<%=dsp.build_url("admin/vpn/brook_server/update")%>';
// Download file
doAjaxGet(brookUpdateUrl, {
token: tokenStr,
url: brookInfo ? brookInfo.url.download : ''
}, function(json) {
if(json.code) {
removePageNotice_brook();
onRequestError_brook(btn, json.error);
} else {
btn.value = '<%:解压中...%>';
// Move file to target dir
doAjaxGet(brookUpdateUrl, {
token: tokenStr,
task: 'move',
file: json.file
}, function(json) {
removePageNotice_brook();
if(json.code) {
onRequestError_brook(btn, json.error);
} else {
onUpdateSuccess_brook(btn);
}
})
}
})
}
//]]>
</script>
<div class="cbi-value">
<label class="cbi-value-title">Brook
<%:Version%>
</label>
<div class="cbi-value-field">
<div class="cbi-value-description">
<span><%=brook_version%>】</span>
<input class="cbi-button cbi-input-apply" type="submit" id="_brook-check_btn" onclick="onBtnClick_brook(this);" value="<%:Manually update%>">
<span id="_brook-check_btn-detail"></span>
</div>
</div>
</div>

View File

@ -1,31 +0,0 @@
<script type="text/javascript">
//<![CDATA[
function clear_log(btn) {
XHR.get('<%=url([[admin]], [[vpn]], [[brook_server]], [[clear_log]])%>', null,
function(x, data) {
if(x && x.status == 200) {
var log_textarea = document.getElementById('log_textarea');
log_textarea.innerHTML = "";
log_textarea.scrollTop = log_textarea.scrollHeight;
}
}
);
}
XHR.poll(3, '<%=url([[admin]], [[vpn]], [[brook_server]], [[get_log]])%>', null,
function(x, data) {
if(x && x.status == 200) {
var log_textarea = document.getElementById('log_textarea');
log_textarea.innerHTML = x.responseText;
log_textarea.scrollTop = log_textarea.scrollHeight;
}
}
);
//]]>
</script>
<fieldset class="cbi-section" id="_log_fieldset">
<legend>
<%:Logs%>
</legend>
<input class="cbi-button cbi-input-remove" type="button" onclick="clear_log()" value="<%:Clear logs%>">
<textarea id="log_textarea" class="cbi-input-textarea" style="width: 100%;margin-top: 10px;" data-update="change" rows="20" wrap="off" readonly="readonly"></textarea>
</fieldset>

View File

@ -1,23 +0,0 @@
<%
local dsp = require "luci.dispatcher"
-%>
<script type="text/javascript">
//<![CDATA[
var brook_users_status = document.getElementsByClassName('brook_users_status');
for(var i = 0; i < brook_users_status.length; i++) {
var id = brook_users_status[i].parentElement.parentElement.parentElement.id;
id = id.substr(id.lastIndexOf("-") + 1);
XHR.poll(1,'<%=dsp.build_url("admin/vpn/brook_server/users_status")%>', {
index: i,
id: id
},
function(x, result) {
brook_users_status[result.index].setAttribute("style","font-weight:bold;");
brook_users_status[result.index].setAttribute("color",result.status ? "green":"red");
brook_users_status[result.index].innerHTML = (result.status ? '✓' : 'X');
}
);
}
//]]>
</script>

View File

@ -1,3 +0,0 @@
<%+cbi/valueheader%>
<font class="brook_users_status" hint="<%=self:cfgvalue(section)%>">--</font>
<%+cbi/valuefooter%>

View File

@ -1,83 +0,0 @@
msgid "Brook Server"
msgstr "Brook 服务器"
msgid "Global Settings"
msgstr "全局设置"
msgid "Brook Path"
msgstr "Brook路径"
msgid "if you want to run from memory, change the path, such as /tmp/brook, Then save the application and update it manually."
msgstr "如果你希望从内存中运行,请更改路径,例如/tmp/brook然后保存应用后再手动更新。"
msgid "Server Config"
msgstr "服务器配置"
msgid "Users Manager"
msgstr "用户管理"
msgid "Remarks"
msgstr "备注"
msgid "Port"
msgstr "端口"
msgid "Password"
msgstr "密码"
msgid "Protocol"
msgstr "协议"
msgid "if shadowsocks server mode, fixed method is aes-256-cfb"
msgid "当shadowsocks服务器模式时固定的加密为 aes-256-cfb"
msgid "Logs"
msgstr "日志"
msgid "Clear logs"
msgstr "清空日志"
msgid "Can't determine ARCH, or ARCH not supported."
msgstr "无法确认ARCH架构或是不支持。"
msgid "Get remote version info failed."
msgstr "获取远程版本信息失败。"
msgid "New version found, but failed to get new version download url."
msgstr "发现新版本,但未能获得新版本的下载地址。"
msgid "Download url is required."
msgstr "请指定下载地址。"
msgid "File download failed or timed out: %s"
msgstr "文件下载失败或超时:%s"
msgid "File path required."
msgstr "请指定文件路径。"
msgid "Can't find client in file: %s"
msgstr "无法在文件中找到客户端:%s"
msgid "Client file is required."
msgstr "请指定客户端文件。"
msgid "The client file is not suitable for current device."
msgstr "客户端文件不适合当前设备。"
msgid "Can't move new file to path: %s"
msgstr "无法移动新文件到:%s"
msgid "Enabled"
msgstr "启用"
msgid "Status"
msgstr "状态"
msgid "Current Condition"
msgstr "当前状态"
msgid "NOT RUNNING"
msgstr "未运行"
msgid "RUNNING"
msgstr "运行中"

View File

@ -1,12 +0,0 @@
config global
option enable '0'
option brook_path '/usr/bin/brook'
config user
option remarks '测试'
option port '12345'
list password 'aaa'
option protocol 'server'
option enable '1'

View File

@ -1,63 +0,0 @@
#!/bin/sh /etc/rc.common
# Copyright (C) 2019-2020 Lienol <lawlienol@gmail.com>
START=99
CONFIG=brook_server
LOG_PATH=/var/log/$CONFIG
LOG_APP_FILE=$LOG_PATH/app.log
echolog() {
echo -e "$(date "+%Y-%m-%d %H:%M:%S"): $1" >> $LOG_APP_FILE
}
gen_brook_config_file() {
config_get enable $1 enable
[ "$enable" = "0" ] && return 0
config_get remarks $1 remarks
config_get port $1 port
config_get protocol $1 protocol
config_get password $1 password
/usr/bin/brook $protocol -l :$port -p $password >/dev/null 2>&1 &
is_run=$(ps -w| grep -v grep | grep "brook $protocol -l :$port -p $password")
if [ -z "$is_run" ];then
echolog "$remarks $port Brook 运行失败"
else
echolog "$remarks $port Brook 运行成功"
fi
}
start_brook_server() {
mkdir -p $LOG_PATH
touch $LOG_APP_FILE
config_foreach gen_brook_config_file "user"
fw3 reload >/dev/null 2>&1 &
}
stop_brook_server() {
fw3 reload >/dev/null 2>&1
ps -w | grep "brook server" | grep -v "grep" | awk '{print $1}' | xargs kill -9 >/dev/null 2>&1 &
ps -w | grep "brook ssserver" | grep -v "grep" | awk '{print $1}' | xargs kill -9 >/dev/null 2>&1 &
rm -rf $LOG_PATH
}
start() {
config_load $CONFIG
enable=$(uci -q get $CONFIG.@global[0].enable)
if [ "$enable" = "0" ];then
stop_brook_server
else
start_brook_server
fi
}
stop() {
stop_brook_server
}
restart() {
stop
start
}

View File

@ -1,21 +0,0 @@
#!/bin/sh
uci -q batch <<-EOF >/dev/null
delete firewall.brook_server
set firewall.brook_server=include
set firewall.brook_server.type=script
set firewall.brook_server.path=/usr/share/brook_server/firewall.include
set firewall.brook_server.reload=1
EOF
uci -q batch <<-EOF >/dev/null
delete ucitrack.@brook_server[-1]
add ucitrack brook_server
set ucitrack.@brook_server[-1].init=brook_server
commit ucitrack
EOF
chmod a+x /usr/share/brook_server/* >/dev/null 2>&1
rm -f /tmp/luci-indexcache
exit 0

View File

@ -1,39 +0,0 @@
#!/bin/sh
count=$(iptables -n -L INPUT 2>/dev/null | grep -c "BROOK-SERVER")
if [ -n "$count" ]; then
until [ "$count" = 0 ]
do
rules=$(iptables -n -L INPUT --line-num 2>/dev/null | grep "BROOK-SERVER" | awk '{print $1}')
for rule in $rules
do
iptables -D INPUT $rule 2>/dev/null
break
done
count=$(expr $count - 1)
done
fi
iptables -F BROOK-SERVER 2>/dev/null && iptables -X BROOK-SERVER 2>/dev/null
enable=$(uci -q get brook_server.@global[0].enable)
if [ $enable -eq 1 ]; then
iptables -N BROOK-SERVER
iptables -I INPUT -j BROOK-SERVER
count=$(uci show brook_server | grep "@user" | sed -n '$p' | cut -d '[' -f 2 | cut -d ']' -f 1)
[ -n "$count" ] && [ "$count" -ge 0 ] && {
u_get() {
local ret=$(uci -q get brook_server.@user[$1].$2)
echo ${ret:=$3}
}
for i in $(seq 0 $count); do
enable=$(u_get $i enable 0)
[ $enable -eq 0 ] && continue
remarks=$(u_get $i remarks)
port=$(u_get $i port)
iptables -A BROOK-SERVER -p tcp --dport $port -m comment --comment "$remarks" -j ACCEPT
iptables -A BROOK-SERVER -p udp --dport $port -m comment --comment "$remarks" -j ACCEPT
done
}
fi

View File

@ -1,14 +0,0 @@
# From https://github.com/DarkDean89/luci-app-filebrowser
# From https://github.com/stuarthua/oh-my-openwrt/tree/master/stuart/luci-app-fileassistant
# This is free software, licensed under the Apache License, Version 2.0 .
include $(TOPDIR)/rules.mk
LUCI_TITLE:=LuCI support for Fileassistant
LUCI_PKGARCH:=all
PKG_VERSION:=1.0
PKG_RELEASE:=2
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature

View File

@ -1,68 +0,0 @@
.fb-container {
margin-top: 1rem;
}
.fb-container .cbi-button {
height: 1.8rem;
}
.fb-container .cbi-input-text {
margin-bottom: 1rem;
width: 100%;
}
.fb-container .panel-title {
padding-bottom: 0;
width: 50%;
border-bottom: none;
}
.fb-container .panel-container {
display: flex;
align-items: center;
justify-content: space-between;
padding-bottom: 1rem;
border-bottom: 1px solid #eee;
}
.fb-container .upload-container {
display: none;
margin: 1rem 0;
}
.fb-container .upload-file {
margin-right: 2rem;
}
.fb-container .cbi-value-field {
text-align: left;
}
.fb-container .parent-icon strong {
margin-left: 1rem;
}
.fb-container td[class$="-icon"] {
cursor: pointer;
}
.fb-container .file-icon, .fb-container .folder-icon, .fb-container .link-icon {
position: relative;
}
.fb-container .file-icon:before, .fb-container .folder-icon:before, .fb-container .link-icon:before {
display: inline-block;
width: 1.5rem;
height: 1.5rem;
content: '';
background-size: contain;
margin: 0 0.5rem 0 1rem;
vertical-align: middle;
}
.fb-container .file-icon:before {
background-image: url(file-icon.png);
}
.fb-container .folder-icon:before {
background-image: url(folder-icon.png);
}
.fb-container .link-icon:before {
background-image: url(link-icon.png);
}
@media screen and (max-width: 480px) {
.fb-container .upload-file {
width: 14.6rem;
}
.fb-container .cbi-value-owner,
.fb-container .cbi-value-perm {
display: none;
}
}

View File

@ -1,288 +0,0 @@
String.prototype.replaceAll = function(search, replacement) {
var target = this;
return target.replace(new RegExp(search, 'g'), replacement);
};
(function () {
var iwxhr = new XHR();
var listElem = document.getElementById("list-content");
listElem.onclick = handleClick;
var currentPath;
var pathElem = document.getElementById("current-path");
pathElem.onblur = function () {
update_list(this.value.trim());
};
pathElem.onkeyup = function (evt) {
if (evt.keyCode == 13) {
this.blur();
}
};
function removePath(filename, isdir) {
var c = confirm('你确定要删除 ' + filename + ' 吗?');
if (c) {
iwxhr.get('/cgi-bin/luci/admin/nas/fileassistant/delete',
{
path: concatPath(currentPath, filename),
isdir: isdir
},
function (x, res) {
if (res.ec === 0) {
refresh_list(res.data, currentPath);
}
});
}
}
function installPath(filename, isdir) {
if (isdir === "1") {
alert('这是一个目录,请选择 ipk 文件进行安装!');
return;
}
var isipk = isIPK(filename);
if (isipk === 0) {
alert('只允许安装 ipk 格式的文件!');
return;
}
var c = confirm('你确定要安装 ' + filename + ' 吗?');
if (c) {
iwxhr.get('/cgi-bin/luci/admin/nas/fileassistant/install',
{
filepath: concatPath(currentPath, filename),
isdir: isdir
},
function (x, res) {
if (res.ec === 0) {
location.reload();
alert('安装成功!');
} else {
alert('安装失败,请检查文件格式!');
}
});
}
}
function isIPK(filename) {
var index= filename.lastIndexOf(".");
var ext = filename.substr(index+1);
if (ext === 'ipk') {
return 1;
} else {
return 0;
}
}
function renamePath(filename) {
var newname = prompt('请输入新的文件名:', filename);
if (newname) {
newname = newname.trim();
if (newname != filename) {
var newpath = concatPath(currentPath, newname);
iwxhr.get('/cgi-bin/luci/admin/nas/fileassistant/rename',
{
filepath: concatPath(currentPath, filename),
newpath: newpath
},
function (x, res) {
if (res.ec === 0) {
refresh_list(res.data, currentPath);
}
}
);
}
}
}
function openpath(filename, dirname) {
dirname = dirname || currentPath;
window.open('/cgi-bin/luci/admin/nas/fileassistant/open?path='
+ encodeURIComponent(dirname) + '&filename='
+ encodeURIComponent(filename));
}
function getFileElem(elem) {
if (elem.className.indexOf('-icon') > -1) {
return elem;
}
else if (elem.parentNode.className.indexOf('-icon') > -1) {
return elem.parentNode;
}
}
function concatPath(path, filename) {
if (path === '/') {
return path + filename;
}
else {
return path.replace(/\/$/, '') + '/' + filename;
}
}
function handleClick(evt) {
var targetElem = evt.target;
var infoElem;
if (targetElem.className.indexOf('cbi-button-remove') > -1) {
infoElem = targetElem.parentNode.parentNode;
removePath(infoElem.dataset['filename'] , infoElem.dataset['isdir'])
}
else if (targetElem.className.indexOf('cbi-button-install') > -1) {
infoElem = targetElem.parentNode.parentNode;
installPath(infoElem.dataset['filename'] , infoElem.dataset['isdir'])
}
else if (targetElem.className.indexOf('cbi-button-edit') > -1) {
renamePath(targetElem.parentNode.parentNode.dataset['filename']);
}
else if (targetElem = getFileElem(targetElem)) {
if (targetElem.className.indexOf('parent-icon') > -1) {
update_list(currentPath.replace(/\/[^/]+($|\/$)/, ''));
}
else if (targetElem.className.indexOf('file-icon') > -1) {
openpath(targetElem.parentNode.dataset['filename']);
}
else if (targetElem.className.indexOf('link-icon') > -1) {
infoElem = targetElem.parentNode;
var filepath = infoElem.dataset['linktarget'];
if (filepath) {
if (infoElem.dataset['isdir'] === "1") {
update_list(filepath);
}
else {
var lastSlash = filepath.lastIndexOf('/');
openpath(filepath.substring(lastSlash + 1), filepath.substring(0, lastSlash));
}
}
}
else if (targetElem.className.indexOf('folder-icon') > -1) {
update_list(concatPath(currentPath, targetElem.parentNode.dataset['filename']))
}
}
}
function refresh_list(filenames, path) {
var listHtml = '<table class="cbi-section-table"><tbody>';
if (path !== '/') {
listHtml += '<tr class="cbi-section-table-row cbi-rowstyle-2"><td class="parent-icon" colspan="6"><strong>..</strong></td></tr>';
}
if (filenames) {
for (var i = 0; i < filenames.length; i++) {
var line = filenames[i];
if (line) {
var f = line.match(/(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+([\S\s]+)/);
var isLink = f[1][0] === 'z' || f[1][0] === 'l' || f[1][0] === 'x';
var o = {
displayname: f[9],
filename: isLink ? f[9].split(' -> ')[0] : f[9],
perms: f[1],
date: f[7] + ' ' + f[6] + ' ' + f[8],
size: f[5],
owner: f[3],
icon: (f[1][0] === 'd') ? "folder-icon" : (isLink ? "link-icon" : "file-icon")
};
var install_btn = '<button class="cbi-button cbi-button-install" style="visibility: hidden;">安装</button>';
var index= o.filename.lastIndexOf(".");
var ext = o.filename.substr(index+1);
if (ext === 'ipk') {
install_btn = '<button class="cbi-button cbi-button-install">安装</button>';
}
listHtml += '<tr class="cbi-section-table-row cbi-rowstyle-' + (1 + i%2)
+ '" data-filename="' + o.filename + '" data-isdir="' + Number(f[1][0] === 'd' || f[1][0] === 'z') + '"'
+ ((f[1][0] === 'z' || f[1][0] === 'l') ? (' data-linktarget="' + f[9].split(' -> ')[1]) : '')
+ '">'
+ '<td class="cbi-value-field ' + o.icon + '">'
+ '<strong>' + o.displayname + '</strong>'
+ '</td>'
+ '<td class="cbi-value-field cbi-value-owner">'+o.owner+'</td>'
+ '<td class="cbi-value-field cbi-value-date">'+o.date+'</td>'
+ '<td class="cbi-value-field cbi-value-size">'+o.size+'</td>'
+ '<td class="cbi-value-field cbi-value-perm">'+o.perms+'</td>'
+ '<td class="cbi-section-table-cell">\
<button class="cbi-button cbi-button-edit">重命名</button>\
<button class="cbi-button cbi-button-remove">删除</button>'
+ install_btn
+ '</td>'
+ '</tr>';
}
}
}
listHtml += "</table>";
listElem.innerHTML = listHtml;
}
function update_list(path, opt) {
opt = opt || {};
path = concatPath(path, '');
if (currentPath != path) {
iwxhr.get('/cgi-bin/luci/admin/nas/fileassistant/list',
{path: path},
function (x, res) {
if (res.ec === 0) {
refresh_list(res.data, path);
}
else {
refresh_list([], path);
}
}
);
if (!opt.popState) {
history.pushState({path: path}, null, '?path=' + path);
}
currentPath = path;
pathElem.value = currentPath;
}
};
var uploadToggle = document.getElementById('upload-toggle');
var uploadContainer = document.getElementById('upload-container');
var isUploadHide = true;
uploadToggle.onclick = function() {
if (isUploadHide) {
uploadContainer.style.display = 'inline-flex';
}
else {
uploadContainer.style.display = 'none';
}
isUploadHide = !isUploadHide;
};
var uploadBtn = uploadContainer.getElementsByClassName('cbi-input-apply')[0];
uploadBtn.onclick = function (evt) {
var uploadinput = document.getElementById('upload-file');
var fullPath = uploadinput.value;
if (!fullPath) {
evt.preventDefault();
}
else {
var formData = new FormData();
var startIndex = (fullPath.indexOf('\\') >= 0 ? fullPath.lastIndexOf('\\') : fullPath.lastIndexOf('/'));
formData.append('upload-filename', fullPath.substring(startIndex + 1));
formData.append('upload-dir', concatPath(currentPath, ''));
formData.append('upload-file', uploadinput.files[0]);
var xhr = new XMLHttpRequest();
xhr.open("POST", "/cgi-bin/luci/admin/nas/fileassistant/upload", true);
xhr.onload = function() {
if (xhr.status == 200) {
var res = JSON.parse(xhr.responseText);
refresh_list(res.data, currentPath);
uploadinput.value = '';
}
else {
alert('上传失败,请稍后再试...');
}
};
xhr.send(formData);
}
};
document.addEventListener('DOMContentLoaded', function(evt) {
var initPath = '/';
if (/path=([/\w]+)/.test(location.search)) {
initPath = RegExp.$1;
}
update_list(initPath, {popState: true});
});
window.addEventListener('popstate', function (evt) {
var path = '/';
if (evt.state && evt.state.path) {
path = evt.state.path;
}
update_list(path, {popState: true});
});
})();

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -1,230 +0,0 @@
module("luci.controller.fileassistant", package.seeall)
function index()
entry({"admin", "nas"}, firstchild(), "NAS", 44).dependent = false
local page
page = entry({"admin", "nas", "fileassistant"}, template("fileassistant"), _("文件助手"), 1)
page.i18n = "base"
page.dependent = true
page = entry({"admin", "nas", "fileassistant", "list"}, call("fileassistant_list"), nil)
page.leaf = true
page = entry({"admin", "nas", "fileassistant", "open"}, call("fileassistant_open"), nil)
page.leaf = true
page = entry({"admin", "nas", "fileassistant", "delete"}, call("fileassistant_delete"), nil)
page.leaf = true
page = entry({"admin", "nas", "fileassistant", "rename"}, call("fileassistant_rename"), nil)
page.leaf = true
page = entry({"admin", "nas", "fileassistant", "upload"}, call("fileassistant_upload"), nil)
page.leaf = true
page = entry({"admin", "nas", "fileassistant", "install"}, call("fileassistant_install"), nil)
page.leaf = true
end
function list_response(path, success)
luci.http.prepare_content("application/json")
local result
if success then
local rv = scandir(path)
result = {
ec = 0,
data = rv
}
else
result = {
ec = 1
}
end
luci.http.write_json(result)
end
function fileassistant_list()
local path = luci.http.formvalue("path")
list_response(path, true)
end
function fileassistant_open()
local path = luci.http.formvalue("path")
local filename = luci.http.formvalue("filename")
local io = require "io"
local mime = to_mime(filename)
file = path..filename
local download_fpi = io.open(file, "r")
luci.http.header('Content-Disposition', 'inline; filename="'..filename..'"' )
luci.http.prepare_content(mime)
luci.ltn12.pump.all(luci.ltn12.source.file(download_fpi), luci.http.write)
end
function fileassistant_delete()
local path = luci.http.formvalue("path")
local isdir = luci.http.formvalue("isdir")
path = path:gsub("<>", "/")
path = path:gsub(" ", "\ ")
local success
if isdir then
success = os.execute('rm -r "'..path..'"')
else
success = os.remove(path)
end
list_response(nixio.fs.dirname(path), success)
end
function fileassistant_rename()
local filepath = luci.http.formvalue("filepath")
local newpath = luci.http.formvalue("newpath")
local success = os.execute('mv "'..filepath..'" "'..newpath..'"')
list_response(nixio.fs.dirname(filepath), success)
end
function fileassistant_install()
local filepath = luci.http.formvalue("filepath")
local isdir = luci.http.formvalue("isdir")
local ext = filepath:match(".+%.(%w+)$")
filepath = filepath:gsub("<>", "/")
filepath = filepath:gsub(" ", "\ ")
local success
if isdir == "1" then
success = false
elseif ext == "ipk" then
success = installIPK(filepath)
else
success = false
end
list_response(nixio.fs.dirname(filepath), success)
end
function installIPK(filepath)
luci.sys.exec('opkg --force-depends install "'..filepath..'"')
luci.sys.exec('rm -rf /tmp/luci-*')
return true;
end
function fileassistant_upload()
local filecontent = luci.http.formvalue("upload-file")
local filename = luci.http.formvalue("upload-filename")
local uploaddir = luci.http.formvalue("upload-dir")
local filepath = uploaddir..filename
local fp
luci.http.setfilehandler(
function(meta, chunk, eof)
if not fp and meta and meta.name == "upload-file" then
fp = io.open(filepath, "w")
end
if fp and chunk then
fp:write(chunk)
end
if fp and eof then
fp:close()
end
end
)
list_response(uploaddir, true)
end
function scandir(directory)
local i, t, popen = 0, {}, io.popen
local pfile = popen("ls -lh \""..directory.."\" | egrep '^d' ; ls -lh \""..directory.."\" | egrep -v '^d|^l'")
for fileinfo in pfile:lines() do
i = i + 1
t[i] = fileinfo
end
pfile:close()
pfile = popen("ls -lh \""..directory.."\" | egrep '^l' ;")
for fileinfo in pfile:lines() do
i = i + 1
linkindex, _, linkpath = string.find(fileinfo, "->%s+(.+)$")
local finalpath;
if string.sub(linkpath, 1, 1) == "/" then
finalpath = linkpath
else
finalpath = nixio.fs.realpath(directory..linkpath)
end
local linktype;
if not finalpath then
finalpath = linkpath;
linktype = 'x'
elseif nixio.fs.stat(finalpath, "type") == "dir" then
linktype = 'z'
else
linktype = 'l'
end
fileinfo = string.sub(fileinfo, 2, linkindex - 1)
fileinfo = linktype..fileinfo.."-> "..finalpath
t[i] = fileinfo
end
pfile:close()
return t
end
MIME_TYPES = {
["txt"] = "text/plain";
["conf"] = "text/plain";
["ovpn"] = "text/plain";
["log"] = "text/plain";
["js"] = "text/javascript";
["json"] = "application/json";
["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";
}
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

View File

@ -1,20 +0,0 @@
<%+header%>
<link rel="stylesheet" href="/luci-static/resources/fileassistant/fb.css?v=@ver">
<h2 name="content">文件助手</h2>
<fieldset class="cbi-section fb-container">
<input id="current-path" type="text" class="current-path cbi-input-text" value="/"/>
<div class="panel-container">
<div class="panel-title">文件列表</div>
<button id="upload-toggle" class="upload-toggle cbi-button cbi-button-edit">上传</button>
</div>
<div class="upload-container" id="upload-container">
<input id="upload-file" name="upload-file" class="upload-file" type="file">
<button type="button" class="cbi-button cbi-input-apply">点我上传</button>
</div>
<div id="list-content"></div>
</fieldset>
<script src="/luci-static/resources/fileassistant/fb.js?v=@ver"></script>
<%+footer%>

View File

@ -1,16 +0,0 @@
# Copyright (C) 2018-2020 Lienol <lawlienol@gmail.com>
#
# This is free software, licensed under the Apache License, Version 2.0 .
#
include $(TOPDIR)/rules.mk
LUCI_TITLE:=LuCI support for IPSec VPN Server
LUCI_DEPENDS:=+strongswan +strongswan-minimal +strongswan-mod-xauth-generic
LUCI_PKGARCH:=all
PKG_VERSION:=1
PKG_RELEASE:=6-20200402
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature

View File

@ -1,24 +0,0 @@
-- Copyright 2018-2019 Lienol <lawlienol@gmail.com>
module("luci.controller.ipsec-server", package.seeall)
function index()
if not nixio.fs.access("/etc/config/ipsec") then return end
entry({"admin", "vpn"}, firstchild(), "VPN", 45).dependent = false
entry({"admin", "vpn", "ipsec-server"},
alias("admin", "vpn", "ipsec-server", "settings"),
_("IPSec VPN Server"), 49).dependent = false
entry({"admin", "vpn", "ipsec-server", "settings"},
cbi("ipsec-server/settings"), _("General Settings"), 10).leaf = true
entry({"admin", "vpn", "ipsec-server", "users"}, cbi("ipsec-server/users"),
_("Users Manager"), 20).leaf = true
entry({"admin", "vpn", "ipsec-server", "status"}, call("status")).leaf =
true
end
function status()
local e = {}
e.status = luci.sys.call("/usr/bin/pgrep ipsec > /dev/null") == 0
luci.http.prepare_content("application/json")
luci.http.write_json(e)
end

View File

@ -1,105 +0,0 @@
local s = require "luci.sys"
local net = require"luci.model.network".init()
local ifaces = s.net:devices()
local m, s, o
mp = Map("ipsec", translate("IPSec VPN Server"))
mp.description = translate(
"IPSec VPN connectivity using the native built-in VPN Client on iOS or Andriod (IKEv1 with PSK and Xauth)")
mp.template = "ipsec-server/index"
s = mp:section(TypedSection, "service")
s.anonymous = true
o = s:option(DummyValue, "ipsec-server_status", translate("Current Condition"))
o.template = "ipsec-server/status"
enabled = s:option(Flag, "enabled", translate("Enable"))
enabled.default = 0
enabled.rmempty = false
clientip = s:option(Value, "clientip", translate("VPN Client IP"))
clientip.datatype = "ip4addr"
clientip.description = translate(
"VPN Client reserved started IP addresses with the same subnet mask")
clientip.optional = false
clientip.rmempty = false
--[[
clientdns = s:option(Value, "clientdns", translate("VPN Client DNS"))
clientdns.datatype = "ip4addr"
clientdns.description = translate("DNS using in VPN tunnel.")
clientdns.optional = false
clientdns.rmempty = false
]]--
secret = s:option(Value, "secret", translate("Secret Pre-Shared Key"))
secret.password = true
function mp.on_save(self)
require "luci.model.uci"
require "luci.sys"
local have_ike_rule = false
local have_ipsec_rule = false
local have_ah_rule = false
local have_esp_rule = false
luci.model.uci.cursor():foreach('firewall', 'rule', function(section)
if section.name == 'ike' then have_ike_rule = true end
if section.name == 'ipsec' then have_ipsec_rule = true end
if section.name == 'ah' then have_ah_rule = true end
if section.name == 'esp' then have_esp_rule = true end
end)
if not have_ike_rule then
local cursor = luci.model.uci.cursor()
local ike_rulename = cursor:add('firewall', 'rule')
cursor:tset('firewall', ike_rulename, {
['name'] = 'ike',
['target'] = 'ACCEPT',
['src'] = 'wan',
['proto'] = 'udp',
['dest_port'] = 500
})
cursor:save('firewall')
cursor:commit('firewall')
end
if not have_ipsec_rule then
local cursor = luci.model.uci.cursor()
local ipsec_rulename = cursor:add('firewall', 'rule')
cursor:tset('firewall', ipsec_rulename, {
['name'] = 'ipsec',
['target'] = 'ACCEPT',
['src'] = 'wan',
['proto'] = 'udp',
['dest_port'] = 4500
})
cursor:save('firewall')
cursor:commit('firewall')
end
if not have_ah_rule then
local cursor = luci.model.uci.cursor()
local ah_rulename = cursor:add('firewall', 'rule')
cursor:tset('firewall', ah_rulename, {
['name'] = 'ah',
['target'] = 'ACCEPT',
['src'] = 'wan',
['proto'] = 'ah'
})
cursor:save('firewall')
cursor:commit('firewall')
end
if not have_esp_rule then
local cursor = luci.model.uci.cursor()
local esp_rulename = cursor:add('firewall', 'rule')
cursor:tset('firewall', esp_rulename, {
['name'] = 'esp',
['target'] = 'ACCEPT',
['src'] = 'wan',
['proto'] = 'esp'
})
cursor:save('firewall')
cursor:commit('firewall')
end
end
return mp

View File

@ -1,18 +0,0 @@
mp = Map("ipsec", translate("IPSec VPN Server"))
mp.description = translate(
"IPSec VPN connectivity using the native built-in VPN Client on iOS or Andriod (IKEv1 with PSK and Xauth)")
s = mp:section(TypedSection, "users", translate("Users Manager"))
s.addremove = true
s.anonymous = true
s.template = "cbi/tblsection"
enabled = s:option(Flag, "enabled", translate("Enabled"))
enabled.rmempty = false
username = s:option(Value, "username", translate("User name"))
username.placeholder = translate("User name")
username.rmempty = true
password = s:option(Value, "password", translate("Password"))
password.rmempty = true
return mp

View File

@ -1,13 +0,0 @@
<% include("cbi/map") %>
<script type="text/javascript">//<![CDATA[
XHR.poll(2, '<%=luci.dispatcher.build_url("admin", "vpn", "ipsec-server", "status")%>', null,
function(x, result)
{
var status = document.getElementsByClassName('ipsec-server_status')[0];
status.setAttribute("style","font-weight:bold;");
status.setAttribute("color",result.status ? "green":"red");
status.innerHTML = result.status?'<%=translate("RUNNING")%>':'<%=translate("NOT RUNNING")%>';
}
)
//]]>
</script>

View File

@ -1,3 +0,0 @@
<%+cbi/valueheader%>
<font class="ipsec-server_status"><%=pcdata(self:cfgvalue(section) or self.default or "")%></font>
<%+cbi/valuefooter%>

View File

@ -1,50 +0,0 @@
msgid "IPSec VPN Server"
msgstr "IPSec VPN 服务器"
msgid "IPSec VPN connectivity using the native built-in VPN Client on iOS or Andriod (IKEv1 with PSK and Xauth)"
msgstr "使用iOS 或者 Andriod (IKEv1 with PSK and Xauth) 原生内置 IPSec VPN 客户端进行连接"
msgid "IPSec VPN Server status"
msgstr "IPSec VPN 服务器运行状态"
msgid "Current Condition"
msgstr "当前状态"
msgid "General settings"
msgstr "基本设置"
msgid "VPN Client IP"
msgstr "VPN客户端地址段"
msgid "VPN Client reserved started IP addresses with the same subnet mask"
msgstr "VPN客户端获取IP的起始地址例如 192.168.100.10/24"
msgid "VPN Client DNS"
msgstr "VPN客户端DNS服务器"
msgid "DNS using in VPN tunnel."
msgstr "指定VPN客户端的DNS地址。"
msgid "Secret Pre-Shared Key"
msgstr "PSK密钥"
msgid "is_nat"
msgstr "NAT转发"
msgid "Interface"
msgstr "接口"
msgid "Specify interface forwarding traffic."
msgstr "指定接口转发流量。"
msgid "Disable from startup"
msgstr "禁止开机启动"
msgid "Enable on startup"
msgstr "允许开机启动"
msgid "NOT RUNNING"
msgstr "未运行"
msgid "RUNNING"
msgstr "运行中"

View File

@ -1,12 +0,0 @@
config service 'ipsec'
option enabled '0'
option secret 'ipsec'
option clientip '192.168.100.10/24'
option clientdns '223.5.5.5'
config users
option enabled '1'
option username 'guest'
option password '123456'

View File

@ -1,68 +0,0 @@
#!/bin/sh /etc/rc.common
START=99
IPSEC_SECRETS_FILE=/etc/ipsec.secrets
IPSEC_CONN_FILE=/etc/ipsec.conf
setup_login() {
config_get enabled $1 enabled
[ "$enabled" -eq 0 ] && return 0
config_get username $1 username
config_get password $1 password
[ -n "$username" ] || return 0
[ -n "$password" ] || return 0
echo "$username : XAUTH '$password'" >> $IPSEC_SECRETS_FILE
}
start() {
local vt_enabled=$(uci -q get ipsec.@service[0].enabled)
[ "$vt_enabled" = 0 ] && return 1
local vt_clientip=$(uci -q get ipsec.@service[0].clientip)
local vt_clientdns=$(uci -q get ipsec.@service[0].clientdns)
[ -z "$vt_clientdns" ] && local vt_clientdns="8.8.4.4"
local vt_secret=$(uci -q get ipsec.@service[0].secret)
cat > $IPSEC_CONN_FILE <<EOF
# ipsec.conf - strongSwan IPsec configuration file
# basic configuration
config setup
# strictcrlpolicy=yes
uniqueids=never
# Add connections here.
conn xauth_psk
keyexchange=ikev1
ike=aes128-sha1-modp2048,aes128-sha1-modp1024,3des-sha1-modp1024,3des-sha1-modp1536
esp=aes128-sha1,3des-sha1
left=%defaultroute
leftauth=psk
leftsubnet=0.0.0.0/0
right=%any
rightauth=psk
rightauth2=xauth
rightsourceip=$vt_clientip
rightdns=$vt_clientdns
auto=add
EOF
cat > /etc/ipsec.secrets <<EOF
# /etc/ipsec.secrets - strongSwan IPsec secrets file
: PSK "$vt_secret"
EOF
config_load ipsec
config_foreach setup_login users
/usr/lib/ipsec/starter --daemon charon --nofork > /dev/null 2>&1 &
fw3 -q reload 2>&1 &
}
stop() {
ps -w | grep "/usr/lib/ipsec" | grep -v "grep" | awk '{print $1}' | xargs kill -9 >/dev/null 2>&1
fw3 -q reload 2>&1
}

View File

@ -1,23 +0,0 @@
#!/bin/sh
uci -q batch <<-EOF >/dev/null
delete firewall.ipsecvpn
set firewall.ipsecvpn=include
set firewall.ipsecvpn.type=script
set firewall.ipsecvpn.path=/usr/share/ipsecvpn/firewall.include
set firewall.ipsecvpn.reload=1
EOF
uci -q batch <<-EOF >/dev/null
delete ucitrack.@ipsec[-1]
add ucitrack ipsec
set ucitrack.@ipsec[-1].init=ipsecvpn
commit ucitrack
EOF
/etc/init.d/ipsec disable && /etc/init.d/ipsec stop
rm -f /etc/init.d/ipsec
chmod a+x /usr/share/ipsecvpn/* >/dev/null 2>&1
rm -rf /tmp/luci-*cache
exit 0

View File

@ -1,39 +0,0 @@
#!/bin/sh
iptables -D INPUT -p udp -m multiport --dports 500,4500 -m comment --comment "IPSec VPN Server" -j ACCEPT 2> /dev/null
ipsec_nums=$(iptables -t nat -n -L POSTROUTING 2>/dev/null | grep -c "IPSec VPN Server")
if [ -n "$ipsec_nums" ]; then
until [ "$ipsec_nums" = 0 ]
do
rules=$(iptables -t nat -n -L POSTROUTING --line-num 2>/dev/null | grep "IPSec VPN Server" | awk '{print $1}')
for rule in $rules
do
iptables -t nat -D POSTROUTING $rule 2> /dev/null
break
done
ipsec_nums=$(expr $ipsec_nums - 1)
done
fi
nums=$(iptables -n -L forwarding_rule 2>/dev/null | grep -c "IPSec VPN Server")
if [ -n "$nums" ]; then
until [ "$nums" = 0 ]
do
rules=$(iptables -n -L forwarding_rule --line-num 2>/dev/null | grep "IPSec VPN Server" | awk '{print $1}')
for rule in $rules
do
iptables -D forwarding_rule $rule 2> /dev/null
break
done
nums=$(expr $nums - 1)
done
fi
enable=$(uci -q get ipsec.ipsec.enabled)
if [ -n "$enable" -a "$enable" == 1 ]; then
clientip=$(uci -q get ipsec.ipsec.clientip)
iptables -t nat -I POSTROUTING -s ${clientip%.*}.0/24 -m comment --comment "IPSec VPN Server" -j MASQUERADE
iptables -I forwarding_rule -s ${clientip%.*}.0/24 -m comment --comment "IPSec VPN Server" -j ACCEPT
iptables -I forwarding_rule -m policy --dir in --pol ipsec --proto esp -m comment --comment "IPSec VPN Server" -j ACCEPT
iptables -I forwarding_rule -m policy --dir out --pol ipsec --proto esp -m comment --comment "IPSec VPN Server" -j ACCEPT
iptables -I INPUT -p udp -m multiport --dports 500,4500 -m comment --comment "IPSec VPN Server" -j ACCEPT
fi

View File

@ -1,98 +0,0 @@
# Copyright (C) 2020 Lienol <lawlienol@gmail.com>
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-nginx-pingos
PKG_VERSION:=1.19.6
PKG_RELEASE:=1
PKG_SOURCE:=nginx-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://nginx.org/download/
PKG_HASH:=b11195a02b1d3285ddf2987e02c6b6d28df41bb1b1dd25f33542848ef4fc33b5
PKG_DIR:=$(BUILD_DIR)/$(PKG_NAME)
PKG_BUILD_DIR:=$(PKG_DIR)/nginx-$(PKG_VERSION)
PKG_FIXUP:=autoreconf
PKG_BUILD_PARALLEL:=1
PKG_INSTALL:=1
include $(INCLUDE_DIR)/package.mk
define Package/$(PKG_NAME)
CATEGORY:=LuCI
SUBMENU:=3. Applications
TITLE:=PingOS server
PKGARCH:=all
URL:=https://pingos.io/
DEPENDS:=+libpcre +libopenssl +zlib +libpthread
endef
define Package/$(PKG_NAME)/conffiles
endef
define Build/Prepare
rm -r $(PKG_BUILD_DIR)
mkdir -p $(PKG_BUILD_DIR)
tar -zxvf $(DL_DIR)/$(PKG_SOURCE) -C $(PKG_DIR)
$(CP) -pR ./modules $(PKG_BUILD_DIR)/modules
$(call Build/Prepare/Default,)
endef
ADDITIONAL_MODULES:= --with-http_ssl_module \
--add-module=./modules/nginx-rtmp-module \
--add-module=./modules/nginx-client-module \
--add-module=./modules/nginx-multiport-module \
--add-module=./modules/nginx-toolkit-module
TARGET_CFLAGS += -fvisibility=hidden -ffunction-sections -fdata-sections -DNGX_LUA_NO_BY_LUA_BLOCK
TARGET_LDFLAGS += -Wl,--gc-sections
CONFIGURE_ARGS += \
--crossbuild=Linux::$(ARCH) \
--prefix=/usr \
--conf-path=/usr/share/pingos/conf/nginx.conf \
$(ADDITIONAL_MODULES) \
--error-log-path=/var/etc/pingos/error.log \
--pid-path=/var/etc/pingos/pingos.pid \
--lock-path=/var/etc/pingos/pingos.lock \
--http-log-path=/var/etc/pingos/access.log \
--http-client-body-temp-path=/var/etc/pingos/lib/body \
--http-proxy-temp-path=/var/etc/pingos/lib/proxy \
--with-cc="$(TARGET_CC)" \
--with-cc-opt="$(TARGET_CPPFLAGS) $(TARGET_CFLAGS)" \
--with-ld-opt="$(TARGET_LDFLAGS)" \
--without-http_upstream_zone_module
define Package/$(PKG_NAME)/install
$(INSTALL_DIR) $(1)/etc
$(INSTALL_CONF) ./root/etc/pingos.template $(1)/etc/pingos.template
$(INSTALL_DIR) $(1)/etc/config
$(INSTALL_CONF) ./root/etc/config/pingos $(1)/etc/config/pingos
$(INSTALL_DIR) $(1)/etc/init.d
$(INSTALL_BIN) ./root/etc/init.d/pingos $(1)/etc/init.d/pingos
$(INSTALL_DIR) $(1)/etc/uci-defaults
$(INSTALL_CONF) ./root/etc/uci-defaults/* $(1)/etc/uci-defaults
$(INSTALL_DIR) $(1)/usr/share/pingos
cp -pR $(PKG_INSTALL_DIR)/usr/share/pingos/conf $(1)/usr/share/pingos
$(INSTALL_DATA) ./root/resource/conf-template/nginx.conf $(1)/usr/share/pingos/conf/nginx.conf
cp -pR $(PKG_INSTALL_DIR)/usr/html $(1)/usr/share/pingos/html
$(INSTALL_DATA) ./root/resource/crossdomain.xml $(1)/usr/share/pingos/html/crossdomain.xml
$(INSTALL_DATA) ./root/resource/stat.xsl $(1)/usr/share/pingos/html/stat.xsl
$(INSTALL_DIR) $(1)/usr/lib/lua/luci
cp -pR ./luasrc/* $(1)/usr/lib/lua/luci/
$(INSTALL_DIR) $(1)/usr/lib/lua/luci/i18n
po2lmo ./po/zh-cn/pingos.po $(1)/usr/lib/lua/luci/i18n/pingos.zh-cn.lmo
$(INSTALL_DIR) $(1)/usr/sbin
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/nginx $(1)/usr/sbin/pingos
endef
$(eval $(call BuildPackage,$(PKG_NAME)))

View File

@ -1,17 +0,0 @@
-- Copyright 2020 Lienol <lawlienol@gmail.com>
module("luci.controller.pingos", package.seeall)
function index()
if not nixio.fs.access("/etc/config/pingos") then return end
entry({"admin", "nas"}, firstchild(), "NAS", 44).dependent = false
entry({"admin", "nas", "pingos"}, cbi("pingos"), _("PingOS"), 3).dependent = true
entry({"admin", "nas", "pingos", "status"}, call("act_status")).leaf = true
end
function act_status()
local e = {}
e.status = luci.sys.call("ps -w | grep pingos | grep nginx | grep -v grep > /dev/null") == 0
luci.http.prepare_content("application/json")
luci.http.write_json(e)
end

View File

@ -1,68 +0,0 @@
m = Map("pingos", translate("PingOS"))
m:append(Template("pingos/status"))
s = m:section(TypedSection, "global")
s.anonymous = true
s.addremove = false
s:tab("global", translate("Global Settings"))
s:tab("template", translate("Edit Template"))
nginx = s:taboption("template", Value, "_nginx", translatef("Edit the template that is used for generating the %s configuration.", "nginx"),
translatef("This is the content of the file '%s'", "/etc/pingos.template") .. "<br />" ..
translatef("Values enclosed by pipe symbols ('|') should not be changed. They get their values from the '%s' tab.", translate("Global Settings")))
nginx.template = "cbi/tvalue"
nginx.rows = 30
function nginx.cfgvalue(self, section)
return nixio.fs.readfile("/etc/pingos.template")
end
function nginx.write(self, section, value)
value = value:gsub("\r\n?", "\n")
nixio.fs.writefile("/etc/pingos.template", value)
end
o = s:taboption("global", Flag, "enable", translate("Enable"))
o.rmempty = false
o = s:taboption("global", Flag, "ipv6", translate("Listen IPv6"))
o.rmempty = false
o = s:taboption("global", Value, "http_port", "HTTP(S)" ..translate("Port"))
o.datatype = "port"
o.default = 8082
o.rmempty = false
o = s:taboption("global", Flag, "https", translate("HTTPS"))
o.rmempty = false
o = s:taboption("global", FileUpload, "certificate", translate("certificate"))
o:depends("https", 1)
o = s:taboption("global", FileUpload, "key", translate("key"))
o:depends("https", 1)
o = s:taboption("global", Value, "rtmp_port", "RTMP" ..translate("Port"))
o.datatype = "port"
o.default = 1935
o.rmempty = false
o = s:taboption("global", Flag, "hls", translate("HLS"))
o.rmempty = false
o = s:taboption("global", Flag, "hls2", translate("HLS2"))
o.rmempty = false
o = s:taboption("global", Flag, "ts_record", "TS " .. translate("Record"))
o.rmempty = false
o = s:taboption("global", Flag, "flv_record", "FLV " .. translate("Record"))
o.rmempty = false
o = s:taboption("global", Value, "record_path", translate("Record") .. translate("Path"))
o.default = "/tmp/record"
o:depends("ts_record", 1)
o:depends("flv_record", 1)
return m

View File

@ -1,52 +0,0 @@
<fieldset class="cbi-section">
<legend><%:Running Status%></legend>
<fieldset class="cbi-section">
<div class="cbi-value">
<label class="cbi-value-title">Nginx <%:Status%></label>
<div class="cbi-value-field" id="_nginx_status"><%:Collecting data...%></div>
</div>
<div class="cbi-value">
<label class="cbi-value-title"></label>
<div class="cbi-value-field">
<font color="red" id="tips"></font>
<ul id="tips2">
<li>rtmp rtmp://ip/live/<%:Stream name%></li>
<li>http(s)-flv http(s)://ip/flv/<%:Stream name%></li>
<li>hls http(s)://ip/hls/<%:Stream name%>.m3u8</li>
<li>hls+ http(s)://ip/hls2/<%:Stream name%>.m3u8</li>
<li>http(s)-ts http(s)://ip/ts/<%:Stream name%></li>
</ul>
</div>
</div>
</fieldset>
</fieldset>
<script type="text/javascript">//<![CDATA[
var nginx_status = document.getElementById('_nginx_status');
XHR.poll(3, '<%=url([[admin]], [[nas]], [[pingos]], [[status]])%>', null,
function(x, json) {
if (x && x.status == 200) {
if (nginx_status) {
var str = "";
if (json.status) {
document.getElementById("tips").innerHTML = '<%:If you need external network access, please open the port by yourself.%>';
str = '<font color="green"><%:RUNNING%> ✓</font><input type="button" class="cbi-button cbi-input-apply" value="<%:Enter interface%>" onclick="open_web()" />'
} else {
document.getElementById("tips").innerHTML = '';
str = '<font color="red"><%:NOT RUNNING%> X</font>';
}
nginx_status.innerHTML = str;
}
}
}
);
function open_web(){
var port = '<%=luci.sys.exec("uci -q get pingos.@global[0].http_port"):gsub("^%s*(.-)%s*$", "%1")%>';
var ishttps = '<%=luci.sys.exec("uci -q get pingos.@global[0].https"):gsub("^%s*(.-)%s*$", "%1")%>';
var protocol = (ishttps == "1") ? "https://" : "http://";
var hostname = location.hostname;
var url = protocol + hostname + ":" + port;
window.open(url, 'target', '');
}
//]]></script>

View File

@ -1,24 +0,0 @@
Copyright (C) 2016-2020, by Jie Wu "AlexWoo" <wj19840501@gmail.com>.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.

View File

@ -1,19 +0,0 @@
ngx_addon_name=ngx_client_module
CORE_MODULES="$CORE_MODULES \
ngx_client_module \
ngx_http_client_module \
"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS \
$ngx_addon_dir/ngx_client.c \
$ngx_addon_dir/ngx_http_client.c \
"
NGX_ADDON_DEPS="$NGX_ADDON_DEPS \
$ngx_addon_dir/ngx_client.h \
$ngx_addon_dir/ngx_http_client.h \
"
CFLAGS="$CFLAGS -I $ngx_addon_dir"

View File

@ -1,165 +0,0 @@
/*
* Copyright (C) AlexWoo(Wu Jie) wj19840501@gmail.com
*/
#ifndef _NGX_CLIENT_H_INCLUDED_
#define _NGX_CLIENT_H_INCLUDED_
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>
#include <ngx_http.h>
typedef struct ngx_client_session_s ngx_client_session_t;
typedef void (* ngx_client_connect_pt)(ngx_client_session_t *s);
typedef void (* ngx_client_recv_pt)(ngx_client_session_t *s);
typedef void (* ngx_client_send_pt)(ngx_client_session_t *s);
typedef void (* ngx_client_closed_pt)(ngx_client_session_t *s);
struct ngx_client_session_s {
ngx_peer_connection_t peer;
ngx_str_t server; /* server original address */
in_port_t port; /* server port */
ngx_connection_t *connection;
ngx_pool_t *pool;
ngx_log_t log;
void *data; /* save ctx for callback */
ngx_chain_t *out; /* save data unsend */
/* configured part */
/* timer for connecting to server */
ngx_msec_t connect_timeout;
/* timer for sending buffer full */
ngx_msec_t send_timeout;
/*
* data will be postponed until nginx has at least
* postpone_output bytes of data to send
*/
size_t postpone_output;
/* use dynamic resolver mechanism for resolving domain */
unsigned dynamic_resolver:1;
unsigned tcp_nodelay:1; /* TCP_NODELAY */
unsigned tcp_nopush:1; /* TCP_CORK */
/* runtime part */
size_t recv; /* client recv bytes */
unsigned connected:1; /* client connected to server */
unsigned closed:1; /* client has been closed */
ngx_event_t close; /* for async close */
/* callback */
ngx_client_connect_pt client_connected; /* connect successd */
ngx_client_recv_pt client_recv; /* recv msg from peer */
ngx_client_send_pt client_send; /* send msg to peer */
ngx_client_closed_pt client_closed; /* finalize connection */
};
/*
* create a client session
*
* return value:
* return client session for successd, return NULL for failed
* paras:
* peer: server address and port, address could be domain or ip
* local: set if need to bind local address, or set NULL
* udp: set 1, use udp, set 0, use tcp
* log: for logging error when create client session failed
*/
ngx_client_session_t *ngx_client_create(ngx_str_t *peer, ngx_str_t *local,
ngx_flag_t udp, ngx_log_t *log);
/*
* connect to client server, should use client session created by
* ngx_client_create. before connect to server, user can set paras in
* configured part.
*
* return value:
* void
* paras:
* s: client session created by ngx_client_create
*/
void ngx_client_connect(ngx_client_session_t *s);
/*
* send data to server
*
* return value:
* NGX_ERROR: write error, client session will be closed
* NGX_AGAIN: data not sent completely, it will save in client session out
* NGX_OK: data sent completely
* paras:
* s: client session
* out: data for sending
*/
ngx_int_t ngx_client_write(ngx_client_session_t *s, ngx_chain_t *out);
/*
* read data from server
*
* return value:
* NGX_ERROR: read error, client session will be closed
* NGX_DECLINED: buf for receiving data is full
* NGX_AGAIN: no data for reading
* 0: server closed
* >0: bytes read into buffer
* paras:
* s: client session
* b: buffer for receiving data
*/
ngx_int_t ngx_client_read(ngx_client_session_t *s, ngx_buf_t *b);
/*
* keepalive client connection, and destroy session
* if use client connect the same ip:port,
* new client session will reuse the connection
*
* return value:
* void
* paras:
* s: client session
*/
void ngx_client_set_keepalive(ngx_client_session_t *s);
/*
* close client session
*
* return value:
* void
* paras:
* s: client session
*/
void ngx_client_close(ngx_client_session_t *s);
/*
* paras:
* r: http request to query status of client
*/
ngx_chain_t *ngx_client_state(ngx_http_request_t *r, unsigned detail);
#endif

View File

@ -1,323 +0,0 @@
/*
* Copyright (C) AlexWoo(Wu Jie) wj19840501@gmail.com
*/
#ifndef _NGX_HTTP_CLIENT_H_INCLUDE_
#define _NGX_HTTP_CLIENT_H_INCLUDE_
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
#include "ngx_client.h"
#include "ngx_toolkit_misc.h"
// http client method
#define NGX_HTTP_CLIENT_GET 0
#define NGX_HTTP_CLIENT_HEAD 1
#define NGX_HTTP_CLIENT_POST 2
#define NGX_HTTP_CLIENT_PUT 3
#define NGX_HTTP_CLIENT_DELETE 4
#define NGX_HTTP_CLIENT_MKCOL 5
#define NGX_HTTP_CLIENT_COPY 6
#define NGX_HTTP_CLIENT_MOVE 7
#define NGX_HTTP_CLIENT_OPTIONS 8
#define NGX_HTTP_CLIENT_PROPFIND 9
#define NGX_HTTP_CLIENT_PROPPATCH 10
#define NGX_HTTP_CLIENT_LOCK 11
#define NGX_HTTP_CLIENT_UNLOCK 12
#define NGX_HTTP_CLIENT_PATCH 13
#define NGX_HTTP_CLIENT_TRACE 14
// http client version
#define NGX_HTTP_CLIENT_VERSION_9 0
#define NGX_HTTP_CLIENT_VERSION_10 1
#define NGX_HTTP_CLIENT_VERSION_11 2
#define NGX_HTTP_CLIENT_VERSION_20 3
// http client opt
#define NGX_HTTP_CLIENT_OPT_CONNECT_TIMEOUT 0
#define NGX_HTTP_CLIENT_OPT_SEND_TIMEOUT 1
#define NGX_HTTP_CLIENT_OPT_POSTPONE_OUTPUT 2
#define NGX_HTTP_CLIENT_OPT_DYNAMIC_RESOLVER 3
#define NGX_HTTP_CLIENT_OPT_TCP_NODELAY 4
#define NGX_HTTP_CLIENT_OPT_TCP_NOPUSH 5
#define NGX_HTTP_CLIENT_OPT_HEADER_TIMEOUT 6
typedef void (* ngx_http_client_handler_pt)(void *r, ngx_http_request_t *hcr);
/* create and set http request */
/*
* create a http request for sending to server
*
* return value:
* return http request for successd, return NULL for failed
*
* paras:
* log: error in create will use this log
* method: http client method
* url: full request url like "http://test.com/index.html?hello=world"
* headers: http request header for sending
* send_body: callback for sending body
* request: who send http request
*/
ngx_http_request_t *ngx_http_client_create(ngx_log_t *log,
ngx_uint_t method, ngx_str_t *url, ngx_keyval_t *headers,
ngx_http_client_handler_pt send_body, void *request);
/*
* add cleanup as ngx_http_cleanup_add
*/
ngx_http_cleanup_t *ngx_http_client_cleanup_add(ngx_http_request_t *r,
size_t size);
/*
* set read handler for http client, should set before send request,
* otherwise body from server will discard
*
* return value:
* void
*
* paras:
* r: http client request
* read_handler: handler for setting
*/
void ngx_http_client_set_read_handler(ngx_http_request_t *r,
ngx_http_client_handler_pt read_handler);
/*
* set http headers
*
* return value:
* NGX_OK for successd, NGX_ERROR for failed
*
* paras:
* r: http client request
* headers: headers set into r
* if value is not null, will set or modify the header
* if value is null string, will delete the header
*/
ngx_int_t ngx_http_client_set_headers(ngx_http_request_t *r,
ngx_keyval_t *headers);
/*
* set write handler for http client, if set,
* will use this handler for sending body,
*
* return value:
* void
*
* paras:
* r: http client request
* write_handler: handler for setting
*/
void ngx_http_client_set_write_handler(ngx_http_request_t *r,
ngx_http_client_handler_pt write_handler);
/*
* set write handler for http client, if set,
* will use this handler for sending body,
*
* return value:
* void
*
* paras:
* r: http client request
* write_handler: handler for setting
*/
void ngx_http_client_set_version(ngx_http_request_t *r, ngx_uint_t version);
/*
* set http client option
*
* return value:
* void
*
* paras:
* r: http client request
* opt: http client opt
* NGX_HTTP_CLIENT_OPT_CONNECT_TIMEOUT:
* connect server timer
* NGX_HTTP_CLIENT_OPT_SEND_TIMEOUT:
* send data timer when buffer full
* NGX_HTTP_CLIENT_OPT_POSTPONE_OUTPUT:
* size threshold to send
* NGX_HTTP_CLIENT_OPT_DYNAMIC_RESOLVER:
* whether use dynamic resolver to resolv domain
* NGX_HTTP_CLIENT_OPT_TCP_NODELAY:
* whether set TCP_NODELAY
* NGX_HTTP_CLIENT_OPT_TCP_NOPUSH:
* whether set TCP_CORK
* NGX_HTTP_CLIENT_OPT_HEADER_TIMEOUT:
* timer for waiting response header from server
* value: http client opt value want to set
*/
void ngx_http_client_setopt(ngx_http_request_t *r, unsigned opt,
ngx_uint_t value);
/* send http request */
/*
* send http request
*
* return value:
* NGX_OK for successd, NGX_ERROR for failed
*
* paras:
* r: http request for seding, create by ngx_http_client_create
*/
ngx_int_t ngx_http_client_send(ngx_http_request_t *r);
/*
* create and send http GET request to server
*
* return value:
* return http request for successd, return NULL for failed
*
* paras:
* log: error in create will use this log
* url: full request url like "http://test.com/index.html?hello=world"
* headers: http request header for sending
* request: who send http request
*/
ngx_http_request_t *ngx_http_client_get(ngx_log_t *log, ngx_str_t *url,
ngx_keyval_t *headers, void *request);
/*
* create and send http HEAD request to server
*
* return value:
* return http request for successd, return NULL for failed
*
* paras:
* log: error in create will use this log
* url: full request url like "http://test.com/index.html?hello=world"
* headers: http request header for sending
* request: who send http request
*/
ngx_http_request_t *ngx_http_client_head(ngx_log_t *log, ngx_str_t *url,
ngx_keyval_t *headers, void *request);
/*
* create and send http POST request to server
*
* return value:
* return http request for successd, return NULL for failed
*
* paras:
* log: error in create will use this log
* url: full request url like "http://test.com/index.html?hello=world"
* headers: http request header for sending
* send_body: callback for sending body
* request: who send http request
*/
ngx_http_request_t *ngx_http_client_post(ngx_log_t *log, ngx_str_t *url,
ngx_keyval_t *headers, ngx_http_client_handler_pt send_body, void *request);
/* get response */
/*
* get http response version
*
* return value:
* http response version
*
* paras:
* r: http client request
*/
ngx_uint_t ngx_http_client_http_version(ngx_http_request_t *r);
/*
* get http response status code
*
* return value:
* http response status code like 200, 500
*
* paras:
* r: http client request
*/
ngx_uint_t ngx_http_client_status_code(ngx_http_request_t *r);
/*
* get http response header's value
*
* return value:
* http response header's value
*
* paras:
* r: http client request
* key: http header like "Host", "Content-Type"
*/
ngx_str_t *ngx_http_client_header_in(ngx_http_request_t *r, ngx_str_t *key);
/*
* read http response body
*
* return value:
* NGX_AGAIN: read part of data
* 0: tcp connection disconnect, need finalize request with 1
* NGX_ERROR: tcp connection error disconnect, need finalize request with 1
* NGX_DONE: response body has been read, could finalize request with 0
*
* paras:
* r: http client request
* in: where read data put
*/
ngx_int_t ngx_http_client_read_body(ngx_http_request_t *r, ngx_chain_t **in);
/*
* get receive bytes
*
* return value:
* bytes receive from server
*
* paras:
* r: http client request
*/
off_t ngx_http_client_rbytes(ngx_http_request_t *r);
/*
* get send bytes
*
* return value:
* bytes send to server
*
* paras:
* r: http client request
*/
off_t ngx_http_client_wbytes(ngx_http_request_t *r);
/* end request */
/*
* detach http client request with it's creator,
* all read and write handler will not be triggered
*
* return value:
* bytes send to server
*
* paras:
* r: http client request
*/
void ngx_http_client_detach(ngx_http_request_t *r);
/*
* finalize http client request
*
* return value:
* void
*
* paras:
* r: http client request
* closed: set to 1, will close connection to server
* set to 0, will keep connection to server alive
*/
void ngx_http_client_finalize_request(ngx_http_request_t *r, ngx_flag_t closed);
#endif

View File

@ -1,13 +0,0 @@
ngx_addon_name=ngx_client_test_module
HTTP_MODULES="$HTTP_MODULES \
ngx_client_test_module \
ngx_http_client_test_module \
ngx_client_stat_module \
"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS
$ngx_addon_dir/ngx_client_test_module.c \
$ngx_addon_dir/ngx_http_client_test_module.c \
$ngx_addon_dir/ngx_client_stat_module.c \
"

View File

@ -1,57 +0,0 @@
user root;
worker_processes 4;
#error_log logs/error.log;
#error_log logs/error.log notice;
error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
resolver 192.168.84.254;
dynamic_refresh_interval 5m;
}
http {
include mime.types;
default_type application/octet-stream;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
server {
listen 80;
server_name localhost;
location / {
root html;
index index.html index.htm;
}
location /client_test {
client_test;
}
location /http_client_test {
http_client_test;
}
location /client_stat {
client_stat;
}
}
}

View File

@ -1,148 +0,0 @@
/*
* Copyright (C) AlexWoo(Wu Jie) wj19840501@gmail.com
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>
#include <ngx_http.h>
#include "ngx_client.h"
#include "ngx_rbuf.h"
#include "ngx_poold.h"
#include "ngx_timerd.h"
#include "ngx_event_timer_module.h"
#include "ngx_event_resolver.h"
#include "ngx_dynamic_resolver.h"
static char *ngx_client_stat(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static ngx_command_t ngx_client_stat_commands[] = {
{ ngx_string("client_stat"),
NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
ngx_client_stat,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_client_stat_module_ctx = {
NULL, /* preconfiguration */
NULL, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
NULL, /* create location configuration */
NULL /* merge location configuration */
};
ngx_module_t ngx_client_stat_module = {
NGX_MODULE_V1,
&ngx_client_stat_module_ctx, /* module context */
ngx_client_stat_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_int_t
ngx_client_stat_handler(ngx_http_request_t *r)
{
ngx_chain_t **ll, *out;
ngx_buf_t *b;
size_t len;
r->headers_out.status = NGX_HTTP_OK;
ngx_http_send_header(r);
ll = &out;
len = sizeof("--------------------------------------------------\n") - 1
+ sizeof("ngx_worker: ngx_process_slot: pid: \n") - 1
+ 3 * NGX_OFF_T_LEN;
*ll = ngx_alloc_chain_link(r->pool);
if (*ll == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
(*ll)->next = NULL;
b = ngx_create_temp_buf(r->pool, len);
if (b == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
(*ll)->buf = b;
b->last = ngx_snprintf(b->last, len,
"--------------------------------------------------\n"
"ngx_worker: %i ngx_process_slot: %i pid: %i\n",
ngx_worker, ngx_process_slot, ngx_pid);
if (*ll) {
ll = &(*ll)->next;
}
*ll = ngx_rbuf_state(r, 1);
if (*ll) {
ll = &(*ll)->next;
}
*ll = ngx_event_timer_state(r);
if (*ll) {
ll = &(*ll)->next;
}
*ll = ngx_event_resolver_state(r);
if (*ll) {
ll = &(*ll)->next;
}
*ll = ngx_dynamic_resolver_state(r);
if (*ll) {
ll = &(*ll)->next;
}
*ll = ngx_poold_state(r, 1);
if (*ll) {
ll = &(*ll)->next;
}
*ll = ngx_timerd_state(r, 1);
if (*ll) {
ll = &(*ll)->next;
}
*ll = ngx_client_state(r, 1);
(*ll)->buf->last_buf = 1;
return ngx_http_output_filter(r, out);
}
static char *
ngx_client_stat(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_core_loc_conf_t *clcf;
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
clcf->handler = ngx_client_stat_handler;
return NGX_CONF_OK;
}

View File

@ -1,188 +0,0 @@
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
#include "ngx_client.h"
static char *ngx_client_test(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static ngx_command_t ngx_client_test_commands[] = {
{ ngx_string("client_test"),
NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
ngx_client_test,
0,
0,
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_client_test_module_ctx = {
NULL, /* preconfiguration */
NULL, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
NULL, /* create location configuration */
NULL /* merge location configuration */
};
ngx_module_t ngx_client_test_module = {
NGX_MODULE_V1,
&ngx_client_test_module_ctx, /* module context */
ngx_client_test_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static void
ngx_client_test_connected(ngx_client_session_t *s)
{
ngx_buf_t *b;
size_t len;
ngx_chain_t out;
ngx_http_request_t *r;
ngx_event_t *wev;
ngx_log_error(NGX_LOG_ERR, &s->log, 0, "client connected");
r = s->data;
wev = s->peer.connection->write;
len = sizeof("nginx client test\n") - 1;
b = ngx_create_temp_buf(s->pool, len);
if (b == NULL) {
ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
b->last = ngx_copy(b->last, "nginx client test\n", len);
b->last_buf = 1;
out.buf = b;
out.next = NULL;
ngx_client_write(s, &out);
ngx_handle_write_event(wev, 0);
}
static void
ngx_client_test_recv(ngx_client_session_t *s)
{
ngx_buf_t *b;
ngx_int_t n;
ngx_connection_t *c;
ngx_str_t recv;
ngx_http_request_t *r;
c = s->peer.connection;
r = s->data;
b = ngx_create_temp_buf(s->pool, 4096);
if (b == NULL) {
ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
n = c->recv(c, b->pos, b->end - b->last);
if (n == NGX_AGAIN) {
ngx_log_error(NGX_LOG_ERR, &s->log, 0, "client recv NGX_AGAIN");
return;
}
if (n == NGX_ERROR || n == 0) {
ngx_log_error(NGX_LOG_ERR, &s->log, 0, "client recv NGX_ERROR");
ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
ngx_client_close(s);
return;
}
b->last += n;
recv.data = b->pos;
recv.len = b->last - b->pos;
ngx_log_error(NGX_LOG_ERR, &s->log, 0, "client recv %d: %V, %z",
n, &recv, recv.len);
ngx_http_finalize_request(r, NGX_HTTP_FORBIDDEN);
ngx_client_set_keepalive(s);
return;
}
static void
ngx_client_test_send(ngx_client_session_t *s)
{
ngx_log_error(NGX_LOG_ERR, &s->log, 0, "client send");
}
static void
ngx_client_test_closed(ngx_client_session_t *s)
{
ngx_log_error(NGX_LOG_ERR, &s->log, 0, "client closed");
}
static ngx_int_t
ngx_client_test_handler(ngx_http_request_t *r)
{
ngx_client_session_t *s;
ngx_str_t echo;
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "client test handler");
if (ngx_http_arg(r, (u_char *) "echo", sizeof("echo") - 1, &echo)
!= NGX_OK)
{
return NGX_HTTP_BAD_REQUEST;
}
s = ngx_client_create(&echo, NULL, 0, r->connection->log);
if (s == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
//ci->dynamic_resolver = 0;
//ci->recvbuf = 4096;
s->client_connected = ngx_client_test_connected;
s->client_recv = ngx_client_test_recv;
s->client_send = ngx_client_test_send;
s->client_closed = ngx_client_test_closed;
s->data = r;
ngx_client_connect(s);
++r->count;
return NGX_DONE;
}
static char *
ngx_client_test(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_core_loc_conf_t *clcf;
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
clcf->handler = ngx_client_test_handler;
return NGX_CONF_OK;
}

View File

@ -1,203 +0,0 @@
/*
* Copyright (C) AlexWoo(Wu Jie) wj19840501@gmail.com
*/
#include "ngx_http_client.h"
#include "ngx_rbuf.h"
static char *ngx_http_client_test(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static ngx_command_t ngx_http_client_test_commands[] = {
{ ngx_string("http_client_test"),
NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
ngx_http_client_test,
0,
0,
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_http_client_test_module_ctx = {
NULL, /* preconfiguration */
NULL, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
NULL, /* create location configuration */
NULL /* merge location configuration */
};
ngx_module_t ngx_http_client_test_module = {
NGX_MODULE_V1,
&ngx_http_client_test_module_ctx, /* module context */
ngx_http_client_test_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static void
ngx_http_client_test_recv_body(void *request, ngx_http_request_t *hcr)
{
ngx_http_request_t *r;
ngx_chain_t *cl = NULL;
ngx_chain_t **ll;
ngx_int_t rc;
r = request;
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"http client test recv body");
rc = ngx_http_client_read_body(hcr, &cl);
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"http client test recv body, rc %i %i, %O",
rc, ngx_errno, ngx_http_client_rbytes(hcr));
if (rc == 0) {
goto done;
}
if (rc == NGX_ERROR) {
goto done;
}
if (rc == NGX_DONE) {
for (ll = &cl; (*ll)->next; ll = &(*ll)->next);
(*ll)->buf->last_buf = 1;
}
ngx_http_output_filter(r, cl);
ngx_http_run_posted_requests(r->connection);
if (rc == NGX_AGAIN) {
return;
}
if (rc == NGX_DONE) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"all body has been read");
ngx_http_client_finalize_request(hcr, 0);
}
done:
ngx_http_finalize_request(r, NGX_OK);
}
static void
ngx_http_client_test_recv(void *request, ngx_http_request_t *hcr)
{
ngx_http_request_t *r;
static ngx_str_t content_type = ngx_string("Content-Type");
static ngx_str_t connection = ngx_string("Connection");
static ngx_str_t unknown = ngx_string("Unknown");
ngx_str_t *ct, *con;
r = request;
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"http client test recv, connection: %p", hcr->connection);
r->headers_out.status = 200;
ngx_http_client_set_read_handler(hcr, ngx_http_client_test_recv_body);
ngx_http_send_header(r);
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"status_code: %ui http_version: %ui",
ngx_http_client_status_code(hcr),
ngx_http_client_http_version(hcr));
ct = ngx_http_client_header_in(hcr, &content_type);
con = ngx_http_client_header_in(hcr, &connection);
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "Content-Type: %V", ct);
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "Connection: %V", con);
if (ngx_http_client_header_in(hcr, &unknown) == NULL) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "no header Unknown");
}
ngx_http_client_test_recv_body(request, hcr);
}
static ngx_int_t
ngx_http_client_test_handler(ngx_http_request_t *r)
{
ngx_http_request_t *hcr;
static ngx_str_t request_url = ngx_string("http://101.200.241.232/");
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"http client test handler");
// Default header Host, User-Agent, Connection(below HTTP/1.1), Accept, Date
hcr = ngx_http_client_create(r->connection->log, NGX_HTTP_CLIENT_GET,
&request_url, NULL, NULL, r);
// add Connection, delete Date, Modify Host, add new header
ngx_str_t value;
value.data = (u_char *) "World";
value.len = sizeof("World") - 1;
ngx_keyval_t headers[] = {
{ ngx_string("Host"), ngx_string("www.test.com") },
{ ngx_string("Connection"), ngx_string("upgrade") },
{ ngx_string("Date"), ngx_null_string },
{ ngx_string("Hello"), value },
{ ngx_null_string, ngx_null_string } // must end with null str
};
ngx_http_client_set_headers(hcr, headers);
ngx_http_client_set_read_handler(hcr, ngx_http_client_test_recv);
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"http client test before send");
ngx_http_client_send(hcr);
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"http client test after send");
// ngx_http_client_detach(hcr);
// return NGX_HTTP_FORBIDDEN;
++r->count;
return NGX_DONE;
}
static char *
ngx_http_client_test(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_core_loc_conf_t *clcf;
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
clcf->handler = ngx_http_client_test_handler;
return NGX_CONF_OK;
}

View File

@ -1,41 +0,0 @@
package main
import (
"fmt"
"net"
)
func handleConnection(c net.Conn) {
b := make([]byte, 4096)
for {
n, err := c.Read(b)
if err != nil {
fmt.Print("Read Error ", err)
c.Close()
return
}
fmt.Print("recv ", n, " data:", string(b))
c.Write(b[0:n])
}
}
func main() {
ln, err := net.Listen("tcp", ":10000")
if err != nil {
fmt.Print("Listen Error ", err)
return
}
for {
conn, err := ln.Accept()
if err != nil {
fmt.Print("Accept Error ", err)
continue
}
go handleConnection(conn)
}
}

View File

@ -1,24 +0,0 @@
Copyright (C) 2016-2020, by Jie Wu "AlexWoo" <wj19840501@gmail.com>.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.

View File

@ -1,181 +0,0 @@
# Module nginx-multiport-module
---
## Instructions
Every worker process can bind own port, user can visit specific worker process by using the port.
- [ngx-stream-zone-module](doc/ngx-stream-zone-module.md)
Record stream's owner worker process slot
- [ngx-http-broadcast-module](doc/ngx-http-broadcast-module.md)
Broadcast HTTP request to all worker processes when receive HTTP request
## Directives
### multi\_listen
Syntax : multi_listen multiport relationport;
Default : None;
Context : events
multiport can configured as below:
address:port
port
unix:path
when configured with IPv4 or IPv6 port, worker process listen port plus with worker process's slot. For Example, we start four workers, add configured multiport with 9000. worker 0 will listen 9000, worker 1 will listen 9001, worker 2 will listen 9002, worker 3 will listen 9003
when configured with unix path, worker will listen path plus with suffix of worker process's slot. For Example, we start four workers, add configured multiport with unix:/tmp/http. worker 0 will listen /tmp/http.0, worker 1 will listen /tmp/http.1, worker 2 will listen /tmp/http.2, worker 3 will listen /tmp/http.3
relationport must configured same as listen directives in http server, rtmp server, stream server or other server
### inner\_proxy
Syntax : inner_proxy multiport uri;
Default : None;
Context : http, server, location
- multiport: configured in multi_listen
- uri: uri for inner_proxy, configured as below
location /multiport_test/ {
inner_proxy unix:/tmp/http.sock.80 /inner_proxy;
multiport_test;
}
location /inner_proxy/ {
rewrite ^/inner_proxy/(.*):/(.*) /$2 break;
proxy_pass http://$1:;
}
As example above, if send subrequest to process whose workerid is 0, the uri will change to /inner_proxy/unix:/tmp/http.sock.80.0:/multiport_test/xxx
proxy_pass will send current request to process 0 as inner proxy request.
## API
- ngx\_multiport\_get\_port
ngx_int_t ngx_event_multiport_get_port(ngx_pool_t *pool, ngx_str_t *port, ngx_str_t *multiport, ngx_int_t pslot);
- para:
pool: pool for port memory alloc
port: process real listen port while process\_slot is pslot
multiport: port configure for processes, format as below:
port only: port
IPv4: host:port host must be ipaddr of IPv4 or *
IPv6: [host]:port host must be ipaddr of IPv6
Unix: unix:/path
pslot: process\_slot, process\_slot of other worker process can get through ngx\_process\_slot\_get\_slot
- return value:
NGX\_OK for successd, NGX\_ERROR for failed
- ngx\_multiport\_get\_slot
ngx_int_t ngx_multiport_get_slot(ngx_uint_t wpid);
- para:
wpid: worker process id, 0 to ccf->worker_processes - 1
- return value:
ngx_process_slot for successd, NGX_ERROR for failed
- ngx\_http\_inner\_proxy\_request
ngx_int_t ngx_http_inner_proxy_request(ngx_http_request_t *r, ngx_int_t pslot);
send a inner proxy request to specific process, must use with directives inner\_proxy
- paras:
- r: http request for send inner request to sibling worker
- pslot: sibling worker ngx_process_slot
- return values:
- NGX_OK: for successd
- NGX_ERROR: for failed
- NGX_DECLINED: for not configured or send inner proxy to self
## Build
cd to NGINX source directory & run this:
./configure --add-module=/path/to/nginx-multiport-module/
make && make install
## Example
See t/ngx\_http\_process\_slot\_test\_module.c as reference
**Build**:
./configure --with-debug --with-ipv6 --add-module=/path/to/nginx-multiport-module/t/ --add-module=/path/to/nginx-multiport-module/ --add-module=/path/to/echo-nginx-module/
make && make install
**Configure**:
worker_processes 4;
events {
...
multi_listen 9000 80;
multi_listen unix:/tmp/http.sock.80 80;
}
http {
...
server {
...
location /multiport_test/ {
inner_proxy unix:/tmp/http.sock.80 /inner_proxy;
multiport_test;
}
location /inner_proxy/ {
rewrite ^/inner_proxy/(.*):/(.*) /$2 break;
proxy_pass http://$1:;
}
}
}
**Test for API**:
$ curl http://192.168.84.254/multiport_test/123
TEST cases 19, 19 pass
If request send to worker1 to worker3, the request will proxy to worker 0. will get log as below:
2017/10/14 20:45:44 [error] 20065#0: *6 multiport test handler, client: 192.168.84.1, server: localhost, request: "GET /multiport_test/123 HTTP/1.1", host: "192.168.84.254:9003"
2017/10/14 20:45:44 [error] 20065#0: *6 inner proxy return 0, client: 192.168.84.1, server: localhost, request: "GET /multiport_test/123 HTTP/1.1", host: "192.168.84.254:9003"
2017/10/14 20:45:44 [error] 20062#0: *8 multiport test handler, client: unix:, server: localhost, request: "GET //multiport_test/123 HTTP/1.0", host: "localhost"
**Test for multiport**:
curl -v http://127.0.0.1/
curl -v http://127.0.0.1:9000/
curl -v http://127.0.0.1:9001/
curl -v http://127.0.0.1:9002/
curl -v http://127.0.0.1:9003/
curl -v --unix-socket /tmp/http.sock.80.0 http:/
curl -v --unix-socket /tmp/http.sock.80.1 http:/
curl -v --unix-socket /tmp/http.sock.80.2 http:/
curl -v --unix-socket /tmp/http.sock.80.3 http:/
Tests will get the same result, for port 9000 will always send to worker process 0, 9001 to worker process 1 and so on

View File

@ -1,31 +0,0 @@
ngx_addon_name=ngx_multiport_module
EVENT_MODULES="$EVENT_MODULES \
ngx_event_multiport_module \
"
CORE_MODULES="$CORE_MODULES \
ngx_process_slot_module \
ngx_stream_zone_module \
"
HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES \
ngx_http_broadcast_module \
ngx_http_inner_proxy_module \
"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS \
$ngx_addon_dir/ngx_multiport_misc.c \
$ngx_addon_dir/ngx_event_multiport_module.c \
$ngx_addon_dir/ngx_process_slot_module.c \
$ngx_addon_dir/ngx_stream_zone_module.c \
$ngx_addon_dir/ngx_http_broadcast_module.c \
$ngx_addon_dir/ngx_http_inner_proxy_module.c \
"
NGX_ADDON_DEPS="$NGX_ADDON_DEPS \
$ngx_addon_dir/ngx_multiport.h \
$ngx_addon_dir/ngx_stream_zone_module.h \
"
CFLAGS="$CFLAGS -I $ngx_addon_dir"

View File

@ -1,77 +0,0 @@
# ngx-http-broadcast-module
---
## Instructions
Broadcast HTTP request to all worker processes when receive HTTP request
## Directives
### broadcast
Syntax : broadcast multiport uri;
Default : None;
Context : location
- multiport is multi_listen port configured in event
- uri is http proxy_pass uri configured as below
location /auth_proxy/ {
rewrite ^/auth_proxy/(.*) /auth break;
proxy_pass http://$1:;
}
## Build
cd to NGINX source directory & run this:
./configure --add-module=/path/to/nginx-multiport-module/
make && make install
## Example
**Build**:
./configure --with-debug --with-ipv6 --add-module=/path/to/nginx-multiport-module/t/ --add-module=/path/to/nginx-multiport-module/ --add-module=/path/to/echo-nginx-module/
make && make install
**Configure**:
events {
...
multi_listen unix:/tmp/http.sock.80 80;
}
http {
...
server {
listen 80;
server_name localhost;
...
location / {
broadcast unix:/tmp/http.sock.80 /auth_proxy;
}
location /auth_proxy/ {
rewrite ^/auth_proxy/(.*) /auth break;
proxy_pass http://$1:;
}
location /auth {
# return 403;
echo "auth";
echo $scheme://$host$uri?$args;
}
}
}
**Test**:
curl -v 'http://192.168.84.254/aa?a=b&c=d'
curl will get all response content if worker not return non 200 response

View File

@ -1,92 +0,0 @@
# ngx-stream-zone-module
---
## Instructions
Record stream's owner worker process slot
## Directives
### stream\_zone
Syntax : stream_zone buckets=$nbuckets streams=$nstreams;
Default : None;
Context : main
nbuckets is hash buckect number, nstreams is max streams system can store
nbuckets is recommended use a prime number
## API
**header file**
For using this API, You should include the header file as below:
#include "ngx_stream_zone_module.h"
**ngx\_stream\_zone\_insert\_stream**
ngx_int_t ngx_stream_zone_insert_stream(ngx_str_t *name);
- para:
name: stream name
- return value:
process\_slot for owner of stream, NGX\_ERROR for error
**ngx\_stream\_zone\_delete\_stream**
void ngx_stream_zone_delete_stream(ngx_str_t *name);
- para:
name: stream name
**ngx\_stream\_zone\_state**
ngx_chain_t *ngx_stream_zone_state(ngx_http_request_t *r, ngx_flag_t detail);
- para:
- r: http request to query status of rbuf
- detail: print stream detail in log
- return value:
chain of stream zone state for returning to http client
## Build
cd to NGINX source directory & run this:
./configure --add-module=/path/to/nginx-multiport-module/
make && make install
## Example
See t/ngx\_stream\_zone\_test\_module.c as reference
**Build**:
./configure --with-debug --with-ipv6 --add-module=/path/to/nginx-multiport-module/t/ --add-module=/path/to/nginx-multiport-module/
make && make install
**Configure**:
stream_zone buckets=10007 streams=10000;
**Test**:
curl -XPOST -v "http://127.0.0.1:9001/stream_zone_test/ab?stream=test"
curl -XPOST -v "http://127.0.0.1:9002/stream_zone_test/ab?stream=test1"
curl -XPOST -v "http://127.0.0.1:9003/stream_zone_test/ab?stream=test2"
curl -XPOST -v "http://127.0.0.1:9003/stream_zone_test/ab?stream=test"
curl -XDELETE -v "http://127.0.0.1:9000/stream_zone_test/ab?stream=test3"
curl -XDELETE -v "http://127.0.0.1:9002/stream_zone_test/ab?stream=test1"
curl -XDELETE -v "http://127.0.0.1:9001/stream_zone_test/ab?stream=test2"
curl -XGET -v "http://127.0.0.1:9001/stream_zone_test/ab"

View File

@ -1,263 +0,0 @@
/*
* Copyright (C) AlexWoo(Wu Jie) wj19840501@gmail.com
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
#include "ngx_multiport.h"
typedef struct {
ngx_str_t multiport;
ngx_str_t uri;
} ngx_http_broadcast_conf_t;
typedef struct {
ngx_int_t workerid;
ngx_http_request_t *sr;
} ngx_http_broadcast_ctx_t;
static ngx_int_t ngx_http_broadcast_filter_init(ngx_conf_t *cf);
static void *ngx_http_broadcast_create_conf(ngx_conf_t *cf);
static char *ngx_http_broadcast_merge_conf(ngx_conf_t *cf,
void *parent, void *child);
static char *ngx_http_broadcast(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static ngx_command_t ngx_http_broadcast_commands[] = {
{ ngx_string("broadcast"),
NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
ngx_http_broadcast,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_http_broadcast_module_ctx = {
NULL, /* preconfiguration */
ngx_http_broadcast_filter_init, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_broadcast_create_conf, /* create location configuration */
ngx_http_broadcast_merge_conf /* merge location configuration */
};
ngx_module_t ngx_http_broadcast_module = {
NGX_MODULE_V1,
&ngx_http_broadcast_module_ctx, /* module context */
ngx_http_broadcast_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
static void *
ngx_http_broadcast_create_conf(ngx_conf_t *cf)
{
ngx_http_broadcast_conf_t *conf;
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_broadcast_conf_t));
if (conf == NULL) {
return NULL;
}
return conf;
}
static char *
ngx_http_broadcast_merge_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_broadcast_conf_t *prev = parent;
ngx_http_broadcast_conf_t *conf = child;
ngx_conf_merge_str_value(conf->multiport, prev->multiport, "");
ngx_conf_merge_str_value(conf->uri, prev->uri, "");
return NGX_CONF_OK;
}
static char *
ngx_http_broadcast(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_broadcast_conf_t *hbcf;
ngx_str_t *value;
hbcf = conf;
if (hbcf->multiport.data != NULL) {
return "is duplicate";
}
value = cf->args->elts;
hbcf->multiport = value[1];
hbcf->uri = value[2];
return NGX_CONF_OK;
}
static ngx_int_t
ngx_http_broadcast_header_filter(ngx_http_request_t *r)
{
ngx_http_broadcast_conf_t *hbcf;
hbcf = ngx_http_get_module_loc_conf(r, ngx_http_broadcast_module);
if (hbcf == NULL || hbcf->multiport.len == 0) { /* not configured */
goto next;
}
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"broadcast header filter, r:%p r->main:%p, %ui, %O",
r, r->main, r->headers_out.status, r->headers_out.content_length_n);
r->headers_out.status = NGX_HTTP_OK;
ngx_http_clear_content_length(r);
ngx_http_clear_accept_ranges(r);
next:
return ngx_http_next_header_filter(r);
}
static ngx_int_t
ngx_http_broadcast_send_subrequest(ngx_http_request_t *r, ngx_int_t pslot)
{
ngx_http_broadcast_conf_t *hbcf;
ngx_str_t uri;
ngx_str_t port;
ngx_http_request_t *sr;
ngx_int_t rc;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"broadcast send subrequest to %i", pslot);
hbcf = ngx_http_get_module_loc_conf(r, ngx_http_broadcast_module);
if (ngx_multiport_get_port(r->pool, &port, &hbcf->multiport, pslot)
== NGX_ERROR)
{
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"broadcast get port error, %V %i", &hbcf->multiport, pslot);
return NGX_ERROR;
}
uri.len = hbcf->uri.len + 1 + port.len;
uri.data = ngx_pcalloc(r->pool, uri.len);
ngx_snprintf(uri.data, uri.len, "%V/%V", &hbcf->uri, &port);
rc = ngx_http_subrequest(r, &uri, &r->args, &sr, NULL, 0);
sr->method = r->method;
sr->method_name = r->method_name;
return rc;
}
static ngx_int_t
ngx_http_broadcast_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
ngx_http_broadcast_conf_t *hbcf;
ngx_http_broadcast_ctx_t *ctx;
ngx_core_conf_t *ccf;
ngx_int_t rc;
ngx_buf_t *b;
ngx_chain_t cl;
hbcf = ngx_http_get_module_loc_conf(r->main, ngx_http_broadcast_module);
if (hbcf == NULL || hbcf->multiport.len == 0) { /* not configured */
return ngx_http_next_body_filter(r, in);
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"broadcast body filter, r:%p r->main:%p", r, r->main);
if (r != r->main) { /* send subrequest */
if (r->headers_out.status != NGX_HTTP_OK) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"broadcast subrequest send non 200 response: %i",
r->headers_out.status);
return NGX_OK;
}
return ngx_http_next_body_filter(r, in);
}
ctx = ngx_http_get_module_ctx(r, ngx_http_broadcast_module);
if (ctx == NULL) {
ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_broadcast_ctx_t));
if (ctx == NULL) {
return NGX_ERROR;
}
ngx_http_set_ctx(r, ctx, ngx_http_broadcast_module);
}
/* send to all process */
ccf = (ngx_core_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
ngx_core_module);
while (ctx->workerid < ccf->worker_processes) {
rc = ngx_http_broadcast_send_subrequest(r,
ngx_multiport_get_slot(ctx->workerid));
++ctx->workerid;
return rc;
}
b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
if (b == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
b->last_buf = 1;
cl.buf = b;
cl.next = NULL;
return ngx_http_next_body_filter(r, &cl);
}
static ngx_int_t
ngx_http_broadcast_filter_init(ngx_conf_t *cf)
{
ngx_http_next_header_filter = ngx_http_top_header_filter;
ngx_http_top_header_filter = ngx_http_broadcast_header_filter;
ngx_http_next_body_filter = ngx_http_top_body_filter;
ngx_http_top_body_filter = ngx_http_broadcast_body_filter;
return NGX_OK;
}

View File

@ -1,254 +0,0 @@
/*
* Copyright (C) AlexWoo(Wu Jie) wj19840501@gmail.com
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
#include "ngx_multiport.h"
typedef struct {
ngx_str_t multiport;
ngx_str_t uri;
} ngx_http_inner_proxy_conf_t;
typedef struct {
ngx_str_t port;
ngx_flag_t last;
} ngx_http_inner_proxy_ctx_t;
static ngx_int_t ngx_http_inner_proxy_filter_init(ngx_conf_t *cf);
static void *ngx_http_inner_proxy_create_conf(ngx_conf_t *cf);
static char *ngx_http_inner_proxy_merge_conf(ngx_conf_t *cf,
void *parent, void *child);
static char *ngx_http_inner_proxy(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static ngx_command_t ngx_http_inner_proxy_commands[] = {
{ ngx_string("inner_proxy"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
ngx_http_inner_proxy,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_http_inner_proxy_module_ctx = {
NULL, /* preconfiguration */
ngx_http_inner_proxy_filter_init, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_inner_proxy_create_conf, /* create location configuration */
ngx_http_inner_proxy_merge_conf /* merge location configuration */
};
ngx_module_t ngx_http_inner_proxy_module = {
NGX_MODULE_V1,
&ngx_http_inner_proxy_module_ctx, /* module context */
ngx_http_inner_proxy_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
static void *
ngx_http_inner_proxy_create_conf(ngx_conf_t *cf)
{
ngx_http_inner_proxy_conf_t *conf;
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_inner_proxy_conf_t));
if (conf == NULL) {
return NULL;
}
return conf;
}
static char *
ngx_http_inner_proxy_merge_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_inner_proxy_conf_t *prev = parent;
ngx_http_inner_proxy_conf_t *conf = child;
ngx_conf_merge_str_value(conf->multiport, prev->multiport, "");
ngx_conf_merge_str_value(conf->uri, prev->uri, "");
return NGX_CONF_OK;
}
static char *
ngx_http_inner_proxy(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_inner_proxy_conf_t *hipcf;
ngx_str_t *value;
hipcf = conf;
if (hipcf->multiport.data != NULL) {
return "is duplicate";
}
value = cf->args->elts;
hipcf->multiport = value[1];
hipcf->uri = value[2];
return NGX_CONF_OK;
}
static ngx_int_t
ngx_http_inner_proxy_header_filter(ngx_http_request_t *r)
{
ngx_http_inner_proxy_ctx_t *ctx;
ctx = ngx_http_get_module_ctx(r->main, ngx_http_inner_proxy_module);
if (ctx == NULL) { /* not configured */
return ngx_http_next_header_filter(r);
}
if (r == r->main) {
return NGX_OK;
}
r->main->headers_out = r->headers_out;
return ngx_http_next_header_filter(r->main);
}
static ngx_int_t
ngx_http_inner_proxy_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
ngx_http_inner_proxy_ctx_t *ctx;
ngx_chain_t *cl, l;
ngx_buf_t *b;
ctx = ngx_http_get_module_ctx(r->main, ngx_http_inner_proxy_module);
if (ctx == NULL) { /* not configured */
return ngx_http_next_body_filter(r, in);
}
if (r == r->main) {
if (ctx->last == 0) {
return NGX_OK;
}
b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
if (b == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
b->last_buf = 1;
l.buf = b;
l.next = NULL;
return ngx_http_next_body_filter(r, &l);
}
for (cl = in; cl; cl = cl->next) {
if (cl->buf->last_in_chain) {
ctx->last = 1;
}
}
return ngx_http_next_body_filter(r, in);
}
static ngx_int_t
ngx_http_inner_proxy_filter_init(ngx_conf_t *cf)
{
ngx_http_next_header_filter = ngx_http_top_header_filter;
ngx_http_top_header_filter = ngx_http_inner_proxy_header_filter;
ngx_http_next_body_filter = ngx_http_top_body_filter;
ngx_http_top_body_filter = ngx_http_inner_proxy_body_filter;
return NGX_OK;
}
ngx_int_t
ngx_http_inner_proxy_request(ngx_http_request_t *r, ngx_int_t pslot)
{
ngx_http_inner_proxy_conf_t *hipcf;
ngx_http_inner_proxy_ctx_t *ctx;
ngx_http_request_t *sr;
ngx_str_t uri;
ngx_int_t rc;
hipcf = ngx_http_get_module_loc_conf(r, ngx_http_inner_proxy_module);
if (hipcf == NULL || hipcf->multiport.len == 0) { /* not configured */
return NGX_DECLINED;
}
if (pslot == ngx_process_slot) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"inner proxy send request to self: %i", ngx_process_slot);
return NGX_DECLINED;
}
ctx = ngx_http_get_module_ctx(r, ngx_http_inner_proxy_module);
if (ctx) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"inner proxy has been called in this request");
return NGX_ERROR;
}
ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_inner_proxy_ctx_t));
if (ctx == NULL) {
return NGX_ERROR;
}
ngx_http_set_ctx(r, ctx, ngx_http_inner_proxy_module);
if (ngx_multiport_get_port(r->pool, &ctx->port, &hipcf->multiport, pslot)
== NGX_ERROR)
{
return NGX_ERROR;
}
uri.len = hipcf->uri.len + 1 + ctx->port.len + 2 + r->uri.len;
uri.data = ngx_pcalloc(r->pool, uri.len);
ngx_snprintf(uri.data, uri.len, "%V/%V:/%V",
&hipcf->uri, &ctx->port, &r->uri);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"inner proxy send request to %V", &ctx->port);
rc = ngx_http_subrequest(r, &uri, &r->args, &sr, NULL, 0);
sr->method = r->method;
sr->method_name = r->method_name;
return rc;
}

View File

@ -1,53 +0,0 @@
/*
* Copyright (C) AlexWoo(Wu Jie) wj19840501@gmail.com
*/
#ifndef _NGX_MULTIPORT_H_INCLUDED_
#define _NGX_MULTIPORT_H_INCLUDED_
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
/*
* return value:
* NGX_OK for success, NGX_ERROR for failed
* paras:
* pool: pool for port memory alloc
* port: process real listen port while process_slot is pslot
* multiport: port configure for processes, format as below:
* port only: port
* IPv4: host:port host must be ipaddr of IPv4 or *
* IPv6: [host]:port host must be ipaddr of IPv6
* Unix: unix:/path
* pslot: process_slot
*/
ngx_int_t ngx_multiport_get_port(ngx_pool_t *pool, ngx_str_t *port,
ngx_str_t *multiport, ngx_int_t pslot);
/*
* return value:
* ngx_process_slot for successd, NGX_ERROR for failed
* paras:
* wpid: worker process id, 0 to ccf->worker_processes - 1
*/
ngx_int_t ngx_multiport_get_slot(ngx_uint_t wpid);
/*
* return value:
* NGX_OK : for successd
* NGX_ERROR : for failed
* NGX_DECLINED: for not configured or send inner proxy to self
* paras:
* r : http request for send inner request to sibling worker
* pslot: sibling worker ngx_process_slot
*/
ngx_int_t ngx_http_inner_proxy_request(ngx_http_request_t *r, ngx_int_t pslot);
#endif

View File

@ -1,165 +0,0 @@
#include <ngx_config.h>
#include <ngx_core.h>
static ngx_int_t
ngx_multiport_get_port_unix(ngx_pool_t *pool, ngx_str_t *port,
ngx_str_t *multiport, ngx_int_t pslot)
{
#if (NGX_HAVE_UNIX_DOMAIN)
u_char *p;
size_t len;
len = multiport->len + 5; /* unix:/path -> unix:/path.127\0 */
port->data = ngx_pcalloc(pool, len);
if (port->data == NULL) {
return NGX_ERROR;
}
p = ngx_snprintf(port->data, len, "%V.%i", multiport, pslot);
*p = 0;
port->len = p - port->data;
return NGX_OK;
#else
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
"the unix domain sockets not support");
return NGX_ERROR;
#endif
}
static ngx_int_t
ngx_multiport_get_port_inet6(ngx_pool_t *pool, ngx_str_t *port,
ngx_str_t *multiport, ngx_int_t pslot)
{
#if (NGX_HAVE_INET6)
u_char *p, *last;
ngx_str_t addr;
size_t len;
ngx_int_t n;
last = multiport->data + multiport->len;
p = ngx_strlchr(multiport->data, last, ']');
if (p == NULL) {
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "invalid INET6 host");
return NGX_ERROR;
}
++p;
if (p == last || *p != ':') {
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "no INET6 port");
return NGX_ERROR;
}
++p;
addr.data = multiport->data;
addr.len = p - multiport->data;
len = last - p;
n = ngx_atoi(p, len);
/* 65408 + 127 = 65535, pslot in [0, 127] */
if (n < 1 || n + pslot > 65408) {
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "invalid INET6 port");
return NGX_ERROR;
}
n += pslot;
len = multiport->len + 3; /* [::]:1 -> [::]:128\0 */
port->data = ngx_pcalloc(pool, len);
if (port->data == NULL) {
return NGX_ERROR;
}
p = port->data;
p = ngx_snprintf(p, len, "%V%i", &addr, n);
port->len = p - port->data;
return NGX_OK;
#else
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
"the INET6 sockets not support");
return NGX_ERROR;
#endif
}
static ngx_int_t
ngx_multiport_get_port_inet(ngx_pool_t *pool, ngx_str_t *port,
ngx_str_t *multiport, ngx_int_t pslot)
{
u_char *p, *last;
ngx_str_t addr;
size_t len;
ngx_int_t n;
last = multiport->data + multiport->len;
p = ngx_strlchr(multiport->data, last, ':');
if (p == NULL) { /* port */
p = multiport->data;
addr.len = 0;
} else { /* host:port */
++p;
if (p == last) {
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "no port");
return NGX_ERROR;
}
addr.data = multiport->data;
addr.len = p - multiport->data;
}
len = last - p;
n = ngx_atoi(p, len);
/* 65408 + 127 = 65535, pslot in [0, 127] */
if (n < 1 || n + pslot > 65408) {
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "invalid port");
return NGX_ERROR;
}
n += pslot;
len = multiport->len + 3; /* 127.0.0.1:1 -> 127.0.0.1:128\0 */
port->data = ngx_pcalloc(pool, len);
if (port->data == NULL) {
return NGX_ERROR;
}
p = port->data;
if (addr.len == 0) {
p = ngx_snprintf(p, len, "%i", n);
} else {
p = ngx_snprintf(p, len, "%V%i", &addr, n);
}
port->len = p - port->data;
return NGX_OK;
}
ngx_int_t
ngx_multiport_get_port(ngx_pool_t *pool, ngx_str_t *port,
ngx_str_t *multiport, ngx_int_t pslot)
{
u_char *p;
size_t len;
p = multiport->data;
len = multiport->len;
if (pslot < 0 || pslot > 127) {
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "invalid pslot: %i",
pslot);
return NGX_ERROR;
}
if (len >= 5 && ngx_strncasecmp(p, (u_char *) "unix:", 5) == 0) {
return ngx_multiport_get_port_unix(pool, port, multiport, pslot);
}
if (len && p[0] == '[') {
return ngx_multiport_get_port_inet6(pool, port, multiport, pslot);
}
return ngx_multiport_get_port_inet(pool, port, multiport, pslot);
}

View File

@ -1,167 +0,0 @@
/*
* Copyright (C) AlexWoo(Wu Jie) wj19840501@gmail.com
*/
#include <ngx_config.h>
#include <ngx_core.h>
static ngx_int_t ngx_process_slot_module_init(ngx_cycle_t *cycle);
static ngx_int_t ngx_process_slot_process_init(ngx_cycle_t *cycle);
static void ngx_process_slot_process_exit(ngx_cycle_t *cycle);
static void *ngx_process_slot_module_create_conf(ngx_cycle_t *cycle);
static char *ngx_process_slot_module_init_conf(ngx_cycle_t *cycle, void *conf);
#define MAX_PROCESSES 128
typedef struct {
ngx_atomic_int_t process_slot[MAX_PROCESSES];
} ngx_process_slot_ctx_t;
typedef struct {
ngx_process_slot_ctx_t *ctx;
} ngx_process_slot_conf_t;
static ngx_command_t ngx_process_slot_commands[] = {
ngx_null_command
};
static ngx_core_module_t ngx_process_slot_module_ctx = {
ngx_string("process_slot"),
ngx_process_slot_module_create_conf,
ngx_process_slot_module_init_conf
};
ngx_module_t ngx_process_slot_module = {
NGX_MODULE_V1,
&ngx_process_slot_module_ctx, /* module context */
ngx_process_slot_commands, /* module directives */
NGX_CORE_MODULE, /* module type */
NULL, /* init master */
ngx_process_slot_module_init, /* init module */
ngx_process_slot_process_init, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
ngx_process_slot_process_exit, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static void *
ngx_process_slot_module_create_conf(ngx_cycle_t *cycle)
{
ngx_process_slot_conf_t *pscf;
pscf = ngx_palloc(cycle->pool, sizeof(ngx_process_slot_conf_t));
if (pscf == NULL) {
return NULL;
}
return pscf;
}
static char *
ngx_process_slot_module_init_conf(ngx_cycle_t *cycle, void *conf)
{
return NGX_CONF_OK;
}
static ngx_int_t
ngx_process_slot_module_init(ngx_cycle_t *cycle)
{
ngx_process_slot_conf_t *pscf;
ngx_shm_t shm;
ngx_uint_t i;
pscf = (ngx_process_slot_conf_t *) ngx_get_conf(cycle->conf_ctx,
ngx_process_slot_module);
shm.size = sizeof(ngx_process_slot_ctx_t);
shm.name.len = sizeof("process_slot_zone") - 1;
shm.name.data = (u_char *) "process_slot_zone";
shm.log = cycle->log;
if (ngx_shm_alloc(&shm) != NGX_OK) {
return NGX_ERROR;
}
pscf->ctx = (ngx_process_slot_ctx_t *) shm.addr;
for (i = 0; i < MAX_PROCESSES; ++i) {
pscf->ctx->process_slot[i] = -1;
}
return NGX_OK;
}
static ngx_int_t
ngx_process_slot_process_init(ngx_cycle_t *cycle)
{
ngx_process_slot_conf_t *pscf;
ngx_process_slot_ctx_t *ctx;
if (ngx_process != NGX_PROCESS_WORKER) {
return NGX_OK;
}
pscf = (ngx_process_slot_conf_t *) ngx_get_conf(cycle->conf_ctx,
ngx_process_slot_module);
ctx = pscf->ctx;
for (;;) {
if (ngx_atomic_cmp_set((ngx_atomic_t *) &ctx->process_slot[ngx_worker],
(ngx_atomic_uint_t)ctx->process_slot[ngx_worker], ngx_process_slot))
{
break;
}
}
return NGX_OK;
}
static void
ngx_process_slot_process_exit(ngx_cycle_t *cycle)
{
ngx_process_slot_conf_t *pscf;
ngx_process_slot_ctx_t *ctx;
if (ngx_process != NGX_PROCESS_WORKER) {
return;
}
pscf = (ngx_process_slot_conf_t *) ngx_get_conf(cycle->conf_ctx,
ngx_process_slot_module);
ctx = pscf->ctx;
ngx_atomic_cmp_set((ngx_atomic_t *) &ctx->process_slot[ngx_worker],
(ngx_atomic_uint_t)ngx_process_slot, -1);
}
ngx_int_t
ngx_multiport_get_slot(ngx_uint_t wpid)
{
ngx_process_slot_conf_t *pscf;
ngx_process_slot_ctx_t *ctx;
ngx_core_conf_t *ccf;
ccf = (ngx_core_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
ngx_core_module);
if (wpid >= (ngx_uint_t)ccf->worker_processes) {
return -1;
}
pscf = (ngx_process_slot_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
ngx_process_slot_module);
ctx = pscf->ctx;
return ctx->process_slot[wpid];
}

View File

@ -1,551 +0,0 @@
/*
* Copyright (C) AlexWoo(Wu Jie) wj19840501@gmail.com
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
typedef struct ngx_stream_zone_hash_s ngx_stream_zone_hash_t;
typedef struct ngx_stream_zone_node_s ngx_stream_zone_node_t;
typedef struct ngx_stream_zone_conf_s ngx_stream_zone_conf_t;
static ngx_int_t
ngx_stream_zone_init_process(ngx_cycle_t *cycle);
static void
ngx_stream_zone_exit_process(ngx_cycle_t *cycle);
static void *
ngx_stream_zone_create_conf(ngx_cycle_t *cf);
static char *
ngx_stream_zone_init_conf(ngx_cycle_t *cycle, void *conf);
static char *
ngx_stream_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static char *
ngx_stream_zone_shm_init(ngx_shm_t *shm, ngx_stream_zone_conf_t *szcf,
ngx_cycle_t *cycle);
#define NAME_LEN 1024
static ngx_str_t stream_zone_key = ngx_string("stream_zone");
struct ngx_stream_zone_node_s {
u_char name[NAME_LEN];
ngx_int_t slot; /* process slot */
ngx_int_t idx;
ngx_int_t next; /* idx of stream node */
};
struct ngx_stream_zone_hash_s {
ngx_shmtx_t mutex;
ngx_shmtx_sh_t lock;
ngx_int_t node; /* idx of stream node */
};
struct ngx_stream_zone_conf_s {
ngx_int_t nbuckets;
ngx_int_t nstreams;
ngx_pool_t *pool;
ngx_shmtx_t *mutex;
ngx_shmtx_sh_t *lock;
ngx_stream_zone_hash_t *hash; /* hash in shm */
ngx_stream_zone_node_t *stream_node;/* node in shm */
ngx_int_t *free_node; /* free node chain */
ngx_int_t *alloc; /* node number in use*/
};
static ngx_command_t ngx_stream_zone_commands[] = {
{ ngx_string("stream_zone"),
NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE2,
ngx_stream_zone,
0,
0,
NULL },
ngx_null_command
};
static ngx_core_module_t ngx_stream_zone_module_ctx = {
ngx_string("rtmp_stream_zone"),
ngx_stream_zone_create_conf, /* create conf */
ngx_stream_zone_init_conf /* init conf */
};
ngx_module_t ngx_stream_zone_module = {
NGX_MODULE_V1,
&ngx_stream_zone_module_ctx, /* module context */
ngx_stream_zone_commands, /* module directives */
NGX_CORE_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
ngx_stream_zone_init_process, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
ngx_stream_zone_exit_process, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_stream_zone_node_t *
ngx_stream_zone_get_node(ngx_str_t *name, ngx_int_t pslot)
{
ngx_stream_zone_conf_t *szcf;
ngx_stream_zone_node_t *node;
szcf = (ngx_stream_zone_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
ngx_stream_zone_module);
ngx_shmtx_lock(szcf->mutex);
if (*szcf->free_node == -1) {
ngx_shmtx_unlock(szcf->mutex);
return NULL;
}
node = &szcf->stream_node[*szcf->free_node];
*szcf->free_node = node->next;
*ngx_copy(node->name, name->data, ngx_min(NAME_LEN - 1, name->len)) = '\0';
node->slot = pslot;
node->next = -1;
++*szcf->alloc;
ngx_shmtx_unlock(szcf->mutex);
return node;
}
static void
ngx_stream_zone_put_node(ngx_int_t idx)
{
ngx_stream_zone_conf_t *szcf;
ngx_stream_zone_node_t *node;
szcf = (ngx_stream_zone_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
ngx_stream_zone_module);
ngx_shmtx_lock(szcf->mutex);
node = &szcf->stream_node[idx];
node->next = *szcf->free_node;
*szcf->free_node = idx;
--*szcf->alloc;
ngx_shmtx_unlock(szcf->mutex);
}
static void *
ngx_stream_zone_create_conf(ngx_cycle_t *cf)
{
ngx_stream_zone_conf_t *conf;
conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_zone_conf_t));
if (conf == NULL) {
return NULL;
}
conf->nbuckets = NGX_CONF_UNSET;
conf->nstreams = NGX_CONF_UNSET;
conf->pool = ngx_create_pool(4096, cf->log);
return conf;
}
static char *
ngx_stream_zone_init_conf(ngx_cycle_t *cycle, void *conf)
{
size_t len;
ngx_shm_t shm;
ngx_stream_zone_conf_t *szcf = conf;
ngx_conf_init_value(szcf->nbuckets, 512);
ngx_conf_init_value(szcf->nstreams, 40960);
/* create shm zone */
len = sizeof(ngx_shmtx_t) + sizeof(ngx_shmtx_sh_t)
+ sizeof(ngx_stream_zone_hash_t) * szcf->nbuckets
+ sizeof(ngx_stream_zone_node_t) * szcf->nstreams
+ sizeof(ngx_int_t) + sizeof(ngx_int_t);
shm.size = len;
shm.name = stream_zone_key;
shm.log = cycle->log;
if (ngx_shm_alloc(&shm) != NGX_OK) {
return NGX_CONF_ERROR;
}
return ngx_stream_zone_shm_init(&shm, szcf, cycle);
}
static void
ngx_stream_zone_clear(ngx_cycle_t *cycle)
{
ngx_stream_zone_conf_t *szcf;
volatile ngx_int_t idx, cur, next;
szcf = (ngx_stream_zone_conf_t *) ngx_get_conf(cycle->conf_ctx,
ngx_stream_zone_module);
if (szcf->nbuckets <= 0 || szcf->nstreams <= 0) {
return;
}
for (idx = 0; idx < szcf->nbuckets; ++idx) {
ngx_shmtx_lock(&szcf->hash[idx].mutex);
cur = -1;
while (1) {
if (cur == -1) {
next = szcf->hash[idx].node;
} else {
next = szcf->stream_node[cur].next;
}
if (next == -1) {
break;
}
if (szcf->stream_node[next].slot == ngx_process_slot) {
if (cur == -1) {
szcf->hash[idx].node = szcf->stream_node[next].next;
} else {
szcf->stream_node[cur].next = szcf->stream_node[next].next;
}
ngx_stream_zone_put_node(next);
continue;
}
cur = next;
}
ngx_shmtx_unlock(&szcf->hash[idx].mutex);
}
}
static ngx_int_t
ngx_stream_zone_init_process(ngx_cycle_t *cycle)
{
ngx_stream_zone_clear(cycle);
return NGX_OK;
}
static void
ngx_stream_zone_exit_process(ngx_cycle_t *cycle)
{
ngx_stream_zone_clear(cycle);
}
static char *
ngx_stream_zone_shm_init(ngx_shm_t *shm, ngx_stream_zone_conf_t *szcf,
ngx_cycle_t *cycle)
{
u_char *p;
ngx_int_t i, next;
p = shm->addr;
szcf->mutex = (ngx_shmtx_t *) p;
p += sizeof(ngx_shmtx_t);
szcf->lock = (ngx_shmtx_sh_t *) p;
p += sizeof(ngx_shmtx_sh_t);
szcf->hash = (ngx_stream_zone_hash_t *) p;
p += sizeof(ngx_stream_zone_hash_t) * szcf->nbuckets;
szcf->stream_node = (ngx_stream_zone_node_t *) p;
p += sizeof(ngx_stream_zone_node_t) * szcf->nstreams;
szcf->free_node = (ngx_int_t *) p;
p += sizeof(ngx_int_t);
szcf->alloc = (ngx_int_t *) p;
/* init shm zone */
#if (NGX_HAVE_ATOMIC_OPS)
p = NULL;
#else
p = ngx_pnalloc(szcf->pool, cycle->lock_file.len
+ stream_zone_key.len);
if (p == NULL) {
return NGX_CONF_ERROR;
}
*ngx_sprintf(p, "%V%V", &cycle->lock_file, &stream_zone_key) = 0;
#endif
if (ngx_shmtx_create(szcf->mutex, szcf->lock, p) != NGX_OK) {
return NGX_CONF_ERROR;
}
for (i = 0; i < szcf->nbuckets; ++i) {
#if (NGX_HAVE_ATOMIC_OPS)
p = NULL;
#else
p = ngx_pnalloc(szcf->pool, cycle->lock_file.len + stream_zone_key.len
+ NGX_INT32_LEN);
if (p == NULL) {
return NGX_CONF_ERROR;
}
*ngx_sprintf(p, "%V%V%d", &cycle->lock_file,
&stream_zone_key, i) = 0;
#endif
if (ngx_shmtx_create(&szcf->hash[i].mutex, &szcf->hash[i].lock, p)
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
szcf->hash[i].node = -1;
}
next = -1;
i = szcf->nstreams;
do {
--i;
szcf->stream_node[i].slot = -1;
szcf->stream_node[i].idx = i;
szcf->stream_node[i].next = next;
next = i;
} while (i);
*szcf->free_node = i;
*szcf->alloc = 0;
return NGX_CONF_OK;
}
static char *
ngx_stream_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_uint_t i;
ngx_str_t *value;
ngx_stream_zone_conf_t *szcf = conf;
value = cf->args->elts;
for (i = 1; i < cf->args->nelts; ++i) {
if (ngx_strncmp(value[i].data, "buckets=", 8) == 0) {
szcf->nbuckets = ngx_atoi(value[i].data + 8, value[i].len - 8);
if (szcf->nbuckets <= 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid buckets \"%V\"", &value[i]);
return NGX_CONF_ERROR;
}
continue;
}
if (ngx_strncmp(value[i].data, "streams=", 8) == 0) {
szcf->nstreams = ngx_atoi(value[i].data + 8, value[i].len - 8);
if (szcf->nstreams <= 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid streams \"%V\"", &value[i]);
return NGX_CONF_ERROR;
}
continue;
}
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid parameter \"%V\"", &value[i]);
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
ngx_int_t
ngx_stream_zone_insert_stream(ngx_str_t *name)
{
ngx_stream_zone_conf_t *szcf;
volatile ngx_uint_t idx;
volatile ngx_int_t i, pslot;
ngx_stream_zone_node_t *node;
szcf = (ngx_stream_zone_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
ngx_stream_zone_module);
if (szcf->nbuckets <= 0 || szcf->nstreams <= 0) {
return NGX_ERROR;
}
if (name->len >= NAME_LEN) {
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
"stream name(%V) too long", name);
return NGX_ERROR;
}
idx = ngx_hash_key(name->data, name->len) % szcf->nbuckets;
ngx_shmtx_lock(&szcf->hash[idx].mutex);
i = szcf->hash[idx].node;
pslot = -1;
while (i != -1) {
if (ngx_strlen(szcf->stream_node[i].name) == name->len
&& ngx_memcmp(szcf->stream_node[i].name, name->data, name->len)
== 0)
{
pslot = szcf->stream_node[i].slot;
break;
}
i = szcf->stream_node[i].next;
}
if (i == -1) { /* stream not in hash */
node = ngx_stream_zone_get_node(name, ngx_process_slot);
if (node == NULL) {
ngx_shmtx_unlock(&szcf->hash[idx].mutex);
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
"stream zone get node failed");
return NGX_ERROR;
}
node->slot = ngx_process_slot;
node->next = szcf->hash[idx].node;
szcf->hash[idx].node = node->idx;
pslot = ngx_process_slot;
}
ngx_shmtx_unlock(&szcf->hash[idx].mutex);
return pslot;
}
void
ngx_stream_zone_delete_stream(ngx_str_t *name)
{
ngx_stream_zone_conf_t *szcf;
volatile ngx_uint_t idx;
volatile ngx_int_t cur, next;
szcf = (ngx_stream_zone_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
ngx_stream_zone_module);
if (szcf->nbuckets <= 0 || szcf->nstreams <= 0) {
return;
}
idx = ngx_hash_key(name->data, name->len) % szcf->nbuckets;
ngx_shmtx_lock(&szcf->hash[idx].mutex);
cur = -1;
next = szcf->hash[idx].node;
while (next != -1) {
if (ngx_strlen(szcf->stream_node[next].name) == name->len
&& ngx_memcmp(szcf->stream_node[next].name, name->data, name->len)
== 0)
{
if (szcf->stream_node[next].slot != ngx_process_slot) {
break;
}
if (cur == -1) { /* link header */
szcf->hash[idx].node = szcf->stream_node[next].next;
} else {
szcf->stream_node[cur].next = szcf->stream_node[next].next;
}
ngx_stream_zone_put_node(next);
break;
}
cur = next;
next = szcf->stream_node[next].next;
}
ngx_shmtx_unlock(&szcf->hash[idx].mutex);
}
ngx_chain_t *
ngx_stream_zone_state(ngx_http_request_t *r, ngx_flag_t detail)
{
ngx_stream_zone_conf_t *szcf;
ngx_chain_t *cl;
ngx_buf_t *b;
size_t len;
volatile ngx_int_t idx, next;
szcf = (ngx_stream_zone_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
ngx_stream_zone_module);
if (szcf->nbuckets <= 0 || szcf->nstreams <= 0) {
return NULL;
}
len = sizeof("##########stream zone state##########\n") - 1
+ sizeof("ngx_stream_zone buckets: \n") - 1 + NGX_OFF_T_LEN
+ sizeof("ngx_stream_zone streams: \n") - 1 + NGX_OFF_T_LEN
+ sizeof("ngx_stream_zone alloc: \n") - 1 + NGX_OFF_T_LEN;
cl = ngx_alloc_chain_link(r->pool);
if (cl == NULL) {
return NULL;
}
cl->next = NULL;
b = ngx_create_temp_buf(r->pool, len);
if (b == NULL) {
return NULL;
}
cl->buf = b;
b->last = ngx_snprintf(b->last, len,
"##########stream zone state##########\n"
"ngx_stream_zone buckets: %i\nngx_stream_zone streams: %i\n"
"ngx_stream_zone alloc: %i\n",
szcf->nbuckets, szcf->nstreams, *szcf->alloc);
if (detail) {
for (idx = 0; idx < szcf->nbuckets; ++idx) {
ngx_shmtx_lock(&szcf->hash[idx].mutex);
next = szcf->hash[idx].node;
if (next == -1) {
ngx_shmtx_unlock(&szcf->hash[idx].mutex);
continue;
}
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "slot: %i", idx);
while (next != -1) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"\t\tname:%s, slot:%i, idx:%i, next:%i",
szcf->stream_node[next].name,
szcf->stream_node[next].slot,
szcf->stream_node[next].idx,
szcf->stream_node[next].next);
next = szcf->stream_node[next].next;
}
ngx_shmtx_unlock(&szcf->hash[idx].mutex);
}
}
return cl;
}

View File

@ -1,39 +0,0 @@
/*
* Copyright (C) AlexWoo(Wu Jie) wj19840501@gmail.com
*/
#ifndef _NGX_STREAM_ZONE_MODULE_H_INCLUDED_
#define _NGX_STREAM_ZONE_MODULE_H_INCLUDED_
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
/*
* return value:
* process_slot for owner of stream, NGX_ERROR for error
* name:
* stream name
*/
ngx_int_t ngx_stream_zone_insert_stream(ngx_str_t *name);
/*
* name:
* stream name
*/
void ngx_stream_zone_delete_stream(ngx_str_t *name);
/*
* return value:
* chain of stream zone state for returning to http client
* paras:
* r: http request to query status of rbuf
* detail: print stream detail in log
*/
ngx_chain_t *ngx_stream_zone_state(ngx_http_request_t *r, ngx_flag_t detail);
#endif

View File

@ -1,12 +0,0 @@
ngx_addon_name=ngx_multiport_test_module
HTTP_MODULES="$HTTP_MODULES \
ngx_stream_zone_test_module \
ngx_multiport_test_module \
"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS
$ngx_addon_dir/ngx_stream_zone_test_module.c \
$ngx_addon_dir/ngx_multiport_test_module.c \
"

View File

@ -1,71 +0,0 @@
user root;
worker_processes 4;
#error_log logs/error.log;
#error_log logs/error.log notice;
error_log logs/error.log info;
#pid logs/nginx.pid;
stream_zone buckets=10007 streams=10000;
events {
worker_connections 1024;
multi_listen 9000 80;
multi_listen unix:/tmp/http.sock.80 80;
}
http {
include mime.types;
default_type application/octet-stream;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
server {
listen 80;
server_name localhost;
location /stream_zone_test/ {
stream_zone_test;
}
location /multiport_test/ {
inner_proxy unix:/tmp/http.sock.80 /inner_proxy;
multiport_test;
}
location /inner_proxy/ {
rewrite ^/inner_proxy/(.*):/(.*) /$2 break;
proxy_pass http://$1:;
}
location / {
broadcast unix:/tmp/http.sock.80 /auth_proxy;
}
location /auth_proxy/ {
rewrite ^/auth_proxy/(.*) /auth break;
proxy_pass http://$1:;
}
location /auth {
# return 403;
echo "auth";
echo $scheme://$host$uri?$args;
}
}
}

View File

@ -1,168 +0,0 @@
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
#include "ngx_multiport.h"
#include "ngx_test_macro.h"
static char *ngx_multiport_test(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static ngx_command_t ngx_multiport_test_commands[] = {
{ ngx_string("multiport_test"),
NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
ngx_multiport_test,
0,
0,
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_multiport_test_module_ctx = {
NULL, /* preconfiguration */
NULL, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
NULL, /* create location configuration */
NULL /* merge location configuration */
};
ngx_module_t ngx_multiport_test_module = {
NGX_MODULE_V1,
&ngx_multiport_test_module_ctx, /* module context */
ngx_multiport_test_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
ngx_int_t
ngx_multiport_test_get_port(ngx_http_request_t *r, char *multiport,
ngx_int_t pslot, char *expect)
{
ngx_str_t port;
ngx_int_t rc;
ngx_str_t mp;
mp.data = (u_char *) multiport;
mp.len = ngx_strlen(multiport);
ngx_memzero(&port, sizeof(ngx_str_t));
rc = ngx_multiport_get_port(r->pool, &port, &mp, pslot);
if (port.len) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "port: %V, %s, %d",
&port, port.data, port.len);
}
if (expect == NULL && rc == NGX_ERROR) {
return 1;
} else {
return ngx_test_str(&port, expect);
}
return 0;
}
static ngx_int_t
ngx_multiport_test_handler(ngx_http_request_t *r)
{
ngx_buf_t *b;
ngx_chain_t cl;
size_t len;
ngx_int_t rc;
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"multiport test handler");
rc = ngx_http_inner_proxy_request(r, ngx_multiport_get_slot(0));
if (rc != NGX_DECLINED) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"inner proxy return %i", rc);
return rc;
}
NGX_TEST_INIT
/* test ngx_multiport_get_port */
/* normal format */
NGX_TEST_ISOK(ngx_multiport_test_get_port(r, "10", 127, "137"));
NGX_TEST_ISOK(ngx_multiport_test_get_port(r, "127.0.0.1:55635", 4,
"127.0.0.1:55639"));
NGX_TEST_ISOK(ngx_multiport_test_get_port(r, "[::127.0.0.1]:1024", 0,
"[::127.0.0.1]:1024"));
NGX_TEST_ISOK(ngx_multiport_test_get_port(r, "unix:/tmp.test", 7,
"unix:/tmp.test.7"));
/* inet6 format error */
NGX_TEST_ISOK(ngx_multiport_test_get_port(r, "[::127.0.0.1:1024", 0, NULL));
NGX_TEST_ISOK(ngx_multiport_test_get_port(r, "[::127.0.0.1]:abcd",
0, NULL));
NGX_TEST_ISOK(ngx_multiport_test_get_port(r, "[::127.0.0.1]:65409",
0, NULL));
NGX_TEST_ISOK(ngx_multiport_test_get_port(r, "[::127.0.0.1]:", 0, NULL));
NGX_TEST_ISOK(ngx_multiport_test_get_port(r, "[::127.0.0.1]", 0, NULL));
/* inet format error */
NGX_TEST_ISOK(ngx_multiport_test_get_port(r, "127.0.0.1:", 4, NULL));
NGX_TEST_ISOK(ngx_multiport_test_get_port(r, "abcd", 4, NULL));
NGX_TEST_ISOK(ngx_multiport_test_get_port(r, "65410", 4, NULL));
/* pslot error */
NGX_TEST_ISOK(ngx_multiport_test_get_port(r, "65410", -1, NULL));
NGX_TEST_ISOK(ngx_multiport_test_get_port(r, "65410", 128, NULL));
/* test ngx_multiport_get_slot */
NGX_TEST_ISOK(ngx_multiport_get_slot(4) == -1);
NGX_TEST_ISOK(ngx_multiport_get_slot(0) == 0);
NGX_TEST_ISOK(ngx_multiport_get_slot(1) == 1);
NGX_TEST_ISOK(ngx_multiport_get_slot(2) == 2);
NGX_TEST_ISOK(ngx_multiport_get_slot(3) == 3);
r->headers_out.status = NGX_HTTP_OK;
ngx_http_send_header(r);
len = sizeof("pslot: %i TEST cases 4294967296, 4294967296 pass\n") - 1;
b = ngx_create_temp_buf(r->pool, len);
if (b == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
b->last = ngx_snprintf(b->last, len, "pslot: %i TEST cases %d, %d pass\n",
ngx_process_slot, count, pass);
b->last_buf = 1;
b->last_in_chain = 1;
cl.buf = b;
cl.next = NULL;
return ngx_http_output_filter(r, &cl);
}
static char *
ngx_multiport_test(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_core_loc_conf_t *clcf;
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
clcf->handler = ngx_multiport_test_handler;
return NGX_CONF_OK;
}

View File

@ -1,134 +0,0 @@
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
#include "ngx_stream_zone_module.h"
static char *ngx_stream_zone_test(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static ngx_command_t ngx_stream_zone_test_commands[] = {
{ ngx_string("stream_zone_test"),
NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
ngx_stream_zone_test,
0,
0,
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_stream_zone_test_module_ctx = {
NULL, /* preconfiguration */
NULL, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
NULL, /* create location configuration */
NULL /* merge location configuration */
};
ngx_module_t ngx_stream_zone_test_module = {
NGX_MODULE_V1,
&ngx_stream_zone_test_module_ctx, /* module context */
ngx_stream_zone_test_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_int_t
ngx_stream_zone_test_handler(ngx_http_request_t *r)
{
ngx_chain_t cl, *out;
ngx_buf_t *b;
size_t len;
ngx_str_t stream;
ngx_int_t rc;
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "stream zone test handler");
rc = -1;
if (r->method == NGX_HTTP_GET) {
out = ngx_stream_zone_state(r, 1);
out->buf->last_buf = 1;
out->buf->last_in_chain = 1;
r->headers_out.status = NGX_HTTP_OK;
ngx_http_send_header(r);
return ngx_http_output_filter(r, out);
} else if (r->method == NGX_HTTP_DELETE) {
if (ngx_http_arg(r, (u_char *) "stream", 6, &stream) != NGX_OK) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"stream zone test, no stream in http args");
return NGX_HTTP_BAD_REQUEST;
}
len = sizeof("delete stream=\n") - 1 + stream.len;
ngx_stream_zone_delete_stream(&stream);
} else if (r->method == NGX_HTTP_POST) {
if (ngx_http_arg(r, (u_char *) "stream", 6, &stream) != NGX_OK) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"stream zone test, no stream in http args");
return NGX_HTTP_BAD_REQUEST;
}
len = sizeof("stream= in process:4294967296\n") - 1 + stream.len;
rc = ngx_stream_zone_insert_stream(&stream);
} else {
return NGX_HTTP_BAD_REQUEST;
}
r->headers_out.status = NGX_HTTP_OK;
ngx_http_send_header(r);
b = ngx_create_temp_buf(r->pool, len);
if (b == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
if (r->method == NGX_HTTP_DELETE) {
b->last = ngx_snprintf(b->last, len, "delete stream=%V\n", &stream);
} else {
b->last = ngx_snprintf(b->last, len,
"stream=%V in process:%i\n", &stream, rc);
}
b->last_buf = 1;
b->last_in_chain = 1;
cl.buf = b;
cl.next = NULL;
return ngx_http_output_filter(r, &cl);
}
static char *
ngx_stream_zone_test(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_core_loc_conf_t *clcf;
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
clcf->handler = ngx_stream_zone_test_handler;
return NGX_CONF_OK;
}

View File

@ -1,41 +0,0 @@
/*
* Copyright (C) AlexWoo(Wu Jie) wj19840501@gmail.com
*/
#ifndef _NGX_TEST_MACRO_H_INCLUDE_
#define _NGX_TEST_MACRO_H_INCLUDE_
#include <ngx_config.h>
#include <ngx_core.h>
static ngx_int_t count = 0;
static ngx_int_t pass = 0;
#define NGX_TEST_INIT count = 0, pass = 0;
#define NGX_TEST_ISOK(testcase) \
{ \
ngx_int_t __ret = testcase; \
++count; \
if (__ret) ++pass; \
ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0, " TEST "#testcase"%s",\
(__ret ? " ...OK" : " ...ERROR")); \
}
#define NGX_TEST_INT(di, si) \
(di == si)
static ngx_inline ngx_int_t
ngx_test_str(ngx_str_t *nstr, char *cstr)
{
size_t len;
len = ngx_strlen(cstr);
return (nstr->len == len && ngx_memcmp(nstr->data, cstr, len) == 0);
}
#endif

View File

@ -1,9 +0,0 @@
Project author:
Roman Arutyunyan
Moscow, Russia, Pingo
Contacts:
arut@qip.ru
arutyunyan.roman@gmail.com
cczjp89@gmail.com

View File

@ -1,22 +0,0 @@
Copyright (c) 2012-2014, Roman Arutyunyan
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -1,169 +0,0 @@
ngx_addon_name="ngx_rtmp_module"
RTMP_CORE_MODULES=" \
ngx_live_module \
ngx_rtmp_module \
ngx_rtmp_dynamic_module \
ngx_rtmp_dynamic_core_module \
ngx_rtmp_core_module \
ngx_rtmp_cmd_module \
ngx_rtmp_codec_module \
ngx_rtmp_access_module \
ngx_rtmp_live_module \
ngx_live_record_module \
ngx_live_relay_module \
ngx_live_relay_rtmp_module \
ngx_live_relay_static_module \
ngx_live_relay_simple_module \
ngx_rtmp_exec_module \
ngx_rtmp_notify_module \
ngx_live_relay_inner_module \
ngx_rtmp_log_module \
ngx_rtmp_limit_module \
ngx_rtmp_hls_module \
ngx_rtmp_dash_module \
ngx_rtmp_shared_module \
ngx_rtmp_record_module \
ngx_rtmp_gop_module \
ngx_rtmp_monitor_module \
ngx_mpegts_live_module \
ngx_mpegts_gop_module \
ngx_hls_live_module \
"
RTMP_HTTP_MODULES=" \
ngx_rtmp_stat_module \
ngx_rtmp_sys_stat_module \
ngx_rtmp_control_module \
ngx_http_flv_live_module \
ngx_hls_http_module \
ngx_mpegts_http_module \
"
RTMP_DEPS=" \
$ngx_addon_dir/ngx_netcall.h \
$ngx_addon_dir/ngx_rtmp_amf.h \
$ngx_addon_dir/ngx_rtmp_bandwidth.h \
$ngx_addon_dir/ngx_rtmp_cmd_module.h \
$ngx_addon_dir/ngx_rtmp_codec_module.h \
$ngx_addon_dir/ngx_rtmp_eval.h \
$ngx_addon_dir/ngx_rtmp.h \
$ngx_addon_dir/ngx_rtmp_version.h \
$ngx_addon_dir/ngx_rtmp_live_module.h \
$ngx_addon_dir/ngx_rtmp_notify_module.h \
$ngx_addon_dir/ngx_rtmp_streams.h \
$ngx_addon_dir/ngx_rtmp_bitop.h \
$ngx_addon_dir/ngx_rtmp_proxy_protocol.h \
$ngx_addon_dir/ngx_rtmp_monitor_module.h \
$ngx_addon_dir/hls/ngx_rtmp_mpegts.h \
$ngx_addon_dir/dash/ngx_rtmp_mp4.h \
$ngx_addon_dir/http/ngx_http_set_header.h \
$ngx_addon_dir/ngx_live.h \
$ngx_addon_dir/ngx_live_relay.h \
$ngx_addon_dir/ngx_live_record.h \
$ngx_addon_dir/ngx_rtmp_dynamic.h \
$ngx_addon_dir/ngx_rtmp_variables.h \
$ngx_addon_dir/ngx_rtmp_record_module.h \
$ngx_addon_dir/mpegts/ngx_mpegts_live_module.h \
$ngx_addon_dir/mpegts/ngx_hls_live_module.h \
$ngx_addon_dir/mpegts/ngx_mpegts_gop_module.h \
"
RTMP_CORE_SRCS=" \
$ngx_addon_dir/ngx_netcall.c \
$ngx_addon_dir/ngx_rtmp.c \
$ngx_addon_dir/ngx_rtmp_init.c \
$ngx_addon_dir/ngx_rtmp_handshake.c \
$ngx_addon_dir/ngx_rtmp_handler.c \
$ngx_addon_dir/ngx_rtmp_amf.c \
$ngx_addon_dir/ngx_rtmp_send.c \
$ngx_addon_dir/ngx_rtmp_eval.c \
$ngx_addon_dir/ngx_rtmp_receive.c \
$ngx_addon_dir/ngx_rtmp_core_module.c \
$ngx_addon_dir/ngx_rtmp_cmd_module.c \
$ngx_addon_dir/ngx_rtmp_codec_module.c \
$ngx_addon_dir/ngx_rtmp_access_module.c \
$ngx_addon_dir/ngx_rtmp_live_module.c \
$ngx_addon_dir/ngx_rtmp_bandwidth.c \
$ngx_addon_dir/ngx_rtmp_exec_module.c \
$ngx_addon_dir/ngx_rtmp_notify_module.c \
$ngx_addon_dir/ngx_rtmp_log_module.c \
$ngx_addon_dir/ngx_rtmp_limit_module.c \
$ngx_addon_dir/ngx_rtmp_bitop.c \
$ngx_addon_dir/ngx_rtmp_proxy_protocol.c \
$ngx_addon_dir/hls/ngx_rtmp_hls_module.c \
$ngx_addon_dir/dash/ngx_rtmp_dash_module.c \
$ngx_addon_dir/hls/ngx_rtmp_mpegts.c \
$ngx_addon_dir/dash/ngx_rtmp_mp4.c \
$ngx_addon_dir/ngx_live.c \
$ngx_addon_dir/ngx_live_relay.c \
$ngx_addon_dir/ngx_live_relay_httpflv.c \
$ngx_addon_dir/ngx_live_relay_rtmp.c \
$ngx_addon_dir/ngx_live_relay_inner.c \
$ngx_addon_dir/ngx_live_relay_simple.c \
$ngx_addon_dir/ngx_live_relay_static.c \
$ngx_addon_dir/ngx_live_record.c \
$ngx_addon_dir/ngx_rtmp_shared_module.c \
$ngx_addon_dir/ngx_rtmp_gop_module.c \
$ngx_addon_dir/ngx_rtmp_monitor_module.c \
$ngx_addon_dir/ngx_rtmp_dynamic.c \
$ngx_addon_dir/ngx_rtmp_variables.c \
$ngx_addon_dir/ngx_rtmp_record_module.c \
$ngx_addon_dir/mpegts/ngx_mpegts_live_module.c \
$ngx_addon_dir/mpegts/ngx_hls_live_module.c \
$ngx_addon_dir/mpegts/ngx_mpegts_gop_module.c \
"
RTMP_HTTP_SRCS=" \
$ngx_addon_dir/ngx_rtmp_sys_stat_module.c \
$ngx_addon_dir/ngx_rtmp_stat_module.c \
$ngx_addon_dir/ngx_rtmp_control_module.c \
$ngx_addon_dir/http/ngx_http_flv_live_module.c \
$ngx_addon_dir/http/ngx_http_set_header.c \
$ngx_addon_dir/mpegts/ngx_hls_http_module.c \
$ngx_addon_dir/mpegts/ngx_mpegts_http_module.c \
"
if [ -f auto/module ] ; then
ngx_module_incs=$ngx_addon_dir
ngx_module_deps=$RTMP_DEPS
if [ $ngx_module_link = DYNAMIC ] ; then
ngx_module_name="$RTMP_CORE_MODULES $RTMP_HTTP_MODULES"
ngx_module_srcs="$RTMP_CORE_SRCS $RTMP_HTTP_SRCS"
. auto/module
else
ngx_module_type=CORE
ngx_module_name=$RTMP_CORE_MODULES
ngx_module_srcs=$RTMP_CORE_SRCS
. auto/module
ngx_module_type=HTTP
ngx_module_name=$RTMP_HTTP_MODULES
ngx_module_incs="$ngx_addon_dir $ngx_addon_dir/http $ngx_addon_dir/hls $ngx_addon_dir/mpegts"
ngx_module_deps=
ngx_module_srcs=$RTMP_HTTP_SRCS
. auto/module
fi
else
CORE_MODULES="$CORE_MODULES $RTMP_CORE_MODULES"
HTTP_MODULES="$HTTP_MODULES $RTMP_HTTP_MODULES"
NGX_ADDON_DEPS="$NGX_ADDON_DEPS $RTMP_DEPS"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $RTMP_CORE_SRCS $RTMP_HTTP_SRCS"
CFLAGS="$CFLAGS -I$ngx_addon_dir -I$ngx_addon_dir/http -I$ngx_addon_dir/hls -I$ngx_addon_dir/mpegts"
fi
USE_OPENSSL=YES

View File

@ -1,52 +0,0 @@
#ifndef _NGX_RTMP_MP4_H_INCLUDED_
#define _NGX_RTMP_MP4_H_INCLUDED_
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_rtmp.h>
#define NGX_RTMP_MP4_SAMPLE_SIZE 0x01
#define NGX_RTMP_MP4_SAMPLE_DURATION 0x02
#define NGX_RTMP_MP4_SAMPLE_DELAY 0x04
#define NGX_RTMP_MP4_SAMPLE_KEY 0x08
typedef struct {
uint32_t size;
uint32_t duration;
uint32_t delay;
uint32_t timestamp;
unsigned key:1;
} ngx_rtmp_mp4_sample_t;
typedef enum {
NGX_RTMP_MP4_FILETYPE_INIT,
NGX_RTMP_MP4_FILETYPE_SEG
} ngx_rtmp_mp4_file_type_t;
typedef enum {
NGX_RTMP_MP4_VIDEO_TRACK,
NGX_RTMP_MP4_AUDIO_TRACK
} ngx_rtmp_mp4_track_type_t;
ngx_int_t ngx_rtmp_mp4_write_ftyp(ngx_buf_t *b);
ngx_int_t ngx_rtmp_mp4_write_styp(ngx_buf_t *b);
ngx_int_t ngx_rtmp_mp4_write_moov(ngx_rtmp_session_t *s, ngx_buf_t *b,
ngx_rtmp_mp4_track_type_t ttype);
ngx_int_t ngx_rtmp_mp4_write_moof(ngx_buf_t *b, uint32_t earliest_pres_time,
uint32_t sample_count, ngx_rtmp_mp4_sample_t *samples,
ngx_uint_t sample_mask, uint32_t index);
ngx_int_t ngx_rtmp_mp4_write_sidx(ngx_buf_t *b,
ngx_uint_t reference_size, uint32_t earliest_pres_time,
uint32_t latest_pres_time);
ngx_uint_t ngx_rtmp_mp4_write_mdat(ngx_buf_t *b, ngx_uint_t size);
#endif /* _NGX_RTMP_MP4_H_INCLUDED_ */

View File

@ -1,558 +0,0 @@
/*
* Copyright (C) Roman Arutyunyan
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include "ngx_rtmp.h"
#include "ngx_rtmp_mpegts.h"
u_char ngx_rtmp_mpegts_pat[] = {
/* TS */
0x47, 0x40, 0x00, 0x10, 0x00,
/* PSI */
0x00, 0xb0, 0x0d, 0x00, 0x01, 0xc1, 0x00, 0x00,
/* PAT */
0x00, 0x01, 0xf0, 0x01,
/* CRC */
0x2e, 0x70, 0x19, 0x05,
/* stuffing 167 bytes */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
static u_char ngx_mpegts_ts_header[] = {
/* TS */
0x47, 0x50, 0x01, 0x10, 0x00
};
static u_char ngx_mpegts_pmt_header[] = {
/* PSI */
0x02, 0xb0, 0x17, 0x00, 0x01, 0xc1, 0x00, 0x00,
/* PMT */
0xe1, 0x00,
0xf0, 0x00
};
enum {
NGX_RTMP_MPEGTS_PID_H264 = 0,
NGX_RTMP_MPEGTS_PID_H265,
NGX_RTMP_MPEGTS_PID_AAC,
NGX_RTMP_MPEGTS_PID_MP3
};
static u_char ngx_mpegts_pid[4][5] = {
{0x1b, 0xe1, 0x00, 0xf0, 0x00}, /* h264 */
{0x24, 0xe1, 0x00, 0xf0, 0x00}, /* h265 */
{0x0f, 0xe1, 0x01, 0xf0, 0x00}, /* aac */
{0x03, 0xe1, 0x01, 0xf0, 0x00} /* mp3 */
};
/* 700 ms PCR delay */
#define NGX_RTMP_HLS_DELAY 63000
static uint32_t crc32table[256] = {
0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7,
0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3,
0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef,
0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb,
0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4,
0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08,
0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc,
0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050,
0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1,
0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5,
0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9,
0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd,
0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2,
0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a,
0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676,
0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
};
uint32_t
ngx_rtmp_mpegts_crc32(u_char *crc_buf, const u_char *data, int len)
{
int i;
uint32_t crc = 0xFFFFFFFF;
for(i = 0; i < len; i++) {
crc = (crc << 8) ^ crc32table[((crc >> 24) ^ *data++) & 0xFF];
}
crc_buf[0] = (crc & 0xff000000) >> 24;
crc_buf[1] = (crc & 0x00ff0000) >> 16;
crc_buf[2] = (crc & 0x0000ff00) >> 8;
crc_buf[3] = crc & 0x000000ff;
return crc;
}
ngx_int_t
ngx_rtmp_mpegts_gen_pmt(ngx_int_t vcodec, ngx_int_t acodec,
ngx_log_t *log, u_char *pmt)
{
u_char *p, crc_buf[4], *pmt_pos;
ngx_int_t vpid, apid;
u_char section_length;
vpid = -1;
apid = -1;
if (vcodec == acodec && vcodec == 0) {
return NGX_ERROR;
}
p = pmt;
p = ngx_cpymem(p, ngx_mpegts_ts_header, sizeof(ngx_mpegts_ts_header));
pmt_pos = p;
p = ngx_cpymem(p, ngx_mpegts_pmt_header, sizeof(ngx_mpegts_pmt_header));
if (vcodec == 0) {
// ignore
} else if (vcodec == NGX_RTMP_VIDEO_H264) {
vpid = NGX_RTMP_MPEGTS_PID_H264;
} else if (vcodec == NGX_RTMP_HEVC_CODEC_ID) {
vpid = NGX_RTMP_MPEGTS_PID_H265;
} else if (log) {
ngx_log_error(NGX_LOG_ERR, log, 0,
"rtmp: gen_pmt| unknown video codec (%d)", vcodec);
}
switch (acodec) {
case 0: //ignore
break;
case NGX_RTMP_AUDIO_MP3:
apid = NGX_RTMP_MPEGTS_PID_MP3;
break;
case NGX_RTMP_AUDIO_AAC:
apid = NGX_RTMP_MPEGTS_PID_AAC;
break;
default:
if (log) {
ngx_log_error(NGX_LOG_ERR, log, 0,
"rtmp: gen_pmt| unknown video codec (%d)", vcodec);
}
}
section_length = 13;
if (vpid != -1) {
p = ngx_cpymem(p, ngx_mpegts_pid[vpid], 5);
section_length += 5;
}
if (apid != -1) {
p = ngx_cpymem(p, ngx_mpegts_pid[apid], 5);
section_length += 5;
}
pmt_pos[2] = section_length;
ngx_rtmp_mpegts_crc32(crc_buf, pmt_pos, p - pmt_pos);
p = ngx_cpymem(p, crc_buf, 4);
ngx_memset(p, 0xff, 188 - (p - pmt));
return NGX_OK;
}
static ngx_int_t
ngx_rtmp_mpegts_write_file(ngx_rtmp_mpegts_file_t *file, u_char *in,
size_t in_size)
{
u_char *out;
size_t out_size, n;
ssize_t rc;
static u_char buf[1024];
if (!file->encrypt) {
ngx_log_debug1(NGX_LOG_DEBUG_CORE, file->log, 0,
"mpegts: write %uz bytes", in_size);
if (file->whandle == NULL) {
rc = ngx_write_fd(file->fd, in, in_size);
if (rc < 0) {
return NGX_ERROR;
}
file->file_size += rc;
} else {
rc = file->whandle(file, in, in_size);
if (rc < 0) {
return NGX_ERROR;
}
}
return NGX_OK;
}
/* encrypt */
ngx_log_debug1(NGX_LOG_DEBUG_CORE, file->log, 0,
"mpegts: write %uz encrypted bytes", in_size);
out = buf;
out_size = sizeof(buf);
if (file->size > 0 && file->size + in_size >= 16) {
ngx_memcpy(file->buf + file->size, in, 16 - file->size);
in += 16 - file->size;
in_size -= 16 - file->size;
AES_cbc_encrypt(file->buf, out, 16, &file->key, file->iv, AES_ENCRYPT);
out += 16;
out_size -= 16;
file->size = 0;
}
for ( ;; ) {
n = in_size & ~0x0f;
if (n > 0) {
if (n > out_size) {
n = out_size;
}
AES_cbc_encrypt(in, out, n, &file->key, file->iv, AES_ENCRYPT);
in += n;
in_size -= n;
} else if (out == buf) {
break;
}
rc = ngx_write_fd(file->fd, buf, out - buf + n);
if (rc < 0) {
return NGX_ERROR;
}
out = buf;
out_size = sizeof(buf);
}
if (in_size) {
ngx_memcpy(file->buf + file->size, in, in_size);
file->size += in_size;
}
return NGX_OK;
}
ngx_int_t
ngx_rtmp_mpegts_write_header(ngx_rtmp_mpegts_file_t *file)
{
ngx_int_t ret;
u_char pmt[188];
ret = ngx_rtmp_mpegts_write_file(file, ngx_rtmp_mpegts_pat,
sizeof(ngx_rtmp_mpegts_pat));
if (ret != NGX_OK) {
return ret;
}
if (ngx_rtmp_mpegts_gen_pmt(file->vcodec,
file->acodec, file->log, pmt) != NGX_OK)
{
return NGX_ERROR;
}
return ngx_rtmp_mpegts_write_file(file, pmt, sizeof(pmt));
}
static u_char *
ngx_rtmp_mpegts_write_pcr(u_char *p, uint64_t pcr)
{
*p++ = (u_char) (pcr >> 25);
*p++ = (u_char) (pcr >> 17);
*p++ = (u_char) (pcr >> 9);
*p++ = (u_char) (pcr >> 1);
*p++ = (u_char) (pcr << 7 | 0x7e);
*p++ = 0;
return p;
}
static u_char *
ngx_rtmp_mpegts_write_pts(u_char *p, ngx_uint_t fb, uint64_t pts)
{
ngx_uint_t val;
val = fb << 4 | (((pts >> 30) & 0x07) << 1) | 1;
*p++ = (u_char) val;
val = (((pts >> 15) & 0x7fff) << 1) | 1;
*p++ = (u_char) (val >> 8);
*p++ = (u_char) val;
val = (((pts) & 0x7fff) << 1) | 1;
*p++ = (u_char) (val >> 8);
*p++ = (u_char) val;
return p;
}
ngx_int_t
ngx_rtmp_mpegts_write_frame(ngx_rtmp_mpegts_file_t *file,
ngx_rtmp_mpegts_frame_t *f, ngx_buf_t *b)
{
ngx_uint_t pes_size, header_size, body_size, in_size, stuff_size, flags;
u_char packet[188], *p, *base;
ngx_int_t first, rc;
ngx_log_debug6(NGX_LOG_DEBUG_CORE, file->log, 0,
"mpegts: pid=%ui, sid=%ui, pts=%uL, "
"dts=%uL, key=%ui, size=%ui",
f->pid, f->sid, f->pts, f->dts,
(ngx_uint_t) f->key, (size_t) (b->last - b->pos));
first = 1;
while (b->pos < b->last) {
p = packet;
f->cc++;
*p++ = 0x47;
*p++ = (u_char) (f->pid >> 8);
if (first) {
p[-1] |= 0x40;
}
*p++ = (u_char) f->pid;
*p++ = 0x10 | (f->cc & 0x0f); /* payload */
if (first) {
if (f->key) {
packet[3] |= 0x20; /* adaptation */
*p++ = 7; /* size */
*p++ = 0x50; /* random access + PCR */
p = ngx_rtmp_mpegts_write_pcr(p, f->dts - NGX_RTMP_HLS_DELAY);
}
/* PES header */
*p++ = 0x00;
*p++ = 0x00;
*p++ = 0x01;
*p++ = (u_char) f->sid;
header_size = 5;
flags = 0x80; /* PTS */
if (f->dts != f->pts) {
header_size += 5;
flags |= 0x40; /* DTS */
}
pes_size = (b->last - b->pos) + header_size + 3;
if (pes_size > 0xffff) {
pes_size = 0;
}
*p++ = (u_char) (pes_size >> 8);
*p++ = (u_char) pes_size;
*p++ = 0x80; /* H222 */
*p++ = (u_char) flags;
*p++ = (u_char) header_size;
p = ngx_rtmp_mpegts_write_pts(p, flags >> 6, f->pts +
NGX_RTMP_HLS_DELAY);
if (f->dts != f->pts) {
p = ngx_rtmp_mpegts_write_pts(p, 1, f->dts +
NGX_RTMP_HLS_DELAY);
}
first = 0;
}
body_size = (ngx_uint_t) (packet + sizeof(packet) - p);
in_size = (ngx_uint_t) (b->last - b->pos);
if (body_size <= in_size) {
ngx_memcpy(p, b->pos, body_size);
b->pos += body_size;
} else {
stuff_size = (body_size - in_size);
if (packet[3] & 0x20) {
/* has adaptation */
base = &packet[5] + packet[4];
p = ngx_movemem(base + stuff_size, base, p - base);
ngx_memset(base, 0xff, stuff_size);
packet[4] += (u_char) stuff_size;
} else {
/* no adaptation */
packet[3] |= 0x20;
p = ngx_movemem(&packet[4] + stuff_size, &packet[4],
p - &packet[4]);
packet[4] = (u_char) (stuff_size - 1);
if (stuff_size >= 2) {
packet[5] = 0;
ngx_memset(&packet[6], 0xff, stuff_size - 2);
}
}
ngx_memcpy(p, b->pos, in_size);
b->pos = b->last;
}
rc = ngx_rtmp_mpegts_write_file(file, packet, sizeof(packet));
if (rc != NGX_OK) {
return rc;
}
}
return NGX_OK;
}
ngx_int_t
ngx_rtmp_mpegts_init_encryption(ngx_rtmp_mpegts_file_t *file,
u_char *key, size_t key_len, uint64_t iv)
{
if (AES_set_encrypt_key(key, key_len * 8, &file->key)) {
return NGX_ERROR;
}
ngx_memzero(file->iv, 8);
file->iv[8] = (u_char) (iv >> 56);
file->iv[9] = (u_char) (iv >> 48);
file->iv[10] = (u_char) (iv >> 40);
file->iv[11] = (u_char) (iv >> 32);
file->iv[12] = (u_char) (iv >> 24);
file->iv[13] = (u_char) (iv >> 16);
file->iv[14] = (u_char) (iv >> 8);
file->iv[15] = (u_char) (iv);
file->encrypt = 1;
return NGX_OK;
}
ngx_int_t
ngx_rtmp_mpegts_open_file(ngx_rtmp_mpegts_file_t *file, u_char *path,
ngx_log_t *log)
{
file->log = log;
file->fd = ngx_open_file(path, NGX_FILE_WRONLY, NGX_FILE_TRUNCATE,
NGX_FILE_DEFAULT_ACCESS);
if (file->fd == NGX_INVALID_FILE) {
ngx_log_error(NGX_LOG_ERR, log, ngx_errno,
"hls: error creating fragment file");
return NGX_ERROR;
}
file->size = 0;
if (ngx_rtmp_mpegts_write_header(file) != NGX_OK) {
ngx_log_error(NGX_LOG_ERR, log, ngx_errno,
"hls: error writing fragment header");
ngx_close_file(file->fd);
return NGX_ERROR;
}
return NGX_OK;
}
ngx_int_t
ngx_rtmp_mpegts_close_file(ngx_rtmp_mpegts_file_t *file)
{
u_char buf[16];
ssize_t rc;
if (file->encrypt) {
ngx_memset(file->buf + file->size, 16 - file->size, 16 - file->size);
AES_cbc_encrypt(file->buf, buf, 16, &file->key, file->iv, AES_ENCRYPT);
rc = ngx_write_fd(file->fd, buf, 16);
if (rc < 0) {
return NGX_ERROR;
}
}
ngx_close_file(file->fd);
return NGX_OK;
}

View File

@ -1,58 +0,0 @@
/*
* Copyright (C) Roman Arutyunyan
*/
#ifndef _NGX_RTMP_MPEGTS_H_INCLUDED_
#define _NGX_RTMP_MPEGTS_H_INCLUDED_
#include <ngx_config.h>
#include <ngx_core.h>
#include <openssl/aes.h>
typedef struct ngx_rtmp_mpegts_file_s ngx_rtmp_mpegts_file_t;
typedef ssize_t (*ngx_rtmp_mpegts_write_pt) (ngx_rtmp_mpegts_file_t *file,
u_char *in, size_t in_size);
struct ngx_rtmp_mpegts_file_s {
ngx_fd_t fd;
ngx_log_t *log;
off_t file_size;
unsigned encrypt:1;
unsigned size:4;
u_char buf[16];
u_char iv[16];
AES_KEY key;
ngx_int_t acodec;
ngx_int_t vcodec;
ngx_buf_t wbuf;
ngx_rtmp_mpegts_write_pt whandle;
};
typedef struct {
uint64_t pts;
uint64_t dts;
ngx_uint_t pid;
ngx_uint_t sid;
ngx_uint_t cc;
unsigned key:1;
} ngx_rtmp_mpegts_frame_t;
ngx_int_t ngx_rtmp_mpegts_init_encryption(ngx_rtmp_mpegts_file_t *file,
u_char *key, size_t key_len, uint64_t iv);
ngx_int_t ngx_rtmp_mpegts_open_file(ngx_rtmp_mpegts_file_t *file, u_char *path,
ngx_log_t *log);
ngx_int_t ngx_rtmp_mpegts_close_file(ngx_rtmp_mpegts_file_t *file);
ngx_int_t ngx_rtmp_mpegts_write_header(ngx_rtmp_mpegts_file_t *file);
ngx_int_t ngx_rtmp_mpegts_write_frame(ngx_rtmp_mpegts_file_t *file,
ngx_rtmp_mpegts_frame_t *f, ngx_buf_t *b);
#endif /* _NGX_RTMP_MPEGTS_H_INCLUDED_ */

View File

@ -1,733 +0,0 @@
/*
* Copyright (C) AlexWoo(Wu Jie) wj19840501@gmail.com
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>
#include <ngx_http.h>
#include "ngx_rtmp.h"
#include "ngx_rtmp_cmd_module.h"
#include "ngx_rbuf.h"
#include "ngx_http_set_header.h"
#include "ngx_rtmp_monitor_module.h"
static char *ngx_http_flv_live(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static void *ngx_http_flv_live_create_loc_conf(ngx_conf_t *cf);
static char *ngx_http_flv_live_merge_loc_conf(ngx_conf_t *cf, void *parent,
void *child);
static u_char ngx_flv_live_audio_header[] = "FLV\x1\x1\0\0\0\x9\0\0\0\0";
static u_char ngx_flv_live_video_header[] = "FLV\x1\x4\0\0\0\x9\0\0\0\0";
static u_char ngx_flv_live_av_header[] = "FLV\x1\x5\0\0\0\x9\0\0\0\0";
static ngx_keyval_t ngx_http_flv_live_headers[] = {
{ ngx_string("Cache-Control"), ngx_string("no-cache") },
{ ngx_string("Content-Type"), ngx_string("video/x-flv") },
{ ngx_null_string, ngx_null_string }
};
#define NGX_FLV_TAG_SIZE 11
#define NGX_FLV_PTS_SIZE 4
typedef struct {
ngx_rtmp_session_t *session;
} ngx_http_flv_live_ctx_t;
typedef struct {
ngx_str_t app;
ngx_str_t flashver;
ngx_str_t swf_url;
ngx_str_t tc_url;
ngx_str_t page_url;
ngx_uint_t audio;
ngx_uint_t video;
ngx_rtmp_addr_conf_t *addr_conf;
} ngx_http_flv_live_loc_conf_t;
static ngx_command_t ngx_http_flv_live_commands[] = {
{ ngx_string("flv_live"),
NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,
ngx_http_flv_live,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_http_flv_live_module_ctx = {
NULL, /* preconfiguration */
NULL, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_flv_live_create_loc_conf, /* create location configuration */
ngx_http_flv_live_merge_loc_conf /* merge location configuration */
};
ngx_module_t ngx_http_flv_live_module = {
NGX_MODULE_V1,
&ngx_http_flv_live_module_ctx, /* module context */
ngx_http_flv_live_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_int_t
ngx_http_flv_live_send_header(ngx_http_request_t *r)
{
ngx_int_t rc;
ngx_keyval_t *h;
ngx_buf_t *b;
ngx_chain_t out;
ngx_http_flv_live_loc_conf_t *hflcf;
if (r->header_sent) {
return NGX_OK;
}
hflcf = ngx_http_get_module_loc_conf(r, ngx_http_flv_live_module);
r->headers_out.status = NGX_HTTP_OK;
r->keepalive = 0; /* set Connection to closed */
h = ngx_http_flv_live_headers;
while (h->key.len) {
rc = ngx_http_set_header_out(r, &h->key, &h->value);
if (rc != NGX_OK) {
return rc;
}
++h;
}
rc = ngx_http_send_header(r);
if (rc == NGX_ERROR || rc > NGX_OK) {
return rc;
}
b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
if (b == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
switch (hflcf->audio | (hflcf->video << 1)) {
case 1: // audio only
b->start = b->pos = ngx_flv_live_audio_header;
b->end = b->last = ngx_flv_live_audio_header +
sizeof(ngx_flv_live_audio_header) - 1;
break;
case 2: // video only
b->start = b->pos = ngx_flv_live_video_header;
b->end = b->last = ngx_flv_live_video_header +
sizeof(ngx_flv_live_video_header) - 1;
break;
case 3: // audio and video
b->start = b->pos = ngx_flv_live_av_header;
b->end = b->last = ngx_flv_live_av_header +
sizeof(ngx_flv_live_av_header) - 1;
break;
default:
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"flv-live: send_header| av header config error.");
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
b->memory = 1;
out.buf = b;
out.next = NULL;
return ngx_http_output_filter(r, &out);
}
static ngx_chain_t *
ngx_http_flv_live_prepare_out_chain(ngx_rtmp_session_t *s)
{
ngx_rtmp_frame_t *frame;
ngx_chain_t *head, **ll, *cl;
u_char *p;
size_t datasize, prev_tag_size;
frame = NULL;
head = NULL;
datasize = 0;
while (s->out_pos != s->out_last) {
frame = s->out[s->out_pos];
if (frame->hdr.type != NGX_RTMP_MSG_VIDEO
&& frame->hdr.type != NGX_RTMP_MSG_AUDIO
&& frame->hdr.type != NGX_RTMP_MSG_AMF_META
&& frame->hdr.type != NGX_RTMP_MSG_AMF3_META)
{
ngx_rtmp_shared_free_frame(frame);
++s->out_pos;
s->out_pos %= s->out_queue;
frame = NULL;
continue;
}
break;
}
/* no frame to send */
if (frame == NULL) {
return NULL;
}
for (ll = &head; *ll; ll = &(*ll)->next);
for (cl = frame->chain; cl; cl = cl->next) {
datasize += (cl->buf->last - cl->buf->pos);
}
prev_tag_size = datasize + NGX_FLV_TAG_SIZE;
/* flv tag header */
*ll = ngx_get_chainbuf(NGX_FLV_TAG_SIZE, 1);
if (*ll == NULL) {
goto falied;
}
p = (*ll)->buf->pos;
/* TagType 1 byte */
*p++ = frame->hdr.type;
/* DataSize 3 bytes */
*p++ = ((u_char *) &datasize)[2];
*p++ = ((u_char *) &datasize)[1];
*p++ = ((u_char *) &datasize)[0];
/* Timestamp 4 bytes */
*p++ = ((u_char *) &frame->hdr.timestamp)[2];
*p++ = ((u_char *) &frame->hdr.timestamp)[1];
*p++ = ((u_char *) &frame->hdr.timestamp)[0];
*p++ = ((u_char *) &frame->hdr.timestamp)[3];
/* StreamID 4 bytes, always set to 0 */
*p++ = 0;
*p++ = 0;
*p++ = 0;
(*ll)->buf->last = p;
ll = &(*ll)->next;
/* flv payload */
for (cl = frame->chain; cl; cl = cl->next) {
(*ll) = ngx_get_chainbuf(0, 0);
if (*ll == NULL) {
goto falied;
}
(*ll)->buf->pos = cl->buf->pos;
(*ll)->buf->last = cl->buf->last;
ll = &(*ll)->next;
}
/* flv previous tag size */
*ll = ngx_get_chainbuf(NGX_FLV_PTS_SIZE, 1);
if (*ll == NULL) {
goto falied;
}
p = (*ll)->buf->pos;
*p++ = ((u_char *) &prev_tag_size)[3];
*p++ = ((u_char *) &prev_tag_size)[2];
*p++ = ((u_char *) &prev_tag_size)[1];
*p++ = ((u_char *) &prev_tag_size)[0];
(*ll)->buf->last = p;
(*ll)->buf->flush = 1;
ngx_rtmp_monitor_frame(s, &frame->hdr, NULL, frame->av_header, 0);
return head;
falied:
ngx_put_chainbufs(head);
ngx_rtmp_finalize_session(s);
return NULL;
}
static void
ngx_http_flv_live_write_handler(ngx_http_request_t *r)
{
ngx_http_flv_live_ctx_t *ctx;
ngx_rtmp_session_t *s;
ngx_event_t *wev;
size_t present, sent;
ngx_int_t rc;
ngx_chain_t *cl;
wev = r->connection->write;
if (r->connection->destroyed) {
return;
}
ctx = ngx_http_get_module_ctx(r, ngx_http_flv_live_module);
s = ctx->session;
if (wev->timedout) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, NGX_ETIMEDOUT,
"http flv live, client timed out");
r->connection->timedout = 1;
s->finalize_reason = NGX_LIVE_FLV_SEND_TIMEOUT;
if (r->header_sent) {
ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT);
} else {
r->error_page = 1;
ngx_http_finalize_request(r, NGX_HTTP_SERVICE_UNAVAILABLE);
}
return;
}
if (wev->timer_set) {
ngx_del_timer(wev);
}
if (ngx_rtmp_core_main_conf->fast_reload && (ngx_exiting || ngx_terminate)) {
r->error_page = 1;
ngx_http_finalize_request(r, NGX_HTTP_SERVICE_UNAVAILABLE);
return;
}
if (ngx_rtmp_prepare_merge_frame(s) == NGX_ERROR) {
ngx_http_finalize_request(r, NGX_ERROR);
return;
}
if (s->out_chain) {
rc = ngx_http_flv_live_send_header(r);
if (rc == NGX_ERROR || rc > NGX_OK) {
s->finalize_reason = NGX_LIVE_FLV_SEND_ERR;
ngx_http_finalize_request(r, rc);
return;
}
}
while (s->out_chain) {
present = r->connection->sent;
if (r->connection->buffered) {
rc = ngx_http_output_filter(r, NULL);
} else {
rc = ngx_http_output_filter(r, s->out_chain);
}
sent = r->connection->sent - present;
ngx_rtmp_update_bandwidth(&ngx_rtmp_bw_out, sent);
if (rc == NGX_AGAIN) {
ngx_add_timer(wev, s->timeout);
if (ngx_handle_write_event(wev, 0) != NGX_OK) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, ngx_errno,
"http flv live, handle write event failed");
ngx_http_finalize_request(r, NGX_ERROR);
}
return;
}
if (rc == NGX_ERROR) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, ngx_errno,
"http flv live, send error");
s->finalize_reason = NGX_LIVE_FLV_SEND_ERR;
ngx_http_finalize_request(r, NGX_ERROR);
return;
}
/* NGX_OK */
for (cl = s->out_chain; cl;) {
s->out_chain = cl->next;
ngx_free_chain(s->pool, cl);
cl = s->out_chain;
}
if (ngx_rtmp_prepare_merge_frame(s) == NGX_ERROR) {
ngx_http_finalize_request(r, NGX_ERROR);
return;
}
}
if (wev->active) {
ngx_del_event(wev, NGX_WRITE_EVENT, 0);
}
}
static void
ngx_http_flv_live_parse_url(ngx_http_request_t *r, ngx_str_t *app,
ngx_str_t *name)
{
u_char *p, *end, *pos;
p = r->uri.data + 1; /* skip '/' */
end = r->uri.data + r->uri.len;
app->data = p;
pos = ngx_strnstr(p, ".flv", end - p);
if (pos) {
end = pos;
}
p = (u_char *) ngx_strnstr(p, "/", end - p);
while (p) {
name->data = p;
p = (u_char *) ngx_strnstr(p + 1, "/", end - p);
}
if (name->data == NULL) {
return;
}
app->len = name->data - app->data;
++name->data;
name->len = end - name->data;
}
static ngx_int_t
ngx_http_flv_live_parse(ngx_http_request_t *r, ngx_rtmp_session_t *s,
ngx_rtmp_play_t *v)
{
ngx_http_flv_live_loc_conf_t *hflcf;
ngx_str_t app, stream;
size_t tcurl_len;
u_char *p;
hflcf = ngx_http_get_module_loc_conf(r, ngx_http_flv_live_module);
ngx_memzero(&app, sizeof(ngx_str_t));
ngx_memzero(&stream, sizeof(ngx_str_t));
ngx_http_flv_live_parse_url(r, &app, &stream);
if (app.len == 0 || stream.len == 0 || stream.len > NGX_RTMP_MAX_NAME) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"http flv live, url error: %V", &r->uri);
return NGX_HTTP_BAD_REQUEST;
}
if (hflcf->app.len) {
app = hflcf->app;
}
if (ngx_http_arg(r, (u_char *) "flashver", 8, &s->flashver) != NGX_OK) {
s->flashver = hflcf->flashver;
}
ngx_http_arg(r, (u_char *) "app", 3, &app);
s->app = app;
/* tc_url */
#if (NGX_HTTP_SSL)
if (r->connection->ssl) {
tcurl_len = sizeof("https://") - 1;
} else
#endif
{
tcurl_len = sizeof("http://") - 1;
}
tcurl_len += r->headers_in.server.len + 1 + app.len;
s->tc_url.len = tcurl_len;
s->tc_url.data = ngx_pcalloc(r->pool, tcurl_len);
if (s->tc_url.data == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
p = s->tc_url.data;
#if (NGX_HTTP_SSL)
if (r->connection->ssl) {
p = ngx_cpymem(p, "https://", sizeof("https://") - 1);
} else
#endif
{
p = ngx_cpymem(p, "http://", sizeof("http://") - 1);
}
p = ngx_cpymem(p, r->headers_in.server.data, r->headers_in.server.len);
*p++ = '/';
p = ngx_cpymem(p, app.data, app.len);
/* page_url */
if (r->headers_in.referer) {
s->page_url = r->headers_in.referer->value;
} else {
s->page_url = hflcf->page_url;
}
s->acodecs = 0x0DF7;
s->vcodecs = 0xFC;
ngx_memcpy(v->name, stream.data, stream.len);
if (r->args.len) {
ngx_memcpy(v->args, r->args.data,
ngx_min(r->args.len, NGX_RTMP_MAX_ARGS));
}
ngx_rtmp_cmd_middleware_init(s);
return NGX_OK;
}
static void
ngx_http_flv_live_cleanup(void *data)
{
ngx_http_request_t *r;
ngx_http_flv_live_ctx_t *ctx;
r = data;
ctx = ngx_http_get_module_ctx(r, ngx_http_flv_live_module);
if (ctx == NULL) {
return;
}
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"http flv live, cleanup");
if (ctx->session) {
ctx->session->request = NULL;
if (ctx->session->finalize_reason == 0) {
ctx->session->finalize_reason = r->connection->read->error?
NGX_LIVE_FLV_RECV_ERR:
NGX_LIVE_NORMAL_CLOSE;
}
ngx_rtmp_finalize_fake_session(ctx->session);
}
}
static ngx_int_t
ngx_http_flv_live_handler(ngx_http_request_t *r)
{
ngx_http_flv_live_loc_conf_t *hflcf;
ngx_http_flv_live_ctx_t *ctx;
ngx_rtmp_session_t *s;
ngx_rtmp_play_t v;
ngx_int_t rc;
ngx_uint_t n;
ngx_rtmp_core_srv_conf_t *cscf;
ngx_rtmp_core_app_conf_t **cacfp;
ngx_http_cleanup_t *cln;
ngx_rtmp_core_main_conf_t *cmcf;
rc = ngx_http_discard_request_body(r);
if (rc != NGX_OK) {
return rc;
}
ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_flv_live_ctx_t));
if (ctx == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
ngx_http_set_ctx(r, ctx, ngx_http_flv_live_module);
/* cleanup handler */
cln = ngx_http_cleanup_add(r, 0);
if (cln == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
cln->handler = ngx_http_flv_live_cleanup;
cln->data = r;
hflcf = ngx_http_get_module_loc_conf(r, ngx_http_flv_live_module);
/* create fake session */
s = ngx_rtmp_create_session(hflcf->addr_conf);
if (s == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
s->connection = r->connection;
s->number = r->connection->number;
s->remote_addr_text.data = ngx_pcalloc(s->pool, r->connection->addr_text.len);
s->remote_addr_text.len = r->connection->addr_text.len;
ngx_memcpy(s->remote_addr_text.data,
r->connection->addr_text.data, r->connection->addr_text.len);
ngx_rtmp_set_combined_log(s, r->connection->log->data,
r->connection->log->handler);
s->log->connection = r->connection->number;
ctx->session = s;
/* get host, app, stream name */
ngx_memzero(&v, sizeof(ngx_rtmp_play_t));
rc = ngx_http_flv_live_parse(r, s, &v);
if (rc != NGX_OK) {
return rc;
}
if (ngx_rtmp_set_virtual_server(s, &s->domain)) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
cscf = ngx_rtmp_get_module_srv_conf(s, ngx_rtmp_core_module);
s->live_type = NGX_HTTP_FLV_LIVE;
s->live_server = ngx_live_create_server(&s->serverid);
s->request = r;
v.silent = 1;
cacfp = cscf->applications.elts;
for (n = 0; n < cscf->applications.nelts; ++n, ++cacfp) {
if ((*cacfp)->name.len == s->app.len &&
ngx_strncmp((*cacfp)->name.data, s->app.data, s->app.len) == 0)
{
/* found app! */
s->app_conf = (*cacfp)->app_conf;
break;
}
}
if (s->app_conf == NULL) {
if (cscf->default_app == NULL || cscf->default_app->app_conf == NULL) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"http flv live, application not found '%V'", &s->app);
return NGX_HTTP_NOT_FOUND;
}
s->app_conf = cscf->default_app->app_conf;
}
s->prepare_handler = ngx_http_flv_live_prepare_out_chain;
s->stage = NGX_LIVE_PLAY;
s->ptime = ngx_current_msec;
cmcf = ngx_rtmp_get_module_main_conf(s, ngx_rtmp_core_module);
s->variables = ngx_pcalloc(s->pool, cmcf->variables.nelts
* sizeof(ngx_http_variable_value_t));
if (s->variables == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
if (ngx_rtmp_play_filter(s, &v) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
ngx_add_timer(r->connection->write, s->timeout);
r->read_event_handler = ngx_http_test_reading;
r->write_event_handler = ngx_http_flv_live_write_handler;
++r->count;
return NGX_DONE;
}
static void *
ngx_http_flv_live_create_loc_conf(ngx_conf_t *cf)
{
ngx_http_flv_live_loc_conf_t *hflcf;
hflcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_flv_live_loc_conf_t));
if (hflcf == NULL) {
return NULL;
}
return hflcf;
}
static char *
ngx_http_flv_live_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_flv_live_loc_conf_t *prev = parent;
ngx_http_flv_live_loc_conf_t *conf = child;
ngx_conf_merge_str_value(conf->app, prev->app, "");
ngx_conf_merge_str_value(conf->flashver, prev->flashver, "");
ngx_conf_merge_str_value(conf->swf_url, prev->swf_url, "");
ngx_conf_merge_str_value(conf->tc_url, prev->tc_url, "");
ngx_conf_merge_str_value(conf->page_url, prev->page_url, "");
return NGX_CONF_OK;
}
static char *
ngx_http_flv_live(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_core_loc_conf_t *clcf;
ngx_http_flv_live_loc_conf_t *hflcf;
ngx_str_t *value, v;
ngx_uint_t i;
ngx_uint_t audio, video;
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
clcf->handler = ngx_http_flv_live_handler;
hflcf = conf;
value = cf->args->elts;
hflcf->addr_conf = ngx_rtmp_find_related_addr_conf(cf->cycle, &value[1]);
if (hflcf->addr_conf == NULL) {
return NGX_CONF_ERROR;
}
audio = NGX_CONF_UNSET_UINT;
video = NGX_CONF_UNSET_UINT;
for (i = 2; i < cf->args->nelts; ++i) {
if (ngx_strncmp(value[i].data, "app=", 4) == 0) {
v.data = value[i].data + 4;
v.len = value[i].len - 4;
hflcf->app = v;
} else if (ngx_strncmp(value[i].data, "audio=", 6) == 0) {
v.data = value[i].data + 6;
v.len = value[i].len - 6;
audio = ngx_atoi(v.data, v.len);
} else if (ngx_strncmp(value[i].data, "video=", 6) == 0) {
v.data = value[i].data + 6;
v.len = value[i].len - 6;
video = ngx_atoi(v.data, v.len);
} else {
return NGX_CONF_ERROR;
}
}
if (audio == NGX_CONF_UNSET_UINT) {
audio = 1;
}
if (video == NGX_CONF_UNSET_UINT) {
video = 1;
}
hflcf->audio = audio;
hflcf->video = video;
return NGX_CONF_OK;
}

View File

@ -1,282 +0,0 @@
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
typedef struct ngx_http_header_val_s ngx_http_header_val_t;
typedef ngx_int_t (*ngx_http_set_header_pt)(ngx_http_request_t *r,
ngx_http_header_val_t *hv, ngx_str_t *value);
struct ngx_http_header_val_s {
ngx_http_complex_value_t value;
ngx_uint_t hash;
ngx_str_t key;
ngx_http_set_header_pt handler;
ngx_uint_t offset;
};
typedef struct {
ngx_str_t name;
ngx_uint_t offset;
ngx_http_set_header_pt handler;
} ngx_http_set_header_t;
//TODO need fill all header set in future
/* for header has no quick link in ngx_http_headers_out_t */
static ngx_int_t ngx_http_set_header_out_other(ngx_http_request_t *r,
ngx_http_header_val_t *hv, ngx_str_t *value);
/* for header has quick link like ngx_table_elt_t* in ngx_http_headers_out_t */
static ngx_int_t ngx_http_set_header_out_builtin(ngx_http_request_t *r,
ngx_http_header_val_t *hv, ngx_str_t *value);
/* for header has quick link like ngx_array_t in ngx_http_headers_out_t */
static ngx_int_t ngx_http_set_header_out_builtin_multi(ngx_http_request_t *r,
ngx_http_header_val_t *hv, ngx_str_t *value);
/* for header has quick link like ngx_table_elt_t* and
* other attribute in ngx_http_headers_out_t defined below */
static ngx_int_t ngx_http_set_header_out_content_type(ngx_http_request_t *r,
ngx_http_header_val_t *hv, ngx_str_t *value);
static ngx_http_set_header_t ngx_http_set_header_out_handlers[] = {
{ ngx_string("Server"),
offsetof(ngx_http_headers_out_t, server),
ngx_http_set_header_out_builtin },
{ ngx_string("Date"),
offsetof(ngx_http_headers_out_t, date),
ngx_http_set_header_out_builtin },
{ ngx_string("Content-Type"),
offsetof(ngx_http_headers_out_t, content_type),
ngx_http_set_header_out_content_type },
{ ngx_string("Cache-Control"),
offsetof(ngx_http_headers_out_t, cache_control),
ngx_http_set_header_out_builtin_multi },
{ ngx_null_string, 0, ngx_http_set_header_out_other }
};
static ngx_int_t
ngx_http_set_header_out_helper(ngx_http_request_t *r,
ngx_http_header_val_t *hv, ngx_str_t *value, unsigned no_create)
{
ngx_table_elt_t *h;
ngx_list_part_t *part;
ngx_uint_t i;
part = &r->headers_out.headers.part;
h = part->elts;
for (i = 0; /* void */; ++i) {
if (i >= part->nelts) {
if (part->next == NULL) {
break;
}
part = part->next;
h = part->elts;
i = 0;
}
if (h[i].hash != 0
&& h[i].key.len == hv->key.len
&& ngx_strncasecmp(hv->key.data, h[i].key.data, h[i].key.len) == 0)
/* header has been set */
{
h[i].value = *value;
if (value->len == 0) { /* if value is empty, remove header */
h[i].hash = 0;
} else {
h[i].hash = hv->hash;
}
return NGX_OK;
}
}
if (no_create && value->len == 0) { /* set header to empty but header cannot found */
return NGX_OK;
}
/* header has not been set, create it */
h = ngx_list_push(&r->headers_out.headers);
if (h == NULL) {
return NGX_ERROR;
}
h->hash = hv->hash;
h->key = hv->key;
h->value = *value;
h->lowcase_key = ngx_pnalloc(r->pool, h->key.len);
if (h->lowcase_key == NULL) {
return NGX_ERROR;
}
ngx_strlow(h->lowcase_key, h->key.data, h->key.len);
return NGX_OK;
}
static ngx_int_t
ngx_http_set_header_out_other(ngx_http_request_t * r,
ngx_http_header_val_t * hv,ngx_str_t * value)
{
return ngx_http_set_header_out_helper(r, hv, value, 0);
}
static ngx_int_t
ngx_http_set_header_out_builtin(ngx_http_request_t *r,
ngx_http_header_val_t *hv, ngx_str_t *value)
{
ngx_table_elt_t *h, **old;
if (hv->offset) {
old = (ngx_table_elt_t **) ((char *) &r->headers_out + hv->offset);
} else {
old = NULL;
}
if (old == NULL || *old == NULL) {
/* user should use ngx_http_set_header_out_other but use this func to set header */
//TODO
return ngx_http_set_header_out_helper(r, hv, value, 0);
}
h = *old;
h->value = *value;
if (value->len == 0) { /* if value is empty, remove header */
h->hash = 0;
return NGX_OK;
}
h->hash = hv->hash;
h->key = hv->key;
return NGX_OK;
}
static ngx_int_t
ngx_http_set_header_out_builtin_multi(ngx_http_request_t *r,
ngx_http_header_val_t *hv, ngx_str_t *value)
{
ngx_array_t *pa;
ngx_table_elt_t *ho, **ph;
ngx_uint_t i;
pa = (ngx_array_t *) ((char *) &r->headers_out + hv->offset);
if (pa->elts == NULL) {
if (ngx_array_init(pa, r->pool, 2, sizeof(ngx_table_elt_t *))
!= NGX_OK)
{
return NGX_ERROR;
}
}
if (pa->nelts > 0) {
ph = pa->elts;
for (i = 1; i < pa->nelts; i++) { /* clear old value */
ph[i]->hash = 0;
ph[i]->value.len = 0;
}
ph[0]->value = *value;
if (value->len == 0) {
ph[0]->hash = 0;
} else {
ph[0]->hash = hv->hash;
}
return NGX_OK;
}
/* header does not set */
ph = ngx_array_push(pa);
if (ph == NULL) {
return NGX_ERROR;
}
ho = ngx_list_push(&r->headers_out.headers);
if (ho == NULL) {
return NGX_ERROR;
}
ho->value = *value;
ho->hash = hv->hash;
ho->key = hv->key;
*ph = ho;
return NGX_OK;
}
static ngx_int_t
ngx_http_set_header_out_content_type(ngx_http_request_t *r,
ngx_http_header_val_t *hv, ngx_str_t *value)
{
ngx_uint_t i;
r->headers_out.content_type_len = value->len;
for (i = 0; i < value->len; i++) {
if (value->data[i] == ';') {
r->headers_out.content_type_len = i;
break;
}
}
r->headers_out.content_type = *value;
r->headers_out.content_type_hash = hv->hash;
r->headers_out.content_type_lowcase = NULL;
value->len = 0;
return ngx_http_set_header_out_helper(r, hv, value, 1);
}
ngx_int_t
ngx_http_set_header_out(ngx_http_request_t *r, ngx_str_t *key, ngx_str_t *value)
{
ngx_http_header_val_t hv;
ngx_http_set_header_t *handlers = ngx_http_set_header_out_handlers;
ngx_uint_t i;
ngx_str_t v;
hv.hash = ngx_hash_key_lc(key->data, key->len);
hv.key = *key;
hv.offset = 0;
hv.handler = NULL;
for (i = 0; handlers[i].name.len; ++i) {
if (hv.key.len != handlers[i].name.len
|| ngx_strncasecmp(hv.key.data, handlers[i].name.data,
handlers[i].name.len) != 0) {
continue;
}
/* match handler */
hv.offset = handlers[i].offset;
hv.handler = handlers[i].handler;
break;
}
if (handlers[i].name.len == 0 && handlers[i].handler) { /* if not matched, use ngx_http_set_header as default*/
hv.offset = handlers[i].offset;
hv.handler = handlers[i].handler;
}
v = *value;
return hv.handler(r, &hv, &v);
}

View File

@ -1,11 +0,0 @@
#ifndef _NGX_RTMP_HTTP_HEADER_OUT_H_INCLUDED_
#define _NGX_RTMP_HTTP_HEADER_OUT_H_INCLUDED_
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
ngx_int_t ngx_http_set_header_out(ngx_http_request_t *r,
ngx_str_t *key, ngx_str_t *value);
#endif

View File

@ -1,95 +0,0 @@
/*
* Copyright (C) Pingo (cczjp89@gmail.com)
*/
#ifndef _NGX_HLS_LIVE_MODULE_H_INCLUDE_
#define _NGX_HLS_LIVE_MODULE_H_INCLUDE_
#define ngx_hls_live_next(s, pos) ((pos + 1) % s->out_queue)
#define ngx_hls_live_prev(s, pos) (pos == 0 ? s->out_queue - 1 : pos - 1)
typedef struct ngx_hls_live_frag_s ngx_hls_live_frag_t;
typedef struct ngx_hls_live_play_s ngx_hls_live_play_t;
struct ngx_hls_live_play_s {
ngx_str_t name;
/* connection parameters */
ngx_rtmp_addr_conf_t *addr_conf;
ngx_str_t app;
ngx_str_t stream;
ngx_str_t args;
ngx_str_t flashver;
ngx_str_t swf_url;
ngx_str_t tc_url;
uint32_t acodecs;
uint32_t vcodecs;
ngx_str_t page_url;
ngx_str_t domain;
ngx_str_t serverid;
ngx_log_t *log;
};
struct ngx_hls_live_frag_s {
ngx_uint_t ref;
ngx_hls_live_frag_t *next;
time_t last_modified_time;
uint64_t id;
uint64_t key_id;
double duration;
unsigned active:1;
unsigned discont:1; /* before */
ngx_uint_t length;
ngx_chain_t *out;
ngx_uint_t content_last;
ngx_uint_t content_pos;
ngx_mpegts_frame_t *content[0];
};
struct ngx_hls_live_ctx_s {
unsigned opened:1;
unsigned playing:1;
ngx_buf_t *patpmt;
ngx_rtmp_session_t *session;
ngx_str_t sid;
ngx_live_stream_t *stream;
ngx_str_t name;
uint64_t nfrag;
uint64_t frag_ts;
uint64_t key_id;
ngx_uint_t nfrags;
ngx_hls_live_frag_t **frags; /* circular 2 * winfrags + 1 */
ngx_hls_live_frag_t *frag;
ngx_uint_t audio_cc;
ngx_uint_t video_cc;
ngx_uint_t key_frags;
uint64_t aframe_base;
uint64_t aframe_num;
ngx_buf_t *aframe;
uint64_t aframe_pts;
ngx_event_t ev;
ngx_msec_t timeout;
ngx_msec_t last_time;
time_t playlist_modified_time;
ngx_buf_t *playlist;
ngx_hls_live_ctx_t *next;
};
ngx_int_t ngx_hls_live_write_playlist(ngx_rtmp_session_t *s, ngx_buf_t *out,
time_t *last_modified_time);
ngx_hls_live_frag_t* ngx_hls_live_find_frag(ngx_rtmp_session_t *s,
ngx_str_t *name);
ngx_chain_t* ngx_hls_live_prepare_frag(ngx_rtmp_session_t *s,
ngx_hls_live_frag_t *frag);
void ngx_hls_live_free_frag(ngx_hls_live_frag_t *frag);
ngx_rtmp_session_t* ngx_hls_live_fetch_session(ngx_str_t *server,
ngx_str_t *stream, ngx_str_t *session);
void ngx_rtmp_shared_acquire_frag(ngx_hls_live_frag_t *frag);
#endif

View File

@ -1,731 +0,0 @@
/*
* Copyright (C) Pingo (cczjp89@gmail.com)
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include "ngx_rtmp.h"
#include "ngx_rtmp_codec_module.h"
#include "ngx_rtmp_live_module.h"
#include "ngx_mpegts_live_module.h"
static ngx_rtmp_close_stream_pt next_close_stream;
static ngx_mpegts_video_pt next_mpegts_video;
static ngx_mpegts_audio_pt next_mpegts_audio;
static void *ngx_mpegts_gop_create_app_conf(ngx_conf_t *cf);
static char *ngx_mpegts_gop_merge_app_conf(ngx_conf_t *cf, void *parent,
void *child);
static ngx_int_t ngx_mpegts_gop_postconfiguration(ngx_conf_t *cf);
#define ngx_mpegts_gop_next(s, pos) ((pos + 1) % s->out_queue)
#define ngx_mpegts_gop_prev(s, pos) (pos == 0 ? s->out_queue - 1 : pos - 1)
typedef struct {
/* publisher: head of cache
* player: cache send position of publisher's out
*/
size_t gop_pos;
/* tail of cache */
size_t gop_last;
/* 0 for not send, 1 for sending, 2 for sent */
ngx_flag_t send_gop;
ngx_mpegts_frame_t *keyframe;
ngx_uint_t meta_version;
uint64_t first_timestamp;
uint64_t current_timestamp;
ngx_uint_t base_type;
/* only for publisher, must at last of ngx_mpegts_gop_ctx_t */
ngx_mpegts_frame_t *cache[];
} ngx_mpegts_gop_ctx_t;
typedef struct {
ngx_msec_t cache_time;
ngx_msec_t roll_back;
ngx_msec_t one_off_send;
ngx_flag_t low_latency;
ngx_flag_t send_all;
ngx_msec_t fix_timestamp;
ngx_flag_t zero_start;
} ngx_mpegts_gop_app_conf_t;
static ngx_command_t ngx_mpegts_gop_commands[] = {
{ ngx_string("mpegts_cache_time"),
NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_RTMP_APP_CONF_OFFSET,
offsetof(ngx_mpegts_gop_app_conf_t, cache_time),
NULL },
{ ngx_string("mpegts_roll_back"),
NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_RTMP_APP_CONF_OFFSET,
offsetof(ngx_mpegts_gop_app_conf_t, roll_back),
NULL },
{ ngx_string("mpegts_one_off_send"),
NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_RTMP_APP_CONF_OFFSET,
offsetof(ngx_mpegts_gop_app_conf_t, one_off_send),
NULL },
{ ngx_string("mpegts_low_latency"),
NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
ngx_conf_set_flag_slot,
NGX_RTMP_APP_CONF_OFFSET,
offsetof(ngx_mpegts_gop_app_conf_t, low_latency),
NULL },
{ ngx_string("mpegts_send_all"),
NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
ngx_conf_set_flag_slot,
NGX_RTMP_APP_CONF_OFFSET,
offsetof(ngx_mpegts_gop_app_conf_t, send_all),
NULL },
{ ngx_string("mpegts_fix_timestamp"),
NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_RTMP_APP_CONF_OFFSET,
offsetof(ngx_mpegts_gop_app_conf_t, fix_timestamp),
NULL },
{ ngx_string("mpegts_zero_start"),
NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
ngx_conf_set_flag_slot,
NGX_RTMP_APP_CONF_OFFSET,
offsetof(ngx_mpegts_gop_app_conf_t, zero_start),
NULL },
ngx_null_command
};
static ngx_rtmp_module_t ngx_mpegts_gop_module_ctx = {
NULL, /* preconfiguration */
ngx_mpegts_gop_postconfiguration, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_mpegts_gop_create_app_conf, /* create app configuration */
ngx_mpegts_gop_merge_app_conf /* merge app configuration */
};
ngx_module_t ngx_mpegts_gop_module = {
NGX_MODULE_V1,
&ngx_mpegts_gop_module_ctx, /* module context */
ngx_mpegts_gop_commands, /* module directives */
NGX_RTMP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static void *
ngx_mpegts_gop_create_app_conf(ngx_conf_t *cf)
{
ngx_mpegts_gop_app_conf_t *gacf;
gacf = ngx_pcalloc(cf->pool, sizeof(ngx_mpegts_gop_app_conf_t));
if (gacf == NULL) {
return NULL;
}
gacf->cache_time = NGX_CONF_UNSET_MSEC;
gacf->roll_back = NGX_CONF_UNSET_MSEC;
gacf->one_off_send = NGX_CONF_UNSET_MSEC;
gacf->low_latency = NGX_CONF_UNSET;
gacf->send_all = NGX_CONF_UNSET;
gacf->fix_timestamp = NGX_CONF_UNSET_MSEC;
gacf->zero_start = NGX_CONF_UNSET;
return gacf;
}
static char *
ngx_mpegts_gop_merge_app_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_mpegts_gop_app_conf_t *prev = parent;
ngx_mpegts_gop_app_conf_t *conf = child;
ngx_conf_merge_msec_value(conf->cache_time, prev->cache_time, 0);
ngx_conf_merge_msec_value(conf->roll_back, prev->roll_back, conf->cache_time);
ngx_conf_merge_msec_value(conf->one_off_send, prev->one_off_send, 3000);
ngx_conf_merge_value(conf->low_latency, prev->low_latency, 0);
ngx_conf_merge_value(conf->send_all, prev->send_all, 0);
ngx_conf_merge_msec_value(conf->fix_timestamp, prev->fix_timestamp, 10000);
ngx_conf_merge_value(conf->zero_start, prev->zero_start, 0);
return NGX_CONF_OK;
}
static ngx_int_t
ngx_mpegts_gop_link_frame(ngx_rtmp_session_t *s, ngx_mpegts_frame_t *frame)
{
ngx_uint_t nmsg;
if (frame == NULL) {
return NGX_OK;
}
nmsg = (s->out_last - s->out_pos) % s->out_queue + 1;
if (nmsg >= s->out_queue) {
ngx_log_error(NGX_LOG_ERR, s->log, 0,
"link frame nmsg(%ui) >= out_queue(%O)", nmsg, s->out_queue);
return NGX_AGAIN;
}
#if 0
ngx_log_error(NGX_LOG_DEBUG, s->log, 0,
"link frame pos[%O] last[%O], pts[%ud]",
s->out_pos, s->out_last, frame->pts);
#endif
s->mpegts_out[s->out_last] = frame;
s->out_last = ngx_mpegts_gop_next(s, s->out_last);
ngx_rtmp_shared_acquire_mpegts_frame(frame);
return NGX_OK;
}
static void
ngx_mpegts_gop_reset_gop(ngx_rtmp_session_t *s, ngx_mpegts_gop_ctx_t *ctx,
ngx_mpegts_frame_t *frame)
{
ngx_mpegts_gop_app_conf_t *gacf;
ngx_mpegts_frame_t *f, *next_keyframe;
size_t pos;
ngx_uint_t nmsg;
ngx_msec_t cache_time;
pos = ctx->gop_pos;
f = ctx->cache[pos];
if (f == NULL) {
return;
}
gacf = ngx_rtmp_get_module_app_conf(s, ngx_mpegts_gop_module);
cache_time = gacf->cache_time > gacf->roll_back ?
gacf->cache_time : gacf->roll_back;
/* only audio in cache */
if (ctx->keyframe == NULL) {
if (frame->pts - ctx->cache[ctx->gop_pos]->pts > cache_time * 90) {
ngx_rtmp_shared_free_mpegts_frame(f);
ctx->cache[ctx->gop_pos] = NULL;
ctx->gop_pos = ngx_mpegts_gop_next(s, ctx->gop_pos);
}
return;
}
/* only video of video + audio */
next_keyframe = ctx->keyframe->next;
/* only one gop in cache */
if (next_keyframe == NULL) {
return;
}
nmsg = (ctx->gop_last - ctx->gop_pos) % s->out_queue + 2;
if (nmsg >= s->out_queue) {
goto reset;
}
if (frame->type == NGX_MPEGTS_MSG_AUDIO) {
return;
}
if (frame->type == NGX_MPEGTS_MSG_VIDEO && frame->pts
- next_keyframe->pts < cache_time * 90)
{
return;
}
reset:
for (pos = ctx->gop_pos; ctx->cache[pos] != next_keyframe;
pos = ngx_mpegts_gop_next(s, pos))
{
f = ctx->cache[pos];
ngx_rtmp_shared_free_mpegts_frame(f);
ctx->cache[pos] = NULL;
}
ctx->keyframe = next_keyframe;
ctx->gop_pos = pos;
}
static void
ngx_mpegts_gop_print_cache(ngx_rtmp_session_t *s, ngx_mpegts_gop_ctx_t *ctx)
{
#if (NGX_DEBUG)
ngx_mpegts_frame_t *frame;
u_char content[10240], *p;
size_t pos;
ngx_memzero(content, sizeof(content));
p = content;
for (pos = ctx->gop_pos; pos != ctx->gop_last;
pos = ngx_mpegts_gop_next(s, pos))
{
frame = ctx->cache[pos];
switch (frame->type) {
case NGX_MPEGTS_MSG_AUDIO:
*p++ = 'A';
break;
case NGX_MPEGTS_MSG_VIDEO:
*p++ = 'V';
break;
default:
*p++ = 'O';
break;
}
if (frame->key) {
*p++ = 'I';
}
*p++ = ' ';
}
ngx_log_error(NGX_LOG_DEBUG, s->log, 0,
"[%z %z] %s", ctx->gop_pos, ctx->gop_last, content);
#endif
}
ngx_int_t
ngx_mpegts_gop_cache(ngx_rtmp_session_t *s, ngx_mpegts_frame_t *frame)
{
ngx_mpegts_gop_app_conf_t *gacf;
ngx_mpegts_gop_ctx_t *ctx;
ngx_mpegts_frame_t **keyframe;
ngx_uint_t nmsg;
gacf = ngx_rtmp_get_module_app_conf(s, ngx_mpegts_gop_module);
if (gacf->cache_time == 0) {
return NGX_OK;
}
ctx = ngx_rtmp_get_module_ctx(s, ngx_mpegts_gop_module);
if (ctx == NULL) {
ctx = ngx_pcalloc(s->pool, sizeof(ngx_mpegts_gop_ctx_t)
+ s->out_queue * sizeof(ngx_mpegts_frame_t *));
if (ctx == NULL) {
return NGX_ERROR;
}
ngx_rtmp_set_ctx(s, ctx, ngx_mpegts_gop_module);
}
nmsg = (ctx->gop_last - ctx->gop_pos) % s->out_queue + 1;
if (nmsg >= s->out_queue) {
ngx_log_error(NGX_LOG_ERR, s->log, 0,
"cache frame nmsg(%ui) >= out_queue(%z)", nmsg, s->out_queue);
return NGX_AGAIN;
}
#if 0
ngx_log_error(NGX_LOG_DEBUG, s->log, 0,
"cache frame: %ud[%d], %ud, %ud",
frame->type, frame->key, frame->pts, frame->length);
#endif
/* first video frame is not intra_frame */
if (ctx->keyframe == NULL && frame->type == NGX_MPEGTS_MSG_VIDEO
&& !frame->key)
{
return NGX_OK;
}
/* video intra_frame */
if (frame->key) {
for (keyframe = &ctx->keyframe; *keyframe;
keyframe = &((*keyframe)->next));
*keyframe = frame;
}
frame->pos = ctx->gop_last;
ctx->current_timestamp = frame->pts;
ctx->cache[ctx->gop_last] = frame;
ctx->gop_last = ngx_mpegts_gop_next(s, ctx->gop_last);
ngx_rtmp_shared_acquire_mpegts_frame(frame);
ngx_mpegts_gop_reset_gop(s, ctx, frame);
ngx_mpegts_gop_print_cache(s, ctx);
return NGX_OK;
}
static ngx_int_t
ngx_mpegts_gop_send_gop(ngx_rtmp_session_t *s, ngx_rtmp_session_t *ss)
{
ngx_mpegts_gop_app_conf_t *gacf;
ngx_mpegts_gop_ctx_t *sctx, *ssctx;
ngx_mpegts_frame_t *frame, *keyframe;
size_t pos;
gacf = ngx_rtmp_get_module_app_conf(s, ngx_mpegts_gop_module);
sctx = ngx_rtmp_get_module_ctx(s, ngx_mpegts_gop_module);
ssctx = ngx_rtmp_get_module_ctx(ss, ngx_mpegts_gop_module);
/* already send gop */
if (ssctx->send_gop == 2) {
return NGX_OK;
}
//*****************************************************************
//
// TODO: send pat pmt
//
//*****************************************************************
/* link frame in s to ss */
if (ssctx->send_gop == 0) {
ssctx->gop_pos = sctx->gop_pos;
if (sctx->cache[ssctx->gop_pos] == NULL) {
return NGX_AGAIN;
}
ssctx->send_gop = 1;
ssctx->first_timestamp = sctx->cache[ssctx->gop_pos]->pts;
ssctx->base_type = sctx->cache[ssctx->gop_pos]->type;
} else {
if (sctx->cache[ssctx->gop_pos] == NULL) {
ssctx->gop_pos = sctx->gop_pos;
}
}
frame = NULL;
keyframe = sctx->keyframe;
while (ss->roll_back && keyframe &&
((sctx->current_timestamp - keyframe->pts) > ss->roll_back * 90))
{
ngx_log_error(NGX_LOG_INFO, s->log, 0,
"rtmp-gop: send_gop| curr %D - k %D, %D",
sctx->current_timestamp, keyframe->pts, ss->roll_back * 90);
frame = keyframe;
keyframe = keyframe->next;
}
if (frame == NULL) {
pos = ssctx->gop_pos;
frame = sctx->cache[pos];
} else {
pos = frame->pos;
}
while (frame) {
ngx_log_error(NGX_LOG_DEBUG, s->log, 0,
"send gop link %D, type %d, curr %D",
frame->pts/90, frame->type, sctx->current_timestamp/90);
if (ngx_mpegts_gop_link_frame(ss, frame) == NGX_AGAIN) {
break;
}
if (!gacf->send_all && frame->type == ssctx->base_type &&
frame->pts - ssctx->first_timestamp >= gacf->one_off_send * 90)
{
ngx_log_error(NGX_LOG_DEBUG, s->log, 0,
"gone %D, type %d, first %D, curr %D, send %D",
frame->pts/90, frame->type, ssctx->first_timestamp/90,
sctx->current_timestamp/90, gacf->one_off_send);
ssctx->send_gop = 2;
pos = ngx_mpegts_gop_next(s, pos);
break;
}
pos = ngx_mpegts_gop_next(s, pos);
frame = sctx->cache[pos];
}
if (frame == NULL) { /* send all frame in cache */
ssctx->send_gop = 2;
}
ssctx->gop_pos = pos;
ngx_rtmp_send_message(ss, NULL, 0);
return NGX_AGAIN;
}
ngx_int_t
ngx_mpegts_gop_send(ngx_rtmp_session_t *s, ngx_rtmp_session_t *ss)
{
ngx_mpegts_gop_app_conf_t *gacf;
ngx_mpegts_gop_ctx_t *sctx, *ssctx;
ngx_mpegts_frame_t *frame;
gacf = ngx_rtmp_get_module_app_conf(s, ngx_mpegts_gop_module);
if (gacf->cache_time == 0) {
return NGX_DECLINED;
}
sctx = ngx_rtmp_get_module_ctx(s, ngx_mpegts_gop_module);
if (sctx == NULL) { /* publisher doesn't publish av frame */
return NGX_DECLINED;
}
ssctx = ngx_rtmp_get_module_ctx(ss, ngx_mpegts_gop_module);
if (ssctx == NULL) {
ssctx = ngx_pcalloc(ss->pool, sizeof(ngx_mpegts_gop_ctx_t));
if (ssctx == NULL) {
return NGX_ERROR;
}
ngx_rtmp_set_ctx(ss, ssctx, ngx_mpegts_gop_module);
}
if (ngx_mpegts_gop_send_gop(s, ss) == NGX_AGAIN) {
return NGX_OK;
}
if (sctx->cache[ssctx->gop_pos] == NULL) {
ngx_log_error(NGX_LOG_ERR, ss->log, 0,
"mpegts-gop: gop_send| current gop pos is NULL, "
"skip to new postion [pos %d last %d] %d",
sctx->gop_pos, sctx->gop_last, ssctx->gop_pos);
ssctx->gop_pos = sctx->gop_pos;
}
frame = sctx->cache[ssctx->gop_pos];
if (ngx_mpegts_gop_link_frame(ss, frame) == NGX_AGAIN) {
return NGX_AGAIN;
}
ssctx->gop_pos = ngx_mpegts_gop_next(s, ssctx->gop_pos);
ngx_rtmp_send_message(ss, NULL, 0);
return NGX_OK;
}
static ngx_int_t
ngx_mpegts_gop_offset_frames(ngx_rtmp_session_t *s, ngx_rtmp_session_t *ss,
ngx_msec_t time_offset, ngx_msec_t duration)
{
ngx_mpegts_gop_ctx_t *sctx, *ssctx;
ngx_mpegts_frame_t *frame, *keyframe;
size_t pos;
sctx = ngx_rtmp_get_module_ctx(s, ngx_mpegts_gop_module);
ssctx = ngx_rtmp_get_module_ctx(ss, ngx_mpegts_gop_module);
/* already send gop */
if (ssctx->send_gop == 2) {
return NGX_OK;
}
//*****************************************************************
//
// TODO: send pat pmt
//
//*****************************************************************
/* link frame in s to ss */
if (ssctx->send_gop == 0) {
ssctx->gop_pos = sctx->gop_pos;
if (sctx->cache[ssctx->gop_pos] == NULL) {
return NGX_AGAIN;
}
ssctx->send_gop = 1;
ssctx->first_timestamp = sctx->cache[ssctx->gop_pos]->pts;
ssctx->base_type = sctx->cache[ssctx->gop_pos]->type;
} else {
if (sctx->cache[ssctx->gop_pos] == NULL) {
ssctx->gop_pos = sctx->gop_pos;
}
}
frame = NULL;
keyframe = sctx->keyframe;
while (time_offset && keyframe &&
((sctx->current_timestamp - keyframe->pts) > time_offset * 90))
{
ngx_log_error(NGX_LOG_DEBUG, ss->log, 0,
"mpegts-gop: offset_frames| curr %D - k %D = %D, %D",
sctx->current_timestamp, keyframe->pts,
(sctx->current_timestamp - keyframe->pts) / 90, time_offset);
frame = keyframe;
keyframe = keyframe->next;
ssctx->first_timestamp = frame->pts;
ssctx->base_type = frame->type;
}
if (frame == NULL) {
pos = ssctx->gop_pos;
frame = sctx->cache[pos];
} else {
pos = frame->pos;
}
while (frame) {
if (ngx_mpegts_gop_link_frame(ss, frame) == NGX_AGAIN) {
break;
}
if (frame->type == ssctx->base_type &&
frame->pts - ssctx->first_timestamp >= duration * 90)
{
ngx_log_error(NGX_LOG_INFO, ss->log, 0, "gone %D, first %D, curr %D",
frame->pts, ssctx->first_timestamp, sctx->current_timestamp);
ssctx->send_gop = 2;
pos = ngx_mpegts_gop_next(s, pos);
break;
}
pos = ngx_mpegts_gop_next(s, pos);
frame = sctx->cache[pos];
}
if (frame == NULL) { /* send all frame in cache */
ssctx->send_gop = 2;
}
ssctx->gop_pos = pos;
return NGX_AGAIN;
}
ngx_int_t
ngx_mpegts_gop_link(ngx_rtmp_session_t *s, ngx_rtmp_session_t *ss,
ngx_msec_t time_offset, ngx_msec_t duration)
{
ngx_mpegts_gop_app_conf_t *gacf;
ngx_mpegts_gop_ctx_t *sctx, *ssctx;
ngx_mpegts_frame_t *frame;
gacf = ngx_rtmp_get_module_app_conf(s, ngx_mpegts_gop_module);
if (gacf->cache_time == 0) {
return NGX_DECLINED;
}
sctx = ngx_rtmp_get_module_ctx(s, ngx_mpegts_gop_module);
if (sctx == NULL) { /* publisher doesn't publish av frame */
return NGX_DECLINED;
}
ssctx = ngx_rtmp_get_module_ctx(ss, ngx_mpegts_gop_module);
if (ssctx == NULL) {
ssctx = ngx_pcalloc(ss->pool, sizeof(ngx_mpegts_gop_ctx_t));
if (ssctx == NULL) {
return NGX_ERROR;
}
ngx_rtmp_set_ctx(ss, ssctx, ngx_mpegts_gop_module);
}
if (ngx_mpegts_gop_offset_frames(s, ss, time_offset, duration) == NGX_AGAIN)
{
return NGX_OK;
}
/* new frame is video key frame */
if (sctx->cache[ssctx->gop_pos] == NULL) {
ngx_log_error(NGX_LOG_ERR, ss->log, 0,
"mpegts-gop: link| current gop pos is NULL, "
"skip to new postion [pos %d last %d] %d",
sctx->gop_pos, sctx->gop_last, ssctx->gop_pos);
ssctx->gop_pos = sctx->gop_pos;
}
frame = sctx->cache[ssctx->gop_pos];
if (ngx_mpegts_gop_link_frame(ss, frame) == NGX_AGAIN) {
return NGX_AGAIN;
}
ssctx->gop_pos = ngx_mpegts_gop_next(s, ssctx->gop_pos);
return NGX_OK;
}
static ngx_int_t
ngx_mpegts_gop_close_stream(ngx_rtmp_session_t *s, ngx_rtmp_close_stream_t *v)
{
ngx_mpegts_gop_ctx_t *ctx;
ctx = ngx_rtmp_get_module_ctx(s, ngx_mpegts_gop_module);
if (ctx == NULL) {
goto next;
}
if (!s->published) {
goto next;
}
/* free cache in publisher */
while (ctx->gop_pos != ctx->gop_last) {
ngx_rtmp_shared_free_mpegts_frame(ctx->cache[ctx->gop_pos]);
ctx->gop_pos = ngx_mpegts_gop_next(s, ctx->gop_pos);
}
next:
return next_close_stream(s, v);
}
static ngx_int_t
ngx_mpegts_gop_av(ngx_rtmp_session_t *s, ngx_mpegts_frame_t *frame)
{
if (frame->type == NGX_MPEGTS_MSG_VIDEO) {
ngx_mpegts_gop_cache(s, frame);
return next_mpegts_video(s, frame);
} else if (frame->type == NGX_MPEGTS_MSG_AUDIO) {
ngx_mpegts_gop_cache(s, frame);
return next_mpegts_audio(s, frame);
}
return NGX_ERROR;
}
static ngx_int_t
ngx_mpegts_gop_postconfiguration(ngx_conf_t *cf)
{
next_close_stream = ngx_rtmp_close_stream;
ngx_rtmp_close_stream = ngx_mpegts_gop_close_stream;
next_mpegts_video = ngx_mpegts_video;
ngx_mpegts_video = ngx_mpegts_gop_av;
next_mpegts_audio = ngx_mpegts_audio;
ngx_mpegts_audio = ngx_mpegts_gop_av;
return NGX_OK;
}

View File

@ -1,19 +0,0 @@
/*
* Copyright (C) Pingo (cczjp89@gmail.com)
*/
#ifndef _NGX_MPEGTS_GOP_MODULE_H_INCLUDE_
#define _NGX_MPEGTS_GOP_MODULE_H_INCLUDE_
#include "ngx_rtmp.h"
ngx_int_t
ngx_mpegts_gop_cache(ngx_rtmp_session_t *s, ngx_mpegts_frame_t *frame);
ngx_int_t
ngx_mpegts_gop_link(ngx_rtmp_session_t *s, ngx_rtmp_session_t *ss,
ngx_msec_t time_offset, ngx_msec_t duration);
ngx_int_t
ngx_mpegts_gop_send(ngx_rtmp_session_t *s, ngx_rtmp_session_t *ss);
#endif

View File

@ -1,611 +0,0 @@
/*
* Copyright (C) Pingo (cczjp89@gmail.com)
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>
#include <ngx_http.h>
#include "ngx_rtmp.h"
#include "ngx_rtmp_cmd_module.h"
#include "ngx_rbuf.h"
#include "ngx_http_set_header.h"
#include "ngx_rtmp_monitor_module.h"
#include "ngx_mpegts_gop_module.h"
#include "ngx_mpegts_live_module.h"
static char *ngx_mpegts_http(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static void *ngx_mpegts_http_create_loc_conf(ngx_conf_t *cf);
static char *ngx_mpegts_http_merge_loc_conf(ngx_conf_t *cf, void *parent,
void *child);
static ngx_keyval_t ngx_mpegts_http_headers[] = {
{ ngx_string("Cache-Control"), ngx_string("no-cache") },
// { ngx_string("Content-Type"), ngx_string("video/x-ts") },
{ ngx_null_string, ngx_null_string }
};
typedef struct {
ngx_rtmp_session_t *session;
} ngx_mpegts_http_ctx_t;
typedef struct {
ngx_str_t app;
ngx_str_t flashver;
ngx_str_t swf_url;
ngx_str_t tc_url;
ngx_str_t page_url;
ngx_rtmp_addr_conf_t *addr_conf;
} ngx_mpegts_http_loc_conf_t;
static ngx_command_t ngx_mpegts_http_commands[] = {
{ ngx_string("ts_live"),
NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,
ngx_mpegts_http,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_mpegts_http_module_ctx = {
NULL, /* preconfiguration */
NULL, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_mpegts_http_create_loc_conf, /* create location configuration */
ngx_mpegts_http_merge_loc_conf /* merge location configuration */
};
ngx_module_t ngx_mpegts_http_module = {
NGX_MODULE_V1,
&ngx_mpegts_http_module_ctx, /* module context */
ngx_mpegts_http_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_int_t
ngx_mpegts_http_send_header(ngx_http_request_t *r)
{
ngx_int_t rc;
ngx_keyval_t *h;
ngx_chain_t out;
ngx_mpegts_http_ctx_t *ctx;
ngx_rtmp_session_t *s, *ps;
if (r->header_sent) {
return NGX_OK;
}
ctx = ngx_http_get_module_ctx(r, ngx_mpegts_http_module);
s = ctx->session;
ps = s->live_stream->publish_ctx->session;
r->headers_out.status = NGX_HTTP_OK;
r->keepalive = 0; /* set Connection to closed */
h = ngx_mpegts_http_headers;
while (h->key.len) {
rc = ngx_http_set_header_out(r, &h->key, &h->value);
if (rc != NGX_OK) {
return rc;
}
++h;
}
rc = ngx_http_send_header(r);
if (rc == NGX_ERROR || rc > NGX_OK) {
return rc;
}
ngx_memzero(&out, sizeof(out));
out.buf = ngx_create_temp_buf(s->pool, 376);
out.buf->last = ngx_cpymem(out.buf->pos,
ngx_rtmp_mpegts_pat, 188);
ngx_rtmp_mpegts_gen_pmt(ps->vcodec, ps->acodec, s->log, out.buf->last);
out.buf->last += 188;
out.buf->flush = 1;
out.buf->memory = 1;
return ngx_http_output_filter(r, &out);
}
static void
ngx_mpegts_http_write_handler(ngx_http_request_t *r)
{
ngx_mpegts_http_ctx_t *ctx;
ngx_rtmp_session_t *s;
ngx_event_t *wev;
size_t present, sent;
ngx_int_t rc;
ngx_chain_t *cl;
wev = r->connection->write;
if (r->connection->destroyed) {
return;
}
ctx = ngx_http_get_module_ctx(r, ngx_mpegts_http_module);
s = ctx->session;
if (wev->timedout) {
ngx_log_error(NGX_LOG_INFO, s->log, NGX_ETIMEDOUT,
"mpegts-http: write_handler| client timed out");
r->connection->timedout = 1;
s->finalize_reason = NGX_LIVE_FLV_SEND_TIMEOUT;
if (r->header_sent) {
ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT);
} else {
r->error_page = 1;
ngx_http_finalize_request(r, NGX_HTTP_SERVICE_UNAVAILABLE);
}
return;
}
if (wev->timer_set) {
ngx_del_timer(wev);
}
if (ngx_rtmp_core_main_conf->fast_reload && (ngx_exiting || ngx_terminate)) {
r->error_page = 1;
ngx_http_finalize_request(r, NGX_HTTP_SERVICE_UNAVAILABLE);
return;
}
if (ngx_rtmp_prepare_merge_frame(s) == NGX_ERROR) {
ngx_http_finalize_request(r, NGX_ERROR);
return;
}
if (s->out_chain) {
rc = ngx_mpegts_http_send_header(r);
if (rc == NGX_ERROR || rc > NGX_OK) {
s->finalize_reason = NGX_LIVE_FLV_SEND_ERR;
ngx_http_finalize_request(r, rc);
return;
}
}
while (s->out_chain) {
present = r->connection->sent;
if (r->connection->buffered) {
rc = ngx_http_output_filter(r, NULL);
} else {
rc = ngx_http_output_filter(r, s->out_chain);
}
sent = r->connection->sent - present;
ngx_rtmp_update_bandwidth(&ngx_rtmp_bw_out, sent);
if (rc == NGX_AGAIN) {
ngx_add_timer(wev, s->timeout);
if (ngx_handle_write_event(wev, 0) != NGX_OK) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, ngx_errno,
"mpegts-http: write_handler| write event failed");
ngx_http_finalize_request(r, NGX_ERROR);
}
return;
}
if (rc == NGX_ERROR) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, ngx_errno,
"mpegts-http: write_handler| send error");
s->finalize_reason = NGX_LIVE_FLV_SEND_ERR;
ngx_http_finalize_request(r, NGX_ERROR);
return;
}
/* NGX_OK */
for (cl = s->out_chain; cl;) {
s->out_chain = cl->next;
ngx_free_chain(s->pool, cl);
cl = s->out_chain;
}
if (ngx_rtmp_prepare_merge_frame(s) == NGX_ERROR) {
ngx_http_finalize_request(r, NGX_ERROR);
return;
}
}
if (wev->active) {
ngx_del_event(wev, NGX_WRITE_EVENT, 0);
}
}
static void
ngx_mpegts_http_parse_url(ngx_http_request_t *r, ngx_str_t *app,
ngx_str_t *name)
{
u_char *p, *end, *pos;
p = r->uri.data + 1; /* skip '/' */
end = r->uri.data + r->uri.len;
app->data = p;
pos = (u_char *) ngx_strnstr(p, ".ts", end - p);
if (pos) {
end = pos;
}
p = (u_char *) ngx_strnstr(p, "/", end - p);
while (p) {
name->data = p;
p = (u_char *) ngx_strnstr(p + 1, "/", end - p);
}
if (name->data == NULL) {
return;
}
app->len = name->data - app->data;
++name->data;
name->len = end - name->data;
}
static ngx_int_t
ngx_mpegts_http_parse(ngx_http_request_t *r, ngx_rtmp_session_t *s,
ngx_rtmp_play_t *v)
{
ngx_mpegts_http_loc_conf_t *hflcf;
ngx_str_t app, stream;
size_t tcurl_len;
u_char *p;
hflcf = ngx_http_get_module_loc_conf(r, ngx_mpegts_http_module);
ngx_memzero(&app, sizeof(ngx_str_t));
ngx_memzero(&stream, sizeof(ngx_str_t));
ngx_mpegts_http_parse_url(r, &app, &stream);
if (app.len == 0 || stream.len == 0 || stream.len > NGX_RTMP_MAX_NAME) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"mpegts-http: http_parse| url error: %V", &r->uri);
return NGX_HTTP_BAD_REQUEST;
}
if (hflcf->app.len) {
app = hflcf->app;
}
ngx_http_arg(r, (u_char *) "app", 3, &app);
s->app = app;
if (ngx_http_arg(r, (u_char *) "flashver", 8, &s->flashver) != NGX_OK) {
s->flashver = hflcf->flashver;
}
/* tc_url */
#if (NGX_HTTP_SSL)
if (r->connection->ssl) {
tcurl_len = sizeof("https://") - 1;
} else
#endif
{
tcurl_len = sizeof("http://") - 1;
}
tcurl_len += r->headers_in.server.len + 1 + app.len;
s->tc_url.len = tcurl_len;
s->tc_url.data = ngx_pcalloc(r->pool, tcurl_len);
if (s->tc_url.data == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
p = s->tc_url.data;
#if (NGX_HTTP_SSL)
if (r->connection->ssl) {
p = ngx_cpymem(p, "https://", sizeof("https://") - 1);
} else
#endif
{
p = ngx_cpymem(p, "http://", sizeof("http://") - 1);
}
p = ngx_cpymem(p, r->headers_in.server.data, r->headers_in.server.len);
*p++ = '/';
p = ngx_cpymem(p, app.data, app.len);
/* page_url */
if (r->headers_in.referer) {
s->page_url = r->headers_in.referer->value;
} else {
s->page_url = hflcf->page_url;
}
s->acodecs = 0x0DF7;
s->vcodecs = 0xFC;
ngx_memcpy(v->name, stream.data, stream.len);
if (r->args.len) {
ngx_memcpy(v->args, r->args.data,
ngx_min(r->args.len, NGX_RTMP_MAX_ARGS));
}
ngx_rtmp_cmd_middleware_init(s);
return NGX_OK;
}
static void
ngx_mpegts_http_cleanup(void *data)
{
ngx_http_request_t *r;
ngx_mpegts_http_ctx_t *ctx;
r = data;
ctx = ngx_http_get_module_ctx(r, ngx_mpegts_http_module);
if (ctx == NULL) {
return;
}
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"mpegts-http: cleanup| cleanup");
if (ctx->session) {
ctx->session->request = NULL;
if (ctx->session->finalize_reason == 0) {
ctx->session->finalize_reason = r->connection->read->error?
NGX_LIVE_FLV_RECV_ERR : NGX_LIVE_NORMAL_CLOSE;
}
ngx_rtmp_finalize_fake_session(ctx->session);
}
}
static ngx_chain_t *
ngx_mpegts_http_prepare_out_chain(ngx_rtmp_session_t *s)
{
ngx_mpegts_frame_t *frame;
ngx_chain_t *head, **ll, *cl;
frame = NULL;
head = NULL;
if (s->out_pos != s->out_last) {
frame = s->mpegts_out[s->out_pos];
}
/* no frame to send */
if (frame == NULL) {
return NULL;
}
ll = &head;
for (cl = frame->chain; cl; cl = cl->next) {
(*ll) = ngx_get_chainbuf(0, 0);
if (*ll == NULL) {
goto falied;
}
(*ll)->buf->pos = cl->buf->pos;
(*ll)->buf->last = cl->buf->last;
if (!(*ll)->next) {
(*ll)->buf->flush = 1;
}
ll = &(*ll)->next;
}
return head;
falied:
ngx_put_chainbufs(head);
ngx_rtmp_finalize_session(s);
return NULL;
}
static ngx_int_t
ngx_mpegts_http_handler(ngx_http_request_t *r)
{
ngx_mpegts_http_loc_conf_t *hflcf;
ngx_mpegts_http_ctx_t *ctx;
ngx_rtmp_session_t *s;
ngx_rtmp_play_t v;
ngx_int_t rc;
ngx_uint_t n;
ngx_rtmp_core_srv_conf_t *cscf;
ngx_rtmp_core_app_conf_t **cacfp;
ngx_http_cleanup_t *cln;
ngx_rtmp_core_main_conf_t *cmcf;
ctx = ngx_pcalloc(r->pool, sizeof(ngx_mpegts_http_ctx_t));
if (ctx == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
ngx_http_set_ctx(r, ctx, ngx_mpegts_http_module);
/* cleanup handler */
cln = ngx_http_cleanup_add(r, 0);
if (cln == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
cln->handler = ngx_mpegts_http_cleanup;
cln->data = r;
hflcf = ngx_http_get_module_loc_conf(r, ngx_mpegts_http_module);
/* create fake session */
s = ngx_rtmp_create_session(hflcf->addr_conf);
if (s == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
ctx->session = s;
s->connection = r->connection;
ngx_rtmp_set_combined_log(s, r->connection->log->data,
r->connection->log->handler);
s->log->connection = r->connection->number;
s->number = r->connection->number;
s->remote_addr_text.data = ngx_pcalloc(s->pool, r->connection->addr_text.len);
s->remote_addr_text.len = r->connection->addr_text.len;
ngx_memcpy(s->remote_addr_text.data,
r->connection->addr_text.data, r->connection->addr_text.len);
/* get host, app, stream name */
ngx_memzero(&v, sizeof(ngx_rtmp_play_t));
rc = ngx_mpegts_http_parse(r, s, &v);
if (rc != NGX_OK) {
return rc;
}
if (ngx_rtmp_set_virtual_server(s, &s->domain)) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
cscf = ngx_rtmp_get_module_srv_conf(s, ngx_rtmp_core_module);
s->live_type = NGX_MPEGTS_LIVE;
s->live_server = ngx_live_create_server(&s->serverid);
s->request = r;
v.silent = 1;
cacfp = cscf->applications.elts;
for (n = 0; n < cscf->applications.nelts; ++n, ++cacfp) {
if ((*cacfp)->name.len == s->app.len &&
ngx_strncmp((*cacfp)->name.data, s->app.data, s->app.len) == 0)
{
/* found app! */
s->app_conf = (*cacfp)->app_conf;
break;
}
}
if (s->app_conf == NULL) {
if (cscf->default_app == NULL || cscf->default_app->app_conf == NULL) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"mpegts-http: http_handler| "
"application not found '%V'", &s->app);
return NGX_HTTP_NOT_FOUND;
}
s->app_conf = cscf->default_app->app_conf;
}
s->prepare_handler = ngx_mpegts_http_prepare_out_chain;
s->stage = NGX_LIVE_PLAY;
s->ptime = ngx_current_msec;
cmcf = ngx_rtmp_get_module_main_conf(s, ngx_rtmp_core_module);
s->variables = ngx_pcalloc(s->pool, cmcf->variables.nelts
* sizeof(ngx_http_variable_value_t));
if (s->variables == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
if (ngx_rtmp_play_filter(s, &v) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
ngx_add_timer(r->connection->write, s->timeout);
r->read_event_handler = ngx_http_test_reading;
r->write_event_handler = ngx_mpegts_http_write_handler;
++r->count;
return NGX_DONE;
}
static void *
ngx_mpegts_http_create_loc_conf(ngx_conf_t *cf)
{
ngx_mpegts_http_loc_conf_t *hflcf;
hflcf = ngx_pcalloc(cf->pool, sizeof(ngx_mpegts_http_loc_conf_t));
if (hflcf == NULL) {
return NULL;
}
return hflcf;
}
static char *
ngx_mpegts_http_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_mpegts_http_loc_conf_t *prev = parent;
ngx_mpegts_http_loc_conf_t *conf = child;
ngx_conf_merge_str_value(conf->app, prev->app, "");
ngx_conf_merge_str_value(conf->flashver, prev->flashver, "");
ngx_conf_merge_str_value(conf->swf_url, prev->swf_url, "");
ngx_conf_merge_str_value(conf->tc_url, prev->tc_url, "");
ngx_conf_merge_str_value(conf->page_url, prev->page_url, "");
return NGX_CONF_OK;
}
static char *
ngx_mpegts_http(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_core_loc_conf_t *clcf;
ngx_mpegts_http_loc_conf_t *hflcf;
ngx_str_t *value, v;
ngx_uint_t i;
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
clcf->handler = ngx_mpegts_http_handler;
hflcf = conf;
value = cf->args->elts;
hflcf->addr_conf = ngx_rtmp_find_related_addr_conf(cf->cycle, &value[1]);
if (hflcf->addr_conf == NULL) {
return NGX_CONF_ERROR;
}
for (i = 2; i < cf->args->nelts; ++i) {
if (ngx_strncmp(value[i].data, "app=", 4) == 0) {
v.data = value[i].data + 4;
v.len = value[i].len - 4;
hflcf->app = v;
} else {
return NGX_CONF_ERROR;
}
}
return NGX_CONF_OK;
}

View File

@ -1,29 +0,0 @@
/*
* Copyright (C) Pingo (cczjp89@gmail.com)
*/
#ifndef _NGX_RTMP_MPEGTS_MODULE_H
#define _NGX_RTMP_MPEGTS_MODULE_H
#include "ngx_rtmp.h"
typedef ngx_int_t (*ngx_mpegts_video_pt)(ngx_rtmp_session_t *s,
ngx_mpegts_frame_t *frame);
typedef ngx_int_t (*ngx_mpegts_audio_pt)(ngx_rtmp_session_t *s,
ngx_mpegts_frame_t *frame);
extern ngx_mpegts_video_pt ngx_mpegts_video;
extern ngx_mpegts_audio_pt ngx_mpegts_audio;
ngx_int_t
ngx_rtmp_mpegts_gen_pmt(ngx_int_t vcodec, ngx_int_t acodec,
ngx_log_t *log, u_char *pmt);
ngx_int_t
ngx_mpegts_live_video_filter(ngx_rtmp_session_t *s, ngx_mpegts_frame_t *frame);
ngx_int_t
ngx_mpegts_live_audio_filter(ngx_rtmp_session_t *s, ngx_mpegts_frame_t *frame);
extern u_char ngx_rtmp_mpegts_pat[];
#endif

View File

@ -1,449 +0,0 @@
/*
* Copyright (C) AlexWoo(Wu Jie) wj19840501@gmail.com
*/
#include "ngx_live.h"
static void *ngx_live_create_conf(ngx_cycle_t *cf);
static char *ngx_live_init_conf(ngx_cycle_t *cycle, void *conf);
static ngx_command_t ngx_live_commands[] = {
{ ngx_string("stream_buckets"),
NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
0,
offsetof(ngx_live_conf_t, stream_buckets),
NULL },
{ ngx_string("server_buckets"),
NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
0,
offsetof(ngx_live_conf_t, server_buckets),
NULL },
ngx_null_command
};
static ngx_core_module_t ngx_live_module_ctx = {
ngx_string("live"),
ngx_live_create_conf, /* create conf */
ngx_live_init_conf /* init conf */
};
ngx_module_t ngx_live_module = {
NGX_MODULE_V1,
&ngx_live_module_ctx, /* module context */
ngx_live_commands, /* module directives */
NGX_CORE_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static void *
ngx_live_create_conf(ngx_cycle_t *cycle)
{
ngx_live_conf_t *lcf;
lcf = ngx_pcalloc(cycle->pool, sizeof(ngx_live_conf_t));
if (lcf == NULL) {
return NULL;
}
lcf->stream_buckets = NGX_CONF_UNSET_SIZE;
lcf->server_buckets = NGX_CONF_UNSET_SIZE;
return lcf;
}
static char *
ngx_live_init_conf(ngx_cycle_t *cycle, void *conf)
{
ngx_live_conf_t *lcf = conf;
lcf->pool = ngx_create_pool(4096, cycle->log);
if (lcf->pool == NULL) {
return NGX_CONF_ERROR;
}
ngx_conf_init_size_value(lcf->stream_buckets, 10007);
ngx_conf_init_size_value(lcf->server_buckets, 1031);
lcf->servers = ngx_pcalloc(lcf->pool,
sizeof(ngx_live_server_t *) * lcf->server_buckets);
if (lcf->servers == NULL) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
static ngx_live_server_t **
ngx_live_find_server(ngx_str_t *serverid)
{
ngx_live_conf_t *lcf;
ngx_live_server_t **psrv;
lcf = (ngx_live_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
ngx_live_module);
psrv = &lcf->servers[ngx_hash_key(serverid->data, serverid->len)
% lcf->server_buckets];
for (; *psrv; psrv = &(*psrv)->next) {
if (ngx_strlen((*psrv)->serverid) == serverid->len &&
ngx_memcmp((*psrv)->serverid, serverid->data, serverid->len) == 0)
{
break;
}
}
return psrv;
}
static ngx_live_server_t *
ngx_live_get_server(ngx_str_t *serverid)
{
ngx_live_conf_t *lcf;
ngx_live_server_t *srv;
if (serverid->len > NGX_LIVE_SERVERID_LEN - 1) {
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
"serverid too long: %ui", serverid->len);
return NULL;
}
lcf = (ngx_live_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
ngx_live_module);
srv = lcf->free_server;
if (srv == NULL) {
srv = ngx_pcalloc(lcf->pool, sizeof(ngx_live_server_t));
if (srv == NULL) {
return NULL;
}
srv->streams = ngx_pcalloc(lcf->pool,
sizeof(ngx_live_stream_t *) * lcf->stream_buckets);
if (srv->streams == NULL) {
return NULL;
}
++lcf->alloc_server_count;
} else {
lcf->free_server = srv->next;
--lcf->free_server_count;
}
*ngx_cpymem(srv->serverid, serverid->data, serverid->len) = 0;
srv->deleted = 0;
srv->n_stream = 0;
return srv;
}
static void
ngx_live_put_server(ngx_live_server_t *server)
{
ngx_live_conf_t *lcf;
lcf = (ngx_live_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
ngx_live_module);
server->next = lcf->free_server;
lcf->free_server = server;
++lcf->free_server_count;
}
static ngx_live_stream_t **
ngx_live_find_stream(ngx_live_server_t *server, ngx_str_t *stream)
{
ngx_live_conf_t *lcf;
ngx_live_stream_t **pst;
lcf = (ngx_live_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
ngx_live_module);
pst = &server->streams[ngx_hash_key(stream->data, stream->len)
% lcf->stream_buckets];
for (; *pst; pst = &(*pst)->next) {
if (ngx_strlen((*pst)->name) == stream->len &&
ngx_memcmp((*pst)->name, stream->data, stream->len) == 0)
{
break;
}
}
return pst;
}
static ngx_live_stream_t *
ngx_live_get_stream(ngx_str_t *stream)
{
ngx_live_conf_t *lcf;
ngx_live_stream_t *st;
if (stream->len > NGX_LIVE_STREAM_LEN - 1) {
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
"stream too long: %ui", stream->len);
return NULL;
}
lcf = (ngx_live_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
ngx_live_module);
st = lcf->free_stream;
if (st == NULL) {
st = ngx_pcalloc(lcf->pool, sizeof(ngx_live_stream_t));
++lcf->alloc_stream_count;
} else {
lcf->free_stream = st->next;
--lcf->free_stream_count;
ngx_memzero(st, sizeof(ngx_live_stream_t));
}
*ngx_cpymem(st->name, stream->data, stream->len) = 0;
st->pslot = -1;
st->epoch = ngx_current_msec;
ngx_map_init(&st->pubctx, ngx_map_hash_int, ngx_cmp_int);
return st;
}
static void
ngx_live_put_stream(ngx_live_stream_t *st)
{
ngx_live_conf_t *lcf;
lcf = (ngx_live_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
ngx_live_module);
st->next = lcf->free_stream;
lcf->free_stream = st;
++lcf->free_stream_count;
}
ngx_live_server_t *
ngx_live_create_server(ngx_str_t *serverid)
{
ngx_live_server_t **psrv;
psrv = ngx_live_find_server(serverid);
if (*psrv) {
(*psrv)->deleted = 0;
return *psrv;
}
*psrv = ngx_live_get_server(serverid);
return *psrv;
}
ngx_live_server_t *
ngx_live_fetch_server(ngx_str_t *serverid)
{
ngx_live_server_t **psrv;
psrv = ngx_live_find_server(serverid);
return *psrv;
}
void
ngx_live_delete_server(ngx_str_t *serverid)
{
ngx_live_server_t **psrv, *srv;
psrv = ngx_live_find_server(serverid);
if (*psrv == NULL) {
return;
}
if ((*psrv)->n_stream != 0) {
(*psrv)->deleted = 1;
}
if ((*psrv)->n_stream == 0) {
srv = *psrv;
*psrv = srv->next;
ngx_live_put_server(srv);
}
}
ngx_live_stream_t *
ngx_live_create_stream(ngx_str_t *serverid, ngx_str_t *stream)
{
ngx_live_server_t **psrv;
ngx_live_stream_t **pst;
psrv = ngx_live_find_server(serverid);
if (*psrv == NULL) {
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
"server %V does not exist when create stream", serverid);
return NULL;
}
pst = ngx_live_find_stream(*psrv, stream);
if (*pst) {
return *pst;
}
*pst = ngx_live_get_stream(stream);
++(*psrv)->n_stream;
return *pst;
}
ngx_live_stream_t *
ngx_live_fetch_stream(ngx_str_t *serverid, ngx_str_t *stream)
{
ngx_live_server_t **psrv;
ngx_live_stream_t **pst;
psrv = ngx_live_find_server(serverid);
if (*psrv == NULL) {
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
"server %V does not exist when fetch stream", serverid);
return NULL;
}
pst = ngx_live_find_stream(*psrv, stream);
return *pst;
}
void
ngx_live_delete_stream(ngx_str_t *serverid, ngx_str_t *stream)
{
ngx_live_server_t **psrv;
ngx_live_stream_t **pst, *st;
psrv = ngx_live_find_server(serverid);
if (*psrv == NULL) {
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
"server %V does not exist when delete stream", serverid);
return;
}
pst = ngx_live_find_stream(*psrv, stream);
if (*pst == NULL) {
return;
}
st = *pst;
*pst = st->next;
ngx_live_put_stream(st);
--(*psrv)->n_stream;
if ((*psrv)->deleted && (*psrv)->n_stream == 0) {
ngx_live_delete_server(serverid);
}
}
void
ngx_live_create_ctx(ngx_rtmp_session_t *s, unsigned publishing)
{
ngx_rtmp_core_ctx_t *ctx, **pctx;
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_core_module);
if (ctx == NULL) {
ctx = ngx_pcalloc(s->pool, sizeof(ngx_rtmp_core_ctx_t));
if (ctx == NULL) {
return;
}
ngx_rtmp_set_ctx(s, ctx, ngx_rtmp_core_module);
}
ctx->publishing = publishing;
ctx->session = s;
if (publishing) {
pctx = &s->live_stream->publish_ctx;
} else {
pctx = &s->live_stream->play_ctx;
}
ctx->next = (*pctx);
*pctx = ctx;
}
void
ngx_live_delete_ctx(ngx_rtmp_session_t *s)
{
ngx_rtmp_core_ctx_t *ctx, **pctx;
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_core_module);
if (ctx == NULL) {
return;
}
if (ctx->publishing) {
pctx = &s->live_stream->publish_ctx;
} else {
pctx = &s->live_stream->play_ctx;
}
for (/* void */; *pctx; pctx = &(*pctx)->next) {
if (*pctx == ctx) {
*pctx = ctx->next;
return;
}
}
}
ngx_chain_t *
ngx_live_state(ngx_http_request_t *r)
{
ngx_live_conf_t *lcf;
ngx_chain_t *cl;
ngx_buf_t *b;
size_t len;
lcf = (ngx_live_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
ngx_live_module);
len = sizeof("##########ngx live state##########\n") - 1
+ sizeof("ngx_live nalloc server: \n") - 1 + NGX_OFF_T_LEN
+ sizeof("ngx_live nfree server: \n") - 1 + NGX_OFF_T_LEN
+ sizeof("ngx_live nalloc stream: \n") - 1 + NGX_OFF_T_LEN
+ sizeof("ngx_live nfree stream: \n") - 1 + NGX_OFF_T_LEN;
cl = ngx_alloc_chain_link(r->pool);
if (cl == NULL) {
return NULL;
}
cl->next = NULL;
b = ngx_create_temp_buf(r->pool, len);
if (b == NULL) {
return NULL;
}
cl->buf = b;
b->last = ngx_snprintf(b->last, len,
"##########ngx live state##########\n"
"ngx_live nalloc server: %ui\nngx_live nfree server: %ui\n"
"ngx_live nalloc stream: %ui\nngx_live nfree stream: %ui\n",
lcf->alloc_server_count, lcf->free_server_count,
lcf->alloc_stream_count, lcf->free_stream_count);
return cl;
}

View File

@ -1,42 +0,0 @@
/*
* Copyright (C) AlexWoo(Wu Jie) wj19840501@gmail.com
*/
#ifndef _NGX_LIVE_H_INCLUDED_
#define _NGX_LIVE_H_INCLUDED_
#include "ngx_rtmp.h"
typedef struct {
size_t stream_buckets;
size_t server_buckets;
ngx_live_server_t **servers;
ngx_live_server_t *free_server;
ngx_live_stream_t *free_stream;
ngx_uint_t alloc_server_count;
ngx_uint_t free_server_count;
ngx_uint_t alloc_stream_count;
ngx_uint_t free_stream_count;
ngx_pool_t *pool;
} ngx_live_conf_t;
extern ngx_module_t ngx_live_module;
/*
* paras:
* r: http request to query status of rbuf
*/
ngx_chain_t *ngx_live_state(ngx_http_request_t *r);
#endif

View File

@ -1,59 +0,0 @@
/*
* Copyright (C) AlexWoo(Wu Jie) wj19840501@gmail.com
*/
#ifndef _NGX_LIVE_RECORD_H_INCLUDED_
#define _NGX_LIVE_RECORD_H_INCLUDED_
#include <ngx_config.h>
#include <ngx_core.h>
#include "ngx_rtmp.h"
#include "hls/ngx_rtmp_mpegts.h"
typedef struct {
unsigned open; /* 0 close, 1 open, 2 wait for key */
time_t last_time;
ngx_file_t index;
ngx_rtmp_mpegts_file_t ts;
ngx_file_t file;
ngx_rtmp_publish_t pubv;
ngx_uint_t audio_cc;
ngx_uint_t video_cc;
ngx_msec_t begintime;
ngx_msec_t starttime;
ngx_msec_t endtime;
off_t startsize;
off_t endsize;
ngx_msec_t publish_epoch;
ngx_msec_t basetime;
} ngx_live_record_ctx_t;
typedef ngx_int_t (*ngx_live_record_start_pt)(ngx_rtmp_session_t *s);
typedef ngx_int_t (*ngx_live_record_update_pt)(ngx_rtmp_session_t *s);
typedef ngx_int_t (*ngx_live_record_done_pt)(ngx_rtmp_session_t *s);
extern ngx_live_record_start_pt ngx_live_record_start;
extern ngx_live_record_update_pt ngx_live_record_update;
extern ngx_live_record_done_pt ngx_live_record_done;
extern ngx_module_t ngx_live_record_module;
const char *ngx_live_record_open(ngx_rtmp_session_t *s);
const char *ngx_live_record_close(ngx_rtmp_session_t *s);
#endif

Some files were not shown because too many files have changed in this diff Show More