Merge Mainline
Signed-off-by: Tianling Shen <cnsztl@immortalwrt.org>
This commit is contained in:
commit
03c8db5bf6
@ -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
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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?
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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))
|
||||
@ -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'
|
||||
@ -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"
|
||||
}
|
||||
@ -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))
|
||||
@ -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))
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
|
||||
@ -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>
|
||||
@ -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>
|
||||
@ -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>
|
||||
@ -1,3 +0,0 @@
|
||||
<%+cbi/valueheader%>
|
||||
<font class="brook_users_status" hint="<%=self:cfgvalue(section)%>">--</font>
|
||||
<%+cbi/valuefooter%>
|
||||
@ -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 "运行中"
|
||||
@ -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'
|
||||
|
||||
@ -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
|
||||
}
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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 |
@ -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
|
||||
@ -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%>
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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>
|
||||
@ -1,3 +0,0 @@
|
||||
<%+cbi/valueheader%>
|
||||
<font class="ipsec-server_status"><%=pcdata(self:cfgvalue(section) or self.default or "")%></font>
|
||||
<%+cbi/valuefooter%>
|
||||
@ -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 "运行中"
|
||||
@ -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'
|
||||
|
||||
@ -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
|
||||
}
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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)))
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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>
|
||||
@ -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.
|
||||
|
||||
@ -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"
|
||||
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
@ -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 \
|
||||
"
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
@ -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"
|
||||
@ -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
|
||||
@ -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"
|
||||
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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
|
||||
@ -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);
|
||||
}
|
||||
@ -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];
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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
|
||||
@ -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 \
|
||||
"
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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
|
||||
@ -1,9 +0,0 @@
|
||||
Project author:
|
||||
|
||||
Roman Arutyunyan
|
||||
Moscow, Russia, Pingo
|
||||
|
||||
Contacts:
|
||||
arut@qip.ru
|
||||
arutyunyan.roman@gmail.com
|
||||
cczjp89@gmail.com
|
||||
@ -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.
|
||||
@ -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
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -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_ */
|
||||
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||
}
|
||||
@ -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_ */
|
||||
@ -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;
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
@ -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;
|
||||
}
|
||||
@ -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
|
||||
@ -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;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
@ -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;
|
||||
}
|
||||
@ -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
|
||||
File diff suppressed because it is too large
Load Diff
@ -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
Loading…
Reference in New Issue
Block a user