You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by ma...@apache.org on 2021/09/03 14:36:32 UTC

[apisix-dashboard] branch master updated: feat: basic support Apache APISIX 2.9 (#2117)

This is an automated email from the ASF dual-hosted git repository.

majunjie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix-dashboard.git


The following commit(s) were added to refs/heads/master by this push:
     new fc89bc4  feat: basic support Apache APISIX 2.9 (#2117)
fc89bc4 is described below

commit fc89bc4b3f70576cb630525985e80fc70068f81f
Author: bzp2010 <bz...@apache.org>
AuthorDate: Fri Sep 3 22:36:24 2021 +0800

    feat: basic support Apache APISIX 2.9 (#2117)
    
    * feat: update json schema file
    
    * feat: update entity definition
    
    * feat: add new upstream property to frontend
    
    * fix: ci test 1
    
    * fix: unit test
    
    * fix: cli test
    
    * fix: lint
    
    * feat: add upstream backend e2e test
    
    * feat: add upstream frontend e2e test
    
    * fix: backend e2e test
    
    * fix: limit-conn plugin form and e2e test
    
    * fix: fe ci error
    
    * fix: review
    
    * fix: etcd 3.4.13
    
    * fix: run test with apisix2.9
    
    * fix: ci
    
    * fix: ci
    
    * fix: ci
    
    Co-authored-by: liuxiran <be...@126.com>
---
 .github/workflows/backend-cli-test.yml             |   2 +-
 .github/workflows/go-lint.yml                      |  22 -
 api/conf/schema.json                               | 516 ++++++++++++++++-----
 api/internal/core/entity/entity.go                 |  69 +--
 api/internal/core/store/validate_test.go           |  21 +-
 api/internal/handler/schema/plugin_test.go         |   2 +-
 api/internal/handler/upstream/upstream_test.go     | 168 +++----
 api/test/docker/docker-compose.yaml                |   2 +-
 api/test/e2e/json_schema_validate_test.go          |  61 ---
 api/test/e2e/route_import_test.go                  |   4 +-
 api/test/e2enew/schema/schema_test.go              |   2 +-
 .../e2enew/upstream/upstream_keepalive_pool.go     |  66 +++
 api/test/e2enew/upstream/upstream_retry.go         |  63 +++
 api/test/e2enew/upstream/upstream_test.go          |   1 -
 web/cypress/fixtures/export-route-dataset.json     |  33 +-
 web/cypress/fixtures/plugin-dataset.json           |  24 +-
 .../consumer/create-with-limit-conn-form.spec.js   |   2 +
 .../upstream/create_and_delete_upstream.spec.js    |  61 +++
 web/src/components/Plugin/UI/limit-conn.tsx        |  29 +-
 web/src/components/Plugin/locales/en-US.ts         |   2 +
 web/src/components/Plugin/locales/zh-CN.ts         |   3 +
 web/src/components/Upstream/UpstreamForm.tsx       |  13 +
 .../Upstream/components/KeepalivePool.tsx          |  89 ++++
 .../Upstream/components/RetryTimeout.tsx           |  41 ++
 web/src/components/Upstream/locales/en-US.ts       |  14 +
 web/src/components/Upstream/locales/zh-CN.ts       |  13 +
 web/src/pages/Upstream/locales/en-US.ts            |   1 +
 web/src/pages/Upstream/locales/zh-CN.ts            |   1 +
 web/src/pages/Upstream/typing.d.ts                 |   8 +
 29 files changed, 967 insertions(+), 366 deletions(-)

diff --git a/.github/workflows/backend-cli-test.yml b/.github/workflows/backend-cli-test.yml
index 55b42bb..23234d7 100644
--- a/.github/workflows/backend-cli-test.yml
+++ b/.github/workflows/backend-cli-test.yml
@@ -17,7 +17,7 @@ jobs:
     runs-on: ubuntu-latest
     strategy:
       matrix:
-        etcd: [3.4.13]
+        etcd: [3.4.14]
     services:
       etcd:
         image: bitnami/etcd:${{ matrix.etcd }}
diff --git a/.github/workflows/go-lint.yml b/.github/workflows/go-lint.yml
index 7f495aa..635e22d 100644
--- a/.github/workflows/go-lint.yml
+++ b/.github/workflows/go-lint.yml
@@ -12,25 +12,6 @@ on:
       - 'api/**'
 
 jobs:
-  go-filter:
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v2
-        with:
-          submodule: true
-
-      - uses: ./.github/actions/paths-filter
-        id: changes
-        with:
-          filters: |
-            go:
-              - '**.go'
-          working-directory: 'api'
-          list-files: shell
-    outputs:
-      matches: ${{ steps.changes.outputs.go }}
-      files: ${{ steps.changes.outputs.go_files }}
-
   golangci:
     runs-on: ubuntu-latest
     needs: go-filter
@@ -45,11 +26,8 @@ jobs:
           working-directory: api
           args: --tests=false
           only-new-issues: true
-
   gofmt:
     runs-on: ubuntu-latest
-    needs: go-filter
-    if: needs.go-filter.outputs.matches == 'true'
     steps:
       - uses: actions/checkout@v2
       - name: setup go
diff --git a/api/conf/schema.json b/api/conf/schema.json
index 9b454b5..2fbbeed 100644
--- a/api/conf/schema.json
+++ b/api/conf/schema.json
@@ -1,7 +1,6 @@
 {
 	"main": {
 		"consumer": {
-			"additionalProperties": false,
 			"properties": {
 				"create_time": {
 					"type": "integer"
@@ -41,7 +40,6 @@
 			"type": "object"
 		},
 		"global_rule": {
-			"additionalProperties": false,
 			"properties": {
 				"create_time": {
 					"type": "integer"
@@ -68,7 +66,6 @@
 			"type": "object"
 		},
 		"plugin_config": {
-			"additionalProperties": false,
 			"properties": {
 				"create_time": {
 					"type": "integer"
@@ -115,7 +112,6 @@
 		"plugins": {
 			"items": {
 				"properties": {
-					"additionalProperties": false,
 					"name": {
 						"minLength": 1,
 						"type": "string"
@@ -130,7 +126,6 @@
 			"type": "array"
 		},
 		"proto": {
-			"additionalProperties": false,
 			"properties": {
 				"content": {
 					"maxLength": 1048576,
@@ -163,7 +158,6 @@
 			"type": "object"
 		},
 		"route": {
-			"additionalProperties": false,
 			"allOf": [{
 				"oneOf": [{
 					"required": ["uri"]
@@ -419,7 +413,6 @@
 					"type": "integer"
 				},
 				"upstream": {
-					"additionalProperties": false,
 					"oneOf": [{
 						"required": ["nodes", "type"]
 					}, {
@@ -427,7 +420,6 @@
 					}],
 					"properties": {
 						"checks": {
-							"additionalProperties": false,
 							"anyOf": [{
 								"required": ["active"]
 							}, {
@@ -652,6 +644,26 @@
 								"type": "integer"
 							}]
 						},
+						"keepalive_pool": {
+							"properties": {
+								"idle_timeout": {
+									"default": 60,
+									"minimum": 0,
+									"type": "number"
+								},
+								"requests": {
+									"default": 1000,
+									"minimum": 1,
+									"type": "integer"
+								},
+								"size": {
+									"default": 320,
+									"minimum": 1,
+									"type": "integer"
+								}
+							},
+							"type": "object"
+						},
 						"key": {
 							"description": "the key of chash for dynamic load balancing",
 							"type": "string"
@@ -728,6 +740,10 @@
 							"minimum": 0,
 							"type": "integer"
 						},
+						"retry_timeout": {
+							"minimum": 0,
+							"type": "number"
+						},
 						"scheme": {
 							"default": "http",
 							"enum": ["grpc", "grpcs", "http", "https"]
@@ -773,7 +789,6 @@
 						},
 						"type": {
 							"description": "algorithms of load balancing",
-							"enum": ["chash", "ewma", "least_conn", "roundrobin"],
 							"type": "string"
 						},
 						"update_time": {
@@ -818,7 +833,6 @@
 			"type": "object"
 		},
 		"service": {
-			"additionalProperties": false,
 			"properties": {
 				"create_time": {
 					"type": "integer"
@@ -873,7 +887,6 @@
 					"type": "integer"
 				},
 				"upstream": {
-					"additionalProperties": false,
 					"oneOf": [{
 						"required": ["nodes", "type"]
 					}, {
@@ -881,7 +894,6 @@
 					}],
 					"properties": {
 						"checks": {
-							"additionalProperties": false,
 							"anyOf": [{
 								"required": ["active"]
 							}, {
@@ -1106,6 +1118,26 @@
 								"type": "integer"
 							}]
 						},
+						"keepalive_pool": {
+							"properties": {
+								"idle_timeout": {
+									"default": 60,
+									"minimum": 0,
+									"type": "number"
+								},
+								"requests": {
+									"default": 1000,
+									"minimum": 1,
+									"type": "integer"
+								},
+								"size": {
+									"default": 320,
+									"minimum": 1,
+									"type": "integer"
+								}
+							},
+							"type": "object"
+						},
 						"key": {
 							"description": "the key of chash for dynamic load balancing",
 							"type": "string"
@@ -1182,6 +1214,10 @@
 							"minimum": 0,
 							"type": "integer"
 						},
+						"retry_timeout": {
+							"minimum": 0,
+							"type": "number"
+						},
 						"scheme": {
 							"default": "http",
 							"enum": ["grpc", "grpcs", "http", "https"]
@@ -1227,7 +1263,6 @@
 						},
 						"type": {
 							"description": "algorithms of load balancing",
-							"enum": ["chash", "ewma", "least_conn", "roundrobin"],
 							"type": "string"
 						},
 						"update_time": {
@@ -1255,7 +1290,6 @@
 			"type": "object"
 		},
 		"ssl": {
-			"additionalProperties": false,
 			"oneOf": [{
 				"required": ["cert", "key", "sni"]
 			}, {
@@ -1444,7 +1478,6 @@
 					"type": "integer"
 				},
 				"upstream": {
-					"additionalProperties": false,
 					"oneOf": [{
 						"required": ["nodes", "type"]
 					}, {
@@ -1452,7 +1485,6 @@
 					}],
 					"properties": {
 						"checks": {
-							"additionalProperties": false,
 							"anyOf": [{
 								"required": ["active"]
 							}, {
@@ -1677,6 +1709,26 @@
 								"type": "integer"
 							}]
 						},
+						"keepalive_pool": {
+							"properties": {
+								"idle_timeout": {
+									"default": 60,
+									"minimum": 0,
+									"type": "number"
+								},
+								"requests": {
+									"default": 1000,
+									"minimum": 1,
+									"type": "integer"
+								},
+								"size": {
+									"default": 320,
+									"minimum": 1,
+									"type": "integer"
+								}
+							},
+							"type": "object"
+						},
 						"key": {
 							"description": "the key of chash for dynamic load balancing",
 							"type": "string"
@@ -1753,6 +1805,10 @@
 							"minimum": 0,
 							"type": "integer"
 						},
+						"retry_timeout": {
+							"minimum": 0,
+							"type": "number"
+						},
 						"scheme": {
 							"default": "http",
 							"enum": ["grpc", "grpcs", "http", "https"]
@@ -1798,7 +1854,6 @@
 						},
 						"type": {
 							"description": "algorithms of load balancing",
-							"enum": ["chash", "ewma", "least_conn", "roundrobin"],
 							"type": "string"
 						},
 						"update_time": {
@@ -1826,7 +1881,6 @@
 			"type": "object"
 		},
 		"upstream": {
-			"additionalProperties": false,
 			"oneOf": [{
 				"required": ["nodes", "type"]
 			}, {
@@ -1834,7 +1888,6 @@
 			}],
 			"properties": {
 				"checks": {
-					"additionalProperties": false,
 					"anyOf": [{
 						"required": ["active"]
 					}, {
@@ -2059,6 +2112,26 @@
 						"type": "integer"
 					}]
 				},
+				"keepalive_pool": {
+					"properties": {
+						"idle_timeout": {
+							"default": 60,
+							"minimum": 0,
+							"type": "number"
+						},
+						"requests": {
+							"default": 1000,
+							"minimum": 1,
+							"type": "integer"
+						},
+						"size": {
+							"default": 320,
+							"minimum": 1,
+							"type": "integer"
+						}
+					},
+					"type": "object"
+				},
 				"key": {
 					"description": "the key of chash for dynamic load balancing",
 					"type": "string"
@@ -2135,6 +2208,10 @@
 					"minimum": 0,
 					"type": "integer"
 				},
+				"retry_timeout": {
+					"minimum": 0,
+					"type": "number"
+				},
 				"scheme": {
 					"default": "http",
 					"enum": ["grpc", "grpcs", "http", "https"]
@@ -2180,7 +2257,6 @@
 				},
 				"type": {
 					"description": "algorithms of load balancing",
-					"enum": ["chash", "ewma", "least_conn", "roundrobin"],
 					"type": "string"
 				},
 				"update_time": {
@@ -2277,6 +2353,51 @@
 			},
 			"version": 0.1
 		},
+		"authz-casbin": {
+			"metadata_schema": {
+				"properties": {
+					"model": {
+						"type": "string"
+					},
+					"policy": {
+						"type": "string"
+					}
+				},
+				"required": ["model", "policy"],
+				"type": "object"
+			},
+			"priority": 2560,
+			"schema": {
+				"$comment": "this is a mark for our injected plugin schema",
+				"oneOf": [{
+					"required": ["model_path", "policy_path", "username"]
+				}, {
+					"required": ["model", "policy", "username"]
+				}],
+				"properties": {
+					"disable": {
+						"type": "boolean"
+					},
+					"model": {
+						"type": "string"
+					},
+					"model_path": {
+						"type": "string"
+					},
+					"policy": {
+						"type": "string"
+					},
+					"policy_path": {
+						"type": "string"
+					},
+					"username": {
+						"type": "string"
+					}
+				},
+				"type": "object"
+			},
+			"version": 0.1
+		},
 		"authz-keycloak": {
 			"priority": 2000,
 			"schema": {
@@ -2373,6 +2494,7 @@
 						"type": "boolean"
 					},
 					"permissions": {
+						"default": {},
 						"items": {
 							"maxLength": 100,
 							"minLength": 1,
@@ -2412,7 +2534,6 @@
 		},
 		"basic-auth": {
 			"consumer_schema": {
-				"additionalProperties": false,
 				"properties": {
 					"password": {
 						"type": "string"
@@ -2428,7 +2549,6 @@
 			"priority": 2520,
 			"schema": {
 				"$comment": "this is a mark for our injected plugin schema",
-				"additionalProperties": false,
 				"properties": {
 					"disable": {
 						"type": "boolean"
@@ -2442,7 +2562,6 @@
 		},
 		"batch-requests": {
 			"metadata_schema": {
-				"additionalProperties": false,
 				"properties": {
 					"max_body_size": {
 						"default": 1048576,
@@ -2456,7 +2575,6 @@
 			"priority": 4010,
 			"schema": {
 				"$comment": "this is a mark for our injected plugin schema",
-				"additionalProperties": false,
 				"properties": {
 					"disable": {
 						"type": "boolean"
@@ -2532,7 +2650,7 @@
 					},
 					"type": {
 						"default": "consumer_name",
-						"enum": ["consumer_name", "service_id"],
+						"enum": ["consumer_name", "route_id", "service_id"],
 						"type": "string"
 					},
 					"whitelist": {
@@ -2570,6 +2688,7 @@
 					"allow_origins": {
 						"default": "*",
 						"description": "you can use '*' to allow all origins when no credentials,'**' to allow forcefully(it will bring some security risks, be carefully),multiple origin use ',' to split. default: *.",
+						"pattern": "^(\\*|\\*\\*|null|\\w+://[^,]+(,\\w+://[^,]+)*)$",
 						"type": "string"
 					},
 					"allow_origins_by_regex": {
@@ -2631,7 +2750,6 @@
 			"priority": 412,
 			"schema": {
 				"$comment": "this is a mark for our injected plugin schema",
-				"additionalProperties": false,
 				"anyOf": [{
 					"required": ["before_body"]
 				}, {
@@ -2746,7 +2864,6 @@
 		},
 		"example-plugin": {
 			"metadata_schema": {
-				"additionalProperties": false,
 				"properties": {
 					"ikey": {
 						"minimum": 0,
@@ -2976,9 +3093,71 @@
 			},
 			"version": 0.1
 		},
+		"gzip": {
+			"priority": 995,
+			"schema": {
+				"$comment": "this is a mark for our injected plugin schema",
+				"properties": {
+					"buffers": {
+						"default": {
+							"number": 32,
+							"size": 4096
+						},
+						"properties": {
+							"number": {
+								"default": 32,
+								"minimum": 1,
+								"type": "integer"
+							},
+							"size": {
+								"default": 4096,
+								"minimum": 1,
+								"type": "integer"
+							}
+						},
+						"type": "object"
+					},
+					"comp_level": {
+						"default": 1,
+						"maximum": 9,
+						"minimum": 1,
+						"type": "integer"
+					},
+					"disable": {
+						"type": "boolean"
+					},
+					"http_version": {
+						"default": 1.1,
+						"enum": [1, 1.1]
+					},
+					"min_length": {
+						"default": 20,
+						"minimum": 1,
+						"type": "integer"
+					},
+					"types": {
+						"anyOf": [{
+							"items": {
+								"minLength": 1,
+								"type": "string"
+							},
+							"minItem": 1,
+							"type": "array"
+						}, {
+							"enum": ["*"]
+						}],
+						"default": ["text/html"]
+					},
+					"vary": {
+						"type": "boolean"
+					}
+				},
+				"type": "object"
+			},
+			"version": 0.1
+		},
 		"hmac-auth": {
 			"consumer_schema": {
-				"additionalProperties": false,
 				"properties": {
 					"access_key": {
 						"maxLength": 256,
@@ -3025,7 +3204,6 @@
 			"priority": 2530,
 			"schema": {
 				"$comment": "this is a mark for our injected plugin schema",
-				"additionalProperties": false,
 				"properties": {
 					"disable": {
 						"type": "boolean"
@@ -3039,7 +3217,6 @@
 		},
 		"http-logger": {
 			"metadata_schema": {
-				"additionalProperties": false,
 				"properties": {
 					"log_format": {
 						"default": {
@@ -3121,67 +3298,65 @@
 			"schema": {
 				"$comment": "this is a mark for our injected plugin schema",
 				"oneOf": [{
-					"additionalProperties": false,
-					"properties": {
-						"whitelist": {
-							"items": {
-								"anyOf": [{
-									"format": "ipv4",
-									"title": "IPv4",
-									"type": "string"
-								}, {
-									"pattern": "^([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])/([12]?[0-9]|3[0-2])$",
-									"title": "IPv4/CIDR",
-									"type": "string"
-								}, {
-									"format": "ipv6",
-									"title": "IPv6",
-									"type": "string"
-								}, {
-									"pattern": "^([a-fA-F0-9]{0,4}:){1,8}(:[a-fA-F0-9]{0,4}){0,8}([a-fA-F0-9]{0,4})?/[0-9]{1,3}$",
-									"title": "IPv6/CIDR",
-									"type": "string"
-								}]
-							},
-							"minItems": 1,
-							"type": "array"
-						}
-					},
-					"required": ["whitelist"],
-					"title": "whitelist"
+					"required": ["whitelist"]
 				}, {
-					"additionalProperties": false,
-					"properties": {
-						"blacklist": {
-							"items": {
-								"anyOf": [{
-									"format": "ipv4",
-									"title": "IPv4",
-									"type": "string"
-								}, {
-									"pattern": "^([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])/([12]?[0-9]|3[0-2])$",
-									"title": "IPv4/CIDR",
-									"type": "string"
-								}, {
-									"format": "ipv6",
-									"title": "IPv6",
-									"type": "string"
-								}, {
-									"pattern": "^([a-fA-F0-9]{0,4}:){1,8}(:[a-fA-F0-9]{0,4}){0,8}([a-fA-F0-9]{0,4})?/[0-9]{1,3}$",
-									"title": "IPv6/CIDR",
-									"type": "string"
-								}]
-							},
-							"minItems": 1,
-							"type": "array"
-						}
-					},
-					"required": ["blacklist"],
-					"title": "blacklist"
+					"required": ["blacklist"]
 				}],
 				"properties": {
+					"blacklist": {
+						"items": {
+							"anyOf": [{
+								"format": "ipv4",
+								"title": "IPv4",
+								"type": "string"
+							}, {
+								"pattern": "^([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])/([12]?[0-9]|3[0-2])$",
+								"title": "IPv4/CIDR",
+								"type": "string"
+							}, {
+								"format": "ipv6",
+								"title": "IPv6",
+								"type": "string"
+							}, {
+								"pattern": "^([a-fA-F0-9]{0,4}:){1,8}(:[a-fA-F0-9]{0,4}){0,8}([a-fA-F0-9]{0,4})?/[0-9]{1,3}$",
+								"title": "IPv6/CIDR",
+								"type": "string"
+							}]
+						},
+						"minItems": 1,
+						"type": "array"
+					},
 					"disable": {
 						"type": "boolean"
+					},
+					"message": {
+						"default": "Your IP address is not allowed",
+						"maxLength": 1024,
+						"minLength": 1,
+						"type": "string"
+					},
+					"whitelist": {
+						"items": {
+							"anyOf": [{
+								"format": "ipv4",
+								"title": "IPv4",
+								"type": "string"
+							}, {
+								"pattern": "^([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])/([12]?[0-9]|3[0-2])$",
+								"title": "IPv4/CIDR",
+								"type": "string"
+							}, {
+								"format": "ipv6",
+								"title": "IPv6",
+								"type": "string"
+							}, {
+								"pattern": "^([a-fA-F0-9]{0,4}:){1,8}(:[a-fA-F0-9]{0,4}){0,8}([a-fA-F0-9]{0,4})?/[0-9]{1,3}$",
+								"title": "IPv6/CIDR",
+								"type": "string"
+							}]
+						},
+						"minItems": 1,
+						"type": "array"
 					}
 				},
 				"type": "object"
@@ -3243,7 +3418,6 @@
 			"priority": 2510,
 			"schema": {
 				"$comment": "this is a mark for our injected plugin schema",
-				"additionalProperties": false,
 				"properties": {
 					"disable": {
 						"type": "boolean"
@@ -3255,6 +3429,19 @@
 			"version": 0.1
 		},
 		"kafka-logger": {
+			"metadata_schema": {
+				"properties": {
+					"log_format": {
+						"default": {
+							"@timestamp": "$time_iso8601",
+							"client_ip": "$remote_addr",
+							"host": "$host"
+						},
+						"type": "object"
+					}
+				},
+				"type": "object"
+			},
 			"priority": 403,
 			"schema": {
 				"$comment": "this is a mark for our injected plugin schema",
@@ -3327,7 +3514,6 @@
 		},
 		"key-auth": {
 			"consumer_schema": {
-				"additionalProperties": false,
 				"properties": {
 					"key": {
 						"type": "string"
@@ -3339,7 +3525,6 @@
 			"priority": 2500,
 			"schema": {
 				"$comment": "this is a mark for our injected plugin schema",
-				"additionalProperties": false,
 				"properties": {
 					"disable": {
 						"type": "boolean"
@@ -3347,6 +3532,10 @@
 					"header": {
 						"default": "apikey",
 						"type": "string"
+					},
+					"query": {
+						"default": "apikey",
+						"type": "string"
 					}
 				},
 				"type": "object"
@@ -3375,14 +3564,12 @@
 						"type": "boolean"
 					},
 					"key": {
-						"enum": ["consumer_name", "http_x_forwarded_for", "http_x_real_ip", "remote_addr", "server_addr"],
+						"enum": ["remote_addr", "server_addr"],
 						"type": "string"
 					},
-					"rejected_code": {
-						"default": 503,
-						"maximum": 599,
-						"minimum": 200,
-						"type": "integer"
+					"only_use_default_delay": {
+						"default": false,
+						"type": "boolean"
 					}
 				},
 				"required": ["burst", "conn", "default_conn_delay", "key"],
@@ -3464,6 +3651,10 @@
 					}
 				},
 				"properties": {
+					"allow_degradation": {
+						"default": false,
+						"type": "boolean"
+					},
 					"count": {
 						"exclusiveMinimum": 0,
 						"type": "integer"
@@ -3487,6 +3678,14 @@
 						"minimum": 200,
 						"type": "integer"
 					},
+					"rejected_msg": {
+						"minLength": 1,
+						"type": "string"
+					},
+					"show_limit_quota_header": {
+						"default": true,
+						"type": "boolean"
+					},
 					"time_window": {
 						"exclusiveMinimum": 0,
 						"type": "integer"
@@ -3502,6 +3701,10 @@
 			"schema": {
 				"$comment": "this is a mark for our injected plugin schema",
 				"properties": {
+					"allow_degradation": {
+						"default": false,
+						"type": "boolean"
+					},
 					"burst": {
 						"minimum": 0,
 						"type": "number"
@@ -3526,6 +3729,10 @@
 						"maximum": 599,
 						"minimum": 200,
 						"type": "integer"
+					},
+					"rejected_msg": {
+						"minLength": 1,
+						"type": "string"
 					}
 				},
 				"required": ["burst", "key", "rate"],
@@ -3690,7 +3897,6 @@
 			"priority": 500,
 			"schema": {
 				"$comment": "this is a mark for our injected plugin schema",
-				"additionalProperties": false,
 				"properties": {
 					"disable": {
 						"type": "boolean"
@@ -3742,8 +3948,8 @@
 					"cache_method": {
 						"default": ["GET", "HEAD"],
 						"items": {
-							"description": "HTTP method",
-							"enum": ["CONNECT", "DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT", "TRACE"],
+							"description": "supported http method",
+							"enum": ["GET", "HEAD", "POST"],
 							"type": "string"
 						},
 						"minItems": 1,
@@ -3799,7 +4005,6 @@
 			"priority": 1008,
 			"schema": {
 				"$comment": "this is a mark for our injected plugin schema",
-				"additionalProperties": false,
 				"minProperties": 1,
 				"properties": {
 					"disable": {
@@ -3842,6 +4047,47 @@
 			},
 			"version": 0.1
 		},
+		"real-ip": {
+			"priority": 23000,
+			"schema": {
+				"$comment": "this is a mark for our injected plugin schema",
+				"properties": {
+					"disable": {
+						"type": "boolean"
+					},
+					"source": {
+						"minLength": 1,
+						"type": "string"
+					},
+					"trusted_addresses": {
+						"items": {
+							"anyOf": [{
+								"format": "ipv4",
+								"title": "IPv4",
+								"type": "string"
+							}, {
+								"pattern": "^([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])/([12]?[0-9]|3[0-2])$",
+								"title": "IPv4/CIDR",
+								"type": "string"
+							}, {
+								"format": "ipv6",
+								"title": "IPv6",
+								"type": "string"
+							}, {
+								"pattern": "^([a-fA-F0-9]{0,4}:){1,8}(:[a-fA-F0-9]{0,4}){0,8}([a-fA-F0-9]{0,4})?/[0-9]{1,3}$",
+								"title": "IPv6/CIDR",
+								"type": "string"
+							}]
+						},
+						"minItems": 1,
+						"type": "array"
+					}
+				},
+				"required": ["source"],
+				"type": "object"
+			},
+			"version": 0.1
+		},
 		"redirect": {
 			"priority": 900,
 			"schema": {
@@ -3897,7 +4143,6 @@
 			"priority": 2990,
 			"schema": {
 				"$comment": "this is a mark for our injected plugin schema",
-				"additionalProperties": false,
 				"properties": {
 					"bypass_missing": {
 						"default": false,
@@ -3925,6 +4170,11 @@
 			"schema": {
 				"$comment": "this is a mark for our injected plugin schema",
 				"properties": {
+					"algorithm": {
+						"default": "uuid",
+						"enum": ["snowflake", "uuid"],
+						"type": "string"
+					},
 					"disable": {
 						"type": "boolean"
 					},
@@ -3976,7 +4226,6 @@
 			"priority": 899,
 			"schema": {
 				"$comment": "this is a mark for our injected plugin schema",
-				"additionalProperties": false,
 				"minProperties": 1,
 				"properties": {
 					"body": {
@@ -4014,7 +4263,6 @@
 			"priority": 990,
 			"schema": {
 				"$comment": "this is a mark for our injected plugin schema",
-				"additionalProperties": false,
 				"properties": {
 					"disable": {
 						"type": "boolean"
@@ -4335,14 +4583,12 @@
 			"priority": 966,
 			"schema": {
 				"$comment": "this is a mark for our injected plugin schema",
-				"additionalProperties": false,
 				"properties": {
 					"disable": {
 						"type": "boolean"
 					},
 					"rules": {
 						"items": {
-							"additionalProperties": false,
 							"properties": {
 								"match": {
 									"items": {
@@ -4362,7 +4608,6 @@
 									"items": {
 										"properties": {
 											"upstream": {
-												"additionalProperties": false,
 												"oneOf": [{
 													"required": ["nodes", "type"]
 												}, {
@@ -4370,7 +4615,6 @@
 												}],
 												"properties": {
 													"checks": {
-														"additionalProperties": false,
 														"anyOf": [{
 															"required": ["active"]
 														}, {
@@ -4595,6 +4839,26 @@
 															"type": "integer"
 														}]
 													},
+													"keepalive_pool": {
+														"properties": {
+															"idle_timeout": {
+																"default": 60,
+																"minimum": 0,
+																"type": "number"
+															},
+															"requests": {
+																"default": 1000,
+																"minimum": 1,
+																"type": "integer"
+															},
+															"size": {
+																"default": 320,
+																"minimum": 1,
+																"type": "integer"
+															}
+														},
+														"type": "object"
+													},
 													"key": {
 														"description": "the key of chash for dynamic load balancing",
 														"type": "string"
@@ -4671,6 +4935,10 @@
 														"minimum": 0,
 														"type": "integer"
 													},
+													"retry_timeout": {
+														"minimum": 0,
+														"type": "number"
+													},
 													"scheme": {
 														"default": "http",
 														"enum": ["grpc", "grpcs", "http", "https"]
@@ -4716,7 +4984,6 @@
 													},
 													"type": {
 														"description": "algorithms of load balancing",
-														"enum": ["chash", "ewma", "least_conn", "roundrobin"],
 														"type": "string"
 													},
 													"update_time": {
@@ -4763,6 +5030,37 @@
 			},
 			"version": 0.1
 		},
+		"ua-restriction": {
+			"priority": 2999,
+			"schema": {
+				"$comment": "this is a mark for our injected plugin schema",
+				"properties": {
+					"allowlist": {
+						"minItems": 1,
+						"type": "array"
+					},
+					"bypass_missing": {
+						"default": false,
+						"type": "boolean"
+					},
+					"denylist": {
+						"minItems": 1,
+						"type": "array"
+					},
+					"disable": {
+						"type": "boolean"
+					},
+					"message": {
+						"default": "Not allowed",
+						"maxLength": 1024,
+						"minLength": 1,
+						"type": "string"
+					}
+				},
+				"type": "object"
+			},
+			"version": 0.1
+		},
 		"udp-logger": {
 			"priority": 400,
 			"schema": {
@@ -4833,6 +5131,10 @@
 						"default": 403,
 						"minimum": 200,
 						"type": "integer"
+					},
+					"rejected_msg": {
+						"minLength": 1,
+						"type": "string"
 					}
 				},
 				"required": ["block_rules"],
diff --git a/api/internal/core/entity/entity.go b/api/internal/core/entity/entity.go
index efa10be..1d17c9a 100644
--- a/api/internal/core/entity/entity.go
+++ b/api/internal/core/entity/entity.go
@@ -97,10 +97,11 @@ type Route struct {
 }
 
 // --- structures for upstream start  ---
+type TimeoutValue float32
 type Timeout struct {
-	Connect int `json:"connect,omitempty"`
-	Send    int `json:"send,omitempty"`
-	Read    int `json:"read,omitempty"`
+	Connect TimeoutValue `json:"connect,omitempty"`
+	Send    TimeoutValue `json:"send,omitempty"`
+	Read    TimeoutValue `json:"read,omitempty"`
 }
 
 type Node struct {
@@ -133,16 +134,16 @@ type UnHealthy struct {
 }
 
 type Active struct {
-	Type                   string    `json:"type,omitempty"`
-	Timeout                int       `json:"timeout,omitempty"`
-	Concurrency            int       `json:"concurrency,omitempty"`
-	Host                   string    `json:"host,omitempty"`
-	Port                   int       `json:"port,omitempty"`
-	HTTPPath               string    `json:"http_path,omitempty"`
-	HTTPSVerifyCertificate string    `json:"https_verify_certificate,omitempty"`
-	Healthy                Healthy   `json:"healthy,omitempty"`
-	UnHealthy              UnHealthy `json:"unhealthy,omitempty"`
-	ReqHeaders             []string  `json:"req_headers,omitempty"`
+	Type                   string       `json:"type,omitempty"`
+	Timeout                TimeoutValue `json:"timeout,omitempty"`
+	Concurrency            int          `json:"concurrency,omitempty"`
+	Host                   string       `json:"host,omitempty"`
+	Port                   int          `json:"port,omitempty"`
+	HTTPPath               string       `json:"http_path,omitempty"`
+	HTTPSVerifyCertificate string       `json:"https_verify_certificate,omitempty"`
+	Healthy                Healthy      `json:"healthy,omitempty"`
+	UnHealthy              UnHealthy    `json:"unhealthy,omitempty"`
+	ReqHeaders             []string     `json:"req_headers,omitempty"`
 }
 
 type Passive struct {
@@ -161,24 +162,32 @@ type UpstreamTLS struct {
 	ClientKey  string `json:"client_key,omitempty"`
 }
 
+type UpstreamKeepalivePool struct {
+	IdleTimeout TimeoutValue `json:"idle_timeout,omitempty"`
+	Requests    int          `json:"requests,omitempty"`
+	Size        int          `json:"size"`
+}
+
 type UpstreamDef struct {
-	Nodes         interface{}       `json:"nodes,omitempty"`
-	Retries       int               `json:"retries,omitempty"`
-	Timeout       interface{}       `json:"timeout,omitempty"`
-	Type          string            `json:"type,omitempty"`
-	Checks        interface{}       `json:"checks,omitempty"`
-	HashOn        string            `json:"hash_on,omitempty"`
-	Key           string            `json:"key,omitempty"`
-	Scheme        string            `json:"scheme,omitempty"`
-	DiscoveryType string            `json:"discovery_type,omitempty"`
-	DiscoveryArgs map[string]string `json:"discovery_args,omitempty"`
-	PassHost      string            `json:"pass_host,omitempty"`
-	UpstreamHost  string            `json:"upstream_host,omitempty"`
-	Name          string            `json:"name,omitempty"`
-	Desc          string            `json:"desc,omitempty"`
-	ServiceName   string            `json:"service_name,omitempty"`
-	Labels        map[string]string `json:"labels,omitempty"`
-	TLS           *UpstreamTLS      `json:"tls,omitempty"`
+	Nodes         interface{}            `json:"nodes,omitempty"`
+	Retries       int                    `json:"retries,omitempty"`
+	Timeout       *Timeout               `json:"timeout,omitempty"`
+	Type          string                 `json:"type,omitempty"`
+	Checks        interface{}            `json:"checks,omitempty"`
+	HashOn        string                 `json:"hash_on,omitempty"`
+	Key           string                 `json:"key,omitempty"`
+	Scheme        string                 `json:"scheme,omitempty"`
+	DiscoveryType string                 `json:"discovery_type,omitempty"`
+	DiscoveryArgs map[string]string      `json:"discovery_args,omitempty"`
+	PassHost      string                 `json:"pass_host,omitempty"`
+	UpstreamHost  string                 `json:"upstream_host,omitempty"`
+	Name          string                 `json:"name,omitempty"`
+	Desc          string                 `json:"desc,omitempty"`
+	ServiceName   string                 `json:"service_name,omitempty"`
+	Labels        map[string]string      `json:"labels,omitempty"`
+	TLS           *UpstreamTLS           `json:"tls,omitempty"`
+	KeepalivePool *UpstreamKeepalivePool `json:"keepalive_pool,omitempty"`
+	RetryTimeout  TimeoutValue           `json:"retry_timeout,omitempty"`
 }
 
 // swagger:model Upstream
diff --git a/api/internal/core/store/validate_test.go b/api/internal/core/store/validate_test.go
index 7b0b6f3..8050499 100644
--- a/api/internal/core/store/validate_test.go
+++ b/api/internal/core/store/validate_test.go
@@ -289,7 +289,7 @@ func TestAPISIXJsonSchemaValidator_Plugin(t *testing.T) {
 	err = json.Unmarshal([]byte(reqBody), route)
 	assert.Nil(t, err)
 	err = validator.Validate(route)
-	assert.Equal(t, fmt.Errorf("schema validate failed: (root): Must validate one and only one schema (oneOf)\n(root): Additional property disable is not allowed\ndisable: Invalid type. Expected: boolean, given: integer"), err)
+	assert.Equal(t, fmt.Errorf("schema validate failed: disable: Invalid type. Expected: boolean, given: integer"), err)
 }
 
 func TestAPISIXJsonSchemaValidator_Route_checkRemoteAddr(t *testing.T) {
@@ -447,23 +447,4 @@ func TestAPISIXSchemaValidator_Validate(t *testing.T) {
 	}`
 	err = validator.Validate([]byte(reqBody))
 	assert.Nil(t, err)
-
-	// config with non existent field, should be failed.
-	reqBody = `{
-		"username": "jack",
-		"not-exist": "val",
-		"plugins": {
-			"limit-count": {
-				"count": 2,
-				"time_window": 60,
-				"rejected_code": 503,
-				"key": "remote_addr"
-			}
-		},
-		"desc": "test description"
-	}`
-	err = validator.Validate([]byte(reqBody))
-	assert.NotNil(t, err)
-	assert.EqualError(t, err, "schema validate failed: (root): Additional property not-exist is not allowed")
-
 }
diff --git a/api/internal/handler/schema/plugin_test.go b/api/internal/handler/schema/plugin_test.go
index d569f2d..eb61e79 100644
--- a/api/internal/handler/schema/plugin_test.go
+++ b/api/internal/handler/schema/plugin_test.go
@@ -62,5 +62,5 @@ func TestPlugin(t *testing.T) {
 	// plugin type
 	assert.ElementsMatch(t, []string{"basic-auth", "jwt-auth", "hmac-auth", "key-auth", "wolf-rbac"}, authPlugins)
 	// consumer schema
-	assert.Equal(t, `{"additionalProperties":false,"properties":{"password":{"type":"string"},"username":{"type":"string"}},"required":["password","username"],"title":"work with consumer object","type":"object"}`, basicAuthConsumerSchema)
+	assert.Equal(t, `{"properties":{"password":{"type":"string"},"username":{"type":"string"}},"required":["password","username"],"title":"work with consumer object","type":"object"}`, basicAuthConsumerSchema)
 }
diff --git a/api/internal/handler/upstream/upstream_test.go b/api/internal/handler/upstream/upstream_test.go
index c2b274d..47c219a 100644
--- a/api/internal/handler/upstream/upstream_test.go
+++ b/api/internal/handler/upstream/upstream_test.go
@@ -55,10 +55,10 @@ func TestUpstream_Get(t *testing.T) {
 				},
 				UpstreamDef: entity.UpstreamDef{
 					Name: "upstream1",
-					Timeout: map[string]interface{}{
-						"connect": 15,
-						"send":    15,
-						"read":    15,
+					Timeout: &entity.Timeout{
+						Connect: 15,
+						Send:    15,
+						Read:    15,
 					},
 					Checks: map[string]interface{}{
 						"active": map[string]interface{}{
@@ -103,10 +103,10 @@ func TestUpstream_Get(t *testing.T) {
 				},
 				UpstreamDef: entity.UpstreamDef{
 					Name: "upstream1",
-					Timeout: map[string]interface{}{
-						"connect": 15,
-						"send":    15,
-						"read":    15,
+					Timeout: &entity.Timeout{
+						Connect: 15,
+						Send:    15,
+						Read:    15,
 					},
 					Checks: map[string]interface{}{
 						"active": map[string]interface{}{
@@ -345,10 +345,10 @@ func TestUpstream_Create(t *testing.T) {
 				},
 				UpstreamDef: entity.UpstreamDef{
 					Name: "upstream1",
-					Timeout: map[string]interface{}{
-						"connect": 15,
-						"send":    15,
-						"read":    15,
+					Timeout: &entity.Timeout{
+						Connect: 15,
+						Send:    15,
+						Read:    15,
 					},
 					Checks: map[string]interface{}{
 						"active": map[string]interface{}{
@@ -393,10 +393,10 @@ func TestUpstream_Create(t *testing.T) {
 				},
 				UpstreamDef: entity.UpstreamDef{
 					Name: "upstream1",
-					Timeout: map[string]interface{}{
-						"connect": 15,
-						"send":    15,
-						"read":    15,
+					Timeout: &entity.Timeout{
+						Connect: 15,
+						Send:    15,
+						Read:    15,
 					},
 					Checks: map[string]interface{}{
 						"active": map[string]interface{}{
@@ -441,10 +441,10 @@ func TestUpstream_Create(t *testing.T) {
 				},
 				UpstreamDef: entity.UpstreamDef{
 					Name: "upstream1",
-					Timeout: map[string]interface{}{
-						"connect": 15,
-						"send":    15,
-						"read":    15,
+					Timeout: &entity.Timeout{
+						Connect: 15,
+						Send:    15,
+						Read:    15,
 					},
 					Checks: map[string]interface{}{
 						"active": map[string]interface{}{
@@ -489,10 +489,10 @@ func TestUpstream_Create(t *testing.T) {
 				},
 				UpstreamDef: entity.UpstreamDef{
 					Name: "upstream1",
-					Timeout: map[string]interface{}{
-						"connect": 15,
-						"send":    15,
-						"read":    15,
+					Timeout: &entity.Timeout{
+						Connect: 15,
+						Send:    15,
+						Read:    15,
 					},
 					Checks: map[string]interface{}{
 						"active": map[string]interface{}{
@@ -542,10 +542,10 @@ func TestUpstream_Create(t *testing.T) {
 				},
 				UpstreamDef: entity.UpstreamDef{
 					Name: "upstream1",
-					Timeout: map[string]interface{}{
-						"connect": 15,
-						"send":    15,
-						"read":    15,
+					Timeout: &entity.Timeout{
+						Connect: 15,
+						Send:    15,
+						Read:    15,
 					},
 					Checks: map[string]interface{}{
 						"active": map[string]interface{}{
@@ -591,10 +591,10 @@ func TestUpstream_Create(t *testing.T) {
 				},
 				UpstreamDef: entity.UpstreamDef{
 					Name: "upstream1",
-					Timeout: map[string]interface{}{
-						"connect": 15,
-						"send":    15,
-						"read":    15,
+					Timeout: &entity.Timeout{
+						Connect: 15,
+						Send:    15,
+						Read:    15,
 					},
 					Checks: map[string]interface{}{
 						"active": map[string]interface{}{
@@ -689,10 +689,10 @@ func TestUpstream_Update(t *testing.T) {
 				Upstream: entity.Upstream{
 					UpstreamDef: entity.UpstreamDef{
 						Name: "upstream1",
-						Timeout: map[string]interface{}{
-							"connect": 15,
-							"send":    15,
-							"read":    15,
+						Timeout: &entity.Timeout{
+							Connect: 15,
+							Send:    15,
+							Read:    15,
 						},
 						Checks: map[string]interface{}{
 							"active": map[string]interface{}{
@@ -738,10 +738,10 @@ func TestUpstream_Update(t *testing.T) {
 				},
 				UpstreamDef: entity.UpstreamDef{
 					Name: "upstream1",
-					Timeout: map[string]interface{}{
-						"connect": 15,
-						"send":    15,
-						"read":    15,
+					Timeout: &entity.Timeout{
+						Connect: 15,
+						Send:    15,
+						Read:    15,
 					},
 					Checks: map[string]interface{}{
 						"active": map[string]interface{}{
@@ -786,10 +786,10 @@ func TestUpstream_Update(t *testing.T) {
 				},
 				UpstreamDef: entity.UpstreamDef{
 					Name: "upstream1",
-					Timeout: map[string]interface{}{
-						"connect": 15,
-						"send":    15,
-						"read":    15,
+					Timeout: &entity.Timeout{
+						Connect: 15,
+						Send:    15,
+						Read:    15,
 					},
 					Checks: map[string]interface{}{
 						"active": map[string]interface{}{
@@ -834,10 +834,10 @@ func TestUpstream_Update(t *testing.T) {
 				},
 				UpstreamDef: entity.UpstreamDef{
 					Name: "upstream1",
-					Timeout: map[string]interface{}{
-						"connect": 15,
-						"send":    15,
-						"read":    15,
+					Timeout: &entity.Timeout{
+						Connect: 15,
+						Send:    15,
+						Read:    15,
 					},
 					Checks: map[string]interface{}{
 						"active": map[string]interface{}{
@@ -887,10 +887,10 @@ func TestUpstream_Update(t *testing.T) {
 					},
 					UpstreamDef: entity.UpstreamDef{
 						Name: "upstream1",
-						Timeout: map[string]interface{}{
-							"connect": 15,
-							"send":    15,
-							"read":    15,
+						Timeout: &entity.Timeout{
+							Connect: 15,
+							Send:    15,
+							Read:    15,
 						},
 						Checks: map[string]interface{}{
 							"active": map[string]interface{}{
@@ -973,10 +973,10 @@ func TestUpstream_Patch(t *testing.T) {
 		},
 		UpstreamDef: entity.UpstreamDef{
 			Name: "upstream1",
-			Timeout: map[string]interface{}{
-				"connect": 15,
-				"send":    15,
-				"read":    15,
+			Timeout: &entity.Timeout{
+				Connect: 15,
+				Send:    15,
+				Read:    15,
 			},
 			Checks: map[string]interface{}{
 				"active": map[string]interface{}{
@@ -1022,10 +1022,10 @@ func TestUpstream_Patch(t *testing.T) {
 		},
 		UpstreamDef: entity.UpstreamDef{
 			Name: "upstream2",
-			Timeout: map[string]interface{}{
-				"connect": float64(20),
-				"send":    float64(20),
-				"read":    float64(20),
+			Timeout: &entity.Timeout{
+				Connect: 20,
+				Send:    20,
+				Read:    20,
 			},
 			Checks: map[string]interface{}{
 				"active": map[string]interface{}{
@@ -1085,10 +1085,10 @@ func TestUpstream_Patch(t *testing.T) {
 				},
 				UpstreamDef: entity.UpstreamDef{
 					Name: "upstream2",
-					Timeout: map[string]interface{}{
-						"connect": float64(20),
-						"send":    float64(20),
-						"read":    float64(20),
+					Timeout:  &entity.Timeout{
+						Connect: 20,
+						Send:    20,
+						Read:    20,
 					},
 					Checks: map[string]interface{}{
 						"active": map[string]interface{}{
@@ -1138,10 +1138,10 @@ func TestUpstream_Patch(t *testing.T) {
 				},
 				UpstreamDef: entity.UpstreamDef{
 					Name: "upstream2",
-					Timeout: map[string]interface{}{
-						"connect": float64(20),
-						"send":    float64(20),
-						"read":    float64(20),
+					Timeout:  &entity.Timeout{
+						Connect: 20,
+						Send:    20,
+						Read:    20,
 					},
 					Checks: map[string]interface{}{
 						"active": map[string]interface{}{
@@ -1186,10 +1186,10 @@ func TestUpstream_Patch(t *testing.T) {
 				},
 				UpstreamDef: entity.UpstreamDef{
 					Name: "upstream2",
-					Timeout: map[string]interface{}{
-						"connect": float64(20),
-						"send":    float64(20),
-						"read":    float64(20),
+					Timeout:  &entity.Timeout{
+						Connect: 20,
+						Send:    20,
+						Read:    20,
 					},
 					Checks: map[string]interface{}{
 						"active": map[string]interface{}{
@@ -1243,10 +1243,10 @@ func TestUpstream_Patch(t *testing.T) {
 				},
 				UpstreamDef: entity.UpstreamDef{
 					Name: "upstream1",
-					Timeout: map[string]interface{}{
-						"connect": float64(20),
-						"send":    float64(20),
-						"read":    float64(20),
+					Timeout:  &entity.Timeout{
+						Connect: 20,
+						Send:    20,
+						Read:    20,
 					},
 					Checks: map[string]interface{}{
 						"active": map[string]interface{}{
@@ -1291,10 +1291,10 @@ func TestUpstream_Patch(t *testing.T) {
 				},
 				UpstreamDef: entity.UpstreamDef{
 					Name: "upstream1",
-					Timeout: map[string]interface{}{
-						"connect": float64(15),
-						"send":    float64(15),
-						"read":    float64(15),
+					Timeout:  &entity.Timeout{
+						Connect: 15,
+						Send:    15,
+						Read:    15,
 					},
 					Checks: map[string]interface{}{
 						"active": map[string]interface{}{
@@ -1339,10 +1339,10 @@ func TestUpstream_Patch(t *testing.T) {
 				},
 				UpstreamDef: entity.UpstreamDef{
 					Name: "upstream1",
-					Timeout: map[string]interface{}{
-						"connect": float64(20),
-						"send":    float64(20),
-						"read":    float64(20),
+					Timeout:  &entity.Timeout{
+						Connect: 20,
+						Send:    20,
+						Read:    20,
 					},
 					Checks: map[string]interface{}{
 						"active": map[string]interface{}{
diff --git a/api/test/docker/docker-compose.yaml b/api/test/docker/docker-compose.yaml
index 3f85c51..072f312 100644
--- a/api/test/docker/docker-compose.yaml
+++ b/api/test/docker/docker-compose.yaml
@@ -127,7 +127,7 @@ services:
 
   apisix:
     hostname: apisix_server1
-    image: apache/apisix:2.7-alpine
+    image: apache/apisix:2.9-alpine
     restart: always
     volumes:
       - ./apisix_config.yaml:/usr/local/apisix/conf/config.yaml:ro
diff --git a/api/test/e2e/json_schema_validate_test.go b/api/test/e2e/json_schema_validate_test.go
deleted file mode 100644
index df46742..0000000
--- a/api/test/e2e/json_schema_validate_test.go
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * 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.
- */
-package e2e
-
-import (
-	"net/http"
-	"testing"
-)
-
-func TestSchema_not_exist_field(t *testing.T) {
-	tests := []HttpTestCase{
-		{
-			Desc:   "config route with non-existent fields",
-			Object: ManagerApiExpect(t),
-			Path:   "/apisix/admin/routes/r1",
-			Method: http.MethodPut,
-			Body: `{
-				"name": "route1",
-                                "uri": "/hello",
-                                "nonexistent": "test non-existent",
-                                "upstream": {
-                                        "type": "roundrobin",
-                                        "nodes": [{
-                                                "host": "` + UpstreamIp + `",
-                                                "port": 1980,
-                                                "weight": 1
-                                        }]
-                                }
-                        }`,
-			Headers:      map[string]string{"Authorization": token},
-			ExpectStatus: http.StatusBadRequest,
-			ExpectBody:   `{"code":10000,"message":"schema validate failed: (root): Additional property nonexistent is not allowed"}`,
-		},
-		{
-			Desc:         "make sure the route create failed",
-			Object:       APISIXExpect(t),
-			Method:       http.MethodGet,
-			Path:         "/hello",
-			ExpectStatus: http.StatusNotFound,
-			Sleep:        sleepTime,
-		},
-	}
-
-	for _, tc := range tests {
-		testCaseCheck(tc, t)
-	}
-}
diff --git a/api/test/e2e/route_import_test.go b/api/test/e2e/route_import_test.go
index bb70562..b482688 100644
--- a/api/test/e2e/route_import_test.go
+++ b/api/test/e2e/route_import_test.go
@@ -319,7 +319,7 @@ func TestImport_with_multi_routes(t *testing.T) {
 				ExpectBody: []string{`"methods":["GET","POST","HEAD","PUT","PATCH","DELETE"]`,
 					`"proxy-rewrite":{"disable":false,"scheme":"https"}`,
 					`"labels":{"API_VERSION":"v2","dev":"test"}`,
-					`"upstream":{"nodes":[{"host":"httpbin.org","port":443,"weight":1}],"timeout":{"connect":6000,"read":6000,"send":6000},"type":"roundrobin","pass_host":"node"}`,
+					`"upstream":{"nodes":[{"host":"httpbin.org","port":443,"weight":1}],"timeout":{"connect":6000,"send":6000,"read":6000},"type":"roundrobin","pass_host":"node"}`,
 				},
 				Sleep: sleepTime,
 			}
@@ -335,7 +335,7 @@ func TestImport_with_multi_routes(t *testing.T) {
 				ExpectBody: []string{`"methods":["POST"]`,
 					`"proxy-rewrite":{"disable":false,"scheme":"https"}`,
 					`"labels":{"API_VERSION":"v1","version":"v1"}`,
-					`"upstream":{"nodes":[{"host":"httpbin.org","port":443,"weight":1}],"timeout":{"connect":6000,"read":6000,"send":6000},"type":"roundrobin","pass_host":"node"}`,
+					`"upstream":{"nodes":[{"host":"httpbin.org","port":443,"weight":1}],"timeout":{"connect":6000,"send":6000,"read":6000},"type":"roundrobin","pass_host":"node"}`,
 				},
 				Sleep: sleepTime,
 			}
diff --git a/api/test/e2enew/schema/schema_test.go b/api/test/e2enew/schema/schema_test.go
index 50a55d3..febb3c7 100644
--- a/api/test/e2enew/schema/schema_test.go
+++ b/api/test/e2enew/schema/schema_test.go
@@ -52,7 +52,7 @@ var _ = ginkgo.Describe("Schema Test", func() {
 			Path:         "/apisix/admin/schema/plugins/jwt-auth",
 			Headers:      map[string]string{"Authorization": base.GetToken()},
 			ExpectStatus: http.StatusOK,
-			ExpectBody:   "{\"$comment\":\"this is a mark for our injected plugin schema\",\"additionalProperties\":false,\"properties\":{\"disable\":{\"type\":\"boolean\"}},\"type\":\"object\"}",
+			ExpectBody:   "{\"$comment\":\"this is a mark for our injected plugin schema\",\"properties\":{\"disable\":{\"type\":\"boolean\"}},\"type\":\"object\"}",
 			Sleep:        base.SleepTime,
 		}),
 		table.Entry("get schema of non-existent plugin", base.HttpTestCase{
diff --git a/api/test/e2enew/upstream/upstream_keepalive_pool.go b/api/test/e2enew/upstream/upstream_keepalive_pool.go
new file mode 100644
index 0000000..9047ac3
--- /dev/null
+++ b/api/test/e2enew/upstream/upstream_keepalive_pool.go
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+package upstream
+
+import (
+	"encoding/json"
+	"net/http"
+
+	"github.com/onsi/ginkgo"
+	"github.com/onsi/gomega"
+
+	"github.com/apisix/manager-api/test/e2enew/base"
+)
+
+// just test for schema check
+var _ = ginkgo.Describe("Upstream keepalive pool", func() {
+	ginkgo.It("create upstream with keepalive pool", func() {
+		createUpstreamBody := make(map[string]interface{})
+		createUpstreamBody["nodes"] = []map[string]interface{}{
+			{
+				"host":   base.UpstreamIp,
+				"port":   1980,
+				"weight": 1,
+			},
+		}
+		createUpstreamBody["type"] = "roundrobin"
+		createUpstreamBody["keepalive_pool"] = map[string]interface{}{
+			"size": 320,
+			"requests": 1000,
+			"idle_timeout": 60,
+		}
+		_createUpstreamBody, err := json.Marshal(createUpstreamBody)
+		gomega.Expect(err).To(gomega.BeNil())
+		base.RunTestCase(base.HttpTestCase{
+			Object:       base.ManagerApiExpect(),
+			Method:       http.MethodPut,
+			Path:         "/apisix/admin/upstreams/kp",
+			Body:         string(_createUpstreamBody),
+			Headers:      map[string]string{"Authorization": base.GetToken()},
+			ExpectStatus: http.StatusOK,
+		})
+	})
+	ginkgo.It("delete upstream", func() {
+		base.RunTestCase(base.HttpTestCase{
+			Object:       base.ManagerApiExpect(),
+			Method:       http.MethodDelete,
+			Path:         "/apisix/admin/upstreams/kp",
+			Headers:      map[string]string{"Authorization": base.GetToken()},
+			ExpectStatus: http.StatusOK,
+		})
+	})
+})
diff --git a/api/test/e2enew/upstream/upstream_retry.go b/api/test/e2enew/upstream/upstream_retry.go
new file mode 100644
index 0000000..adb81bb
--- /dev/null
+++ b/api/test/e2enew/upstream/upstream_retry.go
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+package upstream
+
+import (
+	"encoding/json"
+	"net/http"
+
+	"github.com/onsi/ginkgo"
+	"github.com/onsi/gomega"
+
+	"github.com/apisix/manager-api/test/e2enew/base"
+)
+
+// just test for schema check
+var _ = ginkgo.Describe("Upstream keepalive pool", func() {
+	ginkgo.It("create upstream with keepalive pool", func() {
+		createUpstreamBody := make(map[string]interface{})
+		createUpstreamBody["nodes"] = []map[string]interface{}{
+			{
+				"host":   base.UpstreamIp,
+				"port":   1980,
+				"weight": 1,
+			},
+		}
+		createUpstreamBody["type"] = "roundrobin"
+		createUpstreamBody["retries"] = 5
+		createUpstreamBody["retry_timeout"] = 5.5
+		_createUpstreamBody, err := json.Marshal(createUpstreamBody)
+		gomega.Expect(err).To(gomega.BeNil())
+		base.RunTestCase(base.HttpTestCase{
+			Object:       base.ManagerApiExpect(),
+			Method:       http.MethodPut,
+			Path:         "/apisix/admin/upstreams/retry",
+			Body:         string(_createUpstreamBody),
+			Headers:      map[string]string{"Authorization": base.GetToken()},
+			ExpectStatus: http.StatusOK,
+		})
+	})
+	ginkgo.It("delete upstream", func() {
+		base.RunTestCase(base.HttpTestCase{
+			Object:       base.ManagerApiExpect(),
+			Method:       http.MethodDelete,
+			Path:         "/apisix/admin/upstreams/retry",
+			Headers:      map[string]string{"Authorization": base.GetToken()},
+			ExpectStatus: http.StatusOK,
+		})
+	})
+})
diff --git a/api/test/e2enew/upstream/upstream_test.go b/api/test/e2enew/upstream/upstream_test.go
index f0ff938..2689b0a 100644
--- a/api/test/e2enew/upstream/upstream_test.go
+++ b/api/test/e2enew/upstream/upstream_test.go
@@ -845,7 +845,6 @@ var _ = ginkgo.Describe("test upstream delete (route is in use)", func() {
 			Body: `{
 				"name": "route1",
 				"id": "r1",
-				"name": "route1",
 				"uri": "/hello",
 				"upstream_id": "u1"
 			}`,
diff --git a/web/cypress/fixtures/export-route-dataset.json b/web/cypress/fixtures/export-route-dataset.json
index 2356f82..339b05e 100644
--- a/web/cypress/fixtures/export-route-dataset.json
+++ b/web/cypress/fixtures/export-route-dataset.json
@@ -40,12 +40,17 @@
             ],
             "timeout": {
               "connect": 6,
-              "read": 6,
-              "send": 6
+              "send": 6,
+              "read": 6
             },
             "type": "roundrobin",
             "scheme": "http",
-            "pass_host": "pass"
+            "pass_host": "pass",
+            "keepalive_pool": {
+              "idle_timeout": 60,
+              "requests": 1000,
+              "size": 320
+            }
           }
         }
       }
@@ -92,12 +97,17 @@
             ],
             "timeout": {
               "connect": 6,
-              "read": 6,
-              "send": 6
+              "send": 6,
+              "read": 6
             },
             "type": "roundrobin",
             "scheme": "http",
-            "pass_host": "pass"
+            "pass_host": "pass",
+            "keepalive_pool": {
+              "idle_timeout": 60,
+              "requests": 1000,
+              "size": 320
+            }
           }
         }
       },
@@ -143,12 +153,17 @@
             ],
             "timeout": {
               "connect": 6,
-              "read": 6,
-              "send": 6
+              "send": 6,
+              "read": 6
             },
             "type": "roundrobin",
             "scheme": "http",
-            "pass_host": "pass"
+            "pass_host": "pass",
+            "keepalive_pool": {
+              "idle_timeout": 60,
+              "requests": 1000,
+              "size": 320
+            }
           }
         }
       }
diff --git a/web/cypress/fixtures/plugin-dataset.json b/web/cypress/fixtures/plugin-dataset.json
index 940f34a..0ae63b9 100644
--- a/web/cypress/fixtures/plugin-dataset.json
+++ b/web/cypress/fixtures/plugin-dataset.json
@@ -291,12 +291,12 @@
     {
       "shouldValid": true,
       "data": {
-        "allow_origins": "",
-        "allow_methods": "",
-        "allow_headers": "",
-        "expose_headers": "",
-        "max_age": 600,
-        "allow_credential": true
+        "allow_origins": "*",
+        "allow_methods": "*",
+        "allow_headers": "*",
+        "expose_headers": "*",
+        "max_age": 5,
+        "allow_credential": false
       }
     },
     {
@@ -506,7 +506,7 @@
       }
     },
     {
-      "shouldValid": true,
+      "shouldValid": false,
       "data": {
         "conn": 5,
         "burst": 1,
@@ -516,7 +516,7 @@
       }
     },
     {
-      "shouldValid": true,
+      "shouldValid": false,
       "data": {
         "conn": 5,
         "burst": 1,
@@ -693,13 +693,13 @@
       "data": {}
     },
     {
-      "shouldValid": false,
+      "shouldValid": true,
       "data": {
         "invalid": "invalid"
       }
     },
     {
-      "shouldValid": false,
+      "shouldValid": true,
       "data": {
         "invalid_property": 1
       }
@@ -862,7 +862,7 @@
       }
     },
     {
-      "shouldValid": false,
+      "shouldValid": true,
       "data": {
         "uri": "/apisix/home",
         "host": "apisix.apache.org",
@@ -1054,7 +1054,7 @@
       }
     },
     {
-      "shouldValid": false,
+      "shouldValid": true,
       "data": {
         "body": "Hello world",
         "headers": {
diff --git a/web/cypress/integration/consumer/create-with-limit-conn-form.spec.js b/web/cypress/integration/consumer/create-with-limit-conn-form.spec.js
index b4cac1d..8eb41ae 100644
--- a/web/cypress/integration/consumer/create-with-limit-conn-form.spec.js
+++ b/web/cypress/integration/consumer/create-with-limit-conn-form.spec.js
@@ -30,6 +30,7 @@ context('Create and delete consumer with limit-conn plugin form', () => {
     conn: '#conn',
     burst: '#burst',
     default_conn_delay: '#default_conn_delay',
+    only_use_default_delay: '#only_use_default_delay',
     key: '#key',
     rejected_code: '#rejected_code',
     title: '[title="remote_addr"]',
@@ -89,6 +90,7 @@ context('Create and delete consumer with limit-conn plugin form', () => {
     cy.get(selector.conn).type(data.conn);
     cy.get(selector.burst).type(data.burst);
     cy.get(selector.default_conn_delay).type(data.default_conn_delay);
+    cy.get(selector.only_use_default_delay).click();
     cy.get(selector.key).click();
     cy.get(selector.selectDropdown).should('be.visible');
     cy.get(selector.title).click({
diff --git a/web/cypress/integration/upstream/create_and_delete_upstream.spec.js b/web/cypress/integration/upstream/create_and_delete_upstream.spec.js
index 6798186..70a5ebc 100644
--- a/web/cypress/integration/upstream/create_and_delete_upstream.spec.js
+++ b/web/cypress/integration/upstream/create_and_delete_upstream.spec.js
@@ -246,4 +246,65 @@ context('Create and Delete Upstream', () => {
     cy.contains('button', 'Confirm').click();
     cy.get(selector.notification).should('contain', data.deleteUpstreamSuccess);
   });
+
+  it('should create upstream with keepalive pool default value', function () {
+    cy.visit('/');
+    cy.contains('Upstream').click();
+    cy.contains('Create').click();
+
+    cy.get(selector.name).type(data.upstreamName);
+    cy.get(selector.description).type(data.description);
+
+    cy.get(selector.nodes_0_host).type(data.ip1);
+    cy.get(selector.nodes_0_port).clear().type('7000');
+    cy.get(selector.nodes_0_weight).clear().type(1);
+
+    cy.get('#keepalive_pool_size').clear().type('1');
+    cy.get('#keepalive_pool_idle_timeout').clear().type('15.5');
+    cy.get('#keepalive_pool_requests').clear().type('50');
+
+    cy.contains('Next').click();
+    cy.get(selector.input).should('be.disabled');
+    cy.contains('Submit').click();
+    cy.get(selector.notification).should('contain', data.createUpstreamSuccess);
+    cy.url().should('contains', 'upstream/list');
+  });
+
+  it('should delete the upstream', function () {
+    cy.visit('/');
+    cy.contains('Upstream').click();
+    cy.contains(data.upstreamName).siblings().contains('Delete').click();
+    cy.contains('button', 'Confirm').click();
+    cy.get(selector.notification).should('contain', data.deleteUpstreamSuccess);
+  });
+
+  it('should create upstream with retries and retry timeout', function () {
+    cy.visit('/');
+    cy.contains('Upstream').click();
+    cy.contains('Create').click();
+
+    cy.get(selector.name).type(data.upstreamName);
+    cy.get(selector.description).type(data.description);
+
+    cy.get(selector.nodes_0_host).type(data.ip1);
+    cy.get(selector.nodes_0_port).clear().type('7000');
+    cy.get(selector.nodes_0_weight).clear().type(1);
+
+    cy.get('#retries').clear().type('5');
+    cy.get('#retry_timeout').clear().type('15.5');
+
+    cy.contains('Next').click();
+    cy.get(selector.input).should('be.disabled');
+    cy.contains('Submit').click();
+    cy.get(selector.notification).should('contain', data.createUpstreamSuccess);
+    cy.url().should('contains', 'upstream/list');
+  });
+
+  it('should delete the upstream', function () {
+    cy.visit('/');
+    cy.contains('Upstream').click();
+    cy.contains(data.upstreamName).siblings().contains('Delete').click();
+    cy.contains('button', 'Confirm').click();
+    cy.get(selector.notification).should('contain', data.deleteUpstreamSuccess);
+  });
 });
diff --git a/web/src/components/Plugin/UI/limit-conn.tsx b/web/src/components/Plugin/UI/limit-conn.tsx
index a680b7d..9928e6b 100644
--- a/web/src/components/Plugin/UI/limit-conn.tsx
+++ b/web/src/components/Plugin/UI/limit-conn.tsx
@@ -16,7 +16,7 @@
  */
 import React from 'react';
 import type { FormInstance } from 'antd/es/form';
-import { Form, InputNumber, Select } from 'antd';
+import { Form, InputNumber, Select, Switch } from 'antd';
 import { useIntl } from 'umi';
 
 type Props = {
@@ -37,6 +37,9 @@ const FORM_ITEM_LAYOUT = {
 const LimitConn: React.FC<Props> = ({ form, schema }) => {
   const { formatMessage } = useIntl();
   const propertires = schema?.properties;
+  const onlyUseDefaultDelay = form.getFieldValue('only_use_default_delay')
+    ? form.getFieldValue('only_use_default_delay')
+    : false;
   return (
     <Form form={form} {...FORM_ITEM_LAYOUT}>
       <Form.Item
@@ -67,6 +70,17 @@ const LimitConn: React.FC<Props> = ({ form, schema }) => {
       </Form.Item>
 
       <Form.Item
+        label="only_use_default_delay"
+        name="only_use_default_delay"
+        initialValue={propertires.only_use_default_delay.default}
+        tooltip={formatMessage({
+          id: 'component.pluginForm.limit-conn.only_use_default_delay.tooltip',
+        })}
+      >
+        <Switch defaultChecked={onlyUseDefaultDelay} />
+      </Form.Item>
+
+      <Form.Item
         label="key"
         required
         name="key"
@@ -82,19 +96,6 @@ const LimitConn: React.FC<Props> = ({ form, schema }) => {
           })}
         </Select>
       </Form.Item>
-
-      <Form.Item
-        label="rejected_code"
-        name="rejected_code"
-        initialValue={propertires.rejected_code.default}
-        tooltip={formatMessage({ id: 'component.pluginForm.limit-conn.rejected_code.tooltip' })}
-      >
-        <InputNumber
-          min={propertires.rejected_code.minimum}
-          max={propertires.rejected_code.maximum}
-          required
-        />
-      </Form.Item>
     </Form>
   );
 };
diff --git a/web/src/components/Plugin/locales/en-US.ts b/web/src/components/Plugin/locales/en-US.ts
index 689a6fc..39bdb31 100644
--- a/web/src/components/Plugin/locales/en-US.ts
+++ b/web/src/components/Plugin/locales/en-US.ts
@@ -85,6 +85,8 @@ export default {
     'to limit the concurrency level. For example, one can use the host name (or server zone) as the key so that we limit concurrency per host name. Otherwise, we can also use the client address as the key so that we can avoid a single client from flooding our service with too many parallel connections or requests. Now accept those as key: "remote_addr"(client\'s IP), "server_addr"(server\'s IP), "X-Forwarded-For/X-Real-IP" in request header, "consumer_name"(consumer\'s username).',
   'component.pluginForm.limit-conn.rejected_code.tooltip':
     'returned when the request exceeds conn + burst will be rejected.',
+  'component.pluginForm.limit-conn.only_use_default_delay.tooltip':
+    'enable the strict mode of the latency seconds. If you set this option to true, it will run strictly according to the latency seconds you set without additional calculation logic.',
 
   // limit-req
   'component.pluginForm.limit-req.rate.tooltip':
diff --git a/web/src/components/Plugin/locales/zh-CN.ts b/web/src/components/Plugin/locales/zh-CN.ts
index f7ef721..324ae28 100644
--- a/web/src/components/Plugin/locales/zh-CN.ts
+++ b/web/src/components/Plugin/locales/zh-CN.ts
@@ -80,6 +80,9 @@ export default {
     '用户指定的限制并发级别的关键字,可以是客户端 IP 或服务端 IP。例如,可以使用主机名(或服务器区域)作为关键字,以便限制每个主机名的并发性。 否则,我们也可以使用客户端地址作为关键字,这样我们就可以避免单个客户端用太多的并行连接或请求淹没我们的服务。当前接受的 key 有:"remote_addr"(客户端 IP 地址), "server_addr"(服务端 IP 地址), 请求头中的"X-Forwarded-For" 或 "X-Real-IP", "consumer_name"(consumer 的 username)。',
   'component.pluginForm.limit-conn.rejected_code.tooltip':
     '当请求超过 conn + burst 这个阈值时,返回的 HTTP 状态码。',
+  'component.pluginForm.limit-conn.only_use_default_delay.tooltip':
+    '延迟时间的严格模式。 如果设置为true的话,将会严格按照设置的时间来进行延迟',
+
   // limit-req
   'component.pluginForm.limit-req.rate.tooltip':
     '指定的请求速率(以秒为单位),请求速率超过 rate 但没有超过 (rate + brust)的请求会被加上延时。',
diff --git a/web/src/components/Upstream/UpstreamForm.tsx b/web/src/components/Upstream/UpstreamForm.tsx
index 3afb18b..c12898a 100644
--- a/web/src/components/Upstream/UpstreamForm.tsx
+++ b/web/src/components/Upstream/UpstreamForm.tsx
@@ -31,6 +31,8 @@ import PassHost from './components/PassHost';
 import TLSComponent from './components/TLS';
 import UpstreamType from './components/UpstreamType';
 import { convertToRequestData } from './service';
+import RetryTimeout from './components/RetryTimeout';
+import KeepalivePool from './components/KeepalivePool';
 
 type Upstream = {
   name?: string;
@@ -270,6 +272,14 @@ const UpstreamForm: React.FC<Props> = forwardRef(
       );
     };
 
+    const KeepalivePoolComponent = () => {
+      return (
+        <PanelSection title={formatMessage({ id: 'page.upstream.step.keepalive_pool' })}>
+          <KeepalivePool readonly={readonly} />
+        </PanelSection>
+      );
+    };
+
     return (
       <Form form={form} labelCol={{ span: 3 }}>
         {showSelector && (
@@ -291,6 +301,7 @@ const UpstreamForm: React.FC<Props> = forwardRef(
 
             <PassHost form={form} readonly={readonly} />
             <Retries readonly={readonly} />
+            <RetryTimeout readonly={readonly} />
 
             <Scheme readonly={readonly} />
             <Form.Item noStyle shouldUpdate={(prev, next) => prev.scheme !== next.scheme}>
@@ -307,6 +318,8 @@ const UpstreamForm: React.FC<Props> = forwardRef(
               <Timeout key={index} {...item} readonly={readonly} />
             ))}
 
+            <KeepalivePoolComponent />
+
             <HealthCheckComponent />
           </React.Fragment>
         )}
diff --git a/web/src/components/Upstream/components/KeepalivePool.tsx b/web/src/components/Upstream/components/KeepalivePool.tsx
new file mode 100644
index 0000000..f1fcc98
--- /dev/null
+++ b/web/src/components/Upstream/components/KeepalivePool.tsx
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+import React from 'react';
+import { Row, Col, Form, InputNumber } from 'antd';
+import { useIntl } from 'umi';
+
+type Props = {
+  readonly?: boolean;
+};
+
+const KeepalivePool: React.FC<Props> = ({ readonly }) => {
+  const { formatMessage } = useIntl();
+
+  return (
+    <React.Fragment>
+      <Form.Item
+        label={formatMessage({ id: 'component.upstream.fields.keepalive_pool' })}
+        tooltip={formatMessage({ id: 'component.upstream.fields.keepalive_pool.tooltip' })}
+      >
+        <Row style={{ marginBottom: 10 }} gutter={10}>
+          <Col span={5}>
+            <Form.Item
+              name={['keepalive_pool', 'size']}
+              label={formatMessage({ id: 'component.upstream.fields.keepalive_pool.size' })}
+              style={{ marginBottom: 0 }}
+              initialValue={320}
+            >
+              <InputNumber
+                min={1}
+                placeholder={formatMessage({
+                  id: 'component.upstream.fields.keepalive_pool.size.placeholder',
+                })}
+                disabled={readonly}
+              />
+            </Form.Item>
+          </Col>
+          <Col span={7}>
+            <Form.Item
+              name={['keepalive_pool', 'idle_timeout']}
+              label={formatMessage({ id: 'component.upstream.fields.keepalive_pool.idle_timeout' })}
+              style={{ marginBottom: 0 }}
+              initialValue={60}
+            >
+              <InputNumber
+                min={0}
+                placeholder={formatMessage({
+                  id: 'component.upstream.fields.keepalive_pool.idle_timeout.placeholder',
+                })}
+                disabled={readonly}
+              />
+            </Form.Item>
+          </Col>
+          <Col span={6}>
+            <Form.Item
+              name={['keepalive_pool', 'requests']}
+              label={formatMessage({ id: 'component.upstream.fields.keepalive_pool.requests' })}
+              style={{ marginBottom: 0 }}
+              initialValue={1000}
+            >
+              <InputNumber
+                min={1}
+                placeholder={formatMessage({
+                  id: 'component.upstream.fields.keepalive_pool.requests.placeholder',
+                })}
+                disabled={readonly}
+              />
+            </Form.Item>
+          </Col>
+        </Row>
+      </Form.Item>
+    </React.Fragment>
+  );
+};
+
+export default KeepalivePool;
diff --git a/web/src/components/Upstream/components/RetryTimeout.tsx b/web/src/components/Upstream/components/RetryTimeout.tsx
new file mode 100644
index 0000000..2fab431
--- /dev/null
+++ b/web/src/components/Upstream/components/RetryTimeout.tsx
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+import React from 'react';
+import { Form, InputNumber } from 'antd';
+import { useIntl } from 'umi';
+
+type Props = {
+  readonly?: boolean;
+};
+
+const RetryTimeout: React.FC<Props> = ({ readonly }) => {
+  const { formatMessage } = useIntl();
+
+  return (
+    <React.Fragment>
+      <Form.Item
+        name="retry_timeout"
+        label={formatMessage({ id: 'component.upstream.fields.retry_timeout' })}
+        tooltip={formatMessage({ id: 'component.upstream.fields.retry_timeout.tooltip' })}
+      >
+        <InputNumber disabled={readonly} />
+      </Form.Item>
+    </React.Fragment>
+  );
+};
+
+export default RetryTimeout;
diff --git a/web/src/components/Upstream/locales/en-US.ts b/web/src/components/Upstream/locales/en-US.ts
index c8b8b90..b449349 100644
--- a/web/src/components/Upstream/locales/en-US.ts
+++ b/web/src/components/Upstream/locales/en-US.ts
@@ -57,6 +57,20 @@ export default {
   'component.upstream.fields.retries.tooltip':
     'The retry mechanism sends the request to the next upstream node. A value of 0 disables the retry mechanism and leaves the table empty to use the number of available backend nodes.',
 
+  'component.upstream.fields.retry_timeout': 'Retry Timeout',
+  'component.upstream.fields.retry_timeout.tooltip':
+    'Configure a number to limit the amount of seconds that retries can be continued, and do not continue retries if the previous request and retry requests have taken too long. 0 means disable retry timeout mechanism.',
+
+  'component.upstream.fields.keepalive_pool': 'Keepalive Pool',
+  'component.upstream.fields.keepalive_pool.tooltip': 'Set independent keepalive pool for Upstream',
+  'component.upstream.fields.keepalive_pool.size': 'Size',
+  'component.upstream.fields.keepalive_pool.size.placeholder': 'Please enter the size',
+  'component.upstream.fields.keepalive_pool.idle_timeout': 'Idle Timeout',
+  'component.upstream.fields.keepalive_pool.idle_timeout.placeholder':
+    'Please enter the idle timeout',
+  'component.upstream.fields.keepalive_pool.requests': 'Requests',
+  'component.upstream.fields.keepalive_pool.requests.placeholder': 'Please enter the requests',
+
   'component.upstream.fields.checks.active.type': 'Type',
   'component.upstream.fields.checks.active.type.tooltip':
     'Whether to perform active health checks using HTTP or HTTPS, or just attempt a TCP connection.',
diff --git a/web/src/components/Upstream/locales/zh-CN.ts b/web/src/components/Upstream/locales/zh-CN.ts
index 1adb63f..4e1dc10 100644
--- a/web/src/components/Upstream/locales/zh-CN.ts
+++ b/web/src/components/Upstream/locales/zh-CN.ts
@@ -56,6 +56,19 @@ export default {
   'component.upstream.fields.retries.tooltip':
     '重试机制将请求发到下一个上游节点。值为 0 表示禁用重试机制,留空表示使用可用后端节点的数量。',
 
+  'component.upstream.fields.retry_timeout': '重试超时时间',
+  'component.upstream.fields.retry_timeout.tooltip':
+    '限制是否继续重试的时间,若之前的请求和重试请求花费太多时间就不再继续重试。0 代表不启用重试超时机制。',
+
+  'component.upstream.fields.keepalive_pool': '连接池',
+  'component.upstream.fields.keepalive_pool.tooltip': '为 upstream 对象设置独立的连接池',
+  'component.upstream.fields.keepalive_pool.size': '容量',
+  'component.upstream.fields.keepalive_pool.size.placeholder': '请输入容量',
+  'component.upstream.fields.keepalive_pool.idle_timeout': '空闲超时时间',
+  'component.upstream.fields.keepalive_pool.idle_timeout.placeholder': '请输入空闲超时时间',
+  'component.upstream.fields.keepalive_pool.requests': '请求数量',
+  'component.upstream.fields.keepalive_pool.requests.placeholder': '请输入请求数量',
+
   'component.upstream.fields.checks.active.type': '类型',
   'component.upstream.fields.checks.active.type.tooltip':
     '是使用 HTTP 或 HTTPS 进行主动健康检查,还是只尝试 TCP 连接。',
diff --git a/web/src/pages/Upstream/locales/en-US.ts b/web/src/pages/Upstream/locales/en-US.ts
index 8cbd798..424df4e 100644
--- a/web/src/pages/Upstream/locales/en-US.ts
+++ b/web/src/pages/Upstream/locales/en-US.ts
@@ -65,6 +65,7 @@ export default {
   'page.upstream.step.input.healthyCheck.passive.http_statuses': 'Please enter http status',
   'page.upstream.step.healthyCheck.passive.tcp_failures': 'TCP Failures',
   'page.upstream.step.input.healthyCheck.passive.tcp_failures': 'Please enter TCP failures',
+  'page.upstream.step.keepalive_pool': 'Keepalive Pool',
   'page.upstream.notificationMessage.enableHealthCheckFirst': 'Please enable health check first.',
   'page.upstream.upstream_host.required': 'Please enter the custom Host',
 
diff --git a/web/src/pages/Upstream/locales/zh-CN.ts b/web/src/pages/Upstream/locales/zh-CN.ts
index 2db82eb..b2d6778 100644
--- a/web/src/pages/Upstream/locales/zh-CN.ts
+++ b/web/src/pages/Upstream/locales/zh-CN.ts
@@ -64,6 +64,7 @@ export default {
   'page.upstream.step.input.healthyCheck.passive.http_statuses': '请输入状态码',
   'page.upstream.step.healthyCheck.passive.tcp_failures': 'TCP 失败次数',
   'page.upstream.step.input.healthyCheck.passive.tcp_failures': '请输入 TCP 失败次数',
+  'page.upstream.step.keepalive_pool': '连接池',
   'page.upstream.notificationMessage.enableHealthCheckFirst': '请先启用探活健康检查。',
   'page.upstream.upstream_host.required': '请输入自定义 Host 请求头',
 
diff --git a/web/src/pages/Upstream/typing.d.ts b/web/src/pages/Upstream/typing.d.ts
index 3a7d03e..5c73cc7 100644
--- a/web/src/pages/Upstream/typing.d.ts
+++ b/web/src/pages/Upstream/typing.d.ts
@@ -23,6 +23,12 @@ declare namespace UpstreamModule {
     namespace_id?: string;
   };
 
+  type KeepalivePool = {
+    size?: number;
+    idle_timeout?: number;
+    requests?: number;
+  };
+
   type Timeout = Record<'connect' | 'send' | 'read', number>;
 
   type HealthCheck = {
@@ -81,12 +87,14 @@ declare namespace UpstreamModule {
     key?: string;
     checks?: HealthCheck;
     retries?: number;
+    retry_timeout?: number;
     enable_websocket?: boolean;
     timeout?: Timeout;
     name?: string;
     desc?: string;
     pass_host?: 'pass' | 'node' | 'rewrite';
     upstream_host: UpstreamHost[];
+    keepalive_pool: KeepalivePool;
 
     // Custom Fields that need to be omitted
     custom?: {};