Repository: cloudwego/hertz
Pull request #875 (2023-07-28 03:15:39)

optimize(client): do not write body in stream mode if content-length is 0
#### What type of PR is this?
optimize

#### 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.
client 写请求body的时候，如果明确content-length为0，则返回

#### (Optional) More detail description for this PR(en: English/zh: Chinese).
<!--
Provide more detailed info for review. If it is a perf type PR, perf data is suggested to give.
-->
en:
Under the standard network library, the writing logic with a content-length length of 0 is currently with no special treatment. Although it does not affect the final result, it will enter the [copyBuffer](https://github.com/golang/go/blob/8488309192b0ed4b393e2f7b2a93491139ff8ad0/src/io/io.go#L408) of the standard library logic, execute [buffer initialization logic](https://github.com/golang/go/blob/8488309192b0ed4b393e2f7b2a93491139ff8ad0/src/io/io.go#L418), this will bring unnecessary cpu/mem overhead

zh(optional):
当前未显示处理标准网络库下，对于 content-length 长度为0的写逻辑，导致虽然不影响最终结果，但是会进入标准库的[copyBuffer](https://github.com/golang/go/blob/8488309192b0ed4b393e2f7b2a93491139ff8ad0/src/io/io.go#L408)逻辑，执行[buffer初始化逻辑](https://github.com/golang/go/blob/8488309192b0ed4b393e2f7b2a93491139ff8ad0/src/io/io.go#L418)，这个会带来不必要的cpu/mem开销

#### (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/875?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego) Report
Patch coverage has no change and project coverage change: **`+0.03%`** :tada:
> Comparison is base [(`3161f04`)](https://app.codecov.io/gh/cloudwego/hertz/commit/3161f043a97d062f887fa2d8a9af9ca9bc96fc95?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego) 79.74% compared to head [(`05e9960`)](https://app.codecov.io/gh/cloudwego/hertz/pull/875?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego) 79.78%.

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


```diff
@@             Coverage Diff             @@
##           develop     #875      +/-   ##
===========================================
+ Coverage    79.74%   79.78%   +0.03%     
===========================================
  Files           97       97              
  Lines         9534     9536       +2     
===========================================
+ Hits          7603     7608       +5     
+ Misses        1432     1429       -3     
  Partials       499      499              
```


| [Files Changed](https://app.codecov.io/gh/cloudwego/hertz/pull/875?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego) | Coverage Δ | |
|---|---|---|
| [pkg/protocol/http1/ext/common.go](https://app.codecov.io/gh/cloudwego/hertz/pull/875?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego#diff-cGtnL3Byb3RvY29sL2h0dHAxL2V4dC9jb21tb24uZ28=) | `69.48% <0.00%> (-0.46%)` | :arrow_down: |

... and [2 files with indirect coverage changes](https://app.codecov.io/gh/cloudwego/hertz/pull/875/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego)


</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/cloudwego/hertz/pull/875?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/pkg/protocol/http1/ext/common.go b/pkg/protocol/http1/ext/common.go
index b31b40c..8649883 100644
--- a/pkg/protocol/http1/ext/common.go
+++ b/pkg/protocol/http1/ext/common.go
@@ -135,6 +135,9 @@ func WriteBodyChunked(w network.Writer, r io.Reader) error {
 }
 
 func WriteBodyFixedSize(w network.Writer, r io.Reader, size int64) error {
+	if size == 0 {
+		return nil
+	}
 	if size > consts.MaxSmallFileSize {
 		if err := w.Flush(); err != nil {
 			return err


Test patch:
diff --git a/pkg/common/test/mock/network.go b/pkg/common/test/mock/network.go
index 57293cb..fe671ae 100644
--- a/pkg/common/test/mock/network.go
+++ b/pkg/common/test/mock/network.go
@@ -172,7 +172,11 @@ type BrokenConn struct {
 }
 
 func (o *BrokenConn) Peek(i int) ([]byte, error) {
-	return nil, io.EOF
+	return nil, io.ErrUnexpectedEOF
+}
+
+func (o *BrokenConn) Read(b []byte) (n int, err error) {
+	return 0, io.ErrUnexpectedEOF
 }
 
 func (o *BrokenConn) Flush() error {
diff --git a/pkg/common/test/mock/network_test.go b/pkg/common/test/mock/network_test.go
index bd6ad5e..4c9c4cf 100644
--- a/pkg/common/test/mock/network_test.go
+++ b/pkg/common/test/mock/network_test.go
@@ -202,7 +202,7 @@ func TestBrokenConn_Peek(t *testing.T) {
 	conn := NewBrokenConn("Foo")
 	buf, err := conn.Peek(3)
 	assert.Nil(t, buf)
-	assert.DeepEqual(t, io.EOF, err)
+	assert.DeepEqual(t, io.ErrUnexpectedEOF, err)
 }
 
 func TestOneTimeConn_Flush(t *testing.T) {
diff --git a/pkg/protocol/http1/ext/common_test.go b/pkg/protocol/http1/ext/common_test.go
index 824ccf4..78a9fed 100644
--- a/pkg/protocol/http1/ext/common_test.go
+++ b/pkg/protocol/http1/ext/common_test.go
@@ -155,6 +155,12 @@ func TestBodyFixedSize(t *testing.T) {
 	assert.DeepEqual(t, body, rb)
 }
 
+func TestBodyFixedSizeQuickPath(t *testing.T) {
+	conn := mock.NewBrokenConn("")
+	err := WriteBodyFixedSize(conn.Writer(), conn, 0)
+	assert.Nil(t, err)
+}
+
 func TestBodyIdentity(t *testing.T) {
 	body := mock.CreateFixedBody(1024)
 	zr := mock.NewZeroCopyReader(string(body))