Repository: cloudwego/hertz (go)

Issue:
Proposal: add some error log for 400(missing host)
**Is your feature request related to a problem? Please describe.**
#1003  add a extra check for missing host request, will directly return a 400 response before accesslog recording(kind of confusing).

**Describe the solution you'd like**
May let middleware(accesslog) to record the 400, also with other 400 scenario.

**Describe alternatives you've considered**
Feel free to discuss it here~

Pull request:
feat: add SetHandlers when fast fail for no valid host and invalid rPath
#### 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.
- [x] 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.
当请求缺少Host短路时或者非法path时设置全局中间件

#### (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: As mentioned in https://github.com/cloudwego/hertz/issues/1034, when the request lacks a Host  or a invalid path, the serveError is directly called to return the result, lead to the global middlewares not being executed (accesslog middleware is does not work), so I think the global middlewares should be executed like 404 and 405 scenario.
This is my first time submitting a PR for an open source project. Thank you for your criticism and correction :)

zh(optional): 就像 https://github.com/cloudwego/hertz/issues/1034 提到的那样，当请求缺少Host 或者非法path 请求而短路时时，直接调用serveError返回结果，导致全局中间件没有被执行（accesslog middleware 就不起作用），我认为应该像 404 和 405 错误那样，都应该执行一遍全局中间件;
第一次给开源项目提pr 欢迎大家批评指正  :)

#### (Optional) Which issue(s) this PR fixes:
Fixes #1034 
<!--
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)
-->

Patch:
diff --git a/pkg/route/engine.go b/pkg/route/engine.go
index 8b65e6e..30b899d 100644
--- a/pkg/route/engine.go
+++ b/pkg/route/engine.go
@@ -726,6 +726,7 @@ func (engine *Engine) ServeHTTP(c context.Context, ctx *app.RequestContext) {
 
 	// align with https://datatracker.ietf.org/doc/html/rfc2616#section-5.2
 	if len(ctx.Request.Host()) == 0 && ctx.Request.Header.IsHTTP11() && bytesconv.B2s(ctx.Request.Method()) != consts.MethodConnect {
+		ctx.SetHandlers(engine.Handlers)
 		serveError(c, ctx, consts.StatusBadRequest, requiredHostBody)
 		return
 	}
@@ -743,6 +744,7 @@ func (engine *Engine) ServeHTTP(c context.Context, ctx *app.RequestContext) {
 
 	// Follow RFC7230#section-5.3
 	if rPath == "" || rPath[0] != '/' {
+		ctx.SetHandlers(engine.Handlers)
 		serveError(c, ctx, consts.StatusBadRequest, default400Body)
 		return
 	}


Test patch:
diff --git a/pkg/app/server/hertz_test.go b/pkg/app/server/hertz_test.go
index 4a9cee3..3352d34 100644
--- a/pkg/app/server/hertz_test.go
+++ b/pkg/app/server/hertz_test.go
@@ -196,7 +196,10 @@ func formatAsDate(t time.Time) string {
 }
 
 // copied from router
-var default400Body = []byte("400 bad request")
+var (
+	default400Body   = []byte("400 bad request")
+	requiredHostBody = []byte("missing required Host header")
+)
 
 func TestServer_Use(t *testing.T) {
 	router := New()
@@ -284,6 +287,13 @@ func TestNotAbsolutePath(t *testing.T) {
 
 func TestNotAbsolutePathWithRawPath(t *testing.T) {
 	engine := New(WithHostPorts("127.0.0.1:9991"), WithUseRawPath(true))
+	const (
+		MiddlewareKey   = "middleware_key"
+		MiddlewareValue = "middleware_value"
+	)
+	engine.Use(func(c context.Context, ctx *app.RequestContext) {
+		ctx.Response.Header.Set(MiddlewareKey, MiddlewareValue)
+	})
 	engine.POST("/", func(c context.Context, ctx *app.RequestContext) {
 	})
 	engine.POST("/a", func(c context.Context, ctx *app.RequestContext) {
@@ -301,6 +311,8 @@ func TestNotAbsolutePathWithRawPath(t *testing.T) {
 	engine.ServeHTTP(context.Background(), ctx)
 	assert.DeepEqual(t, consts.StatusBadRequest, ctx.Response.StatusCode())
 	assert.DeepEqual(t, default400Body, ctx.Response.Body())
+	gh := ctx.Response.Header.Get(MiddlewareKey)
+	assert.DeepEqual(t, MiddlewareValue, gh)
 
 	s = "POST a?a=b HTTP/1.1\r\nHost: a.b.c\r\nContent-Length: 5\r\nContent-Type: foo/bar\r\n\r\nabcdef4343"
 	zr = mock.NewZeroCopyReader(s)
@@ -312,6 +324,49 @@ func TestNotAbsolutePathWithRawPath(t *testing.T) {
 	engine.ServeHTTP(context.Background(), ctx)
 	assert.DeepEqual(t, consts.StatusBadRequest, ctx.Response.StatusCode())
 	assert.DeepEqual(t, default400Body, ctx.Response.Body())
+	gh = ctx.Response.Header.Get(MiddlewareKey)
+	assert.DeepEqual(t, MiddlewareValue, gh)
+}
+
+func TestNotValidHost(t *testing.T) {
+	engine := New(WithHostPorts("127.0.0.1:9992"))
+	const (
+		MiddlewareKey   = "middleware_key"
+		MiddlewareValue = "middleware_value"
+	)
+	engine.Use(func(c context.Context, ctx *app.RequestContext) {
+		ctx.Response.Header.Set(MiddlewareKey, MiddlewareValue)
+	})
+	engine.POST("/", func(c context.Context, ctx *app.RequestContext) {
+	})
+	engine.POST("/a", func(c context.Context, ctx *app.RequestContext) {
+	})
+
+	s := "POST ?a=b HTTP/1.1\r\nHost: \r\nContent-Length: 5\r\nContent-Type: foo/bar\r\n\r\nabcdef4343"
+	zr := mock.NewZeroCopyReader(s)
+
+	ctx := app.NewContext(0)
+	if err := req.Read(&ctx.Request, zr); err != nil {
+		t.Fatalf("unexpected error: %s", err)
+	}
+	engine.ServeHTTP(context.Background(), ctx)
+	assert.DeepEqual(t, consts.StatusBadRequest, ctx.Response.StatusCode())
+	assert.DeepEqual(t, requiredHostBody, ctx.Response.Body())
+	gh := ctx.Response.Header.Get(MiddlewareKey)
+	assert.DeepEqual(t, MiddlewareValue, gh)
+
+	s = "POST a?a=b HTTP/1.1\r\nContent-Length: 5\r\nContent-Type: foo/bar\r\n\r\nabcdef4343"
+	zr = mock.NewZeroCopyReader(s)
+
+	ctx = app.NewContext(0)
+	if err := req.Read(&ctx.Request, zr); err != nil {
+		t.Fatalf("unexpected error: %s", err)
+	}
+	engine.ServeHTTP(context.Background(), ctx)
+	assert.DeepEqual(t, consts.StatusBadRequest, ctx.Response.StatusCode())
+	assert.DeepEqual(t, requiredHostBody, ctx.Response.Body())
+	gh = ctx.Response.Header.Get(MiddlewareKey)
+	assert.DeepEqual(t, MiddlewareValue, gh)
 }
 
 func TestWithBasePath(t *testing.T) {