Repository: cloudwego/hertz
Pull request #985 (2023-11-03 05:43:00)

feat: add WithDisableDefaultDate, WithDisableDefaultContentType for server config options
#### What type of PR is this?
feat
<!--
Add one of the following kinds:

build: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
ci: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
docs: Documentation only changes
feat: A new feature
optimize: A new optimization
fix: A bug fix
perf: A code change that improves performance
refactor: A code change that neither fixes a bug nor adds a feature
style: Changes that do not affect the meaning of the code (white space, formatting, missing semi-colons, etc)
test: Adding missing tests or correcting existing tests
chore: Changes to the build process or auxiliary tools and libraries such as documentation generation
-->

#### Check the PR title.
<!--
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:
-->
- [x] This PR title match the format: \<type\>(optional scope): \<description\>
- [x] 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.


#### (Optional) More detailed description for this PR(en: English/zh: Chinese).
<!--
Provide more detailed info for review(e.g., it's recommended to provide perf data if this is a perf type PR).
-->
en:
zh(optional): 给 hertz 添加禁止响应头添加 date 字段和 content-type 字段的选项，主要用于对齐 fasthttp 和 fiber

#### (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)
-->

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/cloudwego/hertz/pull/985?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego) Report
Attention: `10 lines` in your changes are missing coverage. Please review.
> Comparison is base [(`e02c162`)](https://app.codecov.io/gh/cloudwego/hertz/commit/e02c16214bb98372093a3c7817287c208801de7f?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego) 81.86% compared to head [(`229b303`)](https://app.codecov.io/gh/cloudwego/hertz/pull/985?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego) 81.79%.

> :exclamation: Current head 229b303 differs from pull request most recent head e427551. Consider uploading reports for the commit e427551 to get more accurate results

<details><summary>Additional details and impacted files</summary>


```diff
@@             Coverage Diff             @@
##           develop     #985      +/-   ##
===========================================
- Coverage    81.86%   81.79%   -0.08%     
===========================================
  Files           98       98              
  Lines         9932     9948      +16     
===========================================
+ Hits          8131     8137       +6     
- Misses        1311     1321      +10     
  Partials       490      490              
```


| [Files](https://app.codecov.io/gh/cloudwego/hertz/pull/985?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego) | Coverage Δ | |
|---|---|---|
| [pkg/common/config/option.go](https://app.codecov.io/gh/cloudwego/hertz/pull/985?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego#diff-cGtnL2NvbW1vbi9jb25maWcvb3B0aW9uLmdv) | `100.00% <100.00%> (ø)` | |
| [pkg/protocol/http1/server.go](https://app.codecov.io/gh/cloudwego/hertz/pull/985?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego#diff-cGtnL3Byb3RvY29sL2h0dHAxL3NlcnZlci5nbw==) | `73.45% <100.00%> (+0.25%)` | :arrow_up: |
| [pkg/route/engine.go](https://app.codecov.io/gh/cloudwego/hertz/pull/985?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego#diff-cGtnL3JvdXRlL2VuZ2luZS5nbw==) | `61.79% <100.00%> (+0.14%)` | :arrow_up: |
| [pkg/protocol/header.go](https://app.codecov.io/gh/cloudwego/hertz/pull/985?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego#diff-cGtnL3Byb3RvY29sL2hlYWRlci5nbw==) | `77.81% <0.00%> (-0.17%)` | :arrow_down: |
| [pkg/app/server/option.go](https://app.codecov.io/gh/cloudwego/hertz/pull/985?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego#diff-cGtnL2FwcC9zZXJ2ZXIvb3B0aW9uLmdv) | `94.11% <0.00%> (-4.65%)` | :arrow_down: |


</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/cloudwego/hertz/pull/985?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego).


Patch:
diff --git a/cmd/hz/config/cmd.go b/cmd/hz/config/cmd.go
index 983946c..fc83c57 100644
--- a/cmd/hz/config/cmd.go
+++ b/cmd/hz/config/cmd.go
@@ -173,6 +173,7 @@ func BuildPluginCmd(args *Argument) (*exec.Cmd, error) {
 }
 
 func (arg *Argument) GetThriftgoOptions() (string, error) {
+	defaultOpt := "reserve_comments,gen_json_tag=false,"
 	prefix, err := arg.ModelPackagePrefix()
 	if err != nil {
 		return "", err
@@ -181,6 +182,6 @@ func (arg *Argument) GetThriftgoOptions() (string, error) {
 	if arg.JSONEnumStr {
 		arg.ThriftOptions = append(arg.ThriftOptions, "json_enum_as_text")
 	}
-	gas := "go:" + strings.Join(arg.ThriftOptions, ",") + ",reserve_comments,gen_json_tag=false"
+	gas := "go:" + defaultOpt + strings.Join(arg.ThriftOptions, ",")
 	return gas, nil
 }
diff --git a/pkg/app/server/option.go b/pkg/app/server/option.go
index fcf3804..e797034 100644
--- a/pkg/app/server/option.go
+++ b/pkg/app/server/option.go
@@ -382,3 +382,15 @@ func WithDisableHeaderNamesNormalizing(disable bool) config.Option {
 		o.DisableHeaderNamesNormalizing = disable
 	}}
 }
+
+func WithDisableDefaultDate(disable bool) config.Option {
+	return config.Option{F: func(o *config.Options) {
+		o.NoDefaultDate = disable
+	}}
+}
+
+func WithDisableDefaultContentType(disable bool) config.Option {
+	return config.Option{F: func(o *config.Options) {
+		o.NoDefaultContentType = disable
+	}}
+}
diff --git a/pkg/common/config/option.go b/pkg/common/config/option.go
index 048fb36..417955f 100644
--- a/pkg/common/config/option.go
+++ b/pkg/common/config/option.go
@@ -58,6 +58,8 @@ type Options struct {
 	RemoveExtraSlash             bool
 	UnescapePathValues           bool
 	DisablePreParseMultipartForm bool
+	NoDefaultDate                bool
+	NoDefaultContentType         bool
 	StreamRequestBody            bool
 	NoDefaultServerHeader        bool
 	DisablePrintRoute            bool
@@ -191,6 +193,12 @@ func NewOptions(opts []Option) *Options {
 		// like they are normal requests
 		DisablePreParseMultipartForm: false,
 
+		// When set to true, causes the default Content-Type header to be excluded from the response.
+		NoDefaultContentType: false,
+
+		// When set to true, causes the default date header to be excluded from the response.
+		NoDefaultDate: false,
+
 		// Routes info printing is not disabled by default
 		// Disabled when set to True
 		DisablePrintRoute: false,
diff --git a/pkg/protocol/header.go b/pkg/protocol/header.go
index 14df744..7edf19b 100644
--- a/pkg/protocol/header.go
+++ b/pkg/protocol/header.go
@@ -950,6 +950,11 @@ func (h *ResponseHeader) SetNoDefaultContentType(b bool) {
 	h.noDefaultContentType = b
 }
 
+// SetNoDefaultDate set noDefaultDate value of ResponseHeader.
+func (h *ResponseHeader) SetNoDefaultDate(b bool) {
+	h.noDefaultDate = b
+}
+
 // SetServerBytes sets Server header value.
 func (h *ResponseHeader) SetServerBytes(server []byte) {
 	h.server = append(h.server[:0], server...)
diff --git a/pkg/protocol/http1/server.go b/pkg/protocol/http1/server.go
index ef19cd7..c17aa0f 100644
--- a/pkg/protocol/http1/server.go
+++ b/pkg/protocol/http1/server.go
@@ -56,6 +56,8 @@ var (
 type Option struct {
 	StreamRequestBody             bool
 	GetOnly                       bool
+	NoDefaultDate                 bool
+	NoDefaultContentType          bool
 	DisablePreParseMultipartForm  bool
 	DisableKeepalive              bool
 	NoDefaultServerHeader         bool
@@ -181,6 +183,9 @@ func (s Server) Serve(c context.Context, conn network.Conn) (err error) {
 			})
 		}
 
+		ctx.Response.Header.SetNoDefaultDate(s.NoDefaultDate)
+		ctx.Response.Header.SetNoDefaultContentType(s.NoDefaultContentType)
+
 		if s.DisableHeaderNamesNormalizing {
 			ctx.Request.Header.DisableNormalizing()
 			ctx.Response.Header.DisableNormalizing()
diff --git a/pkg/route/engine.go b/pkg/route/engine.go
index 6ab2de6..8b65e6e 100644
--- a/pkg/route/engine.go
+++ b/pkg/route/engine.go
@@ -1065,6 +1065,8 @@ func newHttp1OptionFromEngine(engine *Engine) *http1.Option {
 		EnableTrace:                   engine.IsTraceEnable(),
 		HijackConnHandle:              engine.HijackConnHandle,
 		DisableHeaderNamesNormalizing: engine.options.DisableHeaderNamesNormalizing,
+		NoDefaultDate:                 engine.options.NoDefaultDate,
+		NoDefaultContentType:          engine.options.NoDefaultContentType,
 	}
 	// Idle timeout of standard network must not be zero. Set it to -1 seconds if it is zero.
 	// Due to the different triggering ways of the network library, see the actual use of this value for the detailed reasons.


Test patch:
diff --git a/pkg/app/server/hertz_test.go b/pkg/app/server/hertz_test.go
index 036df76..4a9cee3 100644
--- a/pkg/app/server/hertz_test.go
+++ b/pkg/app/server/hertz_test.go
@@ -1066,3 +1066,29 @@ func TestValidateConfigAndBindConfig(t *testing.T) {
 	assert.Nil(t, err)
 	time.Sleep(100 * time.Millisecond)
 }
+
+func TestWithDisableDefaultDate(t *testing.T) {
+	h := New(
+		WithHostPorts("localhost:8321"),
+		WithDisableDefaultDate(true),
+	)
+	h.GET("/", func(_ context.Context, c *app.RequestContext) {})
+	go h.Spin()
+	time.Sleep(100 * time.Millisecond)
+	hc := http.Client{Timeout: time.Second}
+	r, _ := hc.Get("http://127.0.0.1:8321") //nolint:errcheck
+	assert.DeepEqual(t, "", r.Header.Get("Date"))
+}
+
+func TestWithDisableDefaultContentType(t *testing.T) {
+	h := New(
+		WithHostPorts("localhost:8324"),
+		WithDisableDefaultContentType(true),
+	)
+	h.GET("/", func(_ context.Context, c *app.RequestContext) {})
+	go h.Spin()
+	time.Sleep(100 * time.Millisecond)
+	hc := http.Client{Timeout: time.Second}
+	r, _ := hc.Get("http://127.0.0.1:8324") //nolint:errcheck
+	assert.DeepEqual(t, "", r.Header.Get("Content-Type"))
+}
diff --git a/pkg/protocol/header_test.go b/pkg/protocol/header_test.go
index 8d768fd..35cc001 100644
--- a/pkg/protocol/header_test.go
+++ b/pkg/protocol/header_test.go
@@ -720,3 +720,15 @@ func TestResponseHeaderCopyTo(t *testing.T) {
 	assert.DeepEqual(t, hCopy.noDefaultContentType, true)
 	assert.DeepEqual(t, hCopy.GetHeaderLength(), 100)
 }
+
+func TestResponseHeaderDateEmpty(t *testing.T) {
+	t.Parallel()
+
+	var h ResponseHeader
+	h.noDefaultDate = true
+	headers := string(h.Header())
+
+	if strings.Contains(headers, "\r\nDate: ") {
+		t.Fatalf("ResponseDateNoDefaultNotEmpty fail, response: \n%+v\noutcome: \n%q\n", h, headers) //nolint:govet
+	}
+}