Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
bd9cdda
body rewind when fallback
xuleiming Aug 11, 2026
2c698d3
config AccessibleBodySize in bfe.conf
xuleiming Aug 11, 2026
3915598
monitor and limit TotalBodyBufferSize
xuleiming Aug 12, 2026
229178c
fix log messages.
xuleiming Aug 12, 2026
9a1f05a
Merge pull request #1313 from xuleiming/v1.8.5
mileszhang2016 Aug 12, 2026
3dd7d22
docs(zh_cn): add sys_design docs for mod_ai_route
mileszhang2016 Aug 13, 2026
9968740
refactor(tests): migrate mod_ai_route integration tests to real BFE p…
mileszhang2016 Aug 13, 2026
704a047
feat: multi-API-Key support for AI backend clusters
mileszhang2016 Aug 13, 2026
f4b59d6
feat: support RMB quota in mod_ai_token_auth
mileszhang2016 Aug 13, 2026
3ef8f4c
refactor: remove redundant Currency from QuotaPlan and update token_r…
mileszhang2016 Aug 14, 2026
cdd1541
feat: add req_body_json_prefix_in condition primitive
mileszhang2016 Aug 15, 2026
8339b1f
Merge pull request #1314 from mileszhang2016/v1.8.5
mileszhang2016 Aug 15, 2026
5e7d657
feat(bfe): provider/model prefix routing support
mileszhang2016 Aug 15, 2026
9277384
Merge pull request #1315 from mileszhang2016/v1.8.5
mileszhang2016 Aug 15, 2026
8ccc7e8
feat(mod_ai_token_auth): add key_id field for API-Key token and add A…
mileszhang2016 Aug 19, 2026
0629e46
fix(mod_ai_token_auth): calculate RMB cost at request finish for stre…
mileszhang2016 Aug 19, 2026
abfdadc
fix(bfe): 4xx status codes trigger cluster-level fallback
mileszhang2016 Aug 19, 2026
b07efcf
Merge pull request #1318 from mileszhang2016/v1.8.5
mileszhang2016 Aug 19, 2026
f26f418
feat: adapt bfe-access-pb v0.2.0 AI observability fields
mileszhang2016 Aug 19, 2026
8a55e87
fix(integration): ensure tls_conf/client_crl dir exists for BFE startup
mileszhang2016 Aug 21, 2026
1c76431
optimize(bfe_server): merge multiple AI model body rewrites into one
mileszhang2016 Aug 21, 2026
1ae02e4
fix(bfe_server): prevent model body rewrite leaking across cluster at…
mileszhang2016 Aug 21, 2026
e3e19ef
Merge pull request #1321 from mileszhang2016/v1.8.5
mileszhang2016 Aug 21, 2026
5e97db3
Revert "fix(bfe_server): prevent model body rewrite leaking across cl…
mileszhang2016 Aug 21, 2026
fa45071
Merge branch 'v1.8.5' of https://github.com/mileszhang2016/bfe into v…
mileszhang2016 Aug 21, 2026
33bc923
Merge pull request #1322 from mileszhang2016/v1.8.5
mileszhang2016 Aug 21, 2026
b327d8f
fix(test): prevent TestTC10 from hanging on cleanup
mileszhang2016 Aug 21, 2026
32716e9
Merge pull request #1324 from mileszhang2016/v1.8.5
mileszhang2016 Aug 21, 2026
4c6a4e9
fix(test): prevent TestTC10 from hanging on cleanup and fix stale bin…
mileszhang2016 Aug 21, 2026
99fdbf0
Merge pull request #1325 from mileszhang2016/v1.8.5
mileszhang2016 Aug 21, 2026
cda840f
fix(test): increase holder timeout for TestTC10 in slow CI
mileszhang2016 Aug 21, 2026
d1d8af0
Merge pull request #1326 from mileszhang2016/v1.8.5
mileszhang2016 Aug 21, 2026
ac368e5
refactor(test): redesign TestTC10 to avoid timing-sensitive blocking …
mileszhang2016 Aug 21, 2026
81240e8
Merge pull request #1327 from mileszhang2016/v1.8.5
mileszhang2016 Aug 21, 2026
9aaa4fd
Merge pull request #1320 from bfenetworks/v1.8.5
mileszhang2016 Aug 21, 2026
4928058
chore: update .gitignore to include .omo and .codegraph files
Aug 21, 2026
694ba4e
Merge pull request #1328 from bfenetworks/release/v1.8.5
cc14514 Aug 21, 2026
b9fb26f
Merge pull request #1329 from bfenetworks/master
cc14514 Aug 21, 2026
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
69 changes: 36 additions & 33 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,33 +1,36 @@
# Copyright 2021 The BFE Authors
#
# Licensed 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.
#
.svn
.tmp
.download
output/
.*.swp
.*.swo
/**/y.output
/**/*.log
/**/*.log.*
profile.out
coverage.txt
.idea/*
.vscode/*
dist/*
conf/wasm_plugin

.DS_Store
.git*
!.gitattributes
# Copyright 2021 The BFE Authors
#
# Licensed 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.
#
.svn
.tmp
.omo
.download
.codegraph
output/
.*.swp
.*.swo
/**/y.output
/**/*.log
/**/*.log.*
profile.out
coverage.txt
.idea/*
.vscode/*
dist/*
conf/wasm_plugin
tests/integration/.integration-test-bin/

.DS_Store
.git*
!.gitattributes
116 changes: 116 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# AGENTS.md — BFE Server

This file guides AI coding agents working on the `bfe/` codebase (the data-plane BFE Server).

## Project overview

BFE (Beyond Front End) is a modern layer-7 load balancer originated at Baidu and a CNCF sandbox project. This repository contains **BFE Server**, the data-plane component that forwards user traffic based on content-based routing, load balancing, and a flexible plugin framework.

BFE system context (for orientation only):
- **Data plane**: BFE Server (this repo) — forwards traffic.
- **Control plane**: API-Server, Conf-Agent, Dashboard (separate repos) — manage and distribute configuration.
- **Kubernetes integration**: BFE Ingress Controller (separate repo).

## High-level architecture

Entry point: `bfe.go`
- Parses flags (`-c conf_root`, `-l log_dir`, `-t test_conf`, etc.).
- Loads server config via `bfe_config/bfe_conf.BfeConfigLoad`.
- Starts the server via `bfe_server.StartUp`.

Core request flow:
1. Listener (`bfe_server/`) accepts HTTP/HTTPS/HTTP2/WebSocket/SPDY/FastCGI connections.
2. TLS handshake and session handling (`bfe_tls/`).
3. HTTP request parsing (`bfe_http/`, `bfe_http2/`, `bfe_bufio/`).
4. Routing: host table → cluster table (`bfe_route/`, `bfe_config/bfe_route_conf/`).
5. Module pipeline execution in fixed order (`bfe_modules/`, registered in `bfe_modules/bfe_modules.go`).
6. Backend selection and load balancing (`bfe_balance/`).
7. Proxying and response handling (`bfe_server/reverseproxy.go`, `bfe_server/response.go`).

## Directory structure and module relationships

| Directory | Responsibility |
|-----------|----------------|
| `bfe_server/` | HTTP(S) listeners, connection handling, reverse proxy, TLS termination, module registration, status/monitoring. |
| `bfe_route/` | Host and cluster routing tables, trie-based lookups (`trie/`), server data config (`server_data_conf.go`). |
| `bfe_balance/` | Backend instances (`backend/`), GSLB (`bal_gslb/`) and SLB (`bal_slb/`) balancing policies, balance table. |
| `bfe_config/` | Configuration loading: `bfe_conf/`, `bfe_route_conf/`, `bfe_cluster_conf/`, `bfe_tls_conf/`. |
| `bfe_http/`, `bfe_http2/`, `bfe_websocket/`, `bfe_spdy/`, `bfe_fcgi/`, `bfe_stream/` | Protocol implementations. |
| `bfe_tls/` | TLS handshake, certificates, session cache, server rules. |
| `bfe_module/` | Plugin framework: module interface, callback/handler lists, filters. |
| `bfe_modules/` | Built-in modules (access, WAF, redirect, rewrite, AI routing, rate limiting, etc.). Registered in `bfe_modules/bfe_modules.go`. |
| `bfe_basic/` | Condition parser/primitives used by modules. Generated parser code lives in `bfe_basic/condition/parser`. |
| `bfe_net/`, `bfe_bufio/`, `bfe_util/`, `bfe_debug/` | Shared network, buffered I/O, utilities, debug flags. |
| `conf/` | Sample runtime configuration files. |
| `docs/`, `examples/`, `tests/` | Documentation, deployment examples, and integration tests. |

## Build/test conventions

- **Go version**: 1.22 (`go.mod`).
- **Module**: `github.com/bfenetworks/bfe`.
- **Build**: `make` (or `make all`) → prepare, compile, package.
- `make build` builds the `bfe` binary.
- `make strip` builds without symbols.
- **Test**: `make test` runs `go test -cover ./...` and `go vet ./...`.
- **Prepare**: `make prepare` installs `goyacc` and regenerates `bfe_basic/condition/parser` via `go generate`.
- **Lint/static analysis**: `make check` runs `staticcheck`; `make license-check` / `make license-fix` use `license-eye`.
- **Pre-commit**: Install with `pre-commit install`; `gofmt` is required.
- **Docker**: `make docker` builds prod + debug images; `make docker-push REGISTRY=...` builds and pushes multi-arch images.
- **Release**: `make release` builds tar.gz packages for darwin/amd64, linux/amd64, linux/arm64, windows/amd64.

## Common modification patterns

### Add or modify a BFE module
1. Create a package under `bfe_modules/mod_<name>/`.
2. Implement `bfe_module.BfeModule` and the required callbacks/handlers.
3. Add config loader under `bfe_config/` if new config files are needed.
4. Add module registration in `bfe_modules/bfe_modules.go` in the correct execution order; document ordering requirements in comments.
5. Add sample config under `conf/` and update config documentation.
6. Add unit tests using `testing` + `testify`.
7. Run `make test` before submitting.

### AI gateway module changes

The AI gateway modules under `bfe_modules/mod_ai_*` and `bfe_modules/mod_body_process` have ordering and lifecycle interdependencies:

- `mod_ai_route` runs early to select the target cluster/model.
- `mod_ai_token_auth` runs at `HandleFoundProduct` for API Key validation and quota plan binding; it also performs final quota deduction at `HandleRequestFinish`.
- `mod_body_process` runs at `HandleReadResponse` and is responsible for parsing token usage from streaming (SSE) responses. If you modify RMB quota deduction, ensure streaming scenarios still work when `mod_body_process` is loaded.
- For RMB quota details, see `docs/zh_cn/sys_design/rmb_quota.md`.

### Routing changes
- Host/cluster tables: `bfe_route/`.
- Config loaders: `bfe_config/bfe_route_conf/`.
- Sample configs: `conf/`.

### Load-balancing changes
- Backend model: `bfe_balance/backend/`.
- Balancing policies: `bfe_balance/bal_gslb/` and `bfe_balance/bal_slb/`.
- Balance table: `bfe_balance/bal_table.go`.

### Protocol support changes
- HTTP: `bfe_http/`, `bfe_server/http_conn.go`.
- HTTP/2: `bfe_http2/`.
- WebSocket: `bfe_websocket/`.
- TLS: `bfe_tls/`.

### Condition/rule language changes
- Grammar/parser: `bfe_basic/condition/parser`.
- Run `make prepare` after grammar changes to regenerate parser code.

## Agent guidelines

- **Preserve module order** in `bfe_modules/bfe_modules.go`. Many modules have explicit ordering requirements; keep the comments up to date.
- **Regenerate generated code** after parser/grammar changes (`make prepare`).
- **Keep tests idiomatic**: use `testing` and `testify/assert`/`require`. Place `_test.go` files next to the code under test.
- **License headers**: all new source files need the Apache 2.0 header. Use `make license-fix` if unsure.
- **Do not hand-edit vendored or generated files**.
- **Run `make test`** as the minimal local verification.
- **Configuration changes must be reflected in `conf/`** so that `make package` and Docker builds produce a usable default setup.

## Useful references

- `README.md` / `README-CN.md` — project overview and quick start.
- `CONTRIBUTING.md` — workflow, commit sign-off, code style.
- `Makefile` — build, test, Docker, and release targets.
- `docs/en_us/introduction/overview.md` — detailed architecture documentation.
28 changes: 27 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,30 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [v1.8.5] - 2026-08-21

### Added
- Add multi-API-Key support for AI backend clusters ([Pull #1314](https://github.com/bfenetworks/bfe/pull/1314))
- Add RMB quota support in mod_ai_token_auth ([Pull #1314](https://github.com/bfenetworks/bfe/pull/1314))
- Add key_id field for API-Key token in access logs ([Pull #1318](https://github.com/bfenetworks/bfe/pull/1318))
- Add provider/model prefix routing support ([Pull #1315](https://github.com/bfenetworks/bfe/pull/1315))
- Add req_body_json_prefix_in condition primitive ([Pull #1314](https://github.com/bfenetworks/bfe/pull/1314))
- Adapt bfe-access-pb v0.2.0 AI observability fields in access logs ([Pull #1321](https://github.com/bfenetworks/bfe/pull/1321))
- Support body rewind when fallback ([Pull #1313](https://github.com/bfenetworks/bfe/pull/1313))
- Add AccessibleBodySize config in bfe.conf ([Pull #1313](https://github.com/bfenetworks/bfe/pull/1313))
- Monitor and limit TotalBodyBufferSize ([Pull #1313](https://github.com/bfenetworks/bfe/pull/1313))
- Add release target to Makefile

### Changed
- Trigger cluster-level fallback on 4xx status codes ([Pull #1318](https://github.com/bfenetworks/bfe/pull/1318))
- Merge multiple AI model body rewrites into one ([Pull #1321](https://github.com/bfenetworks/bfe/pull/1321))

### Fixed
- Fix ai token auth default rule path ([Pull #1251](https://github.com/bfenetworks/bfe/pull/1251))
- Fix RMB cost calculation at request finish for streaming responses ([Pull #1318](https://github.com/bfenetworks/bfe/pull/1318))
- Fix log messages ([Pull #1313](https://github.com/bfenetworks/bfe/pull/1313))


## [v1.8.4] - 2026-08-05

### Added
Expand Down Expand Up @@ -267,7 +291,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Fix textproto: not normalize headers with spaces before the colon (CVE-2019-16276)

## [v0.10.0] - 2020-05-25
## [v0.10.0] - 2020-05-25

### Added

Expand Down Expand Up @@ -441,6 +465,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Flexible plugin framework to extend functionality. Based on the framework, developer can add new features rapidly
- Detailed built-in metrics available for service status monitor

[v1.8.5]: https://github.com/bfenetworks/bfe/compare/v1.8.4...v1.8.5
[v1.8.4]: https://github.com/bfenetworks/bfe/compare/v1.8.3...v1.8.4
[v1.6.0]: https://github.com/bfenetworks/bfe/compare/v1.5.0...v1.6.0
[v1.5.0]: https://github.com/bfenetworks/bfe/compare/v1.4.0...v1.5.0
[v1.4.0]: https://github.com/bfenetworks/bfe/compare/v1.3.0...v1.4.0
Expand Down
11 changes: 10 additions & 1 deletion bfe_basic/condition/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -554,10 +554,19 @@ func buildPrimitive(node *parser.CallExpr) (Condition, error) {
name: node.Fun.Name,
node: node,
fetcher: &ReqBodyJsonFetcher{
path: node.Args[0].Value,
path: node.Args[0].Value,
},
matcher: NewInMatcher(node.Args[1].Value, node.Args[2].ToBool()),
}, nil
case "req_body_json_prefix_in":
return &PrimitiveCond{
name: node.Fun.Name,
node: node,
fetcher: &ReqBodyJsonFetcher{
path: node.Args[0].Value,
},
matcher: NewPrefixInMatcher(node.Args[1].Value, node.Args[2].ToBool()),
}, nil

default:
return nil, fmt.Errorf("unsupported primitive %s", node.Fun.Name)
Expand Down
26 changes: 26 additions & 0 deletions bfe_basic/condition/build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,32 @@ var buildPrimitiveTests = []struct {
&QueryExistMatcher{},
false,
},
{
"testBuildReqBodyJsonPrefixIn",
`req_body_json_prefix_in("model", "openrouter/", false)`,
&PrimitiveCond{
name: "req_body_json_prefix_in",
fetcher: &ReqBodyJsonFetcher{path: "model"},
matcher: &PrefixInMatcher{
patterns: []string{"openrouter/"},
foldCase: false,
},
},
false,
},
{
"testBuildReqBodyJsonPrefixInIgnoreCase",
`req_body_json_prefix_in("model", "OpenRouter/", true)`,
&PrimitiveCond{
name: "req_body_json_prefix_in",
fetcher: &ReqBodyJsonFetcher{path: "model"},
matcher: &PrefixInMatcher{
patterns: []string{"OPENROUTER/"},
foldCase: true,
},
},
false,
},
{
"testBuildUrlRegMatch",
"req_url_regmatch(\"123\")",
Expand Down
1 change: 1 addition & 0 deletions bfe_basic/condition/parser/semant.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ var funcProtos = map[string][]Token{
"bfe_time_range": []Token{STRING, STRING},
"bfe_periodic_time_range": []Token{STRING, STRING, STRING},
"req_body_json_in": []Token{STRING, STRING, BOOL},
"req_body_json_prefix_in": []Token{STRING, STRING, BOOL},
}

func prototypeCheck(expr *CallExpr) error {
Expand Down
78 changes: 78 additions & 0 deletions bfe_basic/condition/primitive_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package condition
import (
"net"
"net/http"
"strings"
"testing"
"time"

Expand Down Expand Up @@ -347,3 +348,80 @@ func TestPeriodicTimeMatcher(t *testing.T) {
t.Fatalf("should not match %v", tm)
}
}

func buildRequestWithBody(body string) *bfe_basic.Request {
httpReq, _ := bfe_http.NewRequest("POST", "http://example.com/v1/chat/completions", strings.NewReader(body))
return bfe_basic.NewRequest(httpReq, nil, nil, &bfe_basic.Session{}, nil)
}

func TestReqBodyJsonPrefixIn(t *testing.T) {
cond, err := Build(`req_body_json_prefix_in("model", "openrouter/", false)`)
if err != nil {
t.Fatalf("build failed: %v", err)
}

cases := []struct {
body string
matched bool
}{
{`{"model":"openrouter/anthropic/claude-sonnet-4.6"}`, true},
{`{"model":"openrouter/google/gemini-pro"}`, true},
{`{"model":"google/gemini-pro"}`, false},
{`{"model":"openrouter"}`, false},
{`{}`, false},
}

for _, c := range cases {
req := buildRequestWithBody(c.body)
if matched := cond.Match(req); matched != c.matched {
t.Errorf("body %s matched=%v, want=%v", c.body, matched, c.matched)
}
}
}

func TestReqBodyJsonPrefixInIgnoreCase(t *testing.T) {
cond, err := Build(`req_body_json_prefix_in("model", "OpenRouter/", true)`)
if err != nil {
t.Fatalf("build failed: %v", err)
}

req := buildRequestWithBody(`{"model":"openrouter/anthropic/claude-sonnet-4.6"}`)
if !cond.Match(req) {
t.Errorf("should match openrouter/ prefix with ignore case")
}
}

func TestReqBodyJsonPrefixInMulti(t *testing.T) {
cond, err := Build(`req_body_json_prefix_in("model", "gpt-|claude-", false)`)
if err != nil {
t.Fatalf("build failed: %v", err)
}

cases := []struct {
body string
matched bool
}{
{`{"model":"gpt-4"}`, true},
{`{"model":"claude-3-opus"}`, true},
{`{"model":"openrouter/anthropic/claude-sonnet-4.6"}`, false},
}

for _, c := range cases {
req := buildRequestWithBody(c.body)
if matched := cond.Match(req); matched != c.matched {
t.Errorf("body %s matched=%v, want=%v", c.body, matched, c.matched)
}
}
}

func TestReqBodyJsonPrefixInCombineWithIn(t *testing.T) {
cond, err := Build(`req_body_json_in("model", "gpt-4", false) && req_body_json_prefix_in("model", "gpt-", false)`)
if err != nil {
t.Fatalf("build failed: %v", err)
}

req := buildRequestWithBody(`{"model":"gpt-4"}`)
if !cond.Match(req) {
t.Errorf("should match combined condition")
}
}
Loading
Loading