Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add brotli plugin #10515

Merged
merged 35 commits into from
Dec 8, 2023
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
46dbb82
new deps
yuweizzz Nov 14, 2023
10b97cf
brotli feat in progress
yuweizzz Nov 14, 2023
8b42bf6
add lrucahe
yuweizzz Nov 14, 2023
2e55cb6
add ci test
yuweizzz Nov 15, 2023
c682922
update ci
yuweizzz Nov 15, 2023
86eab3d
fix: attempt to index a nli value
yuweizzz Nov 15, 2023
ddea9bd
fix: ci and header error
yuweizzz Nov 15, 2023
d36558f
debug
yuweizzz Nov 15, 2023
299e719
fix: only do compress once
yuweizzz Nov 15, 2023
b5f1de7
replace deps as brotli-ffi
yuweizzz Nov 16, 2023
dacf773
final version
yuweizzz Nov 16, 2023
d5ee1e8
final t version
yuweizzz Nov 17, 2023
f7f44cf
Merge branch 'apache:master' into brotli
yuweizzz Nov 17, 2023
3d3117b
reset default value of lgwin and lgblock
yuweizzz Nov 17, 2023
fe62e46
Update brotli.t
yuweizzz Nov 17, 2023
dee5fb6
Update brotli.t
yuweizzz Nov 17, 2023
c494021
update ci
yuweizzz Nov 17, 2023
940382f
fix ci and add doc
yuweizzz Nov 21, 2023
2f9c3c7
fix ci
yuweizzz Nov 22, 2023
8f76ef6
fix: redhat ci failed
yuweizzz Nov 22, 2023
f064a5f
fix ci
yuweizzz Nov 22, 2023
75bb0cf
fix ci
yuweizzz Nov 22, 2023
c5e5d08
fix ci
yuweizzz Nov 23, 2023
b959f11
update workflow
yuweizzz Nov 23, 2023
8be4f80
remove sudo
yuweizzz Nov 30, 2023
b1c3f30
refactor
yuweizzz Dec 1, 2023
92d3461
fix local
yuweizzz Dec 4, 2023
111813a
vars rename
yuweizzz Dec 4, 2023
ea83d7e
update docs
yuweizzz Dec 5, 2023
a63f239
fix lint
yuweizzz Dec 5, 2023
fa6aaf1
update
yuweizzz Dec 5, 2023
cd8f323
:Merge branch 'master' of https://github.com/apache/apisix into brotli
yuweizzz Dec 5, 2023
e28bf8d
fix t missing header
yuweizzz Dec 5, 2023
1c179e1
fix t/admin/plugins.t error
yuweizzz Dec 5, 2023
9640cbc
fix fuzzing ci
yuweizzz Dec 6, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion apisix-master-0.rockspec
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ dependencies = {
"nanoid = 0.1-1",
"lua-resty-mediador = 0.1.2-1",
"lua-resty-ldap = 0.1.0-0",
"lua-resty-t1k = 1.1.0"
"lua-resty-t1k = 1.1.0",
"brotli-ffi = 0.3-1"
}

build = {
Expand Down
241 changes: 241 additions & 0 deletions apisix/plugins/brotli.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
--
-- Licensed to the Apache Software Foundation (ASF) under one or more
-- contributor license agreements. See the NOTICE file distributed with
-- this work for additional information regarding copyright ownership.
-- The ASF licenses this file to You under the Apache License, Version 2.0
-- (the "License"); you may not use this file except in compliance with
-- the License. You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
local core = require("apisix.core")
local ngx = ngx
local ngx_re_gmatch = ngx.re.gmatch
local ngx_header = ngx.header
shreemaan-abhishek marked this conversation as resolved.
Show resolved Hide resolved
local req_http_version = ngx.req.http_version
shreemaan-abhishek marked this conversation as resolved.
Show resolved Hide resolved
local str_sub = string.sub
local ipairs = ipairs
local tonumber = tonumber
local type = type
local is_loaded, brotli = pcall(require, "brotli")


local schema = {
type = "object",
properties = {
types = {
anyOf = {
{
type = "array",
minItems = 1,
items = {
type = "string",
minLength = 1,
},
},
{
enum = {"*"}
shreemaan-abhishek marked this conversation as resolved.
Show resolved Hide resolved
}
},
default = {"text/html"}
},
min_length = {
type = "integer",
minimum = 1,
default = 20,
},
mode = {
type = "integer",
minimum = 0,
maximum = 2,
default = 0,
-- 0: MODE_GENERIC (default),
-- 1: MODE_TEXT (for UTF-8 format text input)
-- 2: MODE_FONT (for WOFF 2.0)
},
comp_level = {
type = "integer",
minimum = 0,
maximum = 11,
default = 6,
-- follow the default value from ngx_brotli brotli_comp_level
},
lgwin = {
monkeyDluffy6017 marked this conversation as resolved.
Show resolved Hide resolved
type = "integer",
default = 19,
-- follow the default value from ngx_brotli brotli_window
enum = {0,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24},
},
lgblock = {
type = "integer",
default = 0,
enum = {0,16,17,18,19,20,21,22,23,24},
},
http_version = {
enum = {1.1, 1.0},
default = 1.1,
},
vary = {
type = "boolean",
}
},
}


local _M = {
version = 0.1,
priority = 996,
name = "brotli",
schema = schema,
}


function _M.check_schema(conf)
return core.schema.check(schema, conf)
end


local function create_brotli_compressor(mode, comp_level, lgwin, lgblock)
local options = {
mode = mode,
quality = comp_level,
lgwin = lgwin,
lgblock = lgblock,
}
return brotli.compressor:new(options)
end


local function check_accept_encoding(ctx)
local accept_encoding = core.request.header(ctx, "Accept-Encoding")
-- no Accept-Encoding
if not accept_encoding then
return false
end

-- single Accept-Encoding
if accept_encoding == "*" or accept_encoding == "br" then
return true
end

-- multi Accept-Encoding
local iterator, err = ngx_re_gmatch(accept_encoding,
[[([a-z\*]+)(;q=)?([0-9.]*)?]], "jo")
if not iterator then
core.log.error("gmatch failed, error: ", err)
return false
end

local captures
while true do
captures, err = iterator()
if not captures then
break
end
if err then
core.log.error("iterator failed, error: ", err)
return false
end
if (captures[1] == "br" or captures[1] == "*") and
(not captures[2] or captures[3] ~= "0") then
return true
end
end

return false
end


function _M.header_filter(conf, ctx)
if not is_loaded then
core.log.error("please check the brotli library")
return
end

local allow_encoding = check_accept_encoding(ctx)
if not allow_encoding then
return
end

local types = conf.types
local content_type = ngx_header["Content-Type"]
if not content_type then
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how about checking content-type before check_accept_encoding?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should check accept encoding first, if request not allow this encoding, we don't need to check our conf.

-- Like Nginx, don't compress if Content-Type is missing
return
end

if type(types) == "table" then
local matched = false
local from = core.string.find(content_type, ";")
if from then
content_type = str_sub(content_type, 1, from - 1)
end

for _, ty in ipairs(types) do

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"*" cannot match any MIME type.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we use "*" in "types", it will pass this "ipairs", and will set brotli_matched as true in this func. same as 'gzip' plugin.

if content_type == ty then
matched = true
break
end
end

if not matched then
return
end
end

local content_length = tonumber(ngx_header["Content-Length"])
if content_length then
local min_length = conf.min_length
if content_length < min_length then
return
end
-- Like Nginx, don't check min_length if Content-Length is missing
end

local http_version = req_http_version()
if http_version < conf.http_version then
return
end

if conf.vary then
core.response.add_header("Vary", "Accept-Encoding")
end

local compressor = create_brotli_compressor(conf.mode, conf.comp_level,
conf.lgwin, conf.lgblock)
if not compressor then
core.log.error("failed to create brotli compressor")
return
end

ctx.brotli_matched = true
ctx.compressor = compressor
core.response.clear_header_as_body_modified()
core.response.add_header("Content-Encoding", "br")
end


function _M.body_filter(conf, ctx)
if not ctx.brotli_matched then
return
end

local chunk, eof = ngx.arg[1], ngx.arg[2]
if type(chunk) == "string" and chunk ~= "" then
local encode_chunk = ctx.compressor:compress(chunk)
ngx.arg[1] = encode_chunk .. ctx.compressor:flush()
end

if eof then
ngx.arg[1] = ctx.compressor:finish()
end
end


return _M
4 changes: 4 additions & 0 deletions ci/centos7-ci.sh
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ install_dependencies() {
# install vault cli capabilities
install_vault_cli

# install brotli
yum install -y cmake3
install_brotli

# install test::nginx
yum install -y cpanminus perl
cpanm --notest Test::Nginx IPC::Run > build.log 2>&1 || (cat build.log && exit 1)
Expand Down
17 changes: 17 additions & 0 deletions ci/common.sh
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,23 @@ install_nodejs () {
npm config set registry https://registry.npmjs.org/
}

install_brotli () {
local BORTLI_VERSION="1.1.0"
wget -q https://github.com/google/brotli/archive/refs/tags/v${BORTLI_VERSION}.zip
unzip v${BORTLI_VERSION}.zip && cd ./brotli-${BORTLI_VERSION} && mkdir build && cd build
local CMAKE=$(command -v cmake3 > /dev/null 2>&1 && echo cmake3 || echo cmake)
${CMAKE} -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local/brotli ..
sudo ${CMAKE} --build . --config Release --target install
if [ -d "/usr/local/brotli/lib64" ]; then
echo /usr/local/brotli/lib64 | sudo tee /etc/ld.so.conf.d/brotli.conf
else
echo /usr/local/brotli/lib | sudo tee /etc/ld.so.conf.d/brotli.conf
fi
sudo ldconfig
cd ../..
rm -rf brotli-${BORTLI_VERSION}
}

set_coredns() {
# test a domain name is configured as upstream
echo "127.0.0.1 test.com" | sudo tee -a /etc/hosts
Expand Down
1 change: 1 addition & 0 deletions ci/linux_apisix_current_luarocks_runner.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

do_install() {
linux_get_dependencies
install_brotli

export_or_prefix

Expand Down
1 change: 1 addition & 0 deletions ci/linux_apisix_master_luarocks_runner.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

do_install() {
linux_get_dependencies
install_brotli

export_or_prefix

Expand Down
3 changes: 3 additions & 0 deletions ci/linux_openresty_common_runner.sh
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ do_install() {

# install vault cli capabilities
install_vault_cli

# install brotli
install_brotli
}

script() {
Expand Down
4 changes: 4 additions & 0 deletions ci/redhat-ci.sh
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ install_dependencies() {
# install vault cli capabilities
install_vault_cli

# install brotli
yum install -y cmake3
install_brotli

# install test::nginx
yum install -y --disablerepo=* --enablerepo=ubi-8-appstream-rpms --enablerepo=ubi-8-baseos-rpms cpanminus perl
cpanm --notest Test::Nginx IPC::Run > build.log 2>&1 || (cat build.log && exit 1)
Expand Down
1 change: 1 addition & 0 deletions conf/config-default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,7 @@ plugins: # plugin list (sorted by priority)
- limit-count # priority: 1002
- limit-req # priority: 1001
#- node-status # priority: 1000
#- brotli # priority: 996
- gzip # priority: 995
- server-info # priority: 990
- traffic-split # priority: 966
Expand Down
1 change: 1 addition & 0 deletions docs/en/latest/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
"plugins/redirect",
"plugins/echo",
"plugins/gzip",
"plugins/brotli",
"plugins/real-ip",
"plugins/server-info",
"plugins/ext-plugin-pre-req",
Expand Down
Loading
Loading