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

chore: new version patch #15

Closed
wants to merge 17 commits into from
Closed
8 changes: 4 additions & 4 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ name: CI

on:
push:
branches: [master, 'release/**']
branches: [master, 'chore/lj']
paths-ignore:
- 'docs/**'
- '**/*.md'
pull_request:
branches: [master, 'release/**']
branches: [master, 'chore/lj']
paths-ignore:
- 'docs/**'
- '**/*.md'
Expand All @@ -28,7 +28,6 @@ jobs:
- ubuntu-20.04
os_name:
- linux_openresty
- linux_openresty_1_19
test_dir:
- t/plugin/[a-k]*
- t/plugin/[l-z]*
Expand All @@ -40,6 +39,7 @@ jobs:
env:
SERVER_NAME: ${{ matrix.os_name }}
OPENRESTY_VERSION: default
abt_branch: apisix-base/1.21.4.2.2

steps:
- name: Check out code
Expand Down Expand Up @@ -164,7 +164,7 @@ jobs:

- name: Linux Install
run: |
sudo --preserve-env=OPENRESTY_VERSION \
sudo --preserve-env=OPENRESTY_VERSION,abt_branch \
./ci/${{ matrix.os_name }}_runner.sh do_install

- name: Linux Script
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ ENV_TAR ?= tar
ENV_INSTALL ?= install
ENV_RM ?= rm -vf
ENV_DOCKER ?= docker
ENV_DOCKER_COMPOSE ?= docker-compose --project-directory $(CURDIR) -p $(project_name) -f $(project_compose_ci)
ENV_DOCKER_COMPOSE ?= docker compose --project-directory $(CURDIR) -p $(project_name) -f $(project_compose_ci)
ENV_NGINX ?= $(ENV_NGINX_EXEC) -p $(CURDIR) -c $(CURDIR)/conf/nginx.conf
ENV_NGINX_EXEC := $(shell command -v openresty 2>/dev/null || command -v nginx 2>/dev/null)
ENV_OPENSSL_PREFIX ?= $(addprefix $(ENV_NGINX_PREFIX), openssl)
Expand Down
22 changes: 17 additions & 5 deletions apisix/core/response.lua
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,9 @@ function resp_exit(code, ...)
error("failed to encode data: " .. err, -2)
else
idx = idx + 1
t[idx] = body .. "\n"
t[idx] = body
idx = idx + 1
t[idx] = "\n"
end

elseif v ~= nil then
Expand All @@ -80,7 +82,7 @@ function resp_exit(code, ...)
end

if idx > 0 then
ngx_print(concat_tab(t, "", 1, idx))
ngx_print(t)
end

if code then
Expand Down Expand Up @@ -174,7 +176,7 @@ end
-- final_body = transform(final_body)
-- ngx.arg[1] = final_body
-- ...
function _M.hold_body_chunk(ctx, hold_the_copy)
function _M.hold_body_chunk(ctx, hold_the_copy, max_resp_body_bytes)
local body_buffer
local chunk, eof = arg[1], arg[2]

Expand All @@ -190,22 +192,32 @@ function _M.hold_body_chunk(ctx, hold_the_copy)
n = 1
}
ctx._body_buffer[ctx._plugin_name] = body_buffer
ctx._resp_body_bytes = #chunk
else
local n = body_buffer.n + 1
body_buffer.n = n
body_buffer[n] = chunk
ctx._resp_body_bytes = ctx._resp_body_bytes + #chunk
end
if max_resp_body_bytes and ctx._resp_body_bytes >= max_resp_body_bytes then
local body_data = concat_tab(body_buffer, "", 1, body_buffer.n)
body_data = str_sub(body_data, 1, max_resp_body_bytes)
return body_data
end
end

if eof then
body_buffer = ctx._body_buffer[ctx._plugin_name]
if not body_buffer then
if max_resp_body_bytes and #chunk >= max_resp_body_bytes then
chunk = str_sub(chunk, 1, max_resp_body_bytes)
end
return chunk
end

body_buffer = concat_tab(body_buffer, "", 1, body_buffer.n)
local body_data = concat_tab(body_buffer, "", 1, body_buffer.n)
ctx._body_buffer[ctx._plugin_name] = nil
return body_buffer
return body_data
end

if not hold_the_copy then
Expand Down
248 changes: 248 additions & 0 deletions apisix/plugins/brotli.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
--
-- 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
local req_http_version = ngx.req.http_version
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 = {"*"}
}
},
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 = {
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 content_encoded = ngx_header["Content-Encoding"]
if content_encoded then
-- Don't compress if Content-Encoding is present in upstream data
return
end

local types = conf.types
local content_type = ngx_header["Content-Type"]
if not content_type then
-- 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
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
-- overwriting the arg[1], results into partial response
ngx.arg[1] = ngx.arg[1] .. ctx.compressor:finish()
end
end


return _M
Loading
Loading