Skip to content

Commit ba4e5d1

Browse files
Merge remote-tracking branch 'apache/master' into fix/serverless-request-smuggling
# Conflicts: # apisix/plugins/serverless/generic-upstream.lua
2 parents 8d9df64 + 39b9e43 commit ba4e5d1

389 files changed

Lines changed: 40075 additions & 5151 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/build.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ jobs:
2929
os_name:
3030
- linux_openresty
3131
test_dir:
32-
- t/plugin/[a-k]*.t
33-
- t/stream-plugin t/plugin/[l-z]*.t
34-
- t/admin t/cli t/config-center-yaml t/control t/core t/debug t/discovery t/error_page t/http3/admin t/misc
32+
- t/plugin/[a-k]*.t t/plugin/dubbo-proxy t/plugin/graphql-proxy-cache
33+
- t/stream-plugin t/plugin/[l-z]*.t t/plugin/proxy-cache
34+
- t/admin t/cli t/config-center-json t/config-center-yaml t/control t/core t/debug t/discovery t/error_page t/http3/admin t/misc
3535
- t/node t/pubsub t/router t/script t/secret t/stream-node t/utils t/wasm t/xds-library t/xrpc
3636

3737
runs-on: ${{ matrix.platform }}

.requirements

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,5 @@
1717

1818
APISIX_PACKAGE_NAME=apisix
1919

20-
APISIX_RUNTIME=1.3.6
20+
APISIX_RUNTIME=1.3.11
2121
APISIX_DASHBOARD_COMMIT=c8d3466d3c36386d3888efbc8250cd8183c77298

Makefile

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,16 @@ install: runtime
401401
$(ENV_INSTALL) -d $(ENV_INST_LUADIR)/apisix/plugins/ai-rag/vector-search
402402
$(ENV_INSTALL) apisix/plugins/ai-rag/vector-search/*.lua $(ENV_INST_LUADIR)/apisix/plugins/ai-rag/vector-search
403403

404+
$(ENV_INSTALL) -d $(ENV_INST_LUADIR)/apisix/plugins/ai-cache
405+
$(ENV_INSTALL) apisix/plugins/ai-cache/*.lua $(ENV_INST_LUADIR)/apisix/plugins/ai-cache
406+
$(ENV_INSTALL) -d $(ENV_INST_LUADIR)/apisix/plugins/ai-cache/embeddings
407+
$(ENV_INSTALL) apisix/plugins/ai-cache/embeddings/*.lua $(ENV_INST_LUADIR)/apisix/plugins/ai-cache/embeddings
408+
$(ENV_INSTALL) -d $(ENV_INST_LUADIR)/apisix/plugins/ai-cache/vector-search
409+
$(ENV_INSTALL) apisix/plugins/ai-cache/vector-search/*.lua $(ENV_INST_LUADIR)/apisix/plugins/ai-cache/vector-search
410+
411+
$(ENV_INSTALL) -d $(ENV_INST_LUADIR)/apisix/plugins/ai-lakera-guard
412+
$(ENV_INSTALL) apisix/plugins/ai-lakera-guard/*.lua $(ENV_INST_LUADIR)/apisix/plugins/ai-lakera-guard
413+
404414
$(ENV_INSTALL) -d $(ENV_INST_LUADIR)/apisix/plugins/mcp/broker
405415
$(ENV_INSTALL) -d $(ENV_INST_LUADIR)/apisix/plugins/mcp/transport
406416
$(ENV_INSTALL) apisix/plugins/mcp/*.lua $(ENV_INST_LUADIR)/apisix/plugins/mcp

README.md

Lines changed: 79 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -23,32 +23,88 @@
2323

2424
[![Build Status](https://github.com/apache/apisix/actions/workflows/build.yml/badge.svg?branch=master)](https://github.com/apache/apisix/actions/workflows/build.yml)
2525
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/apache/apisix/blob/master/LICENSE)
26+
[![GitHub release](https://img.shields.io/github/v/release/apache/apisix?color=blue)](https://github.com/apache/apisix/releases)
27+
[![GitHub stars](https://img.shields.io/github/stars/apache/apisix?style=flat&color=blue)](https://github.com/apache/apisix/stargazers)
28+
[![Docker Pulls](https://img.shields.io/docker/pulls/apache/apisix?color=blue)](https://hub.docker.com/r/apache/apisix)
2629
[![Commit activity](https://img.shields.io/github/commit-activity/m/apache/apisix)](https://github.com/apache/apisix/graphs/commit-activity)
27-
[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/apache/apisix.svg)](http://isitmaintained.com/project/apache/apisix "Average time to resolve an issue")
28-
[![Percentage of issues still open](http://isitmaintained.com/badge/open/apache/apisix.svg)](http://isitmaintained.com/project/apache/apisix "Percentage of issues still open")
30+
[![GitHub issues](https://img.shields.io/github/issues/apache/apisix)](https://github.com/apache/apisix/issues)
2931
[![Slack](https://badgen.net/badge/Slack/Join%20Apache%20APISIX?icon=slack)](https://apisix.apache.org/slack)
3032

3133
**Apache APISIX** is a dynamic, real-time, high-performance API Gateway.
3234

33-
APISIX API Gateway provides rich traffic management features such as load balancing, dynamic upstream, canary release, circuit breaking, authentication, observability, and more.
35+
APISIX API Gateway provides rich traffic management features such as load balancing, dynamic upstream, canary release, circuit breaking, authentication, observability, and more. You can use it to handle traditional north-south traffic, as well as east-west traffic between services. It can also be used as a [Kubernetes ingress controller](https://github.com/apache/apisix-ingress-controller).
3436

35-
APISIX can serve as an **[AI Gateway](https://apisix.apache.org/ai-gateway/)** through its flexible plugin system, providing AI proxying, load balancing for LLMs, retries and fallbacks, token-based rate limiting, and robust security to ensure the efficiency and reliability of AI agents. APISIX also provides the [`mcp-bridge`](https://apisix.apache.org/blog/2025/04/21/host-mcp-server-with-api-gateway/) plugin to seamlessly convert stdio-based MCP servers to scalable HTTP SSE services.
37+
## Table of Contents
3638

37-
You can use APISIX API Gateway to handle traditional north-south traffic, as well as east-west traffic between services. It can also be used as a [k8s ingress controller](https://github.com/apache/apisix-ingress-controller).
39+
- [Why APISIX](#why-apisix)
40+
- [AI Gateway](#ai-gateway)
41+
- [Get Started](#get-started)
42+
- [Features](#features)
43+
- [Benchmark](#benchmark)
44+
- [Community](#community)
45+
- [User Stories](#user-stories)
46+
- [Who Uses APISIX API Gateway?](#who-uses-apisix-api-gateway)
47+
- [Logos](#logos)
48+
- [Acknowledgments](#acknowledgments)
49+
- [License](#license)
50+
51+
## Why APISIX
52+
53+
APISIX is built on top of NGINX and etcd. Compared with traditional API gateways, APISIX has dynamic routing and hot-loading of plugins, which is especially suitable for API management under a microservice architecture.
3854

3955
The technical architecture of Apache APISIX:
4056

4157
![Technical architecture of Apache APISIX](docs/assets/images/apisix.png)
4258

43-
## Community
59+
## AI Gateway
4460

45-
- [Kindly Write a Review](https://www.g2.com/products/apache-apisix/reviews) for APISIX in G2.
46-
- Mailing List: Mail to dev-subscribe@apisix.apache.org, follow the reply to subscribe to the mailing list.
47-
- Slack Workspace - [invitation link](https://apisix.apache.org/slack) (Please open an [issue](https://apisix.apache.org/docs/general/submit-issue) if this link is expired), and then join the #apisix channel (Channels -> Browse channels -> search for "apisix").
48-
- ![Twitter Follow](https://img.shields.io/twitter/follow/ApacheAPISIX?style=social) - follow and interact with us using hashtag `#ApacheAPISIX`
49-
- [Documentation](https://apisix.apache.org/docs/)
50-
- [Discussions](https://github.com/apache/apisix/discussions)
51-
- [Blog](https://apisix.apache.org/blog)
61+
APISIX can serve as an **[AI Gateway](https://apisix.apache.org/ai-gateway/)** through its flexible plugin system, providing:
62+
63+
- **AI proxying** to route traffic to different LLM providers through a unified interface.
64+
- **Load balancing, retries, and fallbacks** across multiple LLMs to ensure the efficiency and reliability of AI agents.
65+
- **Token-based rate limiting** to control cost and protect upstream models.
66+
- **Robust security** for authentication, authorization, and traffic control on AI workloads.
67+
68+
APISIX also provides the [`mcp-bridge`](https://apisix.apache.org/blog/2025/04/21/host-mcp-server-with-api-gateway/) plugin to seamlessly convert stdio-based MCP servers to scalable HTTP SSE services.
69+
70+
## Get Started
71+
72+
Install and run APISIX with a single command using the quickstart script (requires [Docker](https://docs.docker.com/get-docker/)):
73+
74+
```shell
75+
curl -sL https://run.api7.ai/apisix/quickstart | sh
76+
```
77+
78+
This starts APISIX (listening on port `9080`) together with its etcd configuration store. Verify it is running:
79+
80+
```shell
81+
curl "http://127.0.0.1:9080" --head | grep Server
82+
```
83+
84+
Create your first route via the Admin API (port `9180`) to proxy requests to an upstream service:
85+
86+
```shell
87+
curl -i "http://127.0.0.1:9180/apisix/admin/routes/1" -X PUT -d '
88+
{
89+
"uri": "/get",
90+
"upstream": {
91+
"type": "roundrobin",
92+
"nodes": {
93+
"httpbin.org:80": 1
94+
}
95+
}
96+
}'
97+
```
98+
99+
Send a request through APISIX to confirm the route works:
100+
101+
```shell
102+
curl "http://127.0.0.1:9080/get"
103+
```
104+
105+
To learn more, follow the [Getting Started](https://apisix.apache.org/docs/apisix/getting-started/) guide and the [installation documentation](https://apisix.apache.org/docs/apisix/installation-guide/) for other deployment methods. To extend APISIX, see the [plugin development guide](docs/en/latest/plugin-develop.md), the [plugin concept](docs/en/latest/terminology/plugin.md), and the [REST Admin API](docs/en/latest/admin-api.md) reference.
106+
107+
For more documents, please refer to the [Apache APISIX Documentation site](https://apisix.apache.org/docs/apisix/getting-started/).
52108

53109
## Features
54110

@@ -162,29 +218,6 @@ A/B testing, canary release, blue-green deployment, limit rate, defense against
162218
- [Azure Functions](docs/en/latest/plugins/azure-functions.md): Seamless integration with Azure Serverless Function as a dynamic upstream to proxy all requests for a particular URI to the Microsoft Azure cloud.
163219
- [Apache OpenWhisk](docs/en/latest/plugins/openwhisk.md): Seamless integration with Apache OpenWhisk as a dynamic upstream to proxy all requests for a particular URI to your own OpenWhisk cluster.
164220

165-
## Get Started
166-
167-
1. Installation
168-
169-
Please refer to [install documentation](https://apisix.apache.org/docs/apisix/installation-guide/).
170-
171-
2. Getting started
172-
173-
The getting started guide is a great way to learn the basics of APISIX. Just follow the steps in [Getting Started](https://apisix.apache.org/docs/apisix/getting-started/).
174-
175-
Further, you can follow the documentation to try more [plugins](docs/en/latest/plugins).
176-
177-
3. Admin API
178-
179-
Apache APISIX provides [REST Admin API](docs/en/latest/admin-api.md) to dynamically control the Apache APISIX cluster.
180-
181-
4. Plugin development
182-
183-
You can refer to [plugin development guide](docs/en/latest/plugin-develop.md), and sample plugin `example-plugin`'s code implementation.
184-
Reading [plugin concept](docs/en/latest/terminology/plugin.md) would help you learn more about the plugin.
185-
186-
For more documents, please refer to [Apache APISIX Documentation site](https://apisix.apache.org/docs/apisix/getting-started/)
187-
188221
## Benchmark
189222

190223
Using AWS's eight-core server, APISIX's QPS reaches 140,000 with a latency of only 0.2 ms.
@@ -193,6 +226,16 @@ Using AWS's eight-core server, APISIX's QPS reaches 140,000 with a latency of on
193226

194227
[APISIX also works perfectly in AWS graviton3 C7g.](https://apisix.apache.org/blog/2022/06/07/installation-performance-test-of-apigateway-apisix-on-aws-graviton3)
195228

229+
## Community
230+
231+
- [Kindly Write a Review](https://www.g2.com/products/apache-apisix/reviews) for APISIX in G2.
232+
- Mailing List: Mail to dev-subscribe@apisix.apache.org, follow the reply to subscribe to the mailing list.
233+
- Slack Workspace - [invitation link](https://apisix.apache.org/slack) (Please open an [issue](https://apisix.apache.org/docs/general/submit-issue) if this link is expired), and then join the #apisix channel (Channels -> Browse channels -> search for "apisix").
234+
- ![Twitter Follow](https://img.shields.io/twitter/follow/ApacheAPISIX?style=social) - follow and interact with us using hashtag `#ApacheAPISIX`
235+
- [Documentation](https://apisix.apache.org/docs/)
236+
- [Discussions](https://github.com/apache/apisix/discussions)
237+
- [Blog](https://apisix.apache.org/blog)
238+
196239
## User Stories
197240

198241
- [European eFactory Platform: API Security Gateway – Using APISIX in the eFactory Platform](https://www.efactory-project.eu/post/api-security-gateway-using-apisix-in-the-efactory-platform)

apisix-master-0.rockspec

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,19 +42,19 @@ dependencies = {
4242
"lua-resty-ngxvar = 0.5.2-0",
4343
"lua-resty-jit-uuid = 0.0.7-2",
4444
"lua-resty-ksuid = 1.0.1-0",
45-
"lua-resty-healthcheck-api7 = 3.2.1-0",
45+
"lua-resty-healthcheck-api7 = 3.2.3-0",
4646
"api7-lua-resty-jwt = 0.2.6-0",
4747
"lua-resty-hmac-ffi = 0.06-1",
4848
"lua-resty-cookie = 0.4.1-1",
4949
"lua-resty-session = 4.1.5-1",
50-
"lua-resty-openapi-validator = 1.0.5-1",
50+
"lua-resty-openapi-validator = 1.0.6-1",
5151
"opentracing-openresty = 0.1-0",
5252
"lua-resty-radixtree = 2.9.2-0",
5353
"lua-protobuf = 0.5.3-1",
54-
"lua-resty-openidc = 1.8.0-1",
54+
"lua-resty-openidc = 1.9.0-1",
5555
"lua-resty-saml = 0.2.5",
5656
"luafilesystem = 1.8.0-1",
57-
"nginx-lua-prometheus-api7 = 0.20250302-1",
57+
"nginx-lua-prometheus-api7 = 1.0.0-1",
5858
"jsonschema = 0.9.13-0",
5959
"lua-resty-ipmatcher = 0.6.1-0",
6060
"lua-resty-kafka = 0.23-0",
@@ -63,7 +63,7 @@ dependencies = {
6363
"base64 = 1.5-3",
6464
"binaryheap = 0.4-1",
6565
"api7-dkjson = 0.1.1-0",
66-
"resty-redis-cluster = 1.05-1",
66+
"lua-resty-redis-cluster = 1.3.3-0",
6767
"lua-resty-expr = 1.3.2",
6868
"graphql = 0.0.2-1",
6969
"argparse = 0.7.1-1",
@@ -79,7 +79,7 @@ dependencies = {
7979
"net-url = 1.2-1",
8080
"xml2lua = 1.6-2",
8181
"lua-resty-mediador = 0.1.2-1",
82-
"lua-resty-ldap = 0.1.0-0",
82+
"lua-resty-ldap = 0.3.1-0",
8383
"lua-resty-t1k = 1.1.6-0",
8484
"brotli-ffi = 0.3-1",
8585
"lua-ffi-zlib = 0.6-0",

apisix/admin/consumers.lua

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ local core = require("apisix.core")
1818
local plugins = require("apisix.admin.plugins")
1919
local plugins_encrypt_conf = require("apisix.admin.plugins").encrypt_conf
2020
local resource = require("apisix.admin.resource")
21+
local apisix_consumer = require("apisix.consumer")
2122

2223

2324
local function check_conf(username, conf, need_username, schema, opts)
@@ -36,6 +37,11 @@ local function check_conf(username, conf, need_username, schema, opts)
3637
if not ok then
3738
return nil, {error_msg = "invalid plugins configuration: " .. err}
3839
end
40+
41+
ok, err = apisix_consumer.check_duplicate_key(conf.plugins, conf.username)
42+
if not ok then
43+
return nil, {error_msg = err}
44+
end
3945
end
4046

4147
if conf.group_id and not opts.skip_references_check then

apisix/admin/credentials.lua

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,12 @@ local core = require("apisix.core")
1818
local plugins = require("apisix.admin.plugins")
1919
local plugin = require("apisix.plugin")
2020
local resource = require("apisix.admin.resource")
21+
local apisix_consumer = require("apisix.consumer")
2122
local plugins_encrypt_conf = require("apisix.admin.plugins").encrypt_conf
2223
local pairs = pairs
2324

24-
local function check_conf(_id, conf, _need_id, schema)
25+
local function check_conf(id, conf, _need_id, schema, opts)
26+
opts = opts or {}
2527
local ok, err = core.schema.check(schema, conf)
2628
if not ok then
2729
return nil, {error_msg = "invalid configuration: " .. err}
@@ -42,6 +44,16 @@ local function check_conf(_id, conf, _need_id, schema)
4244
return nil, {error_msg = "only supports auth type plugins in consumer credential"}
4345
end
4446
end
47+
48+
-- opts.sub_path is in the form of {consumer_name}/credentials or
49+
-- {consumer_name}/credentials/{credential_id}
50+
if opts.sub_path then
51+
local consumer_name = core.utils.split_uri(opts.sub_path)[1]
52+
ok, err = apisix_consumer.check_duplicate_key(conf.plugins, consumer_name, id)
53+
if not ok then
54+
return nil, {error_msg = err}
55+
end
56+
end
4557
end
4658

4759
return true, nil

apisix/admin/init.lua

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,20 @@ local ngx = ngx
2727
local get_method = ngx.req.get_method
2828
local ngx_time = ngx.time
2929
local ngx_timer_at = ngx.timer.at
30+
local ngx_timer_every = ngx.timer.every
3031
local ngx_worker_id = ngx.worker.id
3132
local tonumber = tonumber
3233
local tostring = tostring
3334
local str_lower = string.lower
3435
local reload_event = "/apisix/admin/plugins/reload"
36+
local is_http = ngx.config.subsystem == "http"
37+
-- declared unconditionally for the http subsystem in ngx_tpl.lua, and already a
38+
-- hard dependency of the server-info plugin; absent in the stream subsystem
39+
local plugins_conf_ver_dict = is_http and ngx.shared["internal-status"]
40+
local PLUGINS_CONF_VERSION_KEY = "plugins_conf_version"
41+
-- plugins conf version this process has applied, compared against the shared
42+
-- dict by the reconciliation timer registered in init_worker()
43+
local applied_plugins_conf_version = 0
3544
local ipairs = ipairs
3645
local error = error
3746
local type = type
@@ -288,6 +297,21 @@ end
288297
local function post_reload_plugins()
289298
set_ctx_and_check_token()
290299

300+
if plugins_conf_ver_dict then
301+
-- bump the version before broadcasting, so that a process which never
302+
-- receives the event (e.g. the privileged agent while it is
303+
-- reconnecting to the events broker) still converges through the
304+
-- periodic reconciliation below
305+
local _, err = plugins_conf_ver_dict:incr(PLUGINS_CONF_VERSION_KEY, 1, 0)
306+
if err then
307+
-- if the version cannot be bumped the reconciliation timer will
308+
-- never notice a change, so a worker that misses the broadcast
309+
-- would stay stale forever; fail loud instead of pretending success
310+
core.log.error("failed to increase plugins conf version: ", err)
311+
core.response.exit(503, {error_msg = "failed to record plugins reload"})
312+
end
313+
end
314+
291315
local success, err = events:post(reload_event, get_method(), ngx_time())
292316
if not success then
293317
core.response.exit(503, err)
@@ -375,8 +399,21 @@ end
375399

376400
local function reload_plugins(data, event, source, pid)
377401
core.log.info("start to hot reload plugins")
402+
403+
-- sample the version before loading: if another reload is accepted while
404+
-- plugin.load() runs, the versions stay unequal and the reconciliation
405+
-- timer applies one more round
406+
local ver
407+
if plugins_conf_ver_dict then
408+
ver = plugins_conf_ver_dict:get(PLUGINS_CONF_VERSION_KEY)
409+
end
410+
378411
plugin.load()
379412

413+
if ver then
414+
applied_plugins_conf_version = ver
415+
end
416+
380417
if ngx_worker_id() == 0 then
381418
sync_local_conf_to_etcd()
382419
end
@@ -509,6 +546,30 @@ function _M.init_worker()
509546
events = require("apisix.events")
510547
events:register(reload_plugins, reload_event, "PUT")
511548

549+
if plugins_conf_ver_dict and not is_yaml_config_provider then
550+
-- The events broadcast has no delivery guarantee: a process that is
551+
-- (re)connecting to the events broker loses the event for good, which
552+
-- leaves it running e.g. the timers of plugins that were removed.
553+
-- Reconcile against the version in the shared dict, the same pattern
554+
-- admin/standalone.lua uses for the same reason.
555+
applied_plugins_conf_version =
556+
plugins_conf_ver_dict:get(PLUGINS_CONF_VERSION_KEY) or 0
557+
558+
local ok, err = ngx_timer_every(1, function (premature)
559+
if premature then
560+
return
561+
end
562+
563+
local ver = plugins_conf_ver_dict:get(PLUGINS_CONF_VERSION_KEY) or 0
564+
if ver ~= applied_plugins_conf_version then
565+
reload_plugins()
566+
end
567+
end)
568+
if not ok then
569+
core.log.error("failed to create plugins reconciliation timer: ", err)
570+
end
571+
end
572+
512573
if ngx_worker_id() == 0 then
513574
-- check if admin_key is required
514575
if local_conf.deployment.admin.admin_key_required == false then

apisix/admin/plugin_metadata.lua

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,10 @@ local function encrypt_conf(plugin_name, conf)
9494
inject_metadata_schema(plugin_object)
9595

9696
local schema = plugin_object.metadata_schema
97-
if schema['$comment'] ~= injected_mark and plugin_object.check_schema then
97+
-- encrypt whenever the plugin has a real metadata schema; do not gate on
98+
-- check_schema, or a plugin that validates via core.schema.check would leak
99+
-- its metadata encrypt_fields in plaintext
100+
if schema['$comment'] ~= injected_mark then
98101
plugin_encrypt_conf(plugin_name, conf, core.schema.TYPE_METADATA)
99102
end
100103
end

0 commit comments

Comments
 (0)