Repository: go-kratos/kratos
Pull request #724 (2021-02-24 10:06:05)

init roadmap


Patch:
diff --git a/README.md b/README.md
index e3b317be..83be5251 100644
--- a/README.md
+++ b/README.md
@@ -21,13 +21,13 @@ Kratos boosts your productivity. With the integration of excellent resources and
 
 ### Principles
 
-* **Simple**: appropriate design, plain and simple code.
-* **General**: cover the various utilities for business development.
-* **Reliable**: higher testability and greater test coverage of base libs validated in the production environment.
-* **Robust**: base libs are designed reasonably to reduce misuse.
+* **Simple**: Appropriate design, plain and simple code.
+* **General**: Cover the various utilities for business development.
+* **Reliable**: Higher testability and greater test coverage of base libs validated in the production environment.
+* **Robust**: Base libs are designed reasonably to reduce misuse.
 * **High-performance**: We give you optimal performance without using the hack-way approach like adding the *unsafe* package and guarantee compatibility and stability at the same time.
-* **Expandable**: appropriate API design, you can expand utilities such as base libs to meet your further requirements.
-* **Fault-tolerance**: designed against failure, enhance the understanding and exercising of SRE within Kratos to achieve more robustness.
+* **Expandable**: Appropriate API design, you can expand utilities such as base libs to meet your further requirements.
+* **Fault-tolerance**: Designed against failure, enhance the understanding and exercising of SRE within Kratos to achieve more robustness.
 * **Toolchain**: Includes an extensive toolchain, such as cache code generation, lint tools, etc.
 
 ## Features
diff --git a/ROADMAP.md b/ROADMAP.md
new file mode 100644
index 00000000..b3095104
--- /dev/null
+++ b/ROADMAP.md
@@ -0,0 +1,60 @@
+# Kratos
+
+This document defines the roadmap for Kratos development.
+
+## Features
+- [x] Config
+    - [x] Local Files
+    - [x] K8s ConfigMap
+    - [ ] Consul
+    - [ ] Etcd
+    - [ ] Nacos
+- [ ] Registry
+    - [ ] Consul
+    - [ ] Etcd
+    - [ ] K8s
+    - [ ] Nacos
+- [x] Encoding
+    - [x] JSON
+    - [x] Protobuf
+- [x] Transport
+    - [x] HTTP
+    - [x] gRPC
+- [x] Middleware
+    - [x] Logging
+    - [x] metrics
+    - [x] recovery
+    - [x] gRPC status
+    - [x] transport tracing
+    - [x] validator
+    - [ ] authentication
+    - [ ] ratelimit
+    - [ ] circuitbreaker
+- [x] Metrics
+    - [x] Prometheus
+    - [x] DataDog
+- [x] Tracing
+    - [x] HTTP
+        - [ ] TLS
+        - [x] Client
+        - [x] Service Registrar
+        - [ ] javascript/typescript clients
+    - [x] gRPC
+        - [ ] TLS
+        - [x] Uarry Handler
+        - [ ] Streaming Handler
+- [ ] Cache
+    - [ ] go-redis
+- [ ] Queue
+    - [ ] Broker API
+    - [ ] Kafka
+    - [ ] Nats
+- [ ] Database
+    - [ ] Ent
+    - [ ] Gorm
+
+## Tools
+- [x] Kratos
+- [x] HTTP Generator
+    - [ ] API YAML
+- [x] Errors Generator
diff --git a/app.go b/app.go
index 509a2e51..a451ce01 100644
--- a/app.go
+++ b/app.go
@@ -57,9 +57,9 @@ func (a *App) Server() []transport.Server {
 	return a.opts.servers
 }
 
-// Registry returns registry.
-func (a *App) Registry() registry.Registry {
-	return a.opts.registry
+// Registrar returns registry.
+func (a *App) Registrar() registry.Registrar {
+	return a.opts.registrar
 }
 
 // Run executes all OnStart hooks registered with the application's Lifecycle.
@@ -80,8 +80,8 @@ func (a *App) Run() error {
 			return srv.Start()
 		})
 	}
-	if a.opts.registry != nil {
-		if err := a.opts.registry.Register(a.opts.ctx, a.instance); err != nil {
+	if a.opts.registrar != nil {
+		if err := a.opts.registrar.Register(a.opts.ctx, a.instance); err != nil {
 			return err
 		}
 	}
@@ -105,8 +105,8 @@ func (a *App) Run() error {
 
 // Stop gracefully stops the application.
 func (a *App) Stop() error {
-	if a.opts.registry != nil {
-		if err := a.opts.registry.Deregister(a.opts.ctx, a.instance); err != nil {
+	if a.opts.registrar != nil {
+		if err := a.opts.registrar.Deregister(a.opts.ctx, a.instance); err != nil {
 			return err
 		}
 	}
diff --git a/cmd/kratos/internal/base/get.go b/cmd/kratos/internal/base/get.go
index 00c58c1e..afed66aa 100644
--- a/cmd/kratos/internal/base/get.go
+++ b/cmd/kratos/internal/base/get.go
@@ -1,6 +1,7 @@
 package base
 
 import (
+	"fmt"
 	"os"
 	"os/exec"
 )
@@ -8,6 +9,7 @@ import (
 // GoGet go get path.
 func GoGet(path ...string) error {
 	for _, p := range path {
+		fmt.Printf("go get -u %s\n", p)
 		cmd := exec.Command("go", "get", "-u", p)
 		cmd.Stdout = os.Stdout
 		cmd.Stderr = os.Stderr
diff --git a/cmd/kratos/internal/base/mod.go b/cmd/kratos/internal/base/mod.go
index fd0f6852..178a3f6d 100644
--- a/cmd/kratos/internal/base/mod.go
+++ b/cmd/kratos/internal/base/mod.go
@@ -4,7 +4,6 @@ import (
 	"bufio"
 	"bytes"
 	"io/ioutil"
-	"os"
 	"os/exec"
 	"path/filepath"
 	"strings"
@@ -46,10 +45,17 @@ func ModuleVersion(path string) (string, error) {
 
 // KratosMod returns kratos mod.
 func KratosMod() string {
-	gopath := os.Getenv("GOPATH")
+	// go 1.15+ read from env GOMODCACHE
+	cacheOut, _ := exec.Command("go", "env", "GOMODCACHE").Output()
+	cachePath := strings.Trim(string(cacheOut), "\n")
+	pathOut, _ := exec.Command("go", "env", "GOPATH").Output()
+	gopath := strings.Trim(string(pathOut), "\n")
+	if cachePath == "" {
+		cachePath = filepath.Join(gopath, "pkg", "mod")
+	}
 	if path, err := ModuleVersion("github.com/go-kratos/kratos/v2"); err == nil {
 		// $GOPATH/pkg/mod/github.com/go-kratos/kratos@v2
-		return filepath.Join(gopath, "pkg", "mod", path)
+		return filepath.Join(cachePath, path)
 	}
 	// $GOPATH/src/github.com/go-kratos/kratos
 	return filepath.Join(gopath, "src", "github.com", "go-kratos", "kratos")
diff --git a/cmd/kratos/internal/proto/client/client.go b/cmd/kratos/internal/proto/client/client.go
index 1bee3508..41938a1d 100644
--- a/cmd/kratos/internal/proto/client/client.go
+++ b/cmd/kratos/internal/proto/client/client.go
@@ -30,10 +30,14 @@ func run(cmd *cobra.Command, args []string) {
 		err   error
 		proto = strings.TrimSpace(args[0])
 	)
-	if _, err = exec.LookPath("protoc-gen-go-http"); err != nil {
+	if err = look("protoc-gen-go", "protoc-gen-go-grpc", "protoc-gen-go-http", "protoc-gen-go-errors"); err != nil {
 		// update the kratos plugins
-		if err := exec.Command("kratos", "upgrade").Run(); err != nil {
+		cmd := exec.Command("kratos", "upgrade")
+		cmd.Stdout = os.Stdout
+		cmd.Stderr = os.Stderr
+		if cmd.Run(); err != nil {
 			fmt.Println(err)
+			return
 		}
 	}
 	if strings.HasSuffix(proto, ".proto") {
@@ -46,6 +50,15 @@ func run(cmd *cobra.Command, args []string) {
 	}
 }
 
+func look(name ...string) error {
+	for _, n := range name {
+		if _, err := exec.LookPath(n); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
 func walk(dir string) error {
 	if dir == "" {
 		dir = "."
diff --git a/cmd/protoc-gen-go-errors/go.mod b/cmd/protoc-gen-go-errors/go.mod
index e1efebbe..b42906cf 100644
--- a/cmd/protoc-gen-go-errors/go.mod
+++ b/cmd/protoc-gen-go-errors/go.mod
@@ -3,6 +3,9 @@ module github.com/go-kratos/kratos/cmd/protoc-gen-go-errors/v2
 go 1.15
 
 require (
-	github.com/go-kratos/kratos/v2 v2.0.0-20210217083752-d86d233d93ce
+	github.com/go-kratos/kratos/v2 v2.0.0-alpha4
+	github.com/google/uuid v1.2.0 // indirect
+	golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
+	google.golang.org/grpc v1.36.0 // indirect
 	google.golang.org/protobuf v1.25.0
 )
diff --git a/cmd/protoc-gen-go-errors/go.sum b/cmd/protoc-gen-go-errors/go.sum
index a391308f..4a2185cd 100644
--- a/cmd/protoc-gen-go-errors/go.sum
+++ b/cmd/protoc-gen-go-errors/go.sum
@@ -11,6 +11,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
 github.com/go-kratos/kratos/v2 v2.0.0-20210217083752-d86d233d93ce h1:LfOsLN9s8tAxR8xIZGWQvEVWxHfipTnBSE0dvG4h3k8=
 github.com/go-kratos/kratos/v2 v2.0.0-20210217083752-d86d233d93ce/go.mod h1:oLvFyDBJkkWN8TPqb+NmpvRrSy9uM/K+XQubVRc11a8=
+github.com/go-kratos/kratos/v2 v2.0.0-alpha4 h1:MKkkSZigSMg7Kx8HzrobZ93zlgmi0tAKWM9bMf6YTpU=
+github.com/go-kratos/kratos/v2 v2.0.0-alpha4/go.mod h1:oLvFyDBJkkWN8TPqb+NmpvRrSy9uM/K+XQubVRc11a8=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -32,6 +34,8 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
 github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
 github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
+github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
 github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
 github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
@@ -53,6 +57,8 @@ golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAG
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -74,6 +80,8 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac
 google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
 google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
 google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
+google.golang.org/grpc v1.36.0 h1:o1bcQ6imQMIOpdrO3SWf2z5RV72WbDwdXuK0MDlc8As=
+google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
 google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
diff --git a/cmd/protoc-gen-go-http/go.mod b/cmd/protoc-gen-go-http/go.mod
index 3f2d1a90..a7d44ac4 100644
--- a/cmd/protoc-gen-go-http/go.mod
+++ b/cmd/protoc-gen-go-http/go.mod
@@ -3,9 +3,10 @@ module github.com/go-kratos/kratos/cmd/protoc-gen-go-http/v2
 go 1.15
 
 require (
-	github.com/go-kratos/kratos/v2 v2.0.0-20210218084408-cf599c68a65f
+	github.com/go-kratos/kratos/v2 v2.0.0-20210303135906-ec6ddeeacb79
 	github.com/golang/protobuf v1.4.3
+	github.com/gorilla/mux v1.8.0
 	google.golang.org/genproto v0.0.0-20210202153253-cf70463f6119
-	google.golang.org/grpc v1.35.0
+	google.golang.org/grpc v1.36.0
 	google.golang.org/protobuf v1.25.0
 )
diff --git a/cmd/protoc-gen-go-http/go.sum b/cmd/protoc-gen-go-http/go.sum
index 9eecccc2..1c5cf17e 100644
--- a/cmd/protoc-gen-go-http/go.sum
+++ b/cmd/protoc-gen-go-http/go.sum
@@ -9,10 +9,8 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m
 github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
-github.com/go-kratos/kratos/v2 v2.0.0-20210217083752-d86d233d93ce h1:LfOsLN9s8tAxR8xIZGWQvEVWxHfipTnBSE0dvG4h3k8=
-github.com/go-kratos/kratos/v2 v2.0.0-20210217083752-d86d233d93ce/go.mod h1:oLvFyDBJkkWN8TPqb+NmpvRrSy9uM/K+XQubVRc11a8=
-github.com/go-kratos/kratos/v2 v2.0.0-20210218084408-cf599c68a65f h1:ocHPvNS53zBT6NiGmgOWV2SuAmcNyHbJZk1t2cqEjIU=
-github.com/go-kratos/kratos/v2 v2.0.0-20210218084408-cf599c68a65f/go.mod h1:oLvFyDBJkkWN8TPqb+NmpvRrSy9uM/K+XQubVRc11a8=
+github.com/go-kratos/kratos/v2 v2.0.0-20210303135906-ec6ddeeacb79 h1:30gIrPVUDzCZyHLh9gYbYyDykQAcVIoAWBty1UpwpzU=
+github.com/go-kratos/kratos/v2 v2.0.0-20210303135906-ec6ddeeacb79/go.mod h1:oLvFyDBJkkWN8TPqb+NmpvRrSy9uM/K+XQubVRc11a8=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -80,8 +78,9 @@ google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZi
 google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
 google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
 google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
-google.golang.org/grpc v1.35.0 h1:TwIQcH3es+MojMVojxxfQ3l3OF2KzlRxML2xZq0kRo8=
 google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
+google.golang.org/grpc v1.36.0 h1:o1bcQ6imQMIOpdrO3SWf2z5RV72WbDwdXuK0MDlc8As=
+google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
 google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
diff --git a/cmd/protoc-gen-go-http/http.go b/cmd/protoc-gen-go-http/http.go
index 22a4e69f..97147f7d 100644
--- a/cmd/protoc-gen-go-http/http.go
+++ b/cmd/protoc-gen-go-http/http.go
@@ -13,7 +13,9 @@ import (
 const (
 	contextPackage   = protogen.GoImportPath("context")
 	httpPackage      = protogen.GoImportPath("net/http")
+	muxPackage       = protogen.GoImportPath("github.com/gorilla/mux")
 	transportPackage = protogen.GoImportPath("github.com/go-kratos/kratos/v2/transport/http")
+	bindingPackage   = protogen.GoImportPath("github.com/go-kratos/kratos/v2/transport/http/binding")
 )
 
 var methodSets = make(map[string]int)
@@ -40,7 +42,10 @@ func generateFileContent(gen *protogen.Plugin, file *protogen.File, g *protogen.
 	}
 	g.P("// This is a compile-time assertion to ensure that this generated file")
 	g.P("// is compatible with the kratos package it is being compiled against.")
-	g.P("// ", contextPackage.Ident(""), "/", httpPackage.Ident(""))
+	g.P("var _ = new(", httpPackage.Ident("Request"), ")")
+	g.P("var _ = new(", contextPackage.Ident("Context"), ")")
+	g.P("var _ = ", bindingPackage.Ident("MapProto"))
+	g.P("var _ = ", muxPackage.Ident("NewRouter"))
 	g.P("const _ = ", transportPackage.Ident("SupportPackageIsVersion1"))
 	g.P()
 
@@ -61,22 +66,24 @@ func genService(gen *protogen.Plugin, file *protogen.File, g *protogen.Generated
 		Metadata:    file.Desc.Path(),
 	}
 	for _, method := range service.Methods {
+		if method.Desc.IsStreamingClient() || method.Desc.IsStreamingServer() {
+			continue
+		}
 		rule, ok := proto.GetExtension(method.Desc.Options(), annotations.E_Http).(*annotations.HttpRule)
 		if rule != nil && ok {
 			for _, bind := range rule.AdditionalBindings {
-				sd.Methods = append(sd.Methods, buildHTTPRule(method, bind))
+				sd.Methods = append(sd.Methods, buildHTTPRule(g, method, bind))
 			}
-			sd.Methods = append(sd.Methods, buildHTTPRule(method, rule))
+			sd.Methods = append(sd.Methods, buildHTTPRule(g, method, rule))
 		} else {
 			path := fmt.Sprintf("/%s/%s", service.Desc.FullName(), method.Desc.Name())
-			sd.Methods = append(sd.Methods, buildMethodDesc(method, "POST", path))
-
+			sd.Methods = append(sd.Methods, buildMethodDesc(g, method, "POST", path))
 		}
 	}
 	g.P(sd.execute())
 }
 
-func buildHTTPRule(m *protogen.Method, rule *annotations.HttpRule) *methodDesc {
+func buildHTTPRule(g *protogen.GeneratedFile, m *protogen.Method, rule *annotations.HttpRule) *methodDesc {
 	var (
 		path         string
 		method       string
@@ -105,23 +112,29 @@ func buildHTTPRule(m *protogen.Method, rule *annotations.HttpRule) *methodDesc {
 	}
 	body = rule.Body
 	responseBody = rule.ResponseBody
-	md := buildMethodDesc(m, method, path)
+	md := buildMethodDesc(g, m, method, path)
+	if body == "*" {
+		body = ""
+	}
 	if body != "" {
 		md.Body = "." + camelCaseVars(body)
 	}
+	if responseBody == "*" {
+		responseBody = ""
+	}
 	if responseBody != "" {
 		md.ResponseBody = "." + camelCaseVars(responseBody)
 	}
 	return md
 }
 
-func buildMethodDesc(m *protogen.Method, method, path string) *methodDesc {
+func buildMethodDesc(g *protogen.GeneratedFile, m *protogen.Method, method, path string) *methodDesc {
 	defer func() { methodSets[m.GoName]++ }()
 	return &methodDesc{
 		Name:    m.GoName,
 		Num:     methodSets[m.GoName],
-		Request: m.Input.GoIdent.GoName,
-		Reply:   m.Output.GoIdent.GoName,
+		Request: g.QualifiedGoIdent(m.Input.GoIdent),
+		Reply:   g.QualifiedGoIdent(m.Output.GoIdent),
 		Path:    path,
 		Method:  method,
 		Vars:    buildPathVars(m, path),
diff --git a/cmd/protoc-gen-go-http/template.go b/cmd/protoc-gen-go-http/template.go
index c4f5cc44..6d21337b 100644
--- a/cmd/protoc-gen-go-http/template.go
+++ b/cmd/protoc-gen-go-http/template.go
@@ -7,54 +7,48 @@ import (
 )
 
 var httpTemplate = `
-type {{.ServiceType}}HTTPServer interface {
+type {{.ServiceType}}Handler interface {
 {{range .MethodSets}}
 	{{.Name}}(context.Context, *{{.Request}}) (*{{.Reply}}, error)
 {{end}}
 }
-func Register{{.ServiceType}}HTTPServer(s http1.ServiceRegistrar, srv {{.ServiceType}}HTTPServer) {
-	s.RegisterService(&_HTTP_{{.ServiceType}}_serviceDesc, srv)
-}
-{{range .Methods}}
-func _HTTP_{{$.ServiceType}}_{{.Name}}_{{.Num}}(srv interface{}, ctx context.Context, req *http.Request, dec func(interface{}) error) (interface{}, error) {
-	var in {{.Request}}
-{{if eq .Body ""}}
-	if err := http1.BindForm(req, &in); err != nil {
-		return nil, err
-	}
-{{else if eq .Body ".*"}}
-	if err := dec(&in); err != nil {
-		return nil, err
-	}
-{{else}}
-	if err := dec(in{{.Body}}); err != nil {
-		return nil, err
-	}
-{{end}}
-{{if ne (len .Vars) 0}}
-	if err := http1.BindVars(req, &in); err != nil {
-		return nil, err
-	}
-{{end}}
-	out, err := srv.({{$.ServiceType}}Server).{{.Name}}(ctx, &in)
-	if err != nil {
-		return nil, err
+
+func New{{.ServiceType}}Handler(srv {{.ServiceType}}Handler, opts ...http1.HandleOption) http.Handler {
+	h := http1.DefaultHandleOptions()
+	for _, o := range opts {
+		o(&h)
 	}
-	return out{{.ResponseBody}}, nil
-}
-{{end}}
-var _HTTP_{{.ServiceType}}_serviceDesc = http1.ServiceDesc{
-	ServiceName: "{{.ServiceName}}",
-	Methods: []http1.MethodDesc{
-{{range .Methods}}
-		{
-			Path:    "{{.Path}}",
-			Method:  "{{.Method}}",
-			Handler: _HTTP_{{$.ServiceType}}_{{.Name}}_{{.Num}},
-		},
-{{end}}
-	},
-	Metadata: "{{.Metadata}}",
+	r := mux.NewRouter()
+	{{range .Methods}}
+	r.HandleFunc("{{.Path}}", func(w http.ResponseWriter, r *http.Request) {
+		var in {{.Request}}
+		{{if ne (len .Vars) 0}}
+		if err := binding.MapProto(&in, mux.Vars(r)); err != nil {
+			h.Error(w, r, err)
+			return
+		}
+		{{end}}
+		if err := h.Decode(r, &in{{.Body}}); err != nil {
+			h.Error(w, r, err)
+			return
+		}
+		next := func(ctx context.Context, req interface{}) (interface{}, error) {
+			return srv.{{.Name}}(ctx, req.(*{{.Request}}))
+		}
+		if h.Middleware != nil {
+			next = h.Middleware(next)
+		}
+		out, err := next(r.Context(), &in)
+		if err != nil {
+			h.Error(w, r, err)
+			return
+		}
+		if err := h.Encode(w, r, out{{.ResponseBody}}); err != nil {
+			h.Error(w, r, err)
+		}
+	}).Methods("{{.Method}}")
+	{{end}}
+	return r
 }
 `
 
diff --git a/encoding/encoding.go b/encoding/encoding.go
index 0e9141a7..f9defe60 100644
--- a/encoding/encoding.go
+++ b/encoding/encoding.go
@@ -1,6 +1,8 @@
 package encoding
 
-import "strings"
+import (
+	"strings"
+)
 
 // Codec defines the interface Transport uses to encode and decode messages.  Note
 // that implementations of this interface must be thread safe; a Codec's
diff --git a/encoding/proto/proto.go b/encoding/proto/proto.go
index 26a29144..96051a96 100644
--- a/encoding/proto/proto.go
+++ b/encoding/proto/proto.go
@@ -3,7 +3,8 @@
 package proto
 
 import (
-	"google.golang.org/grpc/encoding"
+	"github.com/go-kratos/kratos/v2/encoding"
+
 	"google.golang.org/protobuf/proto"
 )
 
diff --git a/errors/errors.go b/errors/errors.go
index 69f7b8a6..ad991020 100644
--- a/errors/errors.go
+++ b/errors/errors.go
@@ -3,6 +3,7 @@ package errors
 import (
 	"errors"
 	"fmt"
+	"net/http"
 )
 
 const (
@@ -26,6 +27,48 @@ func (e *StatusError) Is(target error) bool {
 	return false
 }
 
+// HTTPStatus returns the Status represented by se.
+func (e *StatusError) HTTPStatus() int {
+	switch e.Code {
+	case 0:
+		return http.StatusOK
+	case 1:
+		return http.StatusInternalServerError
+	case 2:
+		return http.StatusInternalServerError
+	case 3:
+		return http.StatusBadRequest
+	case 4:
+		return http.StatusRequestTimeout
+	case 5:
+		return http.StatusNotFound
+	case 6:
+		return http.StatusConflict
+	case 7:
+		return http.StatusForbidden
+	case 8:
+		return http.StatusTooManyRequests
+	case 9:
+		return http.StatusPreconditionFailed
+	case 10:
+		return http.StatusConflict
+	case 11:
+		return http.StatusBadRequest
+	case 12:
+		return http.StatusNotImplemented
+	case 13:
+		return http.StatusInternalServerError
+	case 14:
+		return http.StatusServiceUnavailable
+	case 15:
+		return http.StatusInternalServerError
+	case 16:
+		return http.StatusUnauthorized
+	default:
+		return http.StatusInternalServerError
+	}
+}
+
 func (e *StatusError) Error() string {
 	return fmt.Sprintf("error: code = %d reason = %s message = %s details = %+v", e.Code, e.Reason, e.Message, e.Details)
 }
diff --git a/go.sum b/go.sum
index f4c90546..067f82f0 100644
--- a/go.sum
+++ b/go.sum
@@ -20,7 +20,6 @@ github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:x
 github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
 github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
 github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
-github.com/golang/protobuf v1.4.1 h1:ZFgWrT+bLgsYPirOnRfKLYJLvssAegOj/hgyMFdJZe0=
 github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
 github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
@@ -41,7 +40,6 @@ github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+
 github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM=
 github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
@@ -63,7 +61,6 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9 h1:L2auWcuQIvxz9xSEqzESnV/QN/gNRXNApHi3fYwl2w0=
 golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -79,7 +76,6 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl
 google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
-google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
 google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
 google.golang.org/genproto v0.0.0-20210114201628-6edceaf6022f h1:izedQ6yVIc5mZsRuXzmSreCOlzI0lCU1HpG8yEdMiKw=
 google.golang.org/genproto v0.0.0-20210114201628-6edceaf6022f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
diff --git a/middleware/status/status.go b/middleware/status/status.go
index 29fb67e9..5a711115 100644
--- a/middleware/status/status.go
+++ b/middleware/status/status.go
@@ -72,6 +72,7 @@ func errorEncode(err error) error {
 	if !ok {
 		se = &errors.StatusError{
 			Code: 2,
+			Message: err.Error(),
 		}
 	}
 	gs := status.Newf(codes.Code(se.Code), "%s: %s", se.Reason, se.Message)
diff --git a/options.go b/options.go
index d5aa4c80..cecb4eaa 100644
--- a/options.go
+++ b/options.go
@@ -23,9 +23,10 @@ type options struct {
 	ctx  context.Context
 	sigs []os.Signal
 
-	logger   log.Logger
-	registry registry.Registry
-	servers  []transport.Server
+	logger    log.Logger
+	registrar registry.Registrar
+
+	servers []transport.Server
 }
 
 // ID with service id.
@@ -68,9 +69,9 @@ func Logger(logger log.Logger) Option {
 	return func(o *options) { o.logger = logger }
 }
 
-// Registry with service registry.
-func Registry(r registry.Registry) Option {
-	return func(o *options) { o.registry = r }
+// Registrar with service registry.
+func Registrar(r registry.Registrar) Option {
+	return func(o *options) { o.registrar = r }
 }
 
 // Server with transport servers.
diff --git a/registry/registry.go b/registry/registry.go
index 6f491455..58402855 100644
--- a/registry/registry.go
+++ b/registry/registry.go
@@ -2,16 +2,20 @@ package registry
 
 import "context"
 
-// Registry is service registry.
-type Registry interface {
+// Registrar is service registrar.
+type Registrar interface {
 	// Register the registration.
 	Register(ctx context.Context, service *ServiceInstance) error
 	// Deregister the registration.
 	Deregister(ctx context.Context, service *ServiceInstance) error
+}
+
+// Discoverer is service discovery.
+type Discoverer interface {
 	// Service return the service instances in memory according to the service name.
-	Service(ctx context.Context, name string) ([]*ServiceInstance, error)
+	Fetch(ctx context.Context, serviceName string) ([]*ServiceInstance, error)
 	// Watch creates a watcher according to the service name.
-	Watch(ctx context.Context, name string) (Watcher, error)
+	Watch(ctx context.Context, serviceName string) (Watcher, error)
 }
 
 // Watcher is service watcher.
diff --git a/transport/grpc/client.go b/transport/grpc/client.go
index c89ec488..330b6553 100644
--- a/transport/grpc/client.go
+++ b/transport/grpc/client.go
@@ -38,10 +38,10 @@ func WithMiddleware(m middleware.Middleware) ClientOption {
 	}
 }
 
-// WithRegistry with client registry.
-func WithRegistry(r registry.Registry) ClientOption {
+// WithDiscoverer with client registry.
+func WithDiscoverer(d registry.Discoverer) ClientOption {
 	return func(o *clientOptions) {
-		o.registry = r
+		o.discoverer = d
 	}
 }
 
@@ -57,7 +57,7 @@ type clientOptions struct {
 	endpoint   string
 	timeout    time.Duration
 	middleware middleware.Middleware
-	registry   registry.Registry
+	discoverer registry.Discoverer
 	grpcOpts   []grpc.DialOption
 }
 
@@ -86,8 +86,8 @@ func dial(ctx context.Context, insecure bool, opts ...ClientOption) (*grpc.Clien
 		grpc.WithTimeout(options.timeout),
 		grpc.WithUnaryInterceptor(UnaryClientInterceptor(options.middleware)),
 	}
-	if options.registry != nil {
-		grpc.WithResolvers(discovery.NewBuilder(options.registry))
+	if options.discoverer != nil {
+		grpc.WithResolvers(discovery.NewBuilder(options.discoverer))
 	}
 	if insecure {
 		grpcOpts = append(grpcOpts, grpc.WithInsecure())
diff --git a/transport/grpc/resolver/discovery/builder.go b/transport/grpc/resolver/discovery/builder.go
index 462c63c2..aa063a51 100644
--- a/transport/grpc/resolver/discovery/builder.go
+++ b/transport/grpc/resolver/discovery/builder.go
@@ -21,15 +21,15 @@ func WithLogger(logger log.Logger) Option {
 }
 
 type builder struct {
-	registry registry.Registry
-	logger   log.Logger
+	discoverer registry.Discoverer
+	logger     log.Logger
 }
 
 // NewBuilder creates a builder which is used to factory registry resolvers.
-func NewBuilder(r registry.Registry, opts ...Option) resolver.Builder {
+func NewBuilder(r registry.Discoverer, opts ...Option) resolver.Builder {
 	b := &builder{
-		registry: r,
-		logger:   log.DefaultLogger,
+		discoverer: r,
+		logger:     log.DefaultLogger,
 	}
 	for _, o := range opts {
 		o(b)
@@ -38,7 +38,7 @@ func NewBuilder(r registry.Registry, opts ...Option) resolver.Builder {
 }
 
 func (d *builder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) {
-	w, err := d.registry.Watch(context.Background(), target.Endpoint)
+	w, err := d.discoverer.Watch(context.Background(), target.Endpoint)
 	if err != nil {
 		return nil, err
 	}
diff --git a/transport/http/binding/bind.go b/transport/http/binding/bind.go
new file mode 100644
index 00000000..7549c363
--- /dev/null
+++ b/transport/http/binding/bind.go
@@ -0,0 +1,18 @@
+package binding
+
+import (
+	"net/http"
+
+	"google.golang.org/protobuf/proto"
+)
+
+// BindForm bind form parameters to target.
+func BindForm(req *http.Request, target interface{}) error {
+	if err := req.ParseForm(); err != nil {
+		return err
+	}
+	if msg, ok := target.(proto.Message); ok {
+		return mapProto(msg, req.Form)
+	}
+	return mapForm(target, req.Form)
+}
diff --git a/transport/http/binding/form.go b/transport/http/binding/form.go
new file mode 100644
index 00000000..5c83bc12
--- /dev/null
+++ b/transport/http/binding/form.go
@@ -0,0 +1,385 @@
+package binding
+
+// Copyright 2014 Manu Martinez-Almeida.  All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"reflect"
+	"strconv"
+	"strings"
+	"time"
+)
+
+var (
+	errUnknownType = errors.New("unknown type")
+	emptyField     = reflect.StructField{}
+)
+
+func mapForm(ptr interface{}, form map[string][]string) error {
+	return mapFormByTag(ptr, form, "json")
+}
+
+func mapFormByTag(ptr interface{}, form map[string][]string, tag string) error {
+	ptrVal := reflect.ValueOf(ptr)
+	var pointed interface{}
+	if ptrVal.Kind() == reflect.Ptr {
+		ptrVal = ptrVal.Elem()
+		pointed = ptrVal.Interface()
+	}
+	if ptrVal.Kind() == reflect.Map &&
+		ptrVal.Type().Key().Kind() == reflect.String {
+		if pointed != nil {
+			ptr = pointed
+		}
+		return setFormMap(ptr, form)
+	}
+	return mappingByPtr(ptr, formSource(form), tag)
+}
+
+// setter tries to set value on a walking by fields of a struct
+type setter interface {
+	TrySet(value reflect.Value, field reflect.StructField, key string, opt setOptions) (isSetted bool, err error)
+}
+
+type formSource map[string][]string
+
+var _ setter = formSource(nil)
+
+// TrySet tries to set a value by request's form source (like map[string][]string)
+func (form formSource) TrySet(value reflect.Value, field reflect.StructField, tagValue string, opt setOptions) (isSetted bool, err error) {
+	return setByForm(value, field, form, tagValue, opt)
+}
+
+func mappingByPtr(ptr interface{}, setter setter, tag string) error {
+	_, err := mapping(reflect.ValueOf(ptr), emptyField, setter, tag)
+	return err
+}
+
+func mapping(value reflect.Value, field reflect.StructField, setter setter, tag string) (bool, error) {
+	if field.Tag.Get(tag) == "-" { // just ignoring this field
+		return false, nil
+	}
+
+	var vKind = value.Kind()
+
+	if vKind == reflect.Ptr {
+		var isNew bool
+		vPtr := value
+		if value.IsNil() {
+			isNew = true
+			vPtr = reflect.New(value.Type().Elem())
+		}
+		isSetted, err := mapping(vPtr.Elem(), field, setter, tag)
+		if err != nil {
+			return false, err
+		}
+		if isNew && isSetted {
+			value.Set(vPtr)
+		}
+		return isSetted, nil
+	}
+
+	if vKind != reflect.Struct || !field.Anonymous {
+		ok, err := tryToSetValue(value, field, setter, tag)
+		if err != nil {
+			return false, err
+		}
+		if ok {
+			return true, nil
+		}
+	}
+
+	if vKind == reflect.Struct {
+		tValue := value.Type()
+
+		var isSetted bool
+		for i := 0; i < value.NumField(); i++ {
+			sf := tValue.Field(i)
+			if sf.PkgPath != "" && !sf.Anonymous { // unexported
+				continue
+			}
+			ok, err := mapping(value.Field(i), tValue.Field(i), setter, tag)
+			if err != nil {
+				return false, err
+			}
+			isSetted = isSetted || ok
+		}
+		return isSetted, nil
+	}
+	return false, nil
+}
+
+type setOptions struct {
+	isDefaultExists bool
+	defaultValue    string
+}
+
+func tryToSetValue(value reflect.Value, field reflect.StructField, setter setter, tag string) (bool, error) {
+	var tagValue string
+	var setOpt setOptions
+
+	tagValue = field.Tag.Get(tag)
+	tagValue, opts := head(tagValue, ",")
+
+	if tagValue == "" { // default value is FieldName
+		tagValue = field.Name
+	}
+	if tagValue == "" { // when field is "emptyField" variable
+		return false, nil
+	}
+
+	var opt string
+	for len(opts) > 0 {
+		opt, opts = head(opts, ",")
+
+		if k, v := head(opt, "="); k == "default" {
+			setOpt.isDefaultExists = true
+			setOpt.defaultValue = v
+		}
+	}
+
+	return setter.TrySet(value, field, tagValue, setOpt)
+}
+
+func setByForm(value reflect.Value, field reflect.StructField, form map[string][]string, tagValue string, opt setOptions) (isSetted bool, err error) {
+	vs, ok := form[tagValue]
+	if !ok && !opt.isDefaultExists {
+		return false, nil
+	}
+
+	switch value.Kind() {
+	case reflect.Slice:
+		if !ok {
+			vs = []string{opt.defaultValue}
+		}
+		return true, setSlice(vs, value, field)
+	case reflect.Array:
+		if !ok {
+			vs = []string{opt.defaultValue}
+		}
+		if len(vs) != value.Len() {
+			return false, fmt.Errorf("%q is not valid value for %s", vs, value.Type().String())
+		}
+		return true, setArray(vs, value, field)
+	default:
+		var val string
+		if !ok {
+			val = opt.defaultValue
+		}
+
+		if len(vs) > 0 {
+			val = vs[0]
+		}
+		return true, setWithProperType(val, value, field)
+	}
+}
+
+func setWithProperType(val string, value reflect.Value, field reflect.StructField) error {
+	switch value.Kind() {
+	case reflect.Int:
+		return setIntField(val, 0, value)
+	case reflect.Int8:
+		return setIntField(val, 8, value)
+	case reflect.Int16:
+		return setIntField(val, 16, value)
+	case reflect.Int32:
+		return setIntField(val, 32, value)
+	case reflect.Int64:
+		switch value.Interface().(type) {
+		case time.Duration:
+			return setTimeDuration(val, value, field)
+		}
+		return setIntField(val, 64, value)
+	case reflect.Uint:
+		return setUintField(val, 0, value)
+	case reflect.Uint8:
+		return setUintField(val, 8, value)
+	case reflect.Uint16:
+		return setUintField(val, 16, value)
+	case reflect.Uint32:
+		return setUintField(val, 32, value)
+	case reflect.Uint64:
+		return setUintField(val, 64, value)
+	case reflect.Bool:
+		return setBoolField(val, value)
+	case reflect.Float32:
+		return setFloatField(val, 32, value)
+	case reflect.Float64:
+		return setFloatField(val, 64, value)
+	case reflect.String:
+		value.SetString(val)
+	case reflect.Struct:
+		switch value.Interface().(type) {
+		case time.Time:
+			return setTimeField(val, field, value)
+		}
+		return json.Unmarshal([]byte(val), value.Addr().Interface())
+	case reflect.Map:
+		return json.Unmarshal([]byte(val), value.Addr().Interface())
+	default:
+		return errUnknownType
+	}
+	return nil
+}
+
+func setIntField(val string, bitSize int, field reflect.Value) error {
+	if val == "" {
+		val = "0"
+	}
+	intVal, err := strconv.ParseInt(val, 10, bitSize)
+	if err == nil {
+		field.SetInt(intVal)
+	}
+	return err
+}
+
+func setUintField(val string, bitSize int, field reflect.Value) error {
+	if val == "" {
+		val = "0"
+	}
+	uintVal, err := strconv.ParseUint(val, 10, bitSize)
+	if err == nil {
+		field.SetUint(uintVal)
+	}
+	return err
+}
+
+func setBoolField(val string, field reflect.Value) error {
+	if val == "" {
+		val = "false"
+	}
+	boolVal, err := strconv.ParseBool(val)
+	if err == nil {
+		field.SetBool(boolVal)
+	}
+	return err
+}
+
+func setFloatField(val string, bitSize int, field reflect.Value) error {
+	if val == "" {
+		val = "0.0"
+	}
+	floatVal, err := strconv.ParseFloat(val, bitSize)
+	if err == nil {
+		field.SetFloat(floatVal)
+	}
+	return err
+}
+
+func setTimeField(val string, structField reflect.StructField, value reflect.Value) error {
+	timeFormat := structField.Tag.Get("time_format")
+	if timeFormat == "" {
+		timeFormat = time.RFC3339
+	}
+
+	switch tf := strings.ToLower(timeFormat); tf {
+	case "unix", "unixnano":
+		tv, err := strconv.ParseInt(val, 10, 64)
+		if err != nil {
+			return err
+		}
+
+		d := time.Duration(1)
+		if tf == "unixnano" {
+			d = time.Second
+		}
+
+		t := time.Unix(tv/int64(d), tv%int64(d))
+		value.Set(reflect.ValueOf(t))
+		return nil
+
+	}
+
+	if val == "" {
+		value.Set(reflect.ValueOf(time.Time{}))
+		return nil
+	}
+
+	l := time.Local
+	if isUTC, _ := strconv.ParseBool(structField.Tag.Get("time_utc")); isUTC {
+		l = time.UTC
+	}
+
+	if locTag := structField.Tag.Get("time_location"); locTag != "" {
+		loc, err := time.LoadLocation(locTag)
+		if err != nil {
+			return err
+		}
+		l = loc
+	}
+
+	t, err := time.ParseInLocation(timeFormat, val, l)
+	if err != nil {
+		return err
+	}
+
+	value.Set(reflect.ValueOf(t))
+	return nil
+}
+
+func setArray(vals []string, value reflect.Value, field reflect.StructField) error {
+	for i, s := range vals {
+		err := setWithProperType(s, value.Index(i), field)
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func setSlice(vals []string, value reflect.Value, field reflect.StructField) error {
+	slice := reflect.MakeSlice(value.Type(), len(vals), len(vals))
+	err := setArray(vals, slice, field)
+	if err != nil {
+		return err
+	}
+	value.Set(slice)
+	return nil
+}
+
+func setTimeDuration(val string, value reflect.Value, field reflect.StructField) error {
+	d, err := time.ParseDuration(val)
+	if err != nil {
+		return err
+	}
+	value.Set(reflect.ValueOf(d))
+	return nil
+}
+
+func head(str, sep string) (head string, tail string) {
+	idx := strings.Index(str, sep)
+	if idx < 0 {
+		return str, ""
+	}
+	return str[:idx], str[idx+len(sep):]
+}
+
+func setFormMap(ptr interface{}, form map[string][]string) error {
+	el := reflect.TypeOf(ptr).Elem()
+
+	if el.Kind() == reflect.Slice {
+		ptrMap, ok := ptr.(map[string][]string)
+		if !ok {
+			return errors.New("cannot convert to map slices of strings")
+		}
+		for k, v := range form {
+			ptrMap[k] = v
+		}
+
+		return nil
+	}
+
+	ptrMap, ok := ptr.(map[string]string)
+	if !ok {
+		return errors.New("cannot convert to map of strings")
+	}
+	for k, v := range form {
+		ptrMap[k] = v[len(v)-1] // pick last
+	}
+
+	return nil
+}
diff --git a/transport/http/bind.go b/transport/http/binding/proto.go
similarity index 89%
rename from transport/http/bind.go
rename to transport/http/binding/proto.go
index d0a1d7fe..4a9dd994 100644
--- a/transport/http/bind.go
+++ b/transport/http/binding/proto.go
@@ -1,26 +1,26 @@
-package http
+package binding
 
 import (
 	"encoding/base64"
 	"errors"
 	"fmt"
 	"log"
-	"net/http"
 	"strconv"
 	"strings"
 	"time"
 
-	"github.com/golang/protobuf/ptypes"
-	"github.com/golang/protobuf/ptypes/wrappers"
 	"google.golang.org/genproto/protobuf/field_mask"
 	"google.golang.org/protobuf/proto"
 	"google.golang.org/protobuf/reflect/protoreflect"
 	"google.golang.org/protobuf/reflect/protoregistry"
+	"google.golang.org/protobuf/types/known/durationpb"
+	"google.golang.org/protobuf/types/known/timestamppb"
+	"google.golang.org/protobuf/types/known/wrapperspb"
 )
 
-// BindVars parses url parameters.
-func BindVars(req *http.Request, msg proto.Message) error {
-	for key, value := range Vars(req) {
+// MapProto sets a value in a nested Protobuf structure.
+func MapProto(msg proto.Message, values map[string]string) error {
+	for key, value := range values {
 		if err := populateFieldValues(msg.ProtoReflect(), strings.Split(key, "."), []string{value}); err != nil {
 			return err
 		}
@@ -28,12 +28,8 @@ func BindVars(req *http.Request, msg proto.Message) error {
 	return nil
 }
 
-// BindForm parses form parameters.
-func BindForm(req *http.Request, msg proto.Message) error {
-	if err := req.ParseForm(); err != nil {
-		return err
-	}
-	for key, values := range req.Form {
+func mapProto(msg proto.Message, values map[string][]string) error {
+	for key, values := range values {
 		if err := populateFieldValues(msg.ProtoReflect(), strings.Split(key, "."), values); err != nil {
 			return err
 		}
@@ -213,10 +209,7 @@ func parseMessage(md protoreflect.MessageDescriptor, value string) (protoreflect
 		if err != nil {
 			return protoreflect.Value{}, err
 		}
-		msg, err = ptypes.TimestampProto(t)
-		if err != nil {
-			return protoreflect.Value{}, err
-		}
+		msg = timestamppb.New(t)
 	case "google.protobuf.Duration":
 		if value == "null" {
 			break
@@ -225,57 +218,57 @@ func parseMessage(md protoreflect.MessageDescriptor, value string) (protoreflect
 		if err != nil {
 			return protoreflect.Value{}, err
 		}
-		msg = ptypes.DurationProto(d)
+		msg = durationpb.New(d)
 	case "google.protobuf.DoubleValue":
 		v, err := strconv.ParseFloat(value, 64)
 		if err != nil {
 			return protoreflect.Value{}, err
 		}
-		msg = &wrappers.DoubleValue{Value: v}
+		msg = wrapperspb.Double(v)
 	case "google.protobuf.FloatValue":
 		v, err := strconv.ParseFloat(value, 32)
 		if err != nil {
 			return protoreflect.Value{}, err
 		}
-		msg = &wrappers.FloatValue{Value: float32(v)}
+		msg = wrapperspb.Float(float32(v))
 	case "google.protobuf.Int64Value":
 		v, err := strconv.ParseInt(value, 10, 64)
 		if err != nil {
 			return protoreflect.Value{}, err
 		}
-		msg = &wrappers.Int64Value{Value: v}
+		msg = wrapperspb.Int64(v)
 	case "google.protobuf.Int32Value":
 		v, err := strconv.ParseInt(value, 10, 32)
 		if err != nil {
 			return protoreflect.Value{}, err
 		}
-		msg = &wrappers.Int32Value{Value: int32(v)}
+		msg = wrapperspb.Int32(int32(v))
 	case "google.protobuf.UInt64Value":
 		v, err := strconv.ParseUint(value, 10, 64)
 		if err != nil {
 			return protoreflect.Value{}, err
 		}
-		msg = &wrappers.UInt64Value{Value: v}
+		msg = wrapperspb.UInt64(v)
 	case "google.protobuf.UInt32Value":
 		v, err := strconv.ParseUint(value, 10, 32)
 		if err != nil {
 			return protoreflect.Value{}, err
 		}
-		msg = &wrappers.UInt32Value{Value: uint32(v)}
+		msg = wrapperspb.UInt32(uint32(v))
 	case "google.protobuf.BoolValue":
 		v, err := strconv.ParseBool(value)
 		if err != nil {
 			return protoreflect.Value{}, err
 		}
-		msg = &wrappers.BoolValue{Value: v}
+		msg = wrapperspb.Bool(v)
 	case "google.protobuf.StringValue":
-		msg = &wrappers.StringValue{Value: value}
+		msg = wrapperspb.String(value)
 	case "google.protobuf.BytesValue":
 		v, err := base64.StdEncoding.DecodeString(value)
 		if err != nil {
 			return protoreflect.Value{}, err
 		}
-		msg = &wrappers.BytesValue{Value: v}
+		msg = wrapperspb.Bytes(v)
 	case "google.protobuf.FieldMask":
 		fm := &field_mask.FieldMask{}
 		fm.Paths = append(fm.Paths, strings.Split(value, ",")...)
diff --git a/transport/http/client.go b/transport/http/client.go
index 35ba74d6..35cf11f9 100644
--- a/transport/http/client.go
+++ b/transport/http/client.go
@@ -2,11 +2,10 @@ package http
 
 import (
 	"context"
-	"io/ioutil"
+	"encoding/json"
 	"net/http"
 	"time"
 
-	"github.com/go-kratos/kratos/v2/encoding"
 	"github.com/go-kratos/kratos/v2/errors"
 	"github.com/go-kratos/kratos/v2/middleware"
 	"github.com/go-kratos/kratos/v2/transport"
@@ -115,22 +114,13 @@ func Do(client *http.Client, req *http.Request, target interface{}) error {
 	if err != nil {
 		return err
 	}
-	data, err := ioutil.ReadAll(res.Body)
-	if err != nil {
-		return err
-	}
 	defer res.Body.Close()
-	subtype := contentSubtype(res.Header.Get("content-type"))
-	codec := encoding.GetCodec(subtype)
-	if codec == nil {
-		codec = encoding.GetCodec("json")
-	}
 	if res.StatusCode < 200 || res.StatusCode > 299 {
 		se := &errors.StatusError{}
-		if err := codec.Unmarshal(data, se); err != nil {
+		if err := json.NewDecoder(req.Body).Decode(se); err != nil {
 			return err
 		}
 		return se
 	}
-	return codec.Unmarshal(data, target)
+	return json.NewDecoder(req.Body).Decode(target)
 }
diff --git a/transport/http/context.go b/transport/http/context.go
index d12856e3..f7200e91 100644
--- a/transport/http/context.go
+++ b/transport/http/context.go
@@ -3,8 +3,6 @@ package http
 import (
 	"context"
 	"net/http"
-
-	"github.com/gorilla/mux"
 )
 
 // ServerInfo is HTTP server infomation.
@@ -43,8 +41,3 @@ func FromClientContext(ctx context.Context) (info ClientInfo, ok bool) {
 	info, ok = ctx.Value(clientKey{}).(ClientInfo)
 	return
 }
-
-// Vars returns the route variables for the current request, if any.
-func Vars(req *http.Request) map[string]string {
-	return mux.Vars(req)
-}
diff --git a/transport/http/default.go b/transport/http/default.go
deleted file mode 100644
index d1a59e95..00000000
--- a/transport/http/default.go
+++ /dev/null
@@ -1,81 +0,0 @@
-package http
-
-import (
-	"fmt"
-	"io/ioutil"
-	"net/http"
-	"strings"
-
-	"github.com/go-kratos/kratos/v2/encoding"
-)
-
-const baseContentType = "application"
-
-func contentType(subtype string) string {
-	return strings.Join([]string{baseContentType, subtype}, "/")
-}
-
-func contentSubtype(contentType string) string {
-	if contentType == baseContentType {
-		return ""
-	}
-	if !strings.HasPrefix(contentType, baseContentType) {
-		return ""
-	}
-	// guaranteed since != baseContentType and has baseContentType prefix
-	switch contentType[len(baseContentType)] {
-	case '/', ';':
-		if i := strings.Index(contentType, ";"); i != -1 {
-			return contentType[len(baseContentType)+1 : i]
-		}
-		return contentType[len(baseContentType)+1:]
-	default:
-		return ""
-	}
-}
-
-func defaultRequestDecoder(req *http.Request, v interface{}) error {
-	data, err := ioutil.ReadAll(req.Body)
-	if err != nil {
-		return err
-	}
-	defer req.Body.Close()
-	subtype := contentSubtype(req.Header.Get("content-type"))
-	codec := encoding.GetCodec(subtype)
-	if codec == nil {
-		return fmt.Errorf("decoding request failed unknown content-type: %s", subtype)
-	}
-	return codec.Unmarshal(data, v)
-}
-
-func defaultResponseEncoder(res http.ResponseWriter, req *http.Request, v interface{}) error {
-	subtype := contentSubtype(req.Header.Get("accept"))
-	codec := encoding.GetCodec(subtype)
-	if codec == nil {
-		codec = encoding.GetCodec("json")
-	}
-	data, err := codec.Marshal(v)
-	if err != nil {
-		return err
-	}
-	res.Header().Set("content-type", contentType(codec.Name()))
-	res.Write(data)
-	return nil
-}
-
-func defaultErrorEncoder(res http.ResponseWriter, req *http.Request, err error) {
-	se, code := StatusError(err)
-	subtype := contentSubtype(req.Header.Get("accept"))
-	codec := encoding.GetCodec(subtype)
-	if codec == nil {
-		codec = encoding.GetCodec("json")
-	}
-	data, err := codec.Marshal(se)
-	if err != nil {
-		res.WriteHeader(http.StatusInternalServerError)
-		return
-	}
-	res.Header().Set("content-type", contentType(codec.Name()))
-	res.WriteHeader(code)
-	res.Write(data)
-}
diff --git a/transport/http/errors.go b/transport/http/errors.go
deleted file mode 100644
index a0c01653..00000000
--- a/transport/http/errors.go
+++ /dev/null
@@ -1,59 +0,0 @@
-package http
-
-import (
-	"net/http"
-
-	"github.com/go-kratos/kratos/v2/errors"
-)
-
-var (
-	// References: https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto
-	codesMapping = map[int32]int{
-		0:  http.StatusOK,
-		1:  http.StatusInternalServerError,
-		2:  http.StatusInternalServerError,
-		3:  http.StatusBadRequest,
-		4:  http.StatusRequestTimeout,
-		5:  http.StatusNotFound,
-		6:  http.StatusConflict,
-		7:  http.StatusForbidden,
-		8:  http.StatusTooManyRequests,
-		9:  http.StatusPreconditionFailed,
-		10: http.StatusConflict,
-		11: http.StatusBadRequest,
-		12: http.StatusNotImplemented,
-		13: http.StatusInternalServerError,
-		14: http.StatusServiceUnavailable,
-		15: http.StatusInternalServerError,
-		16: http.StatusUnauthorized,
-	}
-	statusMapping = map[int]int32{
-		http.StatusOK:                  0,
-		http.StatusBadRequest:          3,
-		http.StatusRequestTimeout:      4,
-		http.StatusNotFound:            5,
-		http.StatusConflict:            6,
-		http.StatusForbidden:           7,
-		http.StatusUnauthorized:        16,
-		http.StatusPreconditionFailed:  9,
-		http.StatusNotImplemented:      12,
-		http.StatusInternalServerError: 13,
-		http.StatusServiceUnavailable:  14,
-	}
-)
-
-// StatusError converts error to status error.
-func StatusError(err error) (*errors.StatusError, int) {
-	se, ok := errors.FromError(err)
-	if !ok {
-		se = &errors.StatusError{
-			Code:    2,
-			Reason:  "Unknown",
-			Message: "Unknown: " + err.Error(),
-		}
-	}
-	if status, ok := codesMapping[se.Code]; ok {
-		return se, status
-	}
-	return se, http.StatusInternalServerError
-}
diff --git a/transport/http/handle.go b/transport/http/handle.go
new file mode 100644
index 00000000..a517acfb
--- /dev/null
+++ b/transport/http/handle.go
@@ -0,0 +1,147 @@
+package http
+
+import (
+	"encoding/json"
+	"io/ioutil"
+	"net/http"
+	"strings"
+
+	"github.com/go-kratos/kratos/v2/encoding"
+	"github.com/go-kratos/kratos/v2/errors"
+	"github.com/go-kratos/kratos/v2/middleware"
+	"github.com/go-kratos/kratos/v2/transport/http/binding"
+)
+
+const (
+	// SupportPackageIsVersion1 These constants should not be referenced from any other code.
+	SupportPackageIsVersion1 = true
+
+	baseContentType = "application"
+)
+
+var (
+	acceptHeader      = http.CanonicalHeaderKey("Accept")
+	contentTypeHeader = http.CanonicalHeaderKey("Content-Type")
+)
+
+// DecodeRequestFunc is decode request func.
+type DecodeRequestFunc func(*http.Request, interface{}) error
+
+// EncodeResponseFunc is encode response func.
+type EncodeResponseFunc func(http.ResponseWriter, *http.Request, interface{}) error
+
+// EncodeErrorFunc is encode error func.
+type EncodeErrorFunc func(http.ResponseWriter, *http.Request, error)
+
+// HandleOption is handle option.
+type HandleOption func(*HandleOptions)
+
+// HandleOptions is handle options.
+type HandleOptions struct {
+	Decode     DecodeRequestFunc
+	Encode     EncodeResponseFunc
+	Error      EncodeErrorFunc
+	Middleware middleware.Middleware
+}
+
+// DefaultHandleOptions returns a default handle options.
+func DefaultHandleOptions() HandleOptions {
+	return HandleOptions{
+		Decode: decodeRequest,
+		Encode: encodeResponse,
+		Error:  encodeError,
+	}
+}
+
+// RequestDecoder with request decoder.
+func RequestDecoder(dec DecodeRequestFunc) HandleOption {
+	return func(o *HandleOptions) {
+		o.Decode = dec
+	}
+}
+
+// ResponseEncoder with response encoder.
+func ResponseEncoder(en EncodeResponseFunc) HandleOption {
+	return func(o *HandleOptions) {
+		o.Encode = en
+	}
+}
+
+// ErrorEncoder with error encoder.
+func ErrorEncoder(en EncodeErrorFunc) HandleOption {
+	return func(o *HandleOptions) {
+		o.Error = en
+	}
+}
+
+// Middleware with middleware option.
+func Middleware(m middleware.Middleware) HandleOption {
+	return func(o *HandleOptions) {
+		o.Middleware = m
+	}
+}
+
+// decodeRequest decodes the request body to object.
+func decodeRequest(req *http.Request, v interface{}) error {
+	subtype := contentSubtype(req.Header.Get(contentTypeHeader))
+	if codec := encoding.GetCodec(subtype); codec != nil {
+		data, err := ioutil.ReadAll(req.Body)
+		if err != nil {
+			return err
+		}
+		return codec.Unmarshal(data, v)
+	}
+	return binding.BindForm(req, v)
+}
+
+// encodeResponse encodes the object to the HTTP response.
+func encodeResponse(w http.ResponseWriter, r *http.Request, v interface{}) error {
+	for _, accept := range r.Header[acceptHeader] {
+		if codec := encoding.GetCodec(contentSubtype(accept)); codec != nil {
+			data, err := codec.Marshal(v)
+			if err != nil {
+				return err
+			}
+			w.Header().Set(contentTypeHeader, contentType(codec.Name()))
+			w.Write(data)
+			return nil
+		}
+	}
+	return json.NewEncoder(w).Encode(v)
+}
+
+// encodeError encodes the erorr to the HTTP response.
+func encodeError(w http.ResponseWriter, r *http.Request, err error) {
+	se, ok := errors.FromError(err)
+	if !ok {
+		se = &errors.StatusError{
+			Code:    2,
+			Reason:  "",
+			Message: err.Error(),
+		}
+	}
+	w.WriteHeader(se.HTTPStatus())
+	encodeResponse(w, r, se)
+}
+
+func contentType(subtype string) string {
+	return strings.Join([]string{baseContentType, subtype}, "/")
+}
+
+func contentSubtype(contentType string) string {
+	if contentType == baseContentType {
+		return ""
+	}
+	if !strings.HasPrefix(contentType, baseContentType) {
+		return ""
+	}
+	switch contentType[len(baseContentType)] {
+	case '/', ';':
+		if i := strings.Index(contentType, ";"); i != -1 {
+			return contentType[len(baseContentType)+1 : i]
+		}
+		return contentType[len(baseContentType)+1:]
+	default:
+		return ""
+	}
+}
diff --git a/transport/http/server.go b/transport/http/server.go
index 4cfd470d..5d829a0b 100644
--- a/transport/http/server.go
+++ b/transport/http/server.go
@@ -10,8 +10,6 @@ import (
 
 	"github.com/go-kratos/kratos/v2/internal/host"
 	"github.com/go-kratos/kratos/v2/log"
-	"github.com/go-kratos/kratos/v2/middleware"
-	"github.com/go-kratos/kratos/v2/middleware/recovery"
 	"github.com/go-kratos/kratos/v2/transport"
 
 	"github.com/gorilla/mux"
@@ -21,15 +19,6 @@ const loggerName = "transport/http"
 
 var _ transport.Server = (*Server)(nil)
 
-// DecodeRequestFunc deocder request func.
-type DecodeRequestFunc func(req *http.Request, v interface{}) error
-
-// EncodeResponseFunc is encode response func.
-type EncodeResponseFunc func(res http.ResponseWriter, req *http.Request, v interface{}) error
-
-// EncodeErrorFunc is encode error func.
-type EncodeErrorFunc func(res http.ResponseWriter, req *http.Request, err error)
-
 // ServerOption is HTTP server option.
 type ServerOption func(*Server)
 
@@ -61,46 +50,24 @@ func Logger(logger log.Logger) ServerOption {
 	}
 }
 
-// Middleware with server middleware option.
-func Middleware(m middleware.Middleware) ServerOption {
-	return func(s *Server) {
-		s.middleware = m
-	}
-}
-
-// ErrorEncoder with error handler option.
-func ErrorEncoder(fn EncodeErrorFunc) ServerOption {
-	return func(s *Server) {
-		s.errorEncoder = fn
-	}
-}
-
 // Server is a HTTP server wrapper.
 type Server struct {
 	*http.Server
-	lis             net.Listener
-	network         string
-	address         string
-	timeout         time.Duration
-	middleware      middleware.Middleware
-	requestDecoder  DecodeRequestFunc
-	responseEncoder EncodeResponseFunc
-	errorEncoder    EncodeErrorFunc
-	router          *mux.Router
-	log             *log.Helper
+	lis     net.Listener
+	network string
+	address string
+	timeout time.Duration
+	router  *mux.Router
+	log     *log.Helper
 }
 
 // NewServer creates a HTTP server by options.
 func NewServer(opts ...ServerOption) *Server {
 	srv := &Server{
-		network:         "tcp",
-		address:         ":0",
-		timeout:         time.Second,
-		requestDecoder:  defaultRequestDecoder,
-		responseEncoder: defaultResponseEncoder,
-		errorEncoder:    defaultErrorEncoder,
-		middleware:      recovery.Recovery(),
-		log:             log.NewHelper(loggerName, log.DefaultLogger),
+		network: "tcp",
+		address: ":0",
+		timeout: time.Second,
+		log:     log.NewHelper(loggerName, log.DefaultLogger),
 	}
 	for _, o := range opts {
 		o(srv)
@@ -120,33 +87,23 @@ func (s *Server) Handle(path string, h http.Handler) {
 	s.router.Handle(path, h)
 }
 
+// HanldePrefix registers a new route with a matcher for the URL path prefix.
+func (s *Server) HanldePrefix(prefix string, h http.Handler) {
+	s.router.PathPrefix(prefix).Handler(h)
+}
+
 // HandleFunc registers a new route with a matcher for the URL path.
 func (s *Server) HandleFunc(path string, h http.HandlerFunc) {
 	s.router.HandleFunc(path, h)
 }
 
-// PrefixHanlde  registers a new route with a matcher for the URL path prefix.
-func (s *Server) PrefixHanlde(prefix string, h http.Handler) {
-	s.router.PathPrefix(prefix).Handler(h)
-}
-
 // ServeHTTP should write reply headers and data to the ResponseWriter and then return.
 func (s *Server) ServeHTTP(res http.ResponseWriter, req *http.Request) {
 	ctx, cancel := context.WithTimeout(req.Context(), s.timeout)
 	defer cancel()
 	ctx = transport.NewContext(ctx, transport.Transport{Kind: "HTTP"})
 	ctx = NewServerContext(ctx, ServerInfo{Request: req, Response: res})
-
-	h := func(ctx context.Context, req interface{}) (interface{}, error) {
-		s.router.ServeHTTP(res, req.(*http.Request))
-		return res, nil
-	}
-	if s.middleware != nil {
-		h = s.middleware(h)
-	}
-	if _, err := h(ctx, req.WithContext(ctx)); err != nil {
-		s.errorEncoder(res, req, err)
-	}
+	s.router.ServeHTTP(res, req)
 }
 
 // Endpoint return a real address to registry endpoint.
diff --git a/transport/http/service.go b/transport/http/service.go
deleted file mode 100644
index 3fa3595f..00000000
--- a/transport/http/service.go
+++ /dev/null
@@ -1,49 +0,0 @@
-package http
-
-import (
-	"context"
-	"net/http"
-)
-
-// SupportPackageIsVersion1 These constants should not be referenced from any other code.
-const SupportPackageIsVersion1 = true
-
-type methodHandler func(srv interface{}, ctx context.Context, req *http.Request, dec func(interface{}) error) (out interface{}, err error)
-
-// MethodDesc represents a Proto service's method specification.
-type MethodDesc struct {
-	Path    string
-	Method  string
-	Handler methodHandler
-}
-
-// ServiceDesc represents a Proto service's specification.
-type ServiceDesc struct {
-	ServiceName string
-	Methods     []MethodDesc
-	Metadata    interface{}
-}
-
-// ServiceRegistrar wraps a single method that supports service registration.
-type ServiceRegistrar interface {
-	RegisterService(desc *ServiceDesc, impl interface{})
-}
-
-// RegisterService .
-func (s *Server) RegisterService(desc *ServiceDesc, impl interface{}) {
-	for _, m := range desc.Methods {
-		h := m.Handler
-		s.router.HandleFunc(m.Path, func(res http.ResponseWriter, req *http.Request) {
-			out, err := h(impl, req.Context(), req, func(v interface{}) error {
-				return s.requestDecoder(req, v)
-			})
-			if err != nil {
-				s.errorEncoder(res, req, err)
-				return
-			}
-			if err := s.responseEncoder(res, req, out); err != nil {
-				s.errorEncoder(res, req, err)
-			}
-		}).Methods(m.Method)
-	}
-}


Test patch:
diff --git a/cmd/protoc-gen-go-http/internal/testproto/echo_service_errors.pb.go b/cmd/protoc-gen-go-http/internal/testproto/echo_service_errors.pb.go
new file mode 100644
index 00000000..715d8d0d
--- /dev/null
+++ b/cmd/protoc-gen-go-http/internal/testproto/echo_service_errors.pb.go
@@ -0,0 +1,11 @@
+// Code generated by protoc-gen-go-errors. DO NOT EDIT.
+
+package testproto
+
+import (
+	errors "github.com/go-kratos/kratos/v2/errors"
+)
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the kratos package it is being compiled against.
+const _ = errors.SupportPackageIsVersion1
diff --git a/cmd/protoc-gen-go-http/internal/testproto/echo_service_http.pb.go b/cmd/protoc-gen-go-http/internal/testproto/echo_service_http.pb.go
index ccdae48b..27e3c610 100644
--- a/cmd/protoc-gen-go-http/internal/testproto/echo_service_http.pb.go
+++ b/cmd/protoc-gen-go-http/internal/testproto/echo_service_http.pb.go
@@ -5,15 +5,21 @@ package testproto
 import (
 	context "context"
 	http1 "github.com/go-kratos/kratos/v2/transport/http"
+	binding "github.com/go-kratos/kratos/v2/transport/http/binding"
+	mux "github.com/gorilla/mux"
 	http "net/http"
 )
 
 // This is a compile-time assertion to ensure that this generated file
 // is compatible with the kratos package it is being compiled against.
-// context./http.
+var _ = new(http.Request)
+var _ = new(context.Context)
+var _ = binding.MapProto
+var _ = mux.NewRouter
+
 const _ = http1.SupportPackageIsVersion1
 
-type EchoServiceHTTPServer interface {
+type EchoServiceHandler interface {
 	Echo(context.Context, *SimpleMessage) (*SimpleMessage, error)
 
 	EchoBody(context.Context, *SimpleMessage) (*SimpleMessage, error)
@@ -23,193 +29,221 @@ type EchoServiceHTTPServer interface {
 	EchoPatch(context.Context, *DynamicMessageUpdate) (*DynamicMessageUpdate, error)
 }
 
-func RegisterEchoServiceHTTPServer(s http1.ServiceRegistrar, srv EchoServiceHTTPServer) {
-	s.RegisterService(&_HTTP_EchoService_serviceDesc, srv)
-}
-
-func _HTTP_EchoService_Echo_0(srv interface{}, ctx context.Context, req *http.Request, dec func(interface{}) error) (interface{}, error) {
-	var in SimpleMessage
-
-	if err := http1.BindVars(req, &in); err != nil {
-		return nil, err
-	}
-
-	if err := http1.BindForm(req, &in); err != nil {
-		return nil, err
-	}
-
-	out, err := srv.(EchoServiceServer).Echo(ctx, &in)
-	if err != nil {
-		return nil, err
-	}
-	return out, nil
-}
-
-func _HTTP_EchoService_Echo_1(srv interface{}, ctx context.Context, req *http.Request, dec func(interface{}) error) (interface{}, error) {
-	var in SimpleMessage
-
-	if err := http1.BindVars(req, &in); err != nil {
-		return nil, err
-	}
-
-	if err := http1.BindForm(req, &in); err != nil {
-		return nil, err
-	}
-
-	out, err := srv.(EchoServiceServer).Echo(ctx, &in)
-	if err != nil {
-		return nil, err
-	}
-	return out, nil
-}
-
-func _HTTP_EchoService_Echo_2(srv interface{}, ctx context.Context, req *http.Request, dec func(interface{}) error) (interface{}, error) {
-	var in SimpleMessage
-
-	if err := http1.BindVars(req, &in); err != nil {
-		return nil, err
-	}
-
-	if err := http1.BindForm(req, &in); err != nil {
-		return nil, err
-	}
-
-	out, err := srv.(EchoServiceServer).Echo(ctx, &in)
-	if err != nil {
-		return nil, err
-	}
-	return out, nil
-}
-
-func _HTTP_EchoService_Echo_3(srv interface{}, ctx context.Context, req *http.Request, dec func(interface{}) error) (interface{}, error) {
-	var in SimpleMessage
-
-	if err := http1.BindVars(req, &in); err != nil {
-		return nil, err
-	}
-
-	if err := http1.BindForm(req, &in); err != nil {
-		return nil, err
-	}
-
-	out, err := srv.(EchoServiceServer).Echo(ctx, &in)
-	if err != nil {
-		return nil, err
-	}
-	return out, nil
-}
-
-func _HTTP_EchoService_Echo_4(srv interface{}, ctx context.Context, req *http.Request, dec func(interface{}) error) (interface{}, error) {
-	var in SimpleMessage
-
-	if err := http1.BindVars(req, &in); err != nil {
-		return nil, err
-	}
-
-	if err := http1.BindForm(req, &in); err != nil {
-		return nil, err
-	}
-
-	out, err := srv.(EchoServiceServer).Echo(ctx, &in)
-	if err != nil {
-		return nil, err
-	}
-	return out, nil
-}
-
-func _HTTP_EchoService_EchoBody_0(srv interface{}, ctx context.Context, req *http.Request, dec func(interface{}) error) (interface{}, error) {
-	var in SimpleMessage
-
-	if err := dec(&in); err != nil {
-		return nil, err
-	}
-
-	out, err := srv.(EchoServiceServer).EchoBody(ctx, &in)
-	if err != nil {
-		return nil, err
-	}
-	return out, nil
-}
-
-func _HTTP_EchoService_EchoDelete_0(srv interface{}, ctx context.Context, req *http.Request, dec func(interface{}) error) (interface{}, error) {
-	var in SimpleMessage
-
-	if err := http1.BindForm(req, &in); err != nil {
-		return nil, err
-	}
-
-	out, err := srv.(EchoServiceServer).EchoDelete(ctx, &in)
-	if err != nil {
-		return nil, err
-	}
-	return out, nil
-}
-
-func _HTTP_EchoService_EchoPatch_0(srv interface{}, ctx context.Context, req *http.Request, dec func(interface{}) error) (interface{}, error) {
-	var in DynamicMessageUpdate
-
-	if err := dec(in.Body); err != nil {
-		return nil, err
-	}
-
-	out, err := srv.(EchoServiceServer).EchoPatch(ctx, &in)
-	if err != nil {
-		return nil, err
-	}
-	return out, nil
-}
-
-var _HTTP_EchoService_serviceDesc = http1.ServiceDesc{
-	ServiceName: "testproto.EchoService",
-	Methods: []http1.MethodDesc{
-
-		{
-			Path:    "/v1/example/echo/{id}/{num}",
-			Method:  "GET",
-			Handler: _HTTP_EchoService_Echo_0,
-		},
-
-		{
-			Path:    "/v1/example/echo/{id}/{num}/{lang}",
-			Method:  "GET",
-			Handler: _HTTP_EchoService_Echo_1,
-		},
-
-		{
-			Path:    "/v1/example/echo1/{id}/{line_num}/{status.note}",
-			Method:  "GET",
-			Handler: _HTTP_EchoService_Echo_2,
-		},
-
-		{
-			Path:    "/v1/example/echo2/{no.note}",
-			Method:  "GET",
-			Handler: _HTTP_EchoService_Echo_3,
-		},
-
-		{
-			Path:    "/v1/example/echo/{id}",
-			Method:  "POST",
-			Handler: _HTTP_EchoService_Echo_4,
-		},
-
-		{
-			Path:    "/v1/example/echo_body",
-			Method:  "POST",
-			Handler: _HTTP_EchoService_EchoBody_0,
-		},
-
-		{
-			Path:    "/v1/example/echo_delete",
-			Method:  "DELETE",
-			Handler: _HTTP_EchoService_EchoDelete_0,
-		},
-
-		{
-			Path:    "/v1/example/echo_patch",
-			Method:  "PATCH",
-			Handler: _HTTP_EchoService_EchoPatch_0,
-		},
-	},
-	Metadata: "echo_service.proto",
+func NewEchoServiceHandler(srv EchoServiceHandler, opts ...http1.HandleOption) http.Handler {
+	h := http1.DefaultHandleOptions()
+	for _, o := range opts {
+		o(&h)
+	}
+	r := mux.NewRouter()
+
+	r.HandleFunc("/v1/example/echo/{id}/{num}", func(w http.ResponseWriter, r *http.Request) {
+		var in SimpleMessage
+
+		if err := binding.MapProto(&in, mux.Vars(r)); err != nil {
+			h.Error(w, r, err)
+			return
+		}
+
+		if err := h.Decode(r, &in); err != nil {
+			h.Error(w, r, err)
+			return
+		}
+		next := func(ctx context.Context, req interface{}) (interface{}, error) {
+			return srv.Echo(ctx, req.(*SimpleMessage))
+		}
+		if h.Middleware != nil {
+			next = h.Middleware(next)
+		}
+		out, err := next(r.Context(), &in)
+		if err != nil {
+			h.Error(w, r, err)
+			return
+		}
+		if err := h.Encode(w, r, out); err != nil {
+			h.Error(w, r, err)
+		}
+	}).Methods("GET")
+
+	r.HandleFunc("/v1/example/echo/{id}/{num}/{lang}", func(w http.ResponseWriter, r *http.Request) {
+		var in SimpleMessage
+
+		if err := binding.MapProto(&in, mux.Vars(r)); err != nil {
+			h.Error(w, r, err)
+			return
+		}
+
+		if err := h.Decode(r, &in); err != nil {
+			h.Error(w, r, err)
+			return
+		}
+		next := func(ctx context.Context, req interface{}) (interface{}, error) {
+			return srv.Echo(ctx, req.(*SimpleMessage))
+		}
+		if h.Middleware != nil {
+			next = h.Middleware(next)
+		}
+		out, err := next(r.Context(), &in)
+		if err != nil {
+			h.Error(w, r, err)
+			return
+		}
+		if err := h.Encode(w, r, out); err != nil {
+			h.Error(w, r, err)
+		}
+	}).Methods("GET")
+
+	r.HandleFunc("/v1/example/echo1/{id}/{line_num}/{status.note}", func(w http.ResponseWriter, r *http.Request) {
+		var in SimpleMessage
+
+		if err := binding.MapProto(&in, mux.Vars(r)); err != nil {
+			h.Error(w, r, err)
+			return
+		}
+
+		if err := h.Decode(r, &in); err != nil {
+			h.Error(w, r, err)
+			return
+		}
+		next := func(ctx context.Context, req interface{}) (interface{}, error) {
+			return srv.Echo(ctx, req.(*SimpleMessage))
+		}
+		if h.Middleware != nil {
+			next = h.Middleware(next)
+		}
+		out, err := next(r.Context(), &in)
+		if err != nil {
+			h.Error(w, r, err)
+			return
+		}
+		if err := h.Encode(w, r, out); err != nil {
+			h.Error(w, r, err)
+		}
+	}).Methods("GET")
+
+	r.HandleFunc("/v1/example/echo2/{no.note}", func(w http.ResponseWriter, r *http.Request) {
+		var in SimpleMessage
+
+		if err := binding.MapProto(&in, mux.Vars(r)); err != nil {
+			h.Error(w, r, err)
+			return
+		}
+
+		if err := h.Decode(r, &in); err != nil {
+			h.Error(w, r, err)
+			return
+		}
+		next := func(ctx context.Context, req interface{}) (interface{}, error) {
+			return srv.Echo(ctx, req.(*SimpleMessage))
+		}
+		if h.Middleware != nil {
+			next = h.Middleware(next)
+		}
+		out, err := next(r.Context(), &in)
+		if err != nil {
+			h.Error(w, r, err)
+			return
+		}
+		if err := h.Encode(w, r, out); err != nil {
+			h.Error(w, r, err)
+		}
+	}).Methods("GET")
+
+	r.HandleFunc("/v1/example/echo/{id}", func(w http.ResponseWriter, r *http.Request) {
+		var in SimpleMessage
+
+		if err := binding.MapProto(&in, mux.Vars(r)); err != nil {
+			h.Error(w, r, err)
+			return
+		}
+
+		if err := h.Decode(r, &in); err != nil {
+			h.Error(w, r, err)
+			return
+		}
+		next := func(ctx context.Context, req interface{}) (interface{}, error) {
+			return srv.Echo(ctx, req.(*SimpleMessage))
+		}
+		if h.Middleware != nil {
+			next = h.Middleware(next)
+		}
+		out, err := next(r.Context(), &in)
+		if err != nil {
+			h.Error(w, r, err)
+			return
+		}
+		if err := h.Encode(w, r, out); err != nil {
+			h.Error(w, r, err)
+		}
+	}).Methods("POST")
+
+	r.HandleFunc("/v1/example/echo_body", func(w http.ResponseWriter, r *http.Request) {
+		var in SimpleMessage
+
+		if err := h.Decode(r, &in); err != nil {
+			h.Error(w, r, err)
+			return
+		}
+		next := func(ctx context.Context, req interface{}) (interface{}, error) {
+			return srv.EchoBody(ctx, req.(*SimpleMessage))
+		}
+		if h.Middleware != nil {
+			next = h.Middleware(next)
+		}
+		out, err := next(r.Context(), &in)
+		if err != nil {
+			h.Error(w, r, err)
+			return
+		}
+		if err := h.Encode(w, r, out); err != nil {
+			h.Error(w, r, err)
+		}
+	}).Methods("POST")
+
+	r.HandleFunc("/v1/example/echo_delete", func(w http.ResponseWriter, r *http.Request) {
+		var in SimpleMessage
+
+		if err := h.Decode(r, &in); err != nil {
+			h.Error(w, r, err)
+			return
+		}
+		next := func(ctx context.Context, req interface{}) (interface{}, error) {
+			return srv.EchoDelete(ctx, req.(*SimpleMessage))
+		}
+		if h.Middleware != nil {
+			next = h.Middleware(next)
+		}
+		out, err := next(r.Context(), &in)
+		if err != nil {
+			h.Error(w, r, err)
+			return
+		}
+		if err := h.Encode(w, r, out); err != nil {
+			h.Error(w, r, err)
+		}
+	}).Methods("DELETE")
+
+	r.HandleFunc("/v1/example/echo_patch", func(w http.ResponseWriter, r *http.Request) {
+		var in DynamicMessageUpdate
+
+		if err := h.Decode(r, &in.Body); err != nil {
+			h.Error(w, r, err)
+			return
+		}
+		next := func(ctx context.Context, req interface{}) (interface{}, error) {
+			return srv.EchoPatch(ctx, req.(*DynamicMessageUpdate))
+		}
+		if h.Middleware != nil {
+			next = h.Middleware(next)
+		}
+		out, err := next(r.Context(), &in)
+		if err != nil {
+			h.Error(w, r, err)
+			return
+		}
+		if err := h.Encode(w, r, out); err != nil {
+			h.Error(w, r, err)
+		}
+	}).Methods("PATCH")
+
+	return r
 }
diff --git a/cmd/protoc-gen-go-http/internal/testproto/stream.pb.go b/cmd/protoc-gen-go-http/internal/testproto/stream.pb.go
new file mode 100644
index 00000000..a2fed91d
--- /dev/null
+++ b/cmd/protoc-gen-go-http/internal/testproto/stream.pb.go
@@ -0,0 +1,91 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.25.0
+// 	protoc        v3.13.0
+// source: stream.proto
+
+package testproto
+
+import (
+	proto "github.com/golang/protobuf/proto"
+	empty "github.com/golang/protobuf/ptypes/empty"
+	_ "google.golang.org/genproto/googleapis/api/annotations"
+	httpbody "google.golang.org/genproto/googleapis/api/httpbody"
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+// This is a compile-time assertion that a sufficiently up-to-date version
+// of the legacy proto package is being used.
+const _ = proto.ProtoPackageIsVersion4
+
+var File_stream_proto protoreflect.FileDescriptor
+
+var file_stream_proto_rawDesc = []byte{
+	0x0a, 0x0c, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09,
+	0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
+	0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+	0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x19, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f,
+	0x61, 0x70, 0x69, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x62, 0x6f, 0x64, 0x79, 0x2e, 0x70, 0x72, 0x6f,
+	0x74, 0x6f, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32,
+	0x69, 0x0a, 0x0d, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
+	0x12, 0x58, 0x0a, 0x08, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x16, 0x2e, 0x67,
+	0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45,
+	0x6d, 0x70, 0x74, 0x79, 0x1a, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70,
+	0x69, 0x2e, 0x48, 0x74, 0x74, 0x70, 0x42, 0x6f, 0x64, 0x79, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93,
+	0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2f,
+	0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x30, 0x01, 0x42, 0x51, 0x5a, 0x4f, 0x67, 0x69,
+	0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x2d, 0x6b, 0x72, 0x61, 0x74,
+	0x6f, 0x73, 0x2f, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2f, 0x63, 0x6d, 0x64, 0x2f, 0x70, 0x72,
+	0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x67, 0x6f, 0x2d, 0x68, 0x74, 0x74, 0x70,
+	0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72,
+	0x6f, 0x74, 0x6f, 0x3b, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70,
+	0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var file_stream_proto_goTypes = []interface{}{
+	(*empty.Empty)(nil),       // 0: google.protobuf.Empty
+	(*httpbody.HttpBody)(nil), // 1: google.api.HttpBody
+}
+var file_stream_proto_depIdxs = []int32{
+	0, // 0: testproto.StreamService.Download:input_type -> google.protobuf.Empty
+	1, // 1: testproto.StreamService.Download:output_type -> google.api.HttpBody
+	1, // [1:2] is the sub-list for method output_type
+	0, // [0:1] is the sub-list for method input_type
+	0, // [0:0] is the sub-list for extension type_name
+	0, // [0:0] is the sub-list for extension extendee
+	0, // [0:0] is the sub-list for field type_name
+}
+
+func init() { file_stream_proto_init() }
+func file_stream_proto_init() {
+	if File_stream_proto != nil {
+		return
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_stream_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   0,
+			NumExtensions: 0,
+			NumServices:   1,
+		},
+		GoTypes:           file_stream_proto_goTypes,
+		DependencyIndexes: file_stream_proto_depIdxs,
+	}.Build()
+	File_stream_proto = out.File
+	file_stream_proto_rawDesc = nil
+	file_stream_proto_goTypes = nil
+	file_stream_proto_depIdxs = nil
+}
diff --git a/cmd/protoc-gen-go-http/internal/testproto/stream.proto b/cmd/protoc-gen-go-http/internal/testproto/stream.proto
new file mode 100644
index 00000000..f343c8f4
--- /dev/null
+++ b/cmd/protoc-gen-go-http/internal/testproto/stream.proto
@@ -0,0 +1,15 @@
+syntax = "proto3";
+option go_package = "github.com/go-kratos/kratos/cmd/protoc-gen-go-http/internal/testproto;testproto";
+package testproto;
+
+import "google/api/annotations.proto";
+import "google/api/httpbody.proto";
+import "google/protobuf/empty.proto";
+
+service StreamService {
+	rpc Download(google.protobuf.Empty) returns (stream google.api.HttpBody) {
+		option (google.api.http) = {
+		  get : "/v1/example/download"
+		};
+	}
+}
diff --git a/cmd/protoc-gen-go-http/internal/testproto/stream_grpc.pb.go b/cmd/protoc-gen-go-http/internal/testproto/stream_grpc.pb.go
new file mode 100644
index 00000000..ed744d6b
--- /dev/null
+++ b/cmd/protoc-gen-go-http/internal/testproto/stream_grpc.pb.go
@@ -0,0 +1,130 @@
+// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
+
+package testproto
+
+import (
+	context "context"
+	empty "github.com/golang/protobuf/ptypes/empty"
+	httpbody "google.golang.org/genproto/googleapis/api/httpbody"
+	grpc "google.golang.org/grpc"
+	codes "google.golang.org/grpc/codes"
+	status "google.golang.org/grpc/status"
+)
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the grpc package it is being compiled against.
+// Requires gRPC-Go v1.32.0 or later.
+const _ = grpc.SupportPackageIsVersion7
+
+// StreamServiceClient is the client API for StreamService service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
+type StreamServiceClient interface {
+	Download(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (StreamService_DownloadClient, error)
+}
+
+type streamServiceClient struct {
+	cc grpc.ClientConnInterface
+}
+
+func NewStreamServiceClient(cc grpc.ClientConnInterface) StreamServiceClient {
+	return &streamServiceClient{cc}
+}
+
+func (c *streamServiceClient) Download(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (StreamService_DownloadClient, error) {
+	stream, err := c.cc.NewStream(ctx, &StreamService_ServiceDesc.Streams[0], "/testproto.StreamService/Download", opts...)
+	if err != nil {
+		return nil, err
+	}
+	x := &streamServiceDownloadClient{stream}
+	if err := x.ClientStream.SendMsg(in); err != nil {
+		return nil, err
+	}
+	if err := x.ClientStream.CloseSend(); err != nil {
+		return nil, err
+	}
+	return x, nil
+}
+
+type StreamService_DownloadClient interface {
+	Recv() (*httpbody.HttpBody, error)
+	grpc.ClientStream
+}
+
+type streamServiceDownloadClient struct {
+	grpc.ClientStream
+}
+
+func (x *streamServiceDownloadClient) Recv() (*httpbody.HttpBody, error) {
+	m := new(httpbody.HttpBody)
+	if err := x.ClientStream.RecvMsg(m); err != nil {
+		return nil, err
+	}
+	return m, nil
+}
+
+// StreamServiceServer is the server API for StreamService service.
+// All implementations must embed UnimplementedStreamServiceServer
+// for forward compatibility
+type StreamServiceServer interface {
+	Download(*empty.Empty, StreamService_DownloadServer) error
+	mustEmbedUnimplementedStreamServiceServer()
+}
+
+// UnimplementedStreamServiceServer must be embedded to have forward compatible implementations.
+type UnimplementedStreamServiceServer struct {
+}
+
+func (UnimplementedStreamServiceServer) Download(*empty.Empty, StreamService_DownloadServer) error {
+	return status.Errorf(codes.Unimplemented, "method Download not implemented")
+}
+func (UnimplementedStreamServiceServer) mustEmbedUnimplementedStreamServiceServer() {}
+
+// UnsafeStreamServiceServer may be embedded to opt out of forward compatibility for this service.
+// Use of this interface is not recommended, as added methods to StreamServiceServer will
+// result in compilation errors.
+type UnsafeStreamServiceServer interface {
+	mustEmbedUnimplementedStreamServiceServer()
+}
+
+func RegisterStreamServiceServer(s grpc.ServiceRegistrar, srv StreamServiceServer) {
+	s.RegisterService(&StreamService_ServiceDesc, srv)
+}
+
+func _StreamService_Download_Handler(srv interface{}, stream grpc.ServerStream) error {
+	m := new(empty.Empty)
+	if err := stream.RecvMsg(m); err != nil {
+		return err
+	}
+	return srv.(StreamServiceServer).Download(m, &streamServiceDownloadServer{stream})
+}
+
+type StreamService_DownloadServer interface {
+	Send(*httpbody.HttpBody) error
+	grpc.ServerStream
+}
+
+type streamServiceDownloadServer struct {
+	grpc.ServerStream
+}
+
+func (x *streamServiceDownloadServer) Send(m *httpbody.HttpBody) error {
+	return x.ServerStream.SendMsg(m)
+}
+
+// StreamService_ServiceDesc is the grpc.ServiceDesc for StreamService service.
+// It's only intended for direct use with grpc.RegisterService,
+// and not to be introspected or modified (even as a copy)
+var StreamService_ServiceDesc = grpc.ServiceDesc{
+	ServiceName: "testproto.StreamService",
+	HandlerType: (*StreamServiceServer)(nil),
+	Methods:     []grpc.MethodDesc{},
+	Streams: []grpc.StreamDesc{
+		{
+			StreamName:    "Download",
+			Handler:       _StreamService_Download_Handler,
+			ServerStreams: true,
+		},
+	},
+	Metadata: "stream.proto",
+}
diff --git a/cmd/protoc-gen-go-http/internal/testproto/stream_http.pb.go b/cmd/protoc-gen-go-http/internal/testproto/stream_http.pb.go
new file mode 100644
index 00000000..4112a458
--- /dev/null
+++ b/cmd/protoc-gen-go-http/internal/testproto/stream_http.pb.go
@@ -0,0 +1,33 @@
+// Code generated by protoc-gen-go-http. DO NOT EDIT.
+
+package testproto
+
+import (
+	context "context"
+	http1 "github.com/go-kratos/kratos/v2/transport/http"
+	binding "github.com/go-kratos/kratos/v2/transport/http/binding"
+	mux "github.com/gorilla/mux"
+	http "net/http"
+)
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the kratos package it is being compiled against.
+var _ = new(http.Request)
+var _ = new(context.Context)
+var _ = binding.MapProto
+var _ = mux.NewRouter
+
+const _ = http1.SupportPackageIsVersion1
+
+type StreamServiceHandler interface {
+}
+
+func NewStreamServiceHandler(srv StreamServiceHandler, opts ...http1.HandleOption) http.Handler {
+	h := http1.DefaultHandleOptions()
+	for _, o := range opts {
+		o(&h)
+	}
+	r := mux.NewRouter()
+
+	return r
+}
diff --git a/transport/http/default_test.go b/transport/http/default_test.go
deleted file mode 100644
index 044f5be2..00000000
--- a/transport/http/default_test.go
+++ /dev/null
@@ -1,23 +0,0 @@
-package http
-
-import "testing"
-
-func TestSubtype(t *testing.T) {
-	tests := []struct {
-		input    string
-		expected string
-	}{
-		{"application/json", "json"},
-		{"application/json;", "json"},
-		{"application/json; charset=utf-8", "json"},
-		{"application/", ""},
-		{"application", ""},
-		{"foo", ""},
-		{"", ""},
-	}
-	for _, test := range tests {
-		if contentSubtype(test.input) != test.expected {
-			t.Errorf("expected %s got %s", test.expected, test.input)
-		}
-	}
-}
diff --git a/transport/http/handle_test.go b/transport/http/handle_test.go
new file mode 100644
index 00000000..e5049ffe
--- /dev/null
+++ b/transport/http/handle_test.go
@@ -0,0 +1,57 @@
+package http
+
+import (
+	"context"
+	"net/http"
+	"testing"
+
+	"github.com/gorilla/mux"
+)
+
+type HelloRequest struct {
+	Name string `json:"name"`
+}
+type HelloReply struct {
+	Message string `json:"message"`
+}
+type GreeterService struct {
+}
+
+func (s *GreeterService) SayHello(ctx context.Context, req *HelloRequest) (*HelloReply, error) {
+	return &HelloReply{Message: "hello " + req.Name}, nil
+}
+
+func newGreeterHandler(srv *GreeterService, opts ...HandleOption) http.Handler {
+	h := DefaultHandleOptions()
+	for _, o := range opts {
+		o(&h)
+	}
+	r := mux.NewRouter()
+	r.HandleFunc("/helloworld", func(w http.ResponseWriter, r *http.Request) {
+		var in HelloRequest
+		if err := h.Decode(r, &in); err != nil {
+			h.Error(w, r, err)
+			return
+		}
+		next := func(ctx context.Context, req interface{}) (interface{}, error) {
+			return srv.SayHello(ctx, &in)
+		}
+		if h.Middleware != nil {
+			next = h.Middleware(next)
+		}
+		out, err := next(r.Context(), &in)
+		if err != nil {
+			h.Error(w, r, err)
+			return
+		}
+		if err := h.Encode(w, r, out); err != nil {
+			h.Error(w, r, err)
+		}
+	}).Methods("POST")
+	return r
+}
+
+func TestHandler(t *testing.T) {
+	s := &GreeterService{}
+	_ = newGreeterHandler(s)
+}
diff --git a/transport/http/service_test.go b/transport/http/service_test.go
deleted file mode 100644
index 39e66d19..00000000
--- a/transport/http/service_test.go
+++ /dev/null
@@ -1,93 +0,0 @@
-package http
-
-import (
-	"bytes"
-	"context"
-	"encoding/json"
-	"fmt"
-	"net/http"
-	"testing"
-	"time"
-
-	"github.com/go-kratos/kratos/v2/internal/host"
-)
-
-type testRequest struct {
-	Name string `json:"name"`
-}
-type testReply struct {
-	Result string `json:"result"`
-}
-type testService struct{}
-
-func (s *testService) SayHello(ctx context.Context, req *testRequest) (*testReply, error) {
-	return &testReply{Result: req.Name}, nil
-}
-
-func TestService(t *testing.T) {
-	h := func(srv interface{}, ctx context.Context, req *http.Request, dec func(interface{}) error) (interface{}, error) {
-		var in testRequest
-		if err := dec(&in); err != nil {
-			return nil, err
-		}
-		out, err := srv.(*testService).SayHello(ctx, &in)
-		if err != nil {
-			return nil, err
-		}
-		return out, nil
-	}
-	sd := &ServiceDesc{
-		ServiceName: "helloworld.Greeter",
-		Methods: []MethodDesc{
-			{
-				Path:    "/helloworld",
-				Method:  "POST",
-				Handler: h,
-			},
-		},
-	}
-
-	svc := &testService{}
-	srv := NewServer()
-	srv.RegisterService(sd, svc)
-
-	time.AfterFunc(time.Second, func() {
-		defer srv.Stop()
-		testServiceClient(t, srv)
-	})
-
-	if err := srv.Start(); err != nil {
-		t.Fatal(err)
-	}
-}
-
-func testServiceClient(t *testing.T, srv *Server) {
-	client, err := NewClient(context.Background())
-	if err != nil {
-		t.Fatal(err)
-	}
-	port, ok := host.Port(srv.lis)
-	if !ok {
-		t.Fatalf("extract port error: %v", srv.lis)
-	}
-	var (
-		in  = testRequest{Name: "hello"}
-		out = testReply{}
-		url = fmt.Sprintf("http://127.0.0.1:%d/helloworld", port)
-	)
-	data, err := json.Marshal(in)
-	if err != nil {
-		t.Fatal(err)
-	}
-	req, err := http.NewRequest("POST", url, bytes.NewReader(data))
-	if err != nil {
-		t.Fatal(err)
-	}
-	req.Header.Set("content-type", "application/json")
-	if err := Do(client, req, &out); err != nil {
-		t.Fatal(err)
-	}
-	if out.Result != in.Name {
-		t.Fatalf("expected %s got %s", in.Name, out.Result)
-	}
-}