Repository: cloudwego/hertz
Pull request #760 (2023-05-05 06:46:15)

chore: release v0.6.3


Patch:
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 4641e32..1099f2a 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -21,9 +21,9 @@ The description of the title will be attached in Release Notes,
 so please describe it from user-oriented, what this PR does / why we need it.
 Please check your PR title with the below requirements:
 -->
-- [ ] This PR title match the format: \<type\>(optional scope): \<description\>
+- [ ] This PR title match the format: `<type>(optional scope): <description>`.
 - [ ] The description of this PR title is user-oriented and clear enough for others to understand.
-
+- [ ] Attach the PR updating the user documentation if the current PR requires user awareness at the usage level. [User docs repo](https://github.com/cloudwego/cloudwego.github.io).
 
 #### (Optional) Translate the PR title into Chinese.
 
@@ -35,8 +35,13 @@ Provide more detailed info for review. If it is a perf type PR, perf data is sug
 en:
 zh(optional):
 
-#### Which issue(s) this PR fixes:
+#### (Optional) Which issue(s) this PR fixes:
 <!--
 Automatically closes linked issue when PR is merged.
 Eg: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.
 -->
+
+#### (Optional) The PR that updates user documentation:
+<!--
+If the current PR requires user awareness at the usage level, please submit a PR to update user docs. [User docs repo](https://github.com/cloudwego/cloudwego.github.io)
+-->
diff --git a/cmd/hz/app/app.go b/cmd/hz/app/app.go
index 835ee03..3f40e91 100644
--- a/cmd/hz/app/app.go
+++ b/cmd/hz/app/app.go
@@ -140,17 +140,16 @@ func Client(c *cli.Context) error {
 }
 
 func PluginMode() {
-	pluginName := filepath.Base(os.Args[0])
-	if util.IsWindows() {
-		pluginName = strings.TrimSuffix(pluginName, ".exe")
-	}
-	switch pluginName {
-	case meta.ThriftPluginName:
-		plugin := new(thrift.Plugin)
-		os.Exit(plugin.Run())
-	case meta.ProtocPluginName:
-		plugin := new(protobuf.Plugin)
-		os.Exit(plugin.Run())
+	mode := os.Getenv(meta.EnvPluginMode)
+	if len(os.Args) <= 1 && mode != "" {
+		switch mode {
+		case meta.ThriftPluginName:
+			plugin := new(thrift.Plugin)
+			os.Exit(plugin.Run())
+		case meta.ProtocPluginName:
+			plugin := new(protobuf.Plugin)
+			os.Exit(plugin.Run())
+		}
 	}
 }
 
diff --git a/cmd/hz/config/cmd.go b/cmd/hz/config/cmd.go
index b825992..983946c 100644
--- a/cmd/hz/config/cmd.go
+++ b/cmd/hz/config/cmd.go
@@ -71,54 +71,6 @@ func lookupTool(idlType string) (string, error) {
 		}
 	}
 
-	exe, err := os.Executable()
-	if err != nil {
-		return "", fmt.Errorf("failed to get executable path: %s", err)
-	}
-	gopath, err := util.GetGOPATH()
-	if err != nil {
-		return "", err
-	}
-	// softlink the plugin to "$GOPATH/bin"
-	dir := gopath + string(os.PathSeparator) + "bin"
-	if tool == meta.TpCompilerProto {
-		pgh, err := exec.LookPath(meta.ProtocPluginName)
-		linkName := filepath.Join(dir, meta.ProtocPluginName)
-		if util.IsWindows() {
-			linkName = linkName + ".exe"
-		}
-		if err != nil {
-			err = link(exe, linkName)
-			if err != nil {
-				return "", err
-			}
-		} else {
-			err = link(exe, pgh)
-			if err != nil {
-				return "", err
-			}
-		}
-	}
-
-	if tool == meta.TpCompilerThrift {
-		tgh, err := exec.LookPath(meta.ThriftPluginName)
-		linkName := filepath.Join(dir, meta.ThriftPluginName)
-		if util.IsWindows() {
-			linkName = linkName + ".exe"
-		}
-		if err != nil {
-			err = link(exe, linkName)
-			if err != nil {
-				return "", err
-			}
-		} else {
-			err = link(exe, tgh)
-			if err != nil {
-				return "", err
-			}
-		}
-	}
-
 	return path, nil
 }
 
@@ -136,6 +88,11 @@ func link(src, dst string) error {
 }
 
 func BuildPluginCmd(args *Argument) (*exec.Cmd, error) {
+	exe, err := os.Executable()
+	if err != nil {
+		return nil, fmt.Errorf("failed to detect current executable, err: %v", err)
+	}
+
 	argPacks, err := args.Pack()
 	if err != nil {
 		return nil, err
@@ -152,6 +109,7 @@ func BuildPluginCmd(args *Argument) (*exec.Cmd, error) {
 
 	if args.IdlType == meta.IdlThrift {
 		// thriftgo
+		os.Setenv(meta.EnvPluginMode, meta.ThriftPluginName)
 		cmd.Args = append(cmd.Args, meta.TpCompilerThrift)
 		for _, inc := range args.Includes {
 			cmd.Args = append(cmd.Args, "-i", inc)
@@ -167,7 +125,7 @@ func BuildPluginCmd(args *Argument) (*exec.Cmd, error) {
 		cmd.Args = append(cmd.Args,
 			"-o", args.ModelOutDir(),
 			"-g", thriftOpt,
-			"-p", "hertz:"+kas,
+			"-p", "hertz="+exe+":"+kas,
 		)
 		for _, p := range args.ThriftPlugins {
 			cmd.Args = append(cmd.Args, "-p", p)
@@ -177,6 +135,7 @@ func BuildPluginCmd(args *Argument) (*exec.Cmd, error) {
 		}
 	} else {
 		// protoc
+		os.Setenv(meta.EnvPluginMode, meta.ProtocPluginName)
 		cmd.Args = append(cmd.Args, meta.TpCompilerProto)
 		for _, inc := range args.Includes {
 			cmd.Args = append(cmd.Args, "-I", inc)
@@ -185,6 +144,7 @@ func BuildPluginCmd(args *Argument) (*exec.Cmd, error) {
 			cmd.Args = append(cmd.Args, "-I", filepath.Dir(inc))
 		}
 		cmd.Args = append(cmd.Args,
+			"--plugin=protoc-gen-hertz="+exe,
 			"--hertz_out="+args.OutDir,
 			"--hertz_opt="+kas,
 		)
diff --git a/cmd/hz/generator/handler.go b/cmd/hz/generator/handler.go
index a39e0fe..dca3282 100644
--- a/cmd/hz/generator/handler.go
+++ b/cmd/hz/generator/handler.go
@@ -152,6 +152,9 @@ func (pkgGen *HttpPackageGenerator) updateHandler(handler interface{}, handlerTp
 	if !isExist {
 		return pkgGen.TemplateGenerator.Generate(handler, handlerTpl, filePath, noRepeat)
 	}
+	if pkgGen.HandlerByMethod { // method by handler, do not need to insert new content
+		return nil
+	}
 
 	file, err := ioutil.ReadFile(filePath)
 	if err != nil {
diff --git a/cmd/hz/meta/const.go b/cmd/hz/meta/const.go
index 7e5e5db..0836c79 100644
--- a/cmd/hz/meta/const.go
+++ b/cmd/hz/meta/const.go
@@ -19,7 +19,7 @@ package meta
 import "runtime"
 
 // Version hz version
-const Version = "v0.6.2"
+const Version = "v0.6.3"
 
 // Mode hz run modes
 type Mode int
@@ -29,6 +29,8 @@ const SysType = runtime.GOOS
 
 const WindowsOS = "windows"
 
+const EnvPluginMode = "HERTZ_PLUGIN_MODE"
+
 // hz Commands
 const (
 	CmdUpdate = "update"
diff --git a/cmd/hz/protobuf/api/api.pb.go b/cmd/hz/protobuf/api/api.pb.go
index 6a38b1b..9707626 100644
--- a/cmd/hz/protobuf/api/api.pb.go
+++ b/cmd/hz/protobuf/api/api.pb.go
@@ -1,17 +1,16 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
 // 	protoc-gen-go v1.28.1
-// 	protoc        v3.19.4
+// 	protoc        v3.21.12
 // source: api.proto
 
 package api
 
 import (
-	reflect "reflect"
-
 	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
 	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
 	descriptorpb "google.golang.org/protobuf/types/descriptorpb"
+	reflect "reflect"
 )
 
 const (
@@ -86,14 +85,6 @@ var file_api_proto_extTypes = []protoimpl.ExtensionInfo{
 		Tag:           "bytes,50108,opt,name=form",
 		Filename:      "api.proto",
 	},
-	{
-		ExtendedType:  (*descriptorpb.FieldOptions)(nil),
-		ExtensionType: (*string)(nil),
-		Field:         51001,
-		Name:          "api.go_tag",
-		Tag:           "bytes,51001,opt,name=go_tag",
-		Filename:      "api.proto",
-	},
 	{
 		ExtendedType:  (*descriptorpb.FieldOptions)(nil),
 		ExtensionType: (*string)(nil),
@@ -110,6 +101,54 @@ var file_api_proto_extTypes = []protoimpl.ExtensionInfo{
 		Tag:           "bytes,50110,opt,name=file_name",
 		Filename:      "api.proto",
 	},
+	{
+		ExtendedType:  (*descriptorpb.FieldOptions)(nil),
+		ExtensionType: (*string)(nil),
+		Field:         50111,
+		Name:          "api.none",
+		Tag:           "bytes,50111,opt,name=none",
+		Filename:      "api.proto",
+	},
+	{
+		ExtendedType:  (*descriptorpb.FieldOptions)(nil),
+		ExtensionType: (*string)(nil),
+		Field:         50131,
+		Name:          "api.form_compatible",
+		Tag:           "bytes,50131,opt,name=form_compatible",
+		Filename:      "api.proto",
+	},
+	{
+		ExtendedType:  (*descriptorpb.FieldOptions)(nil),
+		ExtensionType: (*string)(nil),
+		Field:         50132,
+		Name:          "api.js_conv_compatible",
+		Tag:           "bytes,50132,opt,name=js_conv_compatible",
+		Filename:      "api.proto",
+	},
+	{
+		ExtendedType:  (*descriptorpb.FieldOptions)(nil),
+		ExtensionType: (*string)(nil),
+		Field:         50133,
+		Name:          "api.file_name_compatible",
+		Tag:           "bytes,50133,opt,name=file_name_compatible",
+		Filename:      "api.proto",
+	},
+	{
+		ExtendedType:  (*descriptorpb.FieldOptions)(nil),
+		ExtensionType: (*string)(nil),
+		Field:         50134,
+		Name:          "api.none_compatible",
+		Tag:           "bytes,50134,opt,name=none_compatible",
+		Filename:      "api.proto",
+	},
+	{
+		ExtendedType:  (*descriptorpb.FieldOptions)(nil),
+		ExtensionType: (*string)(nil),
+		Field:         51001,
+		Name:          "api.go_tag",
+		Tag:           "bytes,51001,opt,name=go_tag",
+		Filename:      "api.proto",
+	},
 	{
 		ExtendedType:  (*descriptorpb.MethodOptions)(nil),
 		ExtensionType: (*string)(nil),
@@ -246,6 +285,14 @@ var file_api_proto_extTypes = []protoimpl.ExtensionInfo{
 		Tag:           "bytes,50309,opt,name=handler_path",
 		Filename:      "api.proto",
 	},
+	{
+		ExtendedType:  (*descriptorpb.MethodOptions)(nil),
+		ExtensionType: (*string)(nil),
+		Field:         50331,
+		Name:          "api.handler_path_compatible",
+		Tag:           "bytes,50331,opt,name=handler_path_compatible",
+		Filename:      "api.proto",
+	},
 	{
 		ExtendedType:  (*descriptorpb.EnumValueOptions)(nil),
 		ExtensionType: (*int32)(nil),
@@ -262,6 +309,14 @@ var file_api_proto_extTypes = []protoimpl.ExtensionInfo{
 		Tag:           "bytes,50402,opt,name=base_domain",
 		Filename:      "api.proto",
 	},
+	{
+		ExtendedType:  (*descriptorpb.ServiceOptions)(nil),
+		ExtensionType: (*string)(nil),
+		Field:         50731,
+		Name:          "api.base_domain_compatible",
+		Tag:           "bytes,50731,opt,name=base_domain_compatible",
+		Filename:      "api.proto",
+	},
 }
 
 // Extension fields to descriptorpb.FieldOptions.
@@ -282,62 +337,82 @@ var (
 	E_Vd = &file_api_proto_extTypes[6]
 	// optional string form = 50108;
 	E_Form = &file_api_proto_extTypes[7]
-	// optional string go_tag = 51001;
-	E_GoTag = &file_api_proto_extTypes[8]
 	// optional string js_conv = 50109;
-	E_JsConv = &file_api_proto_extTypes[9]
+	E_JsConv = &file_api_proto_extTypes[8]
 	// optional string file_name = 50110;
-	E_FileName = &file_api_proto_extTypes[10]
+	E_FileName = &file_api_proto_extTypes[9]
+	// optional string none = 50111;
+	E_None = &file_api_proto_extTypes[10]
+	// 50131~50160 used to extend field option by hz
+	//
+	// optional string form_compatible = 50131;
+	E_FormCompatible = &file_api_proto_extTypes[11]
+	// optional string js_conv_compatible = 50132;
+	E_JsConvCompatible = &file_api_proto_extTypes[12]
+	// optional string file_name_compatible = 50133;
+	E_FileNameCompatible = &file_api_proto_extTypes[13]
+	// optional string none_compatible = 50134;
+	E_NoneCompatible = &file_api_proto_extTypes[14]
+	// optional string go_tag = 51001;
+	E_GoTag = &file_api_proto_extTypes[15]
 )
 
 // Extension fields to descriptorpb.MethodOptions.
 var (
 	// optional string get = 50201;
-	E_Get = &file_api_proto_extTypes[11]
+	E_Get = &file_api_proto_extTypes[16]
 	// optional string post = 50202;
-	E_Post = &file_api_proto_extTypes[12]
+	E_Post = &file_api_proto_extTypes[17]
 	// optional string put = 50203;
-	E_Put = &file_api_proto_extTypes[13]
+	E_Put = &file_api_proto_extTypes[18]
 	// optional string delete = 50204;
-	E_Delete = &file_api_proto_extTypes[14]
+	E_Delete = &file_api_proto_extTypes[19]
 	// optional string patch = 50205;
-	E_Patch = &file_api_proto_extTypes[15]
+	E_Patch = &file_api_proto_extTypes[20]
 	// optional string options = 50206;
-	E_Options = &file_api_proto_extTypes[16]
+	E_Options = &file_api_proto_extTypes[21]
 	// optional string head = 50207;
-	E_Head = &file_api_proto_extTypes[17]
+	E_Head = &file_api_proto_extTypes[22]
 	// optional string any = 50208;
-	E_Any = &file_api_proto_extTypes[18]
+	E_Any = &file_api_proto_extTypes[23]
 	// optional string gen_path = 50301;
-	E_GenPath = &file_api_proto_extTypes[19] // The path specified by the user when the client code is generated, with a higher priority than api_version
+	E_GenPath = &file_api_proto_extTypes[24] // The path specified by the user when the client code is generated, with a higher priority than api_version
 	// optional string api_version = 50302;
-	E_ApiVersion = &file_api_proto_extTypes[20] // Specify the value of the :version variable in path when the client code is generated
+	E_ApiVersion = &file_api_proto_extTypes[25] // Specify the value of the :version variable in path when the client code is generated
 	// optional string tag = 50303;
-	E_Tag = &file_api_proto_extTypes[21] // rpc tag, can be multiple, separated by commas
+	E_Tag = &file_api_proto_extTypes[26] // rpc tag, can be multiple, separated by commas
 	// optional string name = 50304;
-	E_Name = &file_api_proto_extTypes[22] // Name of rpc
+	E_Name = &file_api_proto_extTypes[27] // Name of rpc
 	// optional string api_level = 50305;
-	E_ApiLevel = &file_api_proto_extTypes[23] // Interface Level
+	E_ApiLevel = &file_api_proto_extTypes[28] // Interface Level
 	// optional string serializer = 50306;
-	E_Serializer = &file_api_proto_extTypes[24] // Serialization method
+	E_Serializer = &file_api_proto_extTypes[29] // Serialization method
 	// optional string param = 50307;
-	E_Param = &file_api_proto_extTypes[25] // Whether client requests take public parameters
+	E_Param = &file_api_proto_extTypes[30] // Whether client requests take public parameters
 	// optional string baseurl = 50308;
-	E_Baseurl = &file_api_proto_extTypes[26] // Baseurl used in ttnet routing
+	E_Baseurl = &file_api_proto_extTypes[31] // Baseurl used in ttnet routing
 	// optional string handler_path = 50309;
-	E_HandlerPath = &file_api_proto_extTypes[27] // handler_path specifies the path to generate the method
+	E_HandlerPath = &file_api_proto_extTypes[32] // handler_path specifies the path to generate the method
+	// 50331~50360 used to extend method option by hz
+	//
+	// optional string handler_path_compatible = 50331;
+	E_HandlerPathCompatible = &file_api_proto_extTypes[33] // handler_path specifies the path to generate the method
 )
 
 // Extension fields to descriptorpb.EnumValueOptions.
 var (
 	// optional int32 http_code = 50401;
-	E_HttpCode = &file_api_proto_extTypes[28]
+	E_HttpCode = &file_api_proto_extTypes[34]
 )
 
 // Extension fields to descriptorpb.ServiceOptions.
 var (
 	// optional string base_domain = 50402;
-	E_BaseDomain = &file_api_proto_extTypes[29]
+	E_BaseDomain = &file_api_proto_extTypes[35]
+	// 50731~50760 used to extend service option by hz
+	//
+	// optional string base_domain_compatible = 50731;
+	E_BaseDomainCompatible = &file_api_proto_extTypes[36]
 )
 
 var File_api_proto protoreflect.FileDescriptor
@@ -373,88 +448,122 @@ var file_api_proto_rawDesc = []byte{
 	0x76, 0x64, 0x3a, 0x33, 0x0a, 0x04, 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x1d, 0x2e, 0x67, 0x6f, 0x6f,
 	0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65,
 	0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xbc, 0x87, 0x03, 0x20, 0x01, 0x28,
-	0x09, 0x52, 0x04, 0x66, 0x6f, 0x72, 0x6d, 0x3a, 0x36, 0x0a, 0x06, 0x67, 0x6f, 0x5f, 0x74, 0x61,
-	0x67, 0x12, 0x1d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
-	0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73,
-	0x18, 0xb9, 0x8e, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x67, 0x6f, 0x54, 0x61, 0x67, 0x3a,
-	0x38, 0x0a, 0x07, 0x6a, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x76, 0x12, 0x1d, 0x2e, 0x67, 0x6f, 0x6f,
-	0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65,
-	0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xbd, 0x87, 0x03, 0x20, 0x01, 0x28,
-	0x09, 0x52, 0x06, 0x6a, 0x73, 0x43, 0x6f, 0x6e, 0x76, 0x3a, 0x3c, 0x0a, 0x09, 0x66, 0x69, 0x6c,
-	0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
-	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70,
-	0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xbe, 0x87, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66,
-	0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x3a, 0x32, 0x0a, 0x03, 0x67, 0x65, 0x74, 0x12, 0x1e,
+	0x09, 0x52, 0x04, 0x66, 0x6f, 0x72, 0x6d, 0x3a, 0x38, 0x0a, 0x07, 0x6a, 0x73, 0x5f, 0x63, 0x6f,
+	0x6e, 0x76, 0x12, 0x1d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+	0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e,
+	0x73, 0x18, 0xbd, 0x87, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6a, 0x73, 0x43, 0x6f, 0x6e,
+	0x76, 0x3a, 0x3c, 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1d,
+	0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
+	0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xbe, 0x87,
+	0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x3a,
+	0x33, 0x0a, 0x04, 0x6e, 0x6f, 0x6e, 0x65, 0x12, 0x1d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
+	0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f,
+	0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xbf, 0x87, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
+	0x6e, 0x6f, 0x6e, 0x65, 0x3a, 0x48, 0x0a, 0x0f, 0x66, 0x6f, 0x72, 0x6d, 0x5f, 0x63, 0x6f, 0x6d,
+	0x70, 0x61, 0x74, 0x69, 0x62, 0x6c, 0x65, 0x12, 0x1d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
+	0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f,
+	0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xd3, 0x87, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e,
+	0x66, 0x6f, 0x72, 0x6d, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x74, 0x69, 0x62, 0x6c, 0x65, 0x3a, 0x4d,
+	0x0a, 0x12, 0x6a, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x76, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x74,
+	0x69, 0x62, 0x6c, 0x65, 0x12, 0x1d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
+	0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69,
+	0x6f, 0x6e, 0x73, 0x18, 0xd4, 0x87, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x6a, 0x73, 0x43,
+	0x6f, 0x6e, 0x76, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x74, 0x69, 0x62, 0x6c, 0x65, 0x3a, 0x51, 0x0a,
+	0x14, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61,
+	0x74, 0x69, 0x62, 0x6c, 0x65, 0x12, 0x1d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
+	0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74,
+	0x69, 0x6f, 0x6e, 0x73, 0x18, 0xd5, 0x87, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x66, 0x69,
+	0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x74, 0x69, 0x62, 0x6c, 0x65,
+	0x3a, 0x48, 0x0a, 0x0f, 0x6e, 0x6f, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x74, 0x69,
+	0x62, 0x6c, 0x65, 0x12, 0x1d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
+	0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f,
+	0x6e, 0x73, 0x18, 0xd6, 0x87, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6e, 0x6f, 0x6e, 0x65,
+	0x43, 0x6f, 0x6d, 0x70, 0x61, 0x74, 0x69, 0x62, 0x6c, 0x65, 0x3a, 0x36, 0x0a, 0x06, 0x67, 0x6f,
+	0x5f, 0x74, 0x61, 0x67, 0x12, 0x1d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
+	0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69,
+	0x6f, 0x6e, 0x73, 0x18, 0xb9, 0x8e, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x67, 0x6f, 0x54,
+	0x61, 0x67, 0x3a, 0x32, 0x0a, 0x03, 0x67, 0x65, 0x74, 0x12, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
+	0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x74, 0x68,
+	0x6f, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x99, 0x88, 0x03, 0x20, 0x01, 0x28,
+	0x09, 0x52, 0x03, 0x67, 0x65, 0x74, 0x3a, 0x34, 0x0a, 0x04, 0x70, 0x6f, 0x73, 0x74, 0x12, 0x1e,
 	0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
-	0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x99,
-	0x88, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x67, 0x65, 0x74, 0x3a, 0x34, 0x0a, 0x04, 0x70,
-	0x6f, 0x73, 0x74, 0x12, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
+	0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x9a,
+	0x88, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x6f, 0x73, 0x74, 0x3a, 0x32, 0x0a, 0x03,
+	0x70, 0x75, 0x74, 0x12, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
 	0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x4f, 0x70, 0x74, 0x69,
-	0x6f, 0x6e, 0x73, 0x18, 0x9a, 0x88, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x6f, 0x73,
-	0x74, 0x3a, 0x32, 0x0a, 0x03, 0x70, 0x75, 0x74, 0x12, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
-	0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f,
-	0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x9b, 0x88, 0x03, 0x20, 0x01, 0x28, 0x09,
-	0x52, 0x03, 0x70, 0x75, 0x74, 0x3a, 0x38, 0x0a, 0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12,
-	0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
-	0x66, 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18,
-	0x9c, 0x88, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x3a,
-	0x36, 0x0a, 0x05, 0x70, 0x61, 0x74, 0x63, 0x68, 0x12, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
-	0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f,
-	0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x9d, 0x88, 0x03, 0x20, 0x01, 0x28, 0x09,
-	0x52, 0x05, 0x70, 0x61, 0x74, 0x63, 0x68, 0x3a, 0x3a, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f,
-	0x6e, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
-	0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f,
-	0x6e, 0x73, 0x18, 0x9e, 0x88, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69,
-	0x6f, 0x6e, 0x73, 0x3a, 0x34, 0x0a, 0x04, 0x68, 0x65, 0x61, 0x64, 0x12, 0x1e, 0x2e, 0x67, 0x6f,
+	0x6f, 0x6e, 0x73, 0x18, 0x9b, 0x88, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x70, 0x75, 0x74,
+	0x3a, 0x38, 0x0a, 0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x1e, 0x2e, 0x67, 0x6f, 0x6f,
+	0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x74,
+	0x68, 0x6f, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x9c, 0x88, 0x03, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x3a, 0x36, 0x0a, 0x05, 0x70, 0x61,
+	0x74, 0x63, 0x68, 0x12, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
+	0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x4f, 0x70, 0x74, 0x69,
+	0x6f, 0x6e, 0x73, 0x18, 0x9d, 0x88, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x70, 0x61, 0x74,
+	0x63, 0x68, 0x3a, 0x3a, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1e, 0x2e,
+	0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
+	0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x9e, 0x88,
+	0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x3a, 0x34,
+	0x0a, 0x04, 0x68, 0x65, 0x61, 0x64, 0x12, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
+	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x4f,
+	0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x9f, 0x88, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
+	0x68, 0x65, 0x61, 0x64, 0x3a, 0x32, 0x0a, 0x03, 0x61, 0x6e, 0x79, 0x12, 0x1e, 0x2e, 0x67, 0x6f,
 	0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65,
-	0x74, 0x68, 0x6f, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x9f, 0x88, 0x03, 0x20,
-	0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x65, 0x61, 0x64, 0x3a, 0x32, 0x0a, 0x03, 0x61, 0x6e, 0x79,
-	0x12, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
-	0x75, 0x66, 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73,
-	0x18, 0xa0, 0x88, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x6e, 0x79, 0x3a, 0x3b, 0x0a,
-	0x08, 0x67, 0x65, 0x6e, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x12, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
-	0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x74, 0x68,
-	0x6f, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xfd, 0x88, 0x03, 0x20, 0x01, 0x28,
-	0x09, 0x52, 0x07, 0x67, 0x65, 0x6e, 0x50, 0x61, 0x74, 0x68, 0x3a, 0x41, 0x0a, 0x0b, 0x61, 0x70,
-	0x69, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
-	0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x74, 0x68,
-	0x6f, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xfe, 0x88, 0x03, 0x20, 0x01, 0x28,
-	0x09, 0x52, 0x0a, 0x61, 0x70, 0x69, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3a, 0x32, 0x0a,
-	0x03, 0x74, 0x61, 0x67, 0x12, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
+	0x74, 0x68, 0x6f, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xa0, 0x88, 0x03, 0x20,
+	0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x6e, 0x79, 0x3a, 0x3b, 0x0a, 0x08, 0x67, 0x65, 0x6e, 0x5f,
+	0x70, 0x61, 0x74, 0x68, 0x12, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
 	0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x4f, 0x70, 0x74,
-	0x69, 0x6f, 0x6e, 0x73, 0x18, 0xff, 0x88, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61,
-	0x67, 0x3a, 0x34, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
-	0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x74, 0x68,
-	0x6f, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x80, 0x89, 0x03, 0x20, 0x01, 0x28,
-	0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x3a, 0x3d, 0x0a, 0x09, 0x61, 0x70, 0x69, 0x5f, 0x6c,
-	0x65, 0x76, 0x65, 0x6c, 0x12, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
+	0x69, 0x6f, 0x6e, 0x73, 0x18, 0xfd, 0x88, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x67, 0x65,
+	0x6e, 0x50, 0x61, 0x74, 0x68, 0x3a, 0x41, 0x0a, 0x0b, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x65, 0x72,
+	0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
 	0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x4f, 0x70, 0x74,
-	0x69, 0x6f, 0x6e, 0x73, 0x18, 0x81, 0x89, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x61, 0x70,
-	0x69, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x3a, 0x40, 0x0a, 0x0a, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c,
-	0x69, 0x7a, 0x65, 0x72, 0x12, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
+	0x69, 0x6f, 0x6e, 0x73, 0x18, 0xfe, 0x88, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x70,
+	0x69, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3a, 0x32, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x12,
+	0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
+	0x66, 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18,
+	0xff, 0x88, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x3a, 0x34, 0x0a, 0x04,
+	0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
 	0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x4f, 0x70, 0x74,
-	0x69, 0x6f, 0x6e, 0x73, 0x18, 0x82, 0x89, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65,
-	0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x72, 0x3a, 0x36, 0x0a, 0x05, 0x70, 0x61, 0x72, 0x61,
-	0x6d, 0x12, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
-	0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e,
-	0x73, 0x18, 0x83, 0x89, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x70, 0x61, 0x72, 0x61, 0x6d,
-	0x3a, 0x3a, 0x0a, 0x07, 0x62, 0x61, 0x73, 0x65, 0x75, 0x72, 0x6c, 0x12, 0x1e, 0x2e, 0x67, 0x6f,
-	0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65,
-	0x74, 0x68, 0x6f, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x84, 0x89, 0x03, 0x20,
-	0x01, 0x28, 0x09, 0x52, 0x07, 0x62, 0x61, 0x73, 0x65, 0x75, 0x72, 0x6c, 0x3a, 0x43, 0x0a, 0x0c,
-	0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x12, 0x1e, 0x2e, 0x67,
+	0x69, 0x6f, 0x6e, 0x73, 0x18, 0x80, 0x89, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,
+	0x6d, 0x65, 0x3a, 0x3d, 0x0a, 0x09, 0x61, 0x70, 0x69, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12,
+	0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
+	0x66, 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18,
+	0x81, 0x89, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x61, 0x70, 0x69, 0x4c, 0x65, 0x76, 0x65,
+	0x6c, 0x3a, 0x40, 0x0a, 0x0a, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x72, 0x12,
+	0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
+	0x66, 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18,
+	0x82, 0x89, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69,
+	0x7a, 0x65, 0x72, 0x3a, 0x36, 0x0a, 0x05, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x12, 0x1e, 0x2e, 0x67,
 	0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d,
-	0x65, 0x74, 0x68, 0x6f, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x85, 0x89, 0x03,
-	0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x50, 0x61, 0x74,
-	0x68, 0x3a, 0x40, 0x0a, 0x09, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x21,
-	0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
-	0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e,
-	0x73, 0x18, 0xe1, 0x89, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x68, 0x74, 0x74, 0x70, 0x43,
-	0x6f, 0x64, 0x65, 0x3a, 0x42, 0x0a, 0x0b, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x64, 0x6f, 0x6d, 0x61,
-	0x69, 0x6e, 0x12, 0x1f, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
-	0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4f, 0x70, 0x74, 0x69,
-	0x6f, 0x6e, 0x73, 0x18, 0xe2, 0x89, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x62, 0x61, 0x73,
-	0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x42, 0x06, 0x5a, 0x04, 0x2f, 0x61, 0x70, 0x69,
+	0x65, 0x74, 0x68, 0x6f, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x83, 0x89, 0x03,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x3a, 0x3a, 0x0a, 0x07, 0x62,
+	0x61, 0x73, 0x65, 0x75, 0x72, 0x6c, 0x12, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
+	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x4f,
+	0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x84, 0x89, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07,
+	0x62, 0x61, 0x73, 0x65, 0x75, 0x72, 0x6c, 0x3a, 0x43, 0x0a, 0x0c, 0x68, 0x61, 0x6e, 0x64, 0x6c,
+	0x65, 0x72, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x12, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
+	0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64,
+	0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x85, 0x89, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x0b, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x50, 0x61, 0x74, 0x68, 0x3a, 0x58, 0x0a, 0x17,
+	0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x63, 0x6f, 0x6d,
+	0x70, 0x61, 0x74, 0x69, 0x62, 0x6c, 0x65, 0x12, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
+	0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64,
+	0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x9b, 0x89, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x15, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x50, 0x61, 0x74, 0x68, 0x43, 0x6f, 0x6d, 0x70,
+	0x61, 0x74, 0x69, 0x62, 0x6c, 0x65, 0x3a, 0x40, 0x0a, 0x09, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x63,
+	0x6f, 0x64, 0x65, 0x12, 0x21, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
+	0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4f,
+	0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xe1, 0x89, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08,
+	0x68, 0x74, 0x74, 0x70, 0x43, 0x6f, 0x64, 0x65, 0x3a, 0x42, 0x0a, 0x0b, 0x62, 0x61, 0x73, 0x65,
+	0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x1f, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
+	0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
+	0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xe2, 0x89, 0x03, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x0a, 0x62, 0x61, 0x73, 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x3a, 0x57, 0x0a, 0x16,
+	0x62, 0x61, 0x73, 0x65, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x63, 0x6f, 0x6d, 0x70,
+	0x61, 0x74, 0x69, 0x62, 0x6c, 0x65, 0x12, 0x1f, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
+	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
+	0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xab, 0x8c, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x14, 0x62, 0x61, 0x73, 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x43, 0x6f, 0x6d, 0x70, 0x61,
+	0x74, 0x69, 0x62, 0x6c, 0x65, 0x42, 0x06, 0x5a, 0x04, 0x2f, 0x61, 0x70, 0x69,
 }
 
 var file_api_proto_goTypes = []interface{}{
@@ -472,32 +581,39 @@ var file_api_proto_depIdxs = []int32{
 	0,  // 5: api.path:extendee -> google.protobuf.FieldOptions
 	0,  // 6: api.vd:extendee -> google.protobuf.FieldOptions
 	0,  // 7: api.form:extendee -> google.protobuf.FieldOptions
-	0,  // 8: api.go_tag:extendee -> google.protobuf.FieldOptions
-	0,  // 9: api.js_conv:extendee -> google.protobuf.FieldOptions
-	0,  // 10: api.file_name:extendee -> google.protobuf.FieldOptions
-	1,  // 11: api.get:extendee -> google.protobuf.MethodOptions
-	1,  // 12: api.post:extendee -> google.protobuf.MethodOptions
-	1,  // 13: api.put:extendee -> google.protobuf.MethodOptions
-	1,  // 14: api.delete:extendee -> google.protobuf.MethodOptions
-	1,  // 15: api.patch:extendee -> google.protobuf.MethodOptions
-	1,  // 16: api.options:extendee -> google.protobuf.MethodOptions
-	1,  // 17: api.head:extendee -> google.protobuf.MethodOptions
-	1,  // 18: api.any:extendee -> google.protobuf.MethodOptions
-	1,  // 19: api.gen_path:extendee -> google.protobuf.MethodOptions
-	1,  // 20: api.api_version:extendee -> google.protobuf.MethodOptions
-	1,  // 21: api.tag:extendee -> google.protobuf.MethodOptions
-	1,  // 22: api.name:extendee -> google.protobuf.MethodOptions
-	1,  // 23: api.api_level:extendee -> google.protobuf.MethodOptions
-	1,  // 24: api.serializer:extendee -> google.protobuf.MethodOptions
-	1,  // 25: api.param:extendee -> google.protobuf.MethodOptions
-	1,  // 26: api.baseurl:extendee -> google.protobuf.MethodOptions
-	1,  // 27: api.handler_path:extendee -> google.protobuf.MethodOptions
-	2,  // 28: api.http_code:extendee -> google.protobuf.EnumValueOptions
-	3,  // 29: api.base_domain:extendee -> google.protobuf.ServiceOptions
-	30, // [30:30] is the sub-list for method output_type
-	30, // [30:30] is the sub-list for method input_type
-	30, // [30:30] is the sub-list for extension type_name
-	0,  // [0:30] is the sub-list for extension extendee
+	0,  // 8: api.js_conv:extendee -> google.protobuf.FieldOptions
+	0,  // 9: api.file_name:extendee -> google.protobuf.FieldOptions
+	0,  // 10: api.none:extendee -> google.protobuf.FieldOptions
+	0,  // 11: api.form_compatible:extendee -> google.protobuf.FieldOptions
+	0,  // 12: api.js_conv_compatible:extendee -> google.protobuf.FieldOptions
+	0,  // 13: api.file_name_compatible:extendee -> google.protobuf.FieldOptions
+	0,  // 14: api.none_compatible:extendee -> google.protobuf.FieldOptions
+	0,  // 15: api.go_tag:extendee -> google.protobuf.FieldOptions
+	1,  // 16: api.get:extendee -> google.protobuf.MethodOptions
+	1,  // 17: api.post:extendee -> google.protobuf.MethodOptions
+	1,  // 18: api.put:extendee -> google.protobuf.MethodOptions
+	1,  // 19: api.delete:extendee -> google.protobuf.MethodOptions
+	1,  // 20: api.patch:extendee -> google.protobuf.MethodOptions
+	1,  // 21: api.options:extendee -> google.protobuf.MethodOptions
+	1,  // 22: api.head:extendee -> google.protobuf.MethodOptions
+	1,  // 23: api.any:extendee -> google.protobuf.MethodOptions
+	1,  // 24: api.gen_path:extendee -> google.protobuf.MethodOptions
+	1,  // 25: api.api_version:extendee -> google.protobuf.MethodOptions
+	1,  // 26: api.tag:extendee -> google.protobuf.MethodOptions
+	1,  // 27: api.name:extendee -> google.protobuf.MethodOptions
+	1,  // 28: api.api_level:extendee -> google.protobuf.MethodOptions
+	1,  // 29: api.serializer:extendee -> google.protobuf.MethodOptions
+	1,  // 30: api.param:extendee -> google.protobuf.MethodOptions
+	1,  // 31: api.baseurl:extendee -> google.protobuf.MethodOptions
+	1,  // 32: api.handler_path:extendee -> google.protobuf.MethodOptions
+	1,  // 33: api.handler_path_compatible:extendee -> google.protobuf.MethodOptions
+	2,  // 34: api.http_code:extendee -> google.protobuf.EnumValueOptions
+	3,  // 35: api.base_domain:extendee -> google.protobuf.ServiceOptions
+	3,  // 36: api.base_domain_compatible:extendee -> google.protobuf.ServiceOptions
+	37, // [37:37] is the sub-list for method output_type
+	37, // [37:37] is the sub-list for method input_type
+	37, // [37:37] is the sub-list for extension type_name
+	0,  // [0:37] is the sub-list for extension extendee
 	0,  // [0:0] is the sub-list for field type_name
 }
 
@@ -513,7 +629,7 @@ func file_api_proto_init() {
 			RawDescriptor: file_api_proto_rawDesc,
 			NumEnums:      0,
 			NumMessages:   0,
-			NumExtensions: 30,
+			NumExtensions: 37,
 			NumServices:   0,
 		},
 		GoTypes:           file_api_proto_goTypes,
diff --git a/cmd/hz/protobuf/api/api.proto b/cmd/hz/protobuf/api/api.proto
index 5a5cd08..a41e2f8 100644
--- a/cmd/hz/protobuf/api/api.proto
+++ b/cmd/hz/protobuf/api/api.proto
@@ -15,9 +15,17 @@ extend google.protobuf.FieldOptions {
   optional string path = 50106;
   optional string vd = 50107;
   optional string form = 50108;
-  optional string go_tag = 51001;
   optional string js_conv = 50109;
   optional string file_name = 50110;
+  optional string none = 50111;
+
+  // 50131~50160 used to extend field option by hz
+  optional string form_compatible = 50131;
+  optional string js_conv_compatible = 50132;
+  optional string file_name_compatible = 50133;
+  optional string none_compatible = 50134;
+
+  optional string go_tag = 51001;
 }
 
 extend google.protobuf.MethodOptions {
@@ -38,12 +46,20 @@ extend google.protobuf.MethodOptions {
   optional string param = 50307; // Whether client requests take public parameters
   optional string baseurl = 50308; // Baseurl used in ttnet routing
   optional string handler_path = 50309; // handler_path specifies the path to generate the method
+
+  // 50331~50360 used to extend method option by hz
+  optional string handler_path_compatible = 50331; // handler_path specifies the path to generate the method
 }
 
 extend google.protobuf.EnumValueOptions {
   optional int32 http_code = 50401;
+
+  // 50431~50460 used to extend enum option by hz
 }
 
 extend google.protobuf.ServiceOptions {
   optional string base_domain = 50402;
+
+  // 50731~50760 used to extend service option by hz
+  optional string base_domain_compatible = 50731;
 }
\ No newline at end of file
diff --git a/cmd/hz/protobuf/ast.go b/cmd/hz/protobuf/ast.go
index 0e2803e..c86d21c 100644
--- a/cmd/hz/protobuf/ast.go
+++ b/cmd/hz/protobuf/ast.go
@@ -31,6 +31,7 @@ import (
 	"google.golang.org/protobuf/compiler/protogen"
 	"google.golang.org/protobuf/proto"
 	"google.golang.org/protobuf/reflect/protoreflect"
+	"google.golang.org/protobuf/runtime/protoimpl"
 	"google.golang.org/protobuf/types/descriptorpb"
 )
 
@@ -117,7 +118,7 @@ func astToService(ast *descriptorpb.FileDescriptorProto, resolver *Resolver, cmd
 		}
 
 		service.BaseDomain = ""
-		domainAnno := checkFirstOption(api.E_BaseDomain, s.GetOptions())
+		domainAnno := getCompatibleAnnotation(s.GetOptions(), api.E_BaseDomain, api.E_BaseDomainCompatible)
 		if cmdType == meta.CmdClient {
 			val, ok := domainAnno.(string)
 			if ok && len(val) != 0 {
@@ -136,7 +137,7 @@ func astToService(ast *descriptorpb.FileDescriptorProto, resolver *Resolver, cmd
 			path := vpath.(string)
 
 			var handlerOutDir string
-			genPath := checkFirstOption(api.E_HandlerPath, m.GetOptions())
+			genPath := getCompatibleAnnotation(m.GetOptions(), api.E_HandlerPath, api.E_HandlerPathCompatible)
 			handlerOutDir, ok := genPath.(string)
 			if !ok || len(handlerOutDir) == 0 {
 				handlerOutDir = ""
@@ -234,6 +235,16 @@ func astToService(ast *descriptorpb.FileDescriptorProto, resolver *Resolver, cmd
 	return out, nil
 }
 
+func getCompatibleAnnotation(options proto.Message, anno *protoimpl.ExtensionInfo, compatibleAnno *protoimpl.ExtensionInfo) interface{} {
+	if proto.HasExtension(options, anno) {
+		return checkFirstOption(anno, options)
+	} else if proto.HasExtension(options, compatibleAnno) {
+		return checkFirstOption(compatibleAnno, options)
+	}
+
+	return nil
+}
+
 func parseAnnotationToClient(clientMethod *generator.ClientMethod, gen *protogen.Plugin, ast *descriptorpb.FileDescriptorProto, m *descriptorpb.MethodDescriptorProto) error {
 	file, exist := gen.FilesByPath[ast.GetName()]
 	if !exist {
@@ -284,9 +295,8 @@ func parseAnnotationToClient(clientMethod *generator.ClientMethod, gen *protogen
 			}
 		}
 
-		if proto.HasExtension(f.Desc.Options(), api.E_Form) {
+		if formAnnos := getCompatibleAnnotation(f.Desc.Options(), api.E_Form, api.E_FormCompatible); formAnnos != nil {
 			hasAnnotation = true
-			formAnnos := proto.GetExtension(f.Desc.Options(), api.E_Form)
 			hasFormAnnotation = true
 			val := formAnnos.(string)
 			if isStringFieldType {
@@ -301,9 +311,8 @@ func parseAnnotationToClient(clientMethod *generator.ClientMethod, gen *protogen
 			hasBodyAnnotation = true
 		}
 
-		if proto.HasExtension(f.Desc.Options(), api.E_FileName) {
+		if fileAnnos := getCompatibleAnnotation(f.Desc.Options(), api.E_FileName, api.E_FileNameCompatible); fileAnnos != nil {
 			hasAnnotation = true
-			fileAnnos := proto.GetExtension(f.Desc.Options(), api.E_FileName)
 			hasFormAnnotation = true
 			val := fileAnnos.(string)
 			clientMethod.FormFileCode += fmt.Sprintf("%q: req.Get%s(),\n", val, f.GoName)
diff --git a/cmd/hz/protobuf/tags.go b/cmd/hz/protobuf/tags.go
index 30ba1b8..23c0464 100644
--- a/cmd/hz/protobuf/tags.go
+++ b/cmd/hz/protobuf/tags.go
@@ -84,8 +84,9 @@ var (
 		api.E_Cookie: "cookie",
 		api.E_Body:   "json",
 		// Do not change the relative order of "api.E_Form" and "api.E_Body", so that "api.E_Form" can overwrite the form tag generated by "api.E_Body"
-		api.E_Form:    "form",
-		api.E_RawBody: "raw_body",
+		api.E_Form:           "form",
+		api.E_FormCompatible: "form",
+		api.E_RawBody:        "raw_body",
 	}
 
 	ValidatorTags = map[*protoimpl.ExtensionInfo]string{api.E_Vd: "vd"}
@@ -165,6 +166,8 @@ func jsonTag(f *descriptorpb.FieldDescriptorProto) (ret model.Tag) {
 	ret.Value = checkSnakeName(f.GetJsonName())
 	if v := checkFirstOption(api.E_JsConv, f.GetOptions()); v != nil {
 		ret.Value += ",string"
+	} else if v := checkFirstOption(api.E_JsConvCompatible, f.GetOptions()); v != nil {
+		ret.Value += ",string"
 	}
 	if !unsetOmitempty && f.GetLabel() == descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL {
 		ret.Value += ",omitempty"
@@ -234,6 +237,8 @@ func injectTagsToModel(f *descriptorpb.FieldDescriptorProto, gf *model.Field, ne
 func getJsonValue(f *descriptorpb.FieldDescriptorProto, val string) string {
 	if v := checkFirstOption(api.E_JsConv, f.GetOptions()); v != nil {
 		val += ",string"
+	} else if v := checkFirstOption(api.E_JsConvCompatible, f.GetOptions()); v != nil {
+		val += ",string"
 	}
 	if !unsetOmitempty && f.GetLabel() == descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL {
 		val += ",omitempty"
@@ -285,11 +290,13 @@ func defaultBindingStructTags(f protoreflect.FieldDescriptor) []model.Tag {
 		api.E_Path,
 		api.E_Query,
 		api.E_Form,
+		api.E_FormCompatible,
 		api.E_Header,
 		api.E_Cookie,
 		api.E_Body,
 		api.E_RawBody,
 	}
+	// If the user provides an annotation, return json tag directly
 	for _, tag := range bindingTags {
 		if vv := checkFirstOption(tag, opts); vv != nil {
 			out[0] = reflectJsonTag(f)
@@ -314,8 +321,13 @@ func defaultBindingStructTags(f protoreflect.FieldDescriptor) []model.Tag {
 		val := checkStructRequire(f, v.(string))
 		out[2] = tag(BindingTags[api.E_Form], val)
 	} else {
-		val := checkStructRequire(f, checkSnakeName(string(f.Name())))
-		out[2] = tag(BindingTags[api.E_Form], val)
+		if v := checkFirstOption(api.E_FormCompatible, opts); v != nil { // compatible form_compatible
+			val := checkStructRequire(f, v.(string))
+			out[2] = tag(BindingTags[api.E_Form], val)
+		} else {
+			val := checkStructRequire(f, checkSnakeName(string(f.Name())))
+			out[2] = tag(BindingTags[api.E_Form], val)
+		}
 	}
 	return out
 }
@@ -370,11 +382,24 @@ func injectTagsToStructTags(f protoreflect.FieldDescriptor, out *structTags, nee
 			})
 		}
 	}
-
+	disableTag := false
+	if vv := checkFirstOption(api.E_None, as); vv != nil {
+		if strings.EqualFold(vv.(string), "true") {
+			disableTag = true
+		}
+	} else if vv := checkFirstOption(api.E_NoneCompatible, as); vv != nil {
+		if strings.EqualFold(vv.(string), "true") {
+			disableTag = true
+		}
+	}
 	// protobuf tag as first
 	sort.Sort(tags[1:])
 	for _, t := range tags {
-		*out = append(*out, m2s(t))
+		if disableTag {
+			*out = append(*out, [2]string{t.Key, "-"})
+		} else {
+			*out = append(*out, m2s(t))
+		}
 	}
 	return nil
 }
@@ -382,6 +407,8 @@ func injectTagsToStructTags(f protoreflect.FieldDescriptor, out *structTags, nee
 func getStructJsonValue(f protoreflect.FieldDescriptor, val string) string {
 	if v := checkFirstOption(api.E_JsConv, f.Options()); v != nil {
 		val += ",string"
+	} else if v := checkFirstOption(api.E_JsConvCompatible, f.Options()); v != nil {
+		val += ",string"
 	}
 
 	if descriptorpb.FieldDescriptorProto_Label(f.Cardinality()) == descriptorpb.FieldDescriptorProto_LABEL_REQUIRED {
diff --git a/cmd/hz/thrift/plugin.go b/cmd/hz/thrift/plugin.go
index b290398..99d80b4 100644
--- a/cmd/hz/thrift/plugin.go
+++ b/cmd/hz/thrift/plugin.go
@@ -33,6 +33,7 @@ import (
 	"github.com/cloudwego/thriftgo/generator/backend"
 	"github.com/cloudwego/thriftgo/generator/golang"
 	"github.com/cloudwego/thriftgo/generator/golang/styles"
+	"github.com/cloudwego/thriftgo/parser"
 	thriftgo_plugin "github.com/cloudwego/thriftgo/plugin"
 )
 
@@ -333,22 +334,10 @@ func (plugin *Plugin) InsertTag() ([]*thriftgo_plugin.Generated, error) {
 			stName := st.GetName()
 			for _, f := range st.Fields {
 				fieldName := f.GetName()
-				field := model.Field{}
-				err := injectTags(f, &field, true, false)
+				tagString, err := getTagString(f)
 				if err != nil {
 					return nil, err
 				}
-				tags := field.Tags
-				var tagString string
-				for idx, tag := range tags {
-					if idx == 0 {
-						tagString += " " + tag.Key + ":\"" + tag.Value + ":\"" + " "
-					} else if idx == len(tags)-1 {
-						tagString += tag.Key + ":\"" + tag.Value + ":\""
-					} else {
-						tagString += tag.Key + ":\"" + tag.Value + ":\"" + " "
-					}
-				}
 				insertPointer := "struct." + stName + "." + fieldName + "." + "tag"
 				gen := &thriftgo_plugin.Generated{
 					Content:        tagString,
@@ -371,22 +360,10 @@ func (plugin *Plugin) InsertTag() ([]*thriftgo_plugin.Generated, error) {
 			stName := st.GetName()
 			for _, f := range st.Fields {
 				fieldName := f.GetName()
-				field := model.Field{}
-				err := injectTags(f, &field, true, false)
+				tagString, err := getTagString(f)
 				if err != nil {
 					return nil, err
 				}
-				tags := field.Tags
-				var tagString string
-				for idx, tag := range tags {
-					if idx == 0 {
-						tagString += " " + tag.Key + ":\"" + tag.Value + "\"" + " "
-					} else if idx == len(tags)-1 {
-						tagString += tag.Key + ":\"" + tag.Value + "\""
-					} else {
-						tagString += tag.Key + ":\"" + tag.Value + "\"" + " "
-					}
-				}
 				insertPointer := "struct." + stName + "." + fieldName + "." + "tag"
 				gen := &thriftgo_plugin.Generated{
 					Content:        tagString,
@@ -422,3 +399,34 @@ func (plugin *Plugin) GetResponse(files []generator.File, outputDir string) (*th
 		Contents: contents,
 	}, nil
 }
+
+func getTagString(f *parser.Field) (string, error) {
+	field := model.Field{}
+	err := injectTags(f, &field, true, false)
+	if err != nil {
+		return "", err
+	}
+	disableTag := false
+	if v := getAnnotation(f.Annotations, AnnotationNone); len(v) > 0 {
+		if strings.EqualFold(v[0], "true") {
+			disableTag = true
+		}
+	}
+	var tagString string
+	tags := field.Tags
+	for idx, tag := range tags {
+		value := tag.Value
+		if disableTag {
+			value = "-"
+		}
+		if idx == 0 {
+			tagString += " " + tag.Key + ":\"" + value + "\"" + " "
+		} else if idx == len(tags)-1 {
+			tagString += tag.Key + ":\"" + value + "\""
+		} else {
+			tagString += tag.Key + ":\"" + value + "\"" + " "
+		}
+	}
+
+	return tagString, nil
+}
diff --git a/cmd/hz/thrift/resolver.go b/cmd/hz/thrift/resolver.go
index 0bcb4e3..118d0fa 100644
--- a/cmd/hz/thrift/resolver.go
+++ b/cmd/hz/thrift/resolver.go
@@ -283,6 +283,14 @@ func (resolver *Resolver) ResolveIdentifier(id string) (ret ResolvedSymbol, err
 }
 
 func (resolver *Resolver) ResolveTypeName(typ *parser.Type) (string, error) {
+	if typ.GetIsTypedef() {
+		rt, err := resolver.ResolveIdentifier(typ.GetName())
+		if err != nil {
+			return "", err
+		}
+
+		return rt.Expression(), nil
+	}
 	switch typ.GetCategory() {
 	case parser.Category_Map:
 		keyType, err := resolver.ResolveTypeName(typ.GetKeyType())
diff --git a/go.mod b/go.mod
index 79b5fab..78039a7 100644
--- a/go.mod
+++ b/go.mod
@@ -7,7 +7,7 @@ require (
 	github.com/bytedance/gopkg v0.0.0-20220413063733-65bf48ffb3a7
 	github.com/bytedance/mockey v1.2.1
 	github.com/bytedance/sonic v1.8.1
-	github.com/cloudwego/netpoll v0.3.1
+	github.com/cloudwego/netpoll v0.3.2
 	github.com/fsnotify/fsnotify v1.5.4
 	github.com/tidwall/gjson v1.13.0 // indirect
 	golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
diff --git a/go.sum b/go.sum
index f9ea5da..fe32d2b 100644
--- a/go.sum
+++ b/go.sum
@@ -10,8 +10,8 @@ github.com/bytedance/sonic v1.8.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZX
 github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
 github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
 github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
-github.com/cloudwego/netpoll v0.3.1 h1:xByoORmCLIyKZ8gS+da06WDo3j+jvmhaqS2KeKejtBk=
-github.com/cloudwego/netpoll v0.3.1/go.mod h1:1T2WVuQ+MQw6h6DpE45MohSvDTKdy2DlzCx2KsnPI4E=
+github.com/cloudwego/netpoll v0.3.2 h1:/998ICrNMVBo4mlul4j7qcIeY7QnEfuCCPPwck9S3X4=
+github.com/cloudwego/netpoll v0.3.2/go.mod h1:xVefXptcyheopwNDZjDPcfU6kIjZXZ4nY550k1yH9eQ=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
diff --git a/pkg/common/hlog/consts.go b/pkg/common/hlog/consts.go
new file mode 100644
index 0000000..e0d31bb
--- /dev/null
+++ b/pkg/common/hlog/consts.go
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2023 CloudWeGo Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package hlog
+
+const (
+	systemLogPrefix = "HERTZ: "
+
+	EngineErrorFormat = "Error=%s, remoteAddr=%s"
+)
diff --git a/pkg/common/hlog/hlog.go b/pkg/common/hlog/hlog.go
index 11a8161..7f73aaa 100644
--- a/pkg/common/hlog/hlog.go
+++ b/pkg/common/hlog/hlog.go
@@ -22,10 +22,6 @@ import (
 	"os"
 )
 
-const (
-	systemLogPrefix = "HERTZ: "
-)
-
 var (
 	// Provide default logger for users to use
 	logger FullLogger = &defaultLogger{
diff --git a/pkg/common/hlog/system.go b/pkg/common/hlog/system.go
index cb8a145..6d2b86b 100644
--- a/pkg/common/hlog/system.go
+++ b/pkg/common/hlog/system.go
@@ -23,6 +23,15 @@ import (
 	"sync"
 )
 
+var silentMode = false
+
+// SetSilentMode is used to mute engine error log,
+// for example: error when reading request headers.
+// If true, hertz engine will mute it.
+func SetSilentMode(s bool) {
+	silentMode = s
+}
+
 var builderPool = sync.Pool{New: func() interface{} {
 	return &strings.Builder{} // nolint:SA6002
 }}
@@ -80,6 +89,9 @@ func (ll *systemLogger) Fatalf(format string, v ...interface{}) {
 }
 
 func (ll *systemLogger) Errorf(format string, v ...interface{}) {
+	if silentMode && format == EngineErrorFormat {
+		return
+	}
 	ll.logger.Errorf(ll.addPrefix(format), v...)
 }
 
diff --git a/pkg/network/netpoll/transport.go b/pkg/network/netpoll/transport.go
index 12e54a9..17829cb 100644
--- a/pkg/network/netpoll/transport.go
+++ b/pkg/network/netpoll/transport.go
@@ -20,6 +20,7 @@ package netpoll
 
 import (
 	"context"
+	"io"
 	"net"
 	"sync"
 	"time"
@@ -30,6 +31,11 @@ import (
 	"github.com/cloudwego/netpoll"
 )
 
+func init() {
+	// disable netpoll's log
+	netpoll.SetLoggerOutput(io.Discard)
+}
+
 type transporter struct {
 	sync.RWMutex
 	network          string
diff --git a/pkg/protocol/header.go b/pkg/protocol/header.go
index 0e1d48a..b914057 100644
--- a/pkg/protocol/header.go
+++ b/pkg/protocol/header.go
@@ -66,8 +66,6 @@ type RequestHeader struct {
 	noCopy nocopy.NoCopy //lint:ignore U1000 until noCopy is used
 
 	disableNormalizing   bool
-	noHTTP11             bool
-	protocol             string
 	connectionClose      bool
 	noDefaultContentType bool
 
@@ -82,8 +80,10 @@ type RequestHeader struct {
 	requestURI  []byte
 	host        []byte
 	contentType []byte
-	userAgent   []byte
-	mulHeader   [][]byte
+
+	userAgent []byte
+	mulHeader [][]byte
+	protocol  string
 
 	h       []argsKV
 	bufKV   argsKV
@@ -111,7 +111,6 @@ type ResponseHeader struct {
 	noCopy nocopy.NoCopy //lint:ignore U1000 until noCopy is used
 
 	disableNormalizing   bool
-	noHTTP11             bool
 	connectionClose      bool
 	noDefaultContentType bool
 	noDefaultDate        bool
@@ -124,6 +123,7 @@ type ResponseHeader struct {
 	contentType []byte
 	server      []byte
 	mulHeader   [][]byte
+	protocol    string
 
 	h       []argsKV
 	bufKV   argsKV
@@ -173,8 +173,17 @@ func (h *ResponseHeader) PeekArgBytes(key []byte) []byte {
 	return peekArgBytes(h.h, key)
 }
 
+// Deprecated: Use ResponseHeader.SetProtocol(consts.HTTP11) instead
+//
+//	Now SetNoHTTP11(true) equal to SetProtocol(consts.HTTP10)
+//		SetNoHTTP11(false) equal to SetProtocol(consts.HTTP11)
 func (h *ResponseHeader) SetNoHTTP11(b bool) {
-	h.noHTTP11 = b
+	if b {
+		h.protocol = consts.HTTP10
+		return
+	}
+
+	h.protocol = consts.HTTP11
 }
 
 // Cookie fills cookie for the given cookie.Key.
@@ -196,7 +205,7 @@ func (h *ResponseHeader) FullCookie() []byte {
 
 // IsHTTP11 returns true if the response is HTTP/1.1.
 func (h *ResponseHeader) IsHTTP11() bool {
-	return !h.noHTTP11
+	return h.protocol == consts.HTTP11
 }
 
 // SetContentType sets Content-Type header value.
@@ -222,7 +231,6 @@ func (h *ResponseHeader) CopyTo(dst *ResponseHeader) {
 	dst.Reset()
 
 	dst.disableNormalizing = h.disableNormalizing
-	dst.noHTTP11 = h.noHTTP11
 	dst.connectionClose = h.connectionClose
 	dst.noDefaultContentType = h.noDefaultContentType
 	dst.noDefaultDate = h.noDefaultDate
@@ -289,7 +297,7 @@ func (h *ResponseHeader) VisitAll(f func(key, value []byte)) {
 
 // IsHTTP11 returns true if the request is HTTP/1.1.
 func (h *RequestHeader) IsHTTP11() bool {
-	return !h.noHTTP11
+	return h.protocol == consts.HTTP11
 }
 
 func (h *RequestHeader) SetProtocol(p string) {
@@ -300,8 +308,17 @@ func (h *RequestHeader) GetProtocol() string {
 	return h.protocol
 }
 
+// Deprecated: Use RequestHeader.SetProtocol(consts.HTTP11) instead
+//
+//	Now SetNoHTTP11(true) equal to SetProtocol(consts.HTTP10)
+//		SetNoHTTP11(false) equal to SetProtocol(consts.HTTP11)
 func (h *RequestHeader) SetNoHTTP11(b bool) {
-	h.noHTTP11 = b
+	if b {
+		h.protocol = consts.HTTP10
+		return
+	}
+
+	h.protocol = consts.HTTP11
 }
 
 func (h *RequestHeader) InitBufValue(size int) {
@@ -486,7 +503,7 @@ func checkWriteHeaderCode(code int) {
 }
 
 func (h *ResponseHeader) ResetSkipNormalize() {
-	h.noHTTP11 = false
+	h.protocol = ""
 	h.connectionClose = false
 
 	h.statusCode = 0
@@ -1075,7 +1092,6 @@ func (h *RequestHeader) CopyTo(dst *RequestHeader) {
 	dst.Reset()
 
 	dst.disableNormalizing = h.disableNormalizing
-	dst.noHTTP11 = h.noHTTP11
 	dst.connectionClose = h.connectionClose
 	dst.noDefaultContentType = h.noDefaultContentType
 
@@ -1395,7 +1411,6 @@ func (h *RequestHeader) SetCanonical(key, value []byte) {
 }
 
 func (h *RequestHeader) ResetSkipNormalize() {
-	h.noHTTP11 = false
 	h.connectionClose = false
 	h.protocol = ""
 	h.noDefaultContentType = false
@@ -1803,3 +1818,11 @@ func (h *RequestHeader) Trailer() *Trailer {
 	}
 	return h.trailer
 }
+
+func (h *ResponseHeader) SetProtocol(p string) {
+	h.protocol = p
+}
+
+func (h *ResponseHeader) GetProtocol() string {
+	return h.protocol
+}
diff --git a/pkg/protocol/http1/req/header.go b/pkg/protocol/http1/req/header.go
index da7e729..4d571a3 100644
--- a/pkg/protocol/http1/req/header.go
+++ b/pkg/protocol/http1/req/header.go
@@ -153,13 +153,11 @@ func parseFirstLine(h *protocol.RequestHeader, buf []byte) (int, error) {
 	// parse requestURI
 	n = bytes.LastIndexByte(b, ' ')
 	if n < 0 {
-		h.SetNoHTTP11(true)
 		h.SetProtocol(consts.HTTP10)
 		n = len(b)
 	} else if n == 0 {
 		return 0, fmt.Errorf("requestURI cannot be empty in %q", buf)
 	} else if !bytes.Equal(b[n+1:], bytestr.StrHTTP11) {
-		h.SetNoHTTP11(true)
 		h.SetProtocol(consts.HTTP10)
 	}
 	h.SetRequestURIBytes(b[:n])
diff --git a/pkg/protocol/http1/resp/header.go b/pkg/protocol/http1/resp/header.go
index 622c72b..18a0f8a 100644
--- a/pkg/protocol/http1/resp/header.go
+++ b/pkg/protocol/http1/resp/header.go
@@ -239,7 +239,14 @@ func parseFirstLine(h *protocol.ResponseHeader, buf []byte) (int, error) {
 	if n < 0 {
 		return 0, fmt.Errorf("cannot find whitespace in the first line of response %q", buf)
 	}
-	h.SetNoHTTP11(!bytes.Equal(b[:n], bytestr.StrHTTP11))
+
+	isHTTP11 := bytes.Equal(b[:n], bytestr.StrHTTP11)
+	if !isHTTP11 {
+		h.SetProtocol(consts.HTTP10)
+	} else {
+		h.SetProtocol(consts.HTTP11)
+	}
+
 	b = b[n+1:]
 
 	// parse status code
diff --git a/pkg/protocol/multipart.go b/pkg/protocol/multipart.go
index 4290491..e926c94 100644
--- a/pkg/protocol/multipart.go
+++ b/pkg/protocol/multipart.go
@@ -137,11 +137,11 @@ func WriteMultipartFormFile(w *multipart.Writer, fieldName, fileName string, r i
 	// Auto detect actual multipart content type
 	cbuf := make([]byte, 512)
 	size, err := r.Read(cbuf)
-	if err != nil {
+	if err != nil && err != io.EOF {
 		return err
 	}
 
-	partWriter, err := w.CreatePart(CreateMultipartHeader(fieldName, fileName, http.DetectContentType(cbuf)))
+	partWriter, err := w.CreatePart(CreateMultipartHeader(fieldName, fileName, http.DetectContentType(cbuf[:size])))
 	if err != nil {
 		return err
 	}
diff --git a/pkg/route/engine.go b/pkg/route/engine.go
index 8a9a00b..10cffac 100644
--- a/pkg/route/engine.go
+++ b/pkg/route/engine.go
@@ -459,7 +459,7 @@ func errProcess(conn io.Closer, err error) {
 		}
 	}
 	// other errors
-	hlog.SystemLogger().Errorf("Error=%s, remoteAddr=%s", err.Error(), rip)
+	hlog.SystemLogger().Errorf(hlog.EngineErrorFormat, err.Error(), rip)
 }
 
 func getRemoteAddrFromCloser(conn io.Closer) string {
diff --git a/version.go b/version.go
index 208f61c..5428b83 100644
--- a/version.go
+++ b/version.go
@@ -19,5 +19,5 @@ package hertz
 // Name and Version info of this framework, used for statistics and debug
 const (
 	Name    = "Hertz"
-	Version = "v0.6.2"
+	Version = "v0.6.3"
 )


Test patch:
diff --git a/cmd/hz/test_hz_windows.sh b/cmd/hz/test_hz_windows.sh
index b421bab..2f7aad0 100644
--- a/cmd/hz/test_hz_windows.sh
+++ b/cmd/hz/test_hz_windows.sh
@@ -18,7 +18,7 @@ judge_exit() {
 }
 
 compile_hz() {
-  go build -o hz
+  go install .
   judge_exit "$?"
 }
 
@@ -31,15 +31,15 @@ test_thrift() {
   # test thrift
   mkdir -p test
   cd test
-  ../hz new --idl=$thriftIDL --mod=$moduleName -f --model_dir=hertz_model --handler_dir=hertz_handler --router_dir=hertz_router
+  hz new --idl=$thriftIDL --mod=$moduleName -f --model_dir=hertz_model --handler_dir=hertz_handler --router_dir=hertz_router
   judge_exit "$?"
   go mod tidy && go build .
   judge_exit "$?"
-  ../hz update --idl=$thriftIDL
+  hz update --idl=$thriftIDL
   judge_exit "$?"
-  ../hz model --idl=$thriftIDL --model_dir=hertz_model
+  hz model --idl=$thriftIDL --model_dir=hertz_model
   judge_exit "$?"
-  ../hz client --idl=$thriftIDL --client_dir=hertz_client
+  hz client --idl=$thriftIDL --client_dir=hertz_client
   judge_exit "$?"
   cd ..
   rm -rf test
@@ -49,15 +49,15 @@ test_protobuf2() {
   # test protobuf2
   mkdir -p test
   cd test
-  ../hz new -I=$protoSearch -I=$proto2Search --idl=$protobuf2IDL --mod=$moduleName -f --model_dir=hertz_model --handler_dir=hertz_handler --router_dir=hertz_router
+  hz new -I=$protoSearch -I=$proto2Search --idl=$protobuf2IDL --mod=$moduleName -f --model_dir=hertz_model --handler_dir=hertz_handler --router_dir=hertz_router
   judge_exit "$?"
   go mod tidy && go build .
   judge_exit "$?"
-  ../hz update -I=$protoSearch -I=$proto2Search --idl=$protobuf2IDL
+  hz update -I=$protoSearch -I=$proto2Search --idl=$protobuf2IDL
   judge_exit "$?"
-  ../hz model -I=$protoSearch -I=$proto2Search --idl=$protobuf2IDL --model_dir=hertz_model
+  hz model -I=$protoSearch -I=$proto2Search --idl=$protobuf2IDL --model_dir=hertz_model
   judge_exit "$?"
-  ../hz client -I=$protoSearch -I=$proto2Search --idl=$protobuf2IDL --client_dir=hertz_client
+  hz client -I=$protoSearch -I=$proto2Search --idl=$protobuf2IDL --client_dir=hertz_client
   judge_exit "$?"
   cd ..
   rm -rf test
@@ -67,15 +67,15 @@ test_protobuf3() {
   # test protobuf2
   mkdir -p test
   cd test
-  ../hz new -I=$protoSearch -I=$proto3Search --idl=$protobuf3IDL --mod=$moduleName -f --model_dir=hertz_model --handler_dir=hertz_handler --router_dir=hertz_router
+  hz new -I=$protoSearch -I=$proto3Search --idl=$protobuf3IDL --mod=$moduleName -f --model_dir=hertz_model --handler_dir=hertz_handler --router_dir=hertz_router
   judge_exit "$?"
   go mod tidy && go build .
   judge_exit "$?"
-  ../hz update -I=$protoSearch -I=$proto3Search --idl=$protobuf3IDL
+  hz update -I=$protoSearch -I=$proto3Search --idl=$protobuf3IDL
   judge_exit "$?"
-  ../hz model -I=$protoSearch -I=$proto3Search --idl=$protobuf3IDL --model_dir=hertz_model
+  hz model -I=$protoSearch -I=$proto3Search --idl=$protobuf3IDL --model_dir=hertz_model
   judge_exit "$?"
-  ../hz client -I=$protoSearch -I=$proto3Search --idl=$protobuf3IDL --client_dir=hertz_client
+  hz client -I=$protoSearch -I=$proto3Search --idl=$protobuf3IDL --client_dir=hertz_client
   judge_exit "$?"
   cd ..
   rm -rf test
diff --git a/cmd/hz/testdata/protobuf2/api.proto b/cmd/hz/testdata/protobuf2/api.proto
index 15330da..9081737 100644
--- a/cmd/hz/testdata/protobuf2/api.proto
+++ b/cmd/hz/testdata/protobuf2/api.proto
@@ -15,9 +15,17 @@ extend google.protobuf.FieldOptions {
   optional string path = 50106;
   optional string vd = 50107;
   optional string form = 50108;
-  optional string go_tag = 51001;
   optional string js_conv = 50109;
   optional string file_name = 50110;
+  optional string none = 50111;
+
+  // 50131~50160 used to extend field option by hz
+  optional string form_compatible = 50131;
+  optional string js_conv_compatible = 50132;
+  optional string file_name_compatible = 50133;
+  optional string none_compatible = 50134;
+
+  optional string go_tag = 51001;
 }
 
 extend google.protobuf.MethodOptions {
@@ -29,29 +37,29 @@ extend google.protobuf.MethodOptions {
   optional string options = 50206;
   optional string head = 50207;
   optional string any = 50208;
-  optional string re_get = 50211;
-  optional string re_post = 50212;
-  optional string re_put = 50213;
-  optional string re_delete = 50214;
-  optional string re_patch = 50215;
-  optional string re_options = 50216;
-  optional string re_head = 50217;
-  optional string re_any = 50218;
-  optional string gen_path = 50301;
-  optional string api_version = 50302;
-  optional string tag = 50303;
-  optional string name = 50304;
-  optional string api_level = 50305;
-  optional string serializer = 50306;
-  optional string param = 50307;
-  optional string baseurl = 50308;
-  optional string handler_path = 50309;
+  optional string gen_path = 50301; // The path specified by the user when the client code is generated, with a higher priority than api_version
+  optional string api_version = 50302; // Specify the value of the :version variable in path when the client code is generated
+  optional string tag = 50303; // rpc tag, can be multiple, separated by commas
+  optional string name = 50304; // Name of rpc
+  optional string api_level = 50305; // Interface Level
+  optional string serializer = 50306; // Serialization method
+  optional string param = 50307; // Whether client requests take public parameters
+  optional string baseurl = 50308; // Baseurl used in ttnet routing
+  optional string handler_path = 50309; // handler_path specifies the path to generate the method
+
+  // 50331~50360 used to extend method option by hz
+  optional string handler_path_compatible = 50331; // handler_path specifies the path to generate the method
 }
 
 extend google.protobuf.EnumValueOptions {
   optional int32 http_code = 50401;
+
+  // 50431~50460 used to extend enum option by hz
 }
 
 extend google.protobuf.ServiceOptions {
   optional string base_domain = 50402;
+
+  // 50731~50760 used to extend service option by hz
+  optional string base_domain_compatible = 50731;
 }
\ No newline at end of file
diff --git a/cmd/hz/testdata/protobuf2/psm/psm.proto b/cmd/hz/testdata/protobuf2/psm/psm.proto
index 0561d60..b885366 100644
--- a/cmd/hz/testdata/protobuf2/psm/psm.proto
+++ b/cmd/hz/testdata/protobuf2/psm/psm.proto
@@ -9,8 +9,8 @@ import "base.proto";
 import "other/other.proto";
 
 enum EnumType {
-    TWEET = 0;
-    RETWEET = 1;
+  TWEET = 0;
+  RETWEET = 1;
 }
 message UnusedMessageType {
   optional string IsUnusedMessageType = 1;
@@ -74,7 +74,7 @@ message MultiTypeReq {
     // nested oneof
     oneof NestedMsgOneof {
       string IsNestedMsgOneofString = 4;
-      EnumType IsNestedMsgOneofEnumType =5;
+      EnumType IsNestedMsgOneofEnumType = 5;
     }
   }
   // nested message
@@ -92,14 +92,21 @@ message MultiTypeReq {
 }
 
 message MultiTagReq {
-  optional string QueryTag = 1 [(api.query)="query"];
-  optional string RawBodyTag = 2 [(api.raw_body)="raw_body"];
-  optional string CookieTag = 3 [(api.cookie)="cookie"];
-  optional string BodyTag = 4 [(api.body)="body"];
-  optional string PathTag = 5 [(api.path)="path"];
-  optional string VdTag = 6 [(api.vd)="$!='?'"];
-  optional string FormTag = 7 [(api.form)="form"];
-  optional string DefaultTag = 8 [(api.go_tag)="FFF:\"fff\" json:\"json\""];
+  optional string QueryTag = 1 [(api.query) = "query", (api.none) = "true"];
+  optional string RawBodyTag = 2 [(api.raw_body) = "raw_body"];
+  optional string CookieTag = 3 [(api.cookie) = "cookie"];
+  optional string BodyTag = 4 [(api.body) = "body"];
+  optional string PathTag = 5 [(api.path) = "path"];
+  optional string VdTag = 6 [(api.vd) = "$!='?'"];
+  optional string FormTag = 7 [(api.form) = "form"];
+  optional string DefaultTag = 8 [(api.go_tag) = "FFF:\"fff\" json:\"json\""];
+}
+
+message CompatibleAnnoReq {
+  optional string FormCompatibleTag = 1 [(api.form_compatible) = "form"];
+  optional string FilenameCompatibleTag = 2 [(api.file_name_compatible) = "file_name"];
+  optional string NoneCompatibleTag = 3 [(api.none_compatible) = "true"];
+  optional string JsConvCompatibleTag = 4 [(api.js_conv_compatible) = "true"];
 }
 
 message Resp {
@@ -120,29 +127,29 @@ message MultiNameStyleMessage {
 
 service Hertz {
   rpc Method1(MultiTypeReq) returns(Resp) {
-    option (api.get)="/company/department/group/user:id/name";
+    option (api.get) = "/company/department/group/user:id/name";
   }
   rpc Method2(MultiTypeReq) returns(Resp) {
-    option (api.post)="/company/department/group/user:id/sex";
+    option (api.post) = "/company/department/group/user:id/sex";
   }
   rpc Method3(MultiTypeReq) returns(Resp) {
-    option (api.put)="/company/department/group/user:id/number";
+    option (api.put) = "/company/department/group/user:id/number";
   }
   rpc Method4(MultiTypeReq) returns(Resp) {
-    option (api.delete)="/company/department/group/user:id/age";
+    option (api.delete) = "/company/department/group/user:id/age";
   }
 
 
   rpc Method5(MultiTagReq) returns(Resp) {
-    option (api.options)="/school/class/student/name";
+    option (api.options) = "/school/class/student/name";
   }
   rpc Method6(MultiTagReq) returns(Resp) {
-    option (api.head)="/school/class/student/number";
+    option (api.head) = "/school/class/student/number";
   }
   rpc Method7(MultiTagReq) returns(Resp) {
-    option (api.patch)="/school/class/student/sex";
+    option (api.patch) = "/school/class/student/sex";
   }
   rpc Method8(MultiTagReq) returns(Resp) {
-    option (api.any)="/school/class/student/grade/*subjects";
+    option (api.any) = "/school/class/student/grade/*subjects";
   }
 }
\ No newline at end of file
diff --git a/cmd/hz/testdata/protobuf3/api.proto b/cmd/hz/testdata/protobuf3/api.proto
index 15330da..9081737 100644
--- a/cmd/hz/testdata/protobuf3/api.proto
+++ b/cmd/hz/testdata/protobuf3/api.proto
@@ -15,9 +15,17 @@ extend google.protobuf.FieldOptions {
   optional string path = 50106;
   optional string vd = 50107;
   optional string form = 50108;
-  optional string go_tag = 51001;
   optional string js_conv = 50109;
   optional string file_name = 50110;
+  optional string none = 50111;
+
+  // 50131~50160 used to extend field option by hz
+  optional string form_compatible = 50131;
+  optional string js_conv_compatible = 50132;
+  optional string file_name_compatible = 50133;
+  optional string none_compatible = 50134;
+
+  optional string go_tag = 51001;
 }
 
 extend google.protobuf.MethodOptions {
@@ -29,29 +37,29 @@ extend google.protobuf.MethodOptions {
   optional string options = 50206;
   optional string head = 50207;
   optional string any = 50208;
-  optional string re_get = 50211;
-  optional string re_post = 50212;
-  optional string re_put = 50213;
-  optional string re_delete = 50214;
-  optional string re_patch = 50215;
-  optional string re_options = 50216;
-  optional string re_head = 50217;
-  optional string re_any = 50218;
-  optional string gen_path = 50301;
-  optional string api_version = 50302;
-  optional string tag = 50303;
-  optional string name = 50304;
-  optional string api_level = 50305;
-  optional string serializer = 50306;
-  optional string param = 50307;
-  optional string baseurl = 50308;
-  optional string handler_path = 50309;
+  optional string gen_path = 50301; // The path specified by the user when the client code is generated, with a higher priority than api_version
+  optional string api_version = 50302; // Specify the value of the :version variable in path when the client code is generated
+  optional string tag = 50303; // rpc tag, can be multiple, separated by commas
+  optional string name = 50304; // Name of rpc
+  optional string api_level = 50305; // Interface Level
+  optional string serializer = 50306; // Serialization method
+  optional string param = 50307; // Whether client requests take public parameters
+  optional string baseurl = 50308; // Baseurl used in ttnet routing
+  optional string handler_path = 50309; // handler_path specifies the path to generate the method
+
+  // 50331~50360 used to extend method option by hz
+  optional string handler_path_compatible = 50331; // handler_path specifies the path to generate the method
 }
 
 extend google.protobuf.EnumValueOptions {
   optional int32 http_code = 50401;
+
+  // 50431~50460 used to extend enum option by hz
 }
 
 extend google.protobuf.ServiceOptions {
   optional string base_domain = 50402;
+
+  // 50731~50760 used to extend service option by hz
+  optional string base_domain_compatible = 50731;
 }
\ No newline at end of file
diff --git a/cmd/hz/testdata/protobuf3/psm/psm.proto b/cmd/hz/testdata/protobuf3/psm/psm.proto
index 972a85c..3dfeaf5 100644
--- a/cmd/hz/testdata/protobuf3/psm/psm.proto
+++ b/cmd/hz/testdata/protobuf3/psm/psm.proto
@@ -79,7 +79,7 @@ message MultiTypeReq {
 }
 
 message MultiTagReq {
-  optional string QueryTag = 1 [(api.query)="query"];
+  optional string QueryTag = 1 [(api.query) = "query", (api.none) = "true"];
   optional string RawBodyTag = 2 [(api.raw_body)="raw_body"];
   optional string CookieTag = 3 [(api.cookie)="cookie"];
   optional string BodyTag = 4 [(api.body)="body"];
@@ -92,6 +92,13 @@ message MultiTagReq {
   }
 }
 
+message CompatibleAnnoReq {
+  optional string FormCompatibleTag = 1 [(api.form_compatible) = "form"];
+  optional string FilenameCompatibleTag = 2 [(api.file_name_compatible) = "file_name"];
+  optional string NoneCompatibleTag = 3 [(api.none_compatible) = "true"];
+  optional string JsConvCompatibleTag = 4 [(api.js_conv_compatible) = "true"];
+}
+
 message Resp {
   optional string Resp = 1;
 }
diff --git a/cmd/hz/testdata/thrift/psm.thrift b/cmd/hz/testdata/thrift/psm.thrift
index 5baedc8..816e77a 100644
--- a/cmd/hz/testdata/thrift/psm.thrift
+++ b/cmd/hz/testdata/thrift/psm.thrift
@@ -104,6 +104,8 @@ struct MultiDefaultReq {
   24: required common.CommonType IsDepCommonTypeReq = {"IsCommonString":"fffffff", "TTT":"ttt", "HHH":true, "GGG": {"AAA":"test","BBB":32}};
 }
 
+typedef map<string, string> IsTypedefContainer
+
 service Hertz {
     Resp Method1(1: MultiTypeReq request) (api.get="/company/department/group/user:id/name", api.handler_path="v1");
     Resp Method2(1: MultiTagReq request) (api.post="/company/department/group/user:id/sex", api.handler_path="v1");
@@ -114,4 +116,7 @@ service Hertz {
     Resp Method6(1: MultiTagReq request) (api.head="/school/class/student/number", api.handler_path="v2");
     Resp Method7(1: MultiTagReq request) (api.patch="/school/class/student/sex", api.handler_path="v2");
     Resp Method8(1: BaseType request) (api.any="/school/class/student/grade/*subjects", api.handler_path="v2");
+
+    Resp Method9(1: IsTypedefContainer request) (api.get="/typedef/container", api.handler_path="v2");
+    Resp Method10(1:  map<string, string> request) (api.get="/container", api.handler_path="v2");
 }
\ No newline at end of file
diff --git a/pkg/app/server/hertz_test.go b/pkg/app/server/hertz_test.go
index aef7824..b9a07fb 100644
--- a/pkg/app/server/hertz_test.go
+++ b/pkg/app/server/hertz_test.go
@@ -37,6 +37,7 @@ import (
 	"github.com/cloudwego/hertz/pkg/app/server/registry"
 	"github.com/cloudwego/hertz/pkg/common/config"
 	errs "github.com/cloudwego/hertz/pkg/common/errors"
+	"github.com/cloudwego/hertz/pkg/common/hlog"
 	"github.com/cloudwego/hertz/pkg/common/test/assert"
 	"github.com/cloudwego/hertz/pkg/common/test/mock"
 	"github.com/cloudwego/hertz/pkg/common/utils"
@@ -740,3 +741,43 @@ func TestOnprepare(t *testing.T) {
 	time.Sleep(time.Second)
 	c.Get(context.Background(), nil, "http://127.0.0.1:9231/ping")
 }
+
+type lockBuffer struct {
+	sync.Mutex
+	b bytes.Buffer
+}
+
+func (l *lockBuffer) Write(p []byte) (int, error) {
+	l.Lock()
+	defer l.Unlock()
+	return l.b.Write(p)
+}
+
+func (l *lockBuffer) String() string {
+	l.Lock()
+	defer l.Unlock()
+	return l.b.String()
+}
+
+func TestSilentMode(t *testing.T) {
+	hlog.SetSilentMode(true)
+	b := &lockBuffer{b: bytes.Buffer{}}
+
+	hlog.SetOutput(b)
+
+	h := New(WithHostPorts("localhost:9232"), WithTransport(standard.NewTransporter))
+	h.GET("/ping", func(c context.Context, ctx *app.RequestContext) {
+		ctx.Write([]byte("hello, world"))
+	})
+	go h.Spin()
+	time.Sleep(time.Second)
+
+	d := standard.NewDialer()
+	conn, _ := d.DialConnection("tcp", "127.0.0.1:9232", 0, nil)
+	conn.Write([]byte("aaa"))
+	conn.Close()
+
+	if strings.Contains(b.String(), "Error") {
+		t.Fatalf("unexpected error in log: %s", b.String())
+	}
+}
diff --git a/pkg/app/server/render/html_test.go b/pkg/app/server/render/html_test.go
index fb94458..eb8f36e 100644
--- a/pkg/app/server/render/html_test.go
+++ b/pkg/app/server/render/html_test.go
@@ -32,8 +32,8 @@ func TestHTMLDebug_StartChecker_timer(t *testing.T) {
 	}
 	render.startChecker()
 	select {
-	case <-time.After(render.RefreshInterval + 100*time.Millisecond):
-		t.Fatalf("should be triggered in 1 second")
+	case <-time.After(render.RefreshInterval + 500*time.Millisecond):
+		t.Fatalf("should be triggered in 1.5 second")
 	case <-render.reloadCh:
 	}
 }
diff --git a/pkg/network/standard/dial_test.go b/pkg/network/standard/dial_test.go
index 7d9f3a0..c7d9fa2 100644
--- a/pkg/network/standard/dial_test.go
+++ b/pkg/network/standard/dial_test.go
@@ -75,20 +75,25 @@ func TestDialTLS(t *testing.T) {
 		t.Fatalf("timeout")
 	}
 
+	buf := make([]byte, len(data))
+
 	dial := NewDialer()
-	_, err := dial.DialConnection(nw, addr, time.Second, &tls.Config{
+	conn, err := dial.DialConnection(nw, addr, time.Second, &tls.Config{
 		InsecureSkipVerify: true,
 	})
 	assert.Nil(t, err)
 
-	conn, err := dial.DialConnection(nw, addr, time.Second, nil)
+	_, err = conn.Read(buf)
+	assert.Nil(t, err)
+	assert.DeepEqual(t, string(data), string(buf))
+
+	conn, err = dial.DialConnection(nw, addr, time.Second, nil)
 	assert.Nil(t, err)
 	nConn, err := dial.AddTLS(conn, &tls.Config{
 		InsecureSkipVerify: true,
 	})
 	assert.Nil(t, err)
 
-	buf := make([]byte, len(data))
 	_, err = nConn.Read(buf)
 	assert.Nil(t, err)
 	assert.DeepEqual(t, string(data), string(buf))
diff --git a/pkg/protocol/header_test.go b/pkg/protocol/header_test.go
index a48e663..f7c8557 100644
--- a/pkg/protocol/header_test.go
+++ b/pkg/protocol/header_test.go
@@ -70,18 +70,18 @@ func TestResponseHeaderSetHeaderLength(t *testing.T) {
 func TestSetNoHTTP11(t *testing.T) {
 	rh := ResponseHeader{}
 	rh.SetNoHTTP11(true)
-	assert.True(t, rh.noHTTP11)
+	assert.DeepEqual(t, consts.HTTP10, rh.protocol)
 
 	rh.SetNoHTTP11(false)
-	assert.False(t, rh.noHTTP11)
+	assert.DeepEqual(t, consts.HTTP11, rh.protocol)
 	assert.True(t, rh.IsHTTP11())
 
 	h := RequestHeader{}
 	h.SetNoHTTP11(true)
-	assert.True(t, h.noHTTP11)
+	assert.DeepEqual(t, consts.HTTP10, h.protocol)
 
 	h.SetNoHTTP11(false)
-	assert.False(t, h.noHTTP11)
+	assert.DeepEqual(t, consts.HTTP11, h.protocol)
 	assert.True(t, h.IsHTTP11())
 }
 
diff --git a/pkg/protocol/http1/resp/response_test.go b/pkg/protocol/http1/resp/response_test.go
index af146f0..01a38c1 100644
--- a/pkg/protocol/http1/resp/response_test.go
+++ b/pkg/protocol/http1/resp/response_test.go
@@ -157,11 +157,11 @@ func testResponseReadError(t *testing.T, resp *protocol.Response, response strin
 	}
 
 	testResponseReadSuccess(t, resp, "HTTP/1.1 303 Redisred sedfs sdf\r\nContent-Type: aaa\r\nContent-Length: 5\r\n\r\nHELLOaaa",
-		consts.StatusSeeOther, 5, "aaa", "HELLO", nil)
+		consts.StatusSeeOther, 5, "aaa", "HELLO", nil, consts.HTTP11)
 }
 
 func testResponseReadSuccess(t *testing.T, resp *protocol.Response, response string, expectedStatusCode, expectedContentLength int,
-	expectedContentType, expectedBody string, expectedTrailer map[string]string,
+	expectedContentType, expectedBody string, expectedTrailer map[string]string, expectedProtocol string,
 ) {
 	zr := mock.NewZeroCopyReader(response)
 	err := Read(resp, zr)
@@ -169,7 +169,7 @@ func testResponseReadSuccess(t *testing.T, resp *protocol.Response, response str
 		t.Fatalf("Unexpected error: %s", err)
 	}
 
-	verifyResponseHeader(t, &resp.Header, expectedStatusCode, expectedContentLength, expectedContentType, "")
+	verifyResponseHeader(t, &resp.Header, expectedStatusCode, expectedContentLength, expectedContentType, "", expectedProtocol)
 	if !bytes.Equal(resp.Body(), []byte(expectedBody)) {
 		t.Fatalf("Unexpected body %q. Expected %q", resp.Body(), []byte(expectedBody))
 	}
@@ -183,46 +183,46 @@ func TestResponseReadSuccess(t *testing.T) {
 
 	// usual response
 	testResponseReadSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Length: 10\r\nContent-Type: foo/bar\r\n\r\n0123456789",
-		consts.StatusOK, 10, "foo/bar", "0123456789", nil)
+		consts.StatusOK, 10, "foo/bar", "0123456789", nil, consts.HTTP11)
 
 	// zero response
 	testResponseReadSuccess(t, resp, "HTTP/1.1 500 OK\r\nContent-Length: 0\r\nContent-Type: foo/bar\r\n\r\n",
-		consts.StatusInternalServerError, 0, "foo/bar", "", nil)
+		consts.StatusInternalServerError, 0, "foo/bar", "", nil, consts.HTTP11)
 
 	// response with trailer
 	testResponseReadSuccess(t, resp, "HTTP/1.1 300 OK\r\nTransfer-Encoding: chunked\r\nTrailer: foo\r\nContent-Type: bar\r\n\r\n5\r\n56789\r\n0\r\nfoo: bar\r\n\r\n",
-		consts.StatusMultipleChoices, 5, "bar", "56789", map[string]string{"Foo": "bar"})
+		consts.StatusMultipleChoices, 5, "bar", "56789", map[string]string{"Foo": "bar"}, consts.HTTP11)
 
 	// response with trailer disableNormalizing
 	resp.Header.DisableNormalizing()
 	resp.Header.Trailer().DisableNormalizing()
 	testResponseReadSuccess(t, resp, "HTTP/1.1 300 OK\r\nTransfer-Encoding: chunked\r\nTrailer: foo\r\nContent-Type: bar\r\n\r\n5\r\n56789\r\n0\r\nfoo: bar\r\n\r\n",
-		consts.StatusMultipleChoices, 5, "bar", "56789", map[string]string{"foo": "bar"})
+		consts.StatusMultipleChoices, 5, "bar", "56789", map[string]string{"foo": "bar"}, consts.HTTP11)
 
 	// no content-length ('identity' transfer-encoding)
 	testResponseReadSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: foobar\r\n\r\nzxxxx",
-		consts.StatusOK, 5, "foobar", "zxxxx", nil)
+		consts.StatusOK, 5, "foobar", "zxxxx", nil, consts.HTTP11)
 
 	// explicitly stated 'Transfer-Encoding: identity'
 	testResponseReadSuccess(t, resp, "HTTP/1.1 234 ss\r\nContent-Type: xxx\r\n\r\nxag",
-		234, 3, "xxx", "xag", nil)
+		234, 3, "xxx", "xag", nil, consts.HTTP11)
 
 	// big 'identity' response
 	body := string(mock.CreateFixedBody(100500))
 	testResponseReadSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: aa\r\n\r\n"+body,
-		consts.StatusOK, 100500, "aa", body, nil)
+		consts.StatusOK, 100500, "aa", body, nil, consts.HTTP11)
 
 	// chunked response
 	testResponseReadSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nTrailer: Foo2\r\nTransfer-Encoding: chunked\r\n\r\n4\r\nqwer\r\n2\r\nty\r\n0\r\nFoo2: bar2\r\n\r\n",
-		200, 6, "text/html", "qwerty", map[string]string{"Foo2": "bar2"})
+		200, 6, "text/html", "qwerty", map[string]string{"Foo2": "bar2"}, consts.HTTP11)
 
 	// chunked response with non-chunked Transfer-Encoding.
 	testResponseReadSuccess(t, resp, "HTTP/1.1 230 OK\r\nContent-Type: text\r\nTrailer: Foo3\r\nTransfer-Encoding: aaabbb\r\n\r\n2\r\ner\r\n2\r\nty\r\n0\r\nFoo3: bar3\r\n\r\n",
-		230, 4, "text", "erty", map[string]string{"Foo3": "bar3"})
+		230, 4, "text", "erty", map[string]string{"Foo3": "bar3"}, consts.HTTP11)
 
 	// chunked response with empty body
 	testResponseReadSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nTrailer: Foo5\r\nTransfer-Encoding: chunked\r\n\r\n0\r\nFoo5: bar5\r\n\r\n",
-		consts.StatusOK, 0, "text/html", "", map[string]string{"Foo5": "bar5"})
+		consts.StatusOK, 0, "text/html", "", map[string]string{"Foo5": "bar5"}, consts.HTTP11)
 }
 
 func TestResponseReadError(t *testing.T) {
@@ -416,23 +416,23 @@ func TestResponseReadWithoutBody(t *testing.T) {
 	var resp protocol.Response
 
 	testResponseReadWithoutBody(t, &resp, "HTTP/1.1 304 Not Modified\r\nContent-Type: aa\r\nContent-Encoding: gzip\r\nContent-Length: 1235\r\n\r\n", false,
-		consts.StatusNotModified, 1235, "aa", nil, "gzip")
+		consts.StatusNotModified, 1235, "aa", nil, "gzip", consts.HTTP11)
 
 	testResponseReadWithoutBody(t, &resp, "HTTP/1.1 204 Foo Bar\r\nContent-Type: aab\r\nTrailer: Foo\r\nContent-Encoding: deflate\r\nTransfer-Encoding: chunked\r\n\r\n0\r\nFoo: bar\r\n\r\n", false,
-		consts.StatusNoContent, -1, "aab", map[string]string{"Foo": "bar"}, "deflate")
+		consts.StatusNoContent, -1, "aab", map[string]string{"Foo": "bar"}, "deflate", consts.HTTP11)
 
 	testResponseReadWithoutBody(t, &resp, "HTTP/1.1 123 AAA\r\nContent-Type: xxx\r\nContent-Encoding: gzip\r\nContent-Length: 3434\r\n\r\n", false,
-		123, 3434, "xxx", nil, "gzip")
+		123, 3434, "xxx", nil, "gzip", consts.HTTP11)
 
 	testResponseReadWithoutBody(t, &resp, "HTTP 200 OK\r\nContent-Type: text/xml\r\nContent-Encoding: deflate\r\nContent-Length: 123\r\n\r\nfoobar\r\n", true,
-		consts.StatusOK, 123, "text/xml", nil, "deflate")
+		consts.StatusOK, 123, "text/xml", nil, "deflate", consts.HTTP10)
 
 	// '100 Continue' must be skipped.
 	testResponseReadWithoutBody(t, &resp, "HTTP/1.1 100 Continue\r\nFoo-bar: baz\r\n\r\nHTTP/1.1 329 aaa\r\nContent-Type: qwe\r\nContent-Encoding: gzip\r\nContent-Length: 894\r\n\r\n", true,
-		329, 894, "qwe", nil, "gzip")
+		329, 894, "qwe", nil, "gzip", consts.HTTP11)
 }
 
-func verifyResponseHeader(t *testing.T, h *protocol.ResponseHeader, expectedStatusCode, expectedContentLength int, expectedContentType, expectedContentEncoding string) {
+func verifyResponseHeader(t *testing.T, h *protocol.ResponseHeader, expectedStatusCode, expectedContentLength int, expectedContentType, expectedContentEncoding, expectedProtocol string) {
 	if h.StatusCode() != expectedStatusCode {
 		t.Fatalf("Unexpected status code %d. Expected %d", h.StatusCode(), expectedStatusCode)
 	}
@@ -445,6 +445,10 @@ func verifyResponseHeader(t *testing.T, h *protocol.ResponseHeader, expectedStat
 	if string(h.ContentEncoding()) != expectedContentEncoding {
 		t.Fatalf("Unexpected content encoding %q. Expected %q", h.ContentEncoding(), expectedContentEncoding)
 	}
+
+	if h.GetProtocol() != expectedProtocol {
+		t.Fatalf("Unexpected protocol %q. Expected %q", h.GetProtocol(), expectedProtocol)
+	}
 }
 
 func testResponseSuccess(t *testing.T, statusCode int, contentType, serverName, body string,
@@ -492,7 +496,7 @@ func testResponseSuccess(t *testing.T, statusCode int, contentType, serverName,
 }
 
 func testResponseReadWithoutBody(t *testing.T, resp *protocol.Response, s string, skipBody bool,
-	expectedStatusCode, expectedContentLength int, expectedContentType string, expectedTrailer map[string]string, expectedContentEncoding string,
+	expectedStatusCode, expectedContentLength int, expectedContentType string, expectedTrailer map[string]string, expectedContentEncoding, expectedProtocol string,
 ) {
 	zr := mock.NewZeroCopyReader(s)
 	resp.SkipBody = skipBody
@@ -504,13 +508,13 @@ func testResponseReadWithoutBody(t *testing.T, resp *protocol.Response, s string
 		t.Fatalf("Unexpected response body %q. Expected %q. response=%q", resp.Body(), "", s)
 	}
 
-	verifyResponseHeader(t, &resp.Header, expectedStatusCode, expectedContentLength, expectedContentType, expectedContentEncoding)
+	verifyResponseHeader(t, &resp.Header, expectedStatusCode, expectedContentLength, expectedContentType, expectedContentEncoding, expectedProtocol)
 	verifyResponseTrailer(t, &resp.Header, expectedTrailer)
 
 	// verify that ordinal response is read after null-body response
 	resp.SkipBody = false
 	testResponseReadSuccess(t, resp, "HTTP/1.1 300 OK\r\nContent-Length: 5\r\nContent-Type: bar\r\n\r\n56789",
-		consts.StatusMultipleChoices, 5, "bar", "56789", nil)
+		consts.StatusMultipleChoices, 5, "bar", "56789", nil, consts.HTTP11)
 }
 
 func verifyResponseTrailer(t *testing.T, h *protocol.ResponseHeader, expectedTrailers map[string]string) {
@@ -696,7 +700,7 @@ func testSetResponseBodyStreamChunked(t *testing.T, body string, trailer map[str
 }
 
 func testResponseReadBodyStreamSuccess(t *testing.T, resp *protocol.Response, response string, expectedStatusCode, expectedContentLength int,
-	expectedContentType, expectedBody string, expectedTrailer map[string]string,
+	expectedContentType, expectedBody string, expectedTrailer map[string]string, expectedProtocol string,
 ) {
 	zr := mock.NewZeroCopyReader(response)
 	err := ReadBodyStream(resp, zr, 0, nil)
@@ -709,7 +713,7 @@ func testResponseReadBodyStreamSuccess(t *testing.T, resp *protocol.Response, re
 	if err != nil && err != io.EOF {
 		t.Fatalf("Unexpected error: %s", err)
 	}
-	verifyResponseHeader(t, &resp.Header, expectedStatusCode, expectedContentLength, expectedContentType, "")
+	verifyResponseHeader(t, &resp.Header, expectedStatusCode, expectedContentLength, expectedContentType, "", expectedProtocol)
 	if !bytes.Equal(body, []byte(expectedBody)) {
 		t.Fatalf("Unexpected body %q. Expected %q", resp.Body(), []byte(expectedBody))
 	}
@@ -737,46 +741,46 @@ func TestResponseReadBodyStream(t *testing.T) {
 
 	// usual response
 	testResponseReadBodyStreamSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Length: 10\r\nContent-Type: foo/bar\r\n\r\n0123456789",
-		consts.StatusOK, 10, "foo/bar", "0123456789", nil)
+		consts.StatusOK, 10, "foo/bar", "0123456789", nil, consts.HTTP11)
 
 	// zero response
 	testResponseReadBodyStreamSuccess(t, resp, "HTTP/1.1 500 OK\r\nContent-Length: 0\r\nContent-Type: foo/bar\r\n\r\n",
-		consts.StatusInternalServerError, 0, "foo/bar", "", nil)
+		consts.StatusInternalServerError, 0, "foo/bar", "", nil, consts.HTTP11)
 
 	// response with trailer
 	testResponseReadBodyStreamSuccess(t, resp, "HTTP/1.1 300 OK\r\nTransfer-Encoding: chunked\r\nTrailer: Foo\r\nContent-Type: bar\r\n\r\n5\r\n56789\r\n0\r\nfoo: bar\r\n\r\n",
-		consts.StatusMultipleChoices, -1, "bar", "56789", map[string]string{"Foo": "bar"})
+		consts.StatusMultipleChoices, -1, "bar", "56789", map[string]string{"Foo": "bar"}, consts.HTTP11)
 
 	// response with trailer disableNormalizing
 	resp.Header.DisableNormalizing()
 	resp.Header.Trailer().DisableNormalizing()
 	testResponseReadBodyStreamSuccess(t, resp, "HTTP/1.1 300 OK\r\nTransfer-Encoding: chunked\r\nTrailer: foo\r\nContent-Type: bar\r\n\r\n5\r\n56789\r\n0\r\nfoo: bar\r\n\r\n",
-		consts.StatusMultipleChoices, -1, "bar", "56789", map[string]string{"foo": "bar"})
+		consts.StatusMultipleChoices, -1, "bar", "56789", map[string]string{"foo": "bar"}, consts.HTTP11)
 
 	// no content-length ('identity' transfer-encoding)
 	testResponseReadBodyStreamSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: foobar\r\n\r\nzxxxx",
-		consts.StatusOK, -2, "foobar", "zxxxx", nil)
+		consts.StatusOK, -2, "foobar", "zxxxx", nil, consts.HTTP11)
 
 	// explicitly stated 'Transfer-Encoding: identity'
 	testResponseReadBodyStreamSuccess(t, resp, "HTTP/1.1 234 ss\r\nContent-Type: xxx\r\n\r\nxag",
-		234, -2, "xxx", "xag", nil)
+		234, -2, "xxx", "xag", nil, consts.HTTP11)
 
 	// big 'identity' response
 	body := string(mock.CreateFixedBody(100500))
 	testResponseReadBodyStreamSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: aa\r\n\r\n"+body,
-		consts.StatusOK, -2, "aa", body, nil)
+		consts.StatusOK, -2, "aa", body, nil, consts.HTTP11)
 
 	// chunked response
 	testResponseReadBodyStreamSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nTrailer: Foo2\r\nTransfer-Encoding: chunked\r\n\r\n4\r\nqwer\r\n2\r\nty\r\n0\r\nFoo2: bar2\r\n\r\n",
-		200, -1, "text/html", "qwerty", map[string]string{"Foo2": "bar2"})
+		200, -1, "text/html", "qwerty", map[string]string{"Foo2": "bar2"}, consts.HTTP11)
 
 	// chunked response with non-chunked Transfer-Encoding.
 	testResponseReadBodyStreamSuccess(t, resp, "HTTP/1.1 230 OK\r\nContent-Type: text\r\nTrailer: Foo3\r\nTransfer-Encoding: aaabbb\r\n\r\n2\r\ner\r\n2\r\nty\r\n0\r\nFoo3: bar3\r\n\r\n",
-		230, -1, "text", "erty", map[string]string{"Foo3": "bar3"})
+		230, -1, "text", "erty", map[string]string{"Foo3": "bar3"}, consts.HTTP11)
 
 	// chunked response with empty body
 	testResponseReadBodyStreamSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nTrailer: Foo5\r\nTransfer-Encoding: chunked\r\n\r\n0\r\nFoo5: bar5\r\n\r\n",
-		consts.StatusOK, -1, "text/html", "", map[string]string{"Foo5": "bar5"})
+		consts.StatusOK, -1, "text/html", "", map[string]string{"Foo5": "bar5"}, consts.HTTP11)
 }
 
 func TestResponseReadBodyStreamBadTrailer(t *testing.T) {
diff --git a/pkg/protocol/multipart_test.go b/pkg/protocol/multipart_test.go
index be93b89..86995d2 100644
--- a/pkg/protocol/multipart_test.go
+++ b/pkg/protocol/multipart_test.go
@@ -208,6 +208,9 @@ func TestWriteMultipartFormFile(t *testing.T) {
 		t.Fatalf("write multipart error: %s", err)
 	}
 	assert.False(t, strings.Contains(bodyBuffer.String(), f3.Name()))
+
+	// test empty file
+	assert.Nil(t, WriteMultipartFormFile(w, "empty_test", "test.data", bytes.NewBuffer(nil)))
 }
 
 func TestMarshalMultipartForm(t *testing.T) {