Repository: cloudwego/hertz (go)

Issue:
恳请context里面加个GetUint64(key string)方法
从gin重构的hertz，恳请在context上加个GetUint64(key string)方法

Pull request:
feat: add more metadata related APIs to RequestContext
#### What type of PR is this?
<!--
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
-->
feat: A new feature

#### 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.


#### (Optional) Translate the PR title into Chinese.

补充常用的Get***函数

#### (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: Supplement usual Get function
zh(optional): 补充常用的Get***函数

#### Which issue(s) this PR fixes:
<!--
Automatically closes linked issue when PR is merged.
Eg: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.
-->
Fixes #368


Patch:
diff --git a/README.md b/README.md
index 01f7b5a..554319c 100644
--- a/README.md
+++ b/README.md
@@ -87,6 +87,10 @@ Hertz [həːts] is a high-usability, high-performance and high-extensibility Gol
 
 ## Blogs
 - [ByteDance Practice on Go Network Library](https://www.cloudwego.io/blog/2021/10/09/bytedance-practices-on-go-network-library/)
+- [Ultra-large-scale Enterprise-level Microservice HTTP Framework — Hertz is Officially Open Source!](https://www.cloudwego.io/zh/blog/2022/06/21/%E8%B6%85%E5%A4%A7%E8%A7%84%E6%A8%A1%E7%9A%84%E4%BC%81%E4%B8%9A%E7%BA%A7%E5%BE%AE%E6%9C%8D%E5%8A%A1-http-%E6%A1%86%E6%9E%B6-hertz-%E6%AD%A3%E5%BC%8F%E5%BC%80%E6%BA%90/)
+- [ByteDance Open Source Go HTTP Framework Hertz Design Practice](https://www.cloudwego.io/zh/blog/2022/06/21/%E5%AD%97%E8%8A%82%E8%B7%B3%E5%8A%A8%E5%BC%80%E6%BA%90-go-http-%E6%A1%86%E6%9E%B6-hertz-%E8%AE%BE%E8%AE%A1%E5%AE%9E%E8%B7%B5/)
+- [Help ByteDance Reduce Costs and Increase Efficiency, the Design Practice for Large-scale Enterprise-level HTTP Framework Hertz](https://www.cloudwego.io/zh/blog/2022/09/27/%E5%8A%A9%E5%8A%9B%E5%AD%97%E8%8A%82%E9%99%8D%E6%9C%AC%E5%A2%9E%E6%95%88%E5%A4%A7%E8%A7%84%E6%A8%A1%E4%BC%81%E4%B8%9A%E7%BA%A7-http-%E6%A1%86%E6%9E%B6-hertz-%E8%AE%BE%E8%AE%A1%E5%AE%9E%E8%B7%B5/)
+- [A Practical Introduction to the HTTP Framework Hertz: A Guide to Performance Testing](https://www.cloudwego.io/zh/blog/2022/11/01/http-%E6%A1%86%E6%9E%B6-hertz-%E5%AE%9E%E8%B7%B5%E5%85%A5%E9%97%A8%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95%E6%8C%87%E5%8D%97/)
 ## Contributing
 
 [Contributing](https://github.com/cloudwego/hertz/blob/main/CONTRIBUTING.md)
diff --git a/README_cn.md b/README_cn.md
index d06866b..942b743 100644
--- a/README_cn.md
+++ b/README_cn.md
@@ -87,6 +87,10 @@ Hertz[həːts] 是一个 Golang 微服务 HTTP 框架，在设计之初参考了
 
 ## 相关文章
 - [字节跳动在 Go 网络库上的实践](https://www.cloudwego.io/blog/2021/10/09/bytedance-practices-on-go-network-library/)
+- [超大规模的企业级微服务 HTTP 框架 — Hertz 正式开源！](https://www.cloudwego.io/zh/blog/2022/06/21/%E8%B6%85%E5%A4%A7%E8%A7%84%E6%A8%A1%E7%9A%84%E4%BC%81%E4%B8%9A%E7%BA%A7%E5%BE%AE%E6%9C%8D%E5%8A%A1-http-%E6%A1%86%E6%9E%B6-hertz-%E6%AD%A3%E5%BC%8F%E5%BC%80%E6%BA%90/)
+- [字节跳动开源 Go HTTP 框架 Hertz 设计实践](https://www.cloudwego.io/zh/blog/2022/06/21/%E5%AD%97%E8%8A%82%E8%B7%B3%E5%8A%A8%E5%BC%80%E6%BA%90-go-http-%E6%A1%86%E6%9E%B6-hertz-%E8%AE%BE%E8%AE%A1%E5%AE%9E%E8%B7%B5/)
+- [助力字节降本增效，大规模企业级 HTTP 框架 Hertz 设计实践](https://www.cloudwego.io/zh/blog/2022/09/27/%E5%8A%A9%E5%8A%9B%E5%AD%97%E8%8A%82%E9%99%8D%E6%9C%AC%E5%A2%9E%E6%95%88%E5%A4%A7%E8%A7%84%E6%A8%A1%E4%BC%81%E4%B8%9A%E7%BA%A7-http-%E6%A1%86%E6%9E%B6-hertz-%E8%AE%BE%E8%AE%A1%E5%AE%9E%E8%B7%B5/)
+- [HTTP 框架 Hertz 实践入门：性能测试指南](https://www.cloudwego.io/zh/blog/2022/11/01/http-%E6%A1%86%E6%9E%B6-hertz-%E5%AE%9E%E8%B7%B5%E5%85%A5%E9%97%A8%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95%E6%8C%87%E5%8D%97/)
 ## 贡献代码
   [Contributing](https://github.com/cloudwego/hertz/blob/main/CONTRIBUTING.md)
 ## RoadMap
diff --git a/pkg/app/context.go b/pkg/app/context.go
index 5e15f9b..16797d7 100644
--- a/pkg/app/context.go
+++ b/pkg/app/context.go
@@ -727,6 +727,14 @@ func (ctx *RequestContext) GetInt(key string) (i int) {
 	return
 }
 
+// GetInt32 returns the value associated with the key as an integer. Return int32(0) when type is error.
+func (ctx *RequestContext) GetInt32(key string) (i32 int32) {
+	if val, ok := ctx.Get(key); ok && val != nil {
+		i32, _ = val.(int32)
+	}
+	return
+}
+
 // GetInt64 returns the value associated with the key as an integer. Return int64(0) when type is error.
 func (ctx *RequestContext) GetInt64(key string) (i64 int64) {
 	if val, ok := ctx.Get(key); ok && val != nil {
@@ -735,6 +743,38 @@ func (ctx *RequestContext) GetInt64(key string) (i64 int64) {
 	return
 }
 
+// GetUint returns the value associated with the key as an unsigned integer. Return uint(0) when type is error.
+func (ctx *RequestContext) GetUint(key string) (ui uint) {
+	if val, ok := ctx.Get(key); ok && val != nil {
+		ui, _ = val.(uint)
+	}
+	return
+}
+
+// GetUint32 returns the value associated with the key as an unsigned integer. Return uint32(0) when type is error.
+func (ctx *RequestContext) GetUint32(key string) (ui32 uint32) {
+	if val, ok := ctx.Get(key); ok && val != nil {
+		ui32, _ = val.(uint32)
+	}
+	return
+}
+
+// GetUint64 returns the value associated with the key as an unsigned integer. Return uint64(0) when type is error.
+func (ctx *RequestContext) GetUint64(key string) (ui64 uint64) {
+	if val, ok := ctx.Get(key); ok && val != nil {
+		ui64, _ = val.(uint64)
+	}
+	return
+}
+
+// GetFloat32 returns the value associated with the key as a float32. Return float32(0.0) when type is error.
+func (ctx *RequestContext) GetFloat32(key string) (f32 float32) {
+	if val, ok := ctx.Get(key); ok && val != nil {
+		f32, _ = val.(float32)
+	}
+	return
+}
+
 // GetFloat64 returns the value associated with the key as a float64. Return 0.0 when type is error.
 func (ctx *RequestContext) GetFloat64(key string) (f64 float64) {
 	if val, ok := ctx.Get(key); ok && val != nil {
diff --git a/pkg/protocol/http1/server.go b/pkg/protocol/http1/server.go
index b4e9b4e..8df3320 100644
--- a/pkg/protocol/http1/server.go
+++ b/pkg/protocol/http1/server.go
@@ -330,7 +330,8 @@ func (s Server) Serve(c context.Context, conn network.Conn) (err error) {
 		if connectionClose {
 			return errShortConnection
 		}
-
+		// Back to network layer to trigger.
+		// For now, only netpoll network mode has this feature.
 		if s.IdleTimeout == 0 {
 			return
 		}
diff --git a/pkg/route/engine.go b/pkg/route/engine.go
index 2d38b6a..0096eb5 100644
--- a/pkg/route/engine.go
+++ b/pkg/route/engine.go
@@ -945,7 +945,7 @@ func iterate(method string, routes RoutesInfo, root *node) RoutesInfo {
 
 // for built-in http1 impl only.
 func newHttp1OptionFromEngine(engine *Engine) *http1.Option {
-	return &http1.Option{
+	opt := &http1.Option{
 		StreamRequestBody:            engine.options.StreamRequestBody,
 		GetOnly:                      engine.options.GetOnly,
 		DisablePreParseMultipartForm: engine.options.DisablePreParseMultipartForm,
@@ -961,4 +961,10 @@ func newHttp1OptionFromEngine(engine *Engine) *http1.Option {
 		EnableTrace:                  engine.IsTraceEnable(),
 		HijackConnHandle:             engine.HijackConnHandle,
 	}
+	// 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.
+	if opt.IdleTimeout == 0 && engine.GetTransporterName() == "standard" {
+		opt.IdleTimeout = -1
+	}
+	return opt
 }


Test patch:
diff --git a/pkg/app/context_test.go b/pkg/app/context_test.go
index 138852a..2e55eeb 100644
--- a/pkg/app/context_test.go
+++ b/pkg/app/context_test.go
@@ -741,7 +741,8 @@ func TestContextSetGetValues(t *testing.T) {
 	c.Set("string", "this is a string")
 	c.Set("int32", int32(-42))
 	c.Set("int64", int64(42424242424242))
-	c.Set("uint64", uint64(42))
+	c.Set("uint32", uint32(42))
+	c.Set("uint64", uint64(42424242424242))
 	c.Set("float32", float32(4.2))
 	c.Set("float64", 4.2)
 	var a interface{} = 1
@@ -750,7 +751,8 @@ func TestContextSetGetValues(t *testing.T) {
 	assert.DeepEqual(t, c.MustGet("string").(string), "this is a string")
 	assert.DeepEqual(t, c.MustGet("int32").(int32), int32(-42))
 	assert.DeepEqual(t, c.MustGet("int64").(int64), int64(42424242424242))
-	assert.DeepEqual(t, c.MustGet("uint64").(uint64), uint64(42))
+	assert.DeepEqual(t, c.MustGet("uint32").(uint32), uint32(42))
+	assert.DeepEqual(t, c.MustGet("uint64").(uint64), uint64(42424242424242))
 	assert.DeepEqual(t, c.MustGet("float32").(float32), float32(4.2))
 	assert.DeepEqual(t, c.MustGet("float64").(float64), 4.2)
 	assert.DeepEqual(t, c.MustGet("intInterface").(int), 1)
@@ -780,6 +782,14 @@ func TestContextGetInt(t *testing.T) {
 	assert.DeepEqual(t, 0, c.GetInt("string"))
 }
 
+func TestContextGetInt32(t *testing.T) {
+	c := &RequestContext{}
+	c.Set("int32", int32(-42))
+	assert.DeepEqual(t, int32(-42), c.GetInt32("int32"))
+	c.Set("string", "this is a string")
+	assert.DeepEqual(t, int32(0), c.GetInt32("string"))
+}
+
 func TestContextGetInt64(t *testing.T) {
 	c := &RequestContext{}
 	c.Set("int64", int64(42424242424242))
@@ -788,6 +798,38 @@ func TestContextGetInt64(t *testing.T) {
 	assert.DeepEqual(t, int64(0), c.GetInt64("string"))
 }
 
+func TestContextGetUint(t *testing.T) {
+	c := &RequestContext{}
+	c.Set("uint", uint(1))
+	assert.DeepEqual(t, uint(1), c.GetUint("uint"))
+	c.Set("string", "this is a string")
+	assert.DeepEqual(t, uint(0), c.GetUint("string"))
+}
+
+func TestContextGetUint32(t *testing.T) {
+	c := &RequestContext{}
+	c.Set("uint32", uint32(42))
+	assert.DeepEqual(t, uint32(42), c.GetUint32("uint32"))
+	c.Set("string", "this is a string")
+	assert.DeepEqual(t, uint32(0), c.GetUint32("string"))
+}
+
+func TestContextGetUint64(t *testing.T) {
+	c := &RequestContext{}
+	c.Set("uint64", uint64(42424242424242))
+	assert.DeepEqual(t, uint64(42424242424242), c.GetUint64("uint64"))
+	c.Set("string", "this is a string")
+	assert.DeepEqual(t, uint64(0), c.GetUint64("string"))
+}
+
+func TestContextGetFloat32(t *testing.T) {
+	c := &RequestContext{}
+	c.Set("float32", float32(4.2))
+	assert.DeepEqual(t, float32(4.2), c.GetFloat32("float32"))
+	c.Set("string", "this is a string")
+	assert.DeepEqual(t, float32(0.0), c.GetFloat32("string"))
+}
+
 func TestContextGetFloat64(t *testing.T) {
 	c := &RequestContext{}
 	c.Set("float64", 4.2)
diff --git a/pkg/common/test/mock/network.go b/pkg/common/test/mock/network.go
index c7111b2..bb08cb4 100644
--- a/pkg/common/test/mock/network.go
+++ b/pkg/common/test/mock/network.go
@@ -75,6 +75,10 @@ func (m *Conn) Release() error {
 func (m *Conn) Peek(i int) ([]byte, error) {
 	b, err := m.zr.Peek(i)
 	if err != nil || len(b) != i {
+		if m.readTimeout <= 0 {
+			// simulate timeout forever
+			select {}
+		}
 		time.Sleep(m.readTimeout)
 		return nil, errs.ErrTimeout
 	}
diff --git a/pkg/route/engine_test.go b/pkg/route/engine_test.go
index 1d53194..379b53f 100644
--- a/pkg/route/engine_test.go
+++ b/pkg/route/engine_test.go
@@ -257,6 +257,39 @@ func TestIdleTimeout01(t *testing.T) {
 	}
 }
 
+func TestIdleTimeout03(t *testing.T) {
+	engine := NewEngine(config.NewOptions(nil))
+	engine.options.IdleTimeout = 0
+	engine.transport = standard.NewTransporter(engine.options)
+	atomic.StoreUint32(&engine.status, statusRunning)
+	engine.Init()
+	atomic.StoreUint32(&engine.status, statusRunning)
+	engine.GET("/foo", func(c context.Context, ctx *app.RequestContext) {
+		time.Sleep(50 * time.Millisecond)
+		ctx.String(200, "ok")
+	})
+
+	conn := mock.NewConn("GET /foo HTTP/1.1\r\nHost: google.com\r\n\r\n" +
+		"GET /foo HTTP/1.1\r\nHost: google.com\r\nConnection: close\r\n\r\n")
+
+	ch := make(chan error)
+	startCh := make(chan error)
+	go func() {
+		<-startCh
+		ch <- engine.Serve(context.Background(), conn)
+	}()
+	close(startCh)
+	select {
+	case err := <-ch:
+		if !errors.Is(err, errs.ErrShortConnection) {
+			t.Errorf("err should be ErrShortConnection, but got %s", err)
+		}
+		return
+	case <-time.Tick(120 * time.Millisecond):
+		t.Errorf("timeout! should have been finished in 120ms...")
+	}
+}
+
 func TestEngine_Routes(t *testing.T) {
 	engine := NewEngine(config.NewOptions(nil))
 	engine.GET("/", handlerTest1)