From a95093222c691b9ec21b3b77d57c25da5ccecabc Mon Sep 17 00:00:00 2001 From: Jack Date: Tue, 11 Aug 2020 15:23:42 +0800 Subject: [PATCH] add GetRemoteIp for ghttp.Request; add *Var feature for ghttp.Client --- net/ghttp/ghttp_client_api.go | 68 +++++++++++++++++ net/ghttp/ghttp_client_var.go | 77 +++++++++++++++++++ net/ghttp/ghttp_request.go | 17 +++-- net/ghttp/ghttp_unit_init_test.go | 2 +- net/ghttp/ghttp_unit_ip_test.go | 41 ++++++++++ net/ghttp/ghttp_z_example_client_test.go | 86 +++++++++++++++++++++ net/ghttp/ghttp_z_example_get_test.go | 89 ++++++++++++++++++++++ net/ghttp/ghttp_z_example_init_test.go | 95 ++++++++++++++++++++++++ net/ghttp/ghttp_z_example_post_test.go | 79 ++++++++++++++++++++ 9 files changed, 547 insertions(+), 7 deletions(-) create mode 100644 net/ghttp/ghttp_client_var.go create mode 100644 net/ghttp/ghttp_unit_ip_test.go create mode 100644 net/ghttp/ghttp_z_example_client_test.go create mode 100644 net/ghttp/ghttp_z_example_get_test.go create mode 100644 net/ghttp/ghttp_z_example_init_test.go create mode 100644 net/ghttp/ghttp_z_example_post_test.go diff --git a/net/ghttp/ghttp_client_api.go b/net/ghttp/ghttp_client_api.go index bee125190..591d47064 100644 --- a/net/ghttp/ghttp_client_api.go +++ b/net/ghttp/ghttp_client_api.go @@ -6,6 +6,8 @@ package ghttp +import "github.com/gogf/gf/container/gvar" + // Get is a convenience method for sending GET request. // NOTE that remembers CLOSING the response object when it'll never be used. func Get(url string, data ...interface{}) (*ClientResponse, error) { @@ -185,3 +187,69 @@ func TraceBytes(url string, data ...interface{}) []byte { func RequestBytes(method string, url string, data ...interface{}) []byte { return NewClient().RequestBytes(method, url, data...) } + +// GetVar sends a GET request, retrieves and converts the result content to specified pointer. +// The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, et +func GetVar(url string, data ...interface{}) *gvar.Var { + return RequestVar("GET", url, data...) +} + +// PutVar sends a PUT request, retrieves and converts the result content to specified pointer. +// The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, et +func PutVar(url string, data ...interface{}) *gvar.Var { + return RequestVar("PUT", url, data...) +} + +// PostVar sends a POST request, retrieves and converts the result content to specified pointer. +// The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, et +func PostVar(url string, data ...interface{}) *gvar.Var { + return RequestVar("POST", url, data...) +} + +// DeleteVar sends a DELETE request, retrieves and converts the result content to specified pointer. +// The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, et +func DeleteVar(url string, data ...interface{}) *gvar.Var { + return RequestVar("DELETE", url, data...) +} + +// HeadVar sends a HEAD request, retrieves and converts the result content to specified pointer. +// The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, et +func HeadVar(url string, data ...interface{}) *gvar.Var { + return RequestVar("HEAD", url, data...) +} + +// PatchVar sends a PATCH request, retrieves and converts the result content to specified pointer. +// The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, et +func PatchVar(url string, data ...interface{}) *gvar.Var { + return RequestVar("PATCH", url, data...) +} + +// ConnectVar sends a CONNECT request, retrieves and converts the result content to specified pointer. +// The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, et +func ConnectVar(url string, data ...interface{}) *gvar.Var { + return RequestVar("CONNECT", url, data...) +} + +// OptionsVar sends a OPTIONS request, retrieves and converts the result content to specified pointer. +// The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, et +func OptionsVar(url string, data ...interface{}) *gvar.Var { + return RequestVar("OPTIONS", url, data...) +} + +// TraceVar sends a TRACE request, retrieves and converts the result content to specified pointer. +// The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, et +func TraceVar(url string, data ...interface{}) *gvar.Var { + return RequestVar("TRACE", url, data...) +} + +// RequestVar sends request using given HTTP method and data, retrieves converts the result +// to specified pointer. It reads and closes the response object internally automatically. +// The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, et +func RequestVar(method string, url string, data ...interface{}) *gvar.Var { + response, err := DoRequest(method, url, data...) + if err != nil { + return gvar.New(nil) + } + defer response.Close() + return gvar.New(response.ReadAll()) +} diff --git a/net/ghttp/ghttp_client_var.go b/net/ghttp/ghttp_client_var.go new file mode 100644 index 000000000..5102462e6 --- /dev/null +++ b/net/ghttp/ghttp_client_var.go @@ -0,0 +1,77 @@ +// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package ghttp + +import ( + "github.com/gogf/gf/container/gvar" +) + +// GetVar sends a GET request, retrieves and converts the result content to specified pointer. +// The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, etc. +func (c *Client) GetVar(url string, data ...interface{}) *gvar.Var { + return c.RequestVar("GET", url, data...) +} + +// PutVar sends a PUT request, retrieves and converts the result content to specified pointer. +// The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, etc. +func (c *Client) PutVar(url string, data ...interface{}) *gvar.Var { + return c.RequestVar("PUT", url, data...) +} + +// PostVar sends a POST request, retrieves and converts the result content to specified pointer. +// The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, etc. +func (c *Client) PostVar(url string, data ...interface{}) *gvar.Var { + return c.RequestVar("POST", url, data...) +} + +// DeleteVar sends a DELETE request, retrieves and converts the result content to specified pointer. +// The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, etc. +func (c *Client) DeleteVar(url string, data ...interface{}) *gvar.Var { + return c.RequestVar("DELETE", url, data...) +} + +// HeadVar sends a HEAD request, retrieves and converts the result content to specified pointer. +// The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, etc. +func (c *Client) HeadVar(url string, data ...interface{}) *gvar.Var { + return c.RequestVar("HEAD", url, data...) +} + +// PatchVar sends a PATCH request, retrieves and converts the result content to specified pointer. +// The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, etc. +func (c *Client) PatchVar(url string, data ...interface{}) *gvar.Var { + return c.RequestVar("PATCH", url, data...) +} + +// ConnectVar sends a CONNECT request, retrieves and converts the result content to specified pointer. +// The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, etc. +func (c *Client) ConnectVar(url string, data ...interface{}) *gvar.Var { + return c.RequestVar("CONNECT", url, data...) +} + +// OptionsVar sends a OPTIONS request, retrieves and converts the result content to specified pointer. +// The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, etc. +func (c *Client) OptionsVar(url string, data ...interface{}) *gvar.Var { + return c.RequestVar("OPTIONS", url, data...) +} + +// TraceVar sends a TRACE request, retrieves and converts the result content to specified pointer. +// The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, etc. +func (c *Client) TraceVar(url string, data ...interface{}) *gvar.Var { + return c.RequestVar("TRACE", url, data...) +} + +// RequestVar sends request using given HTTP method and data, retrieves converts the result +// to specified pointer. It reads and closes the response object internally automatically. +// The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, etc. +func (c *Client) RequestVar(method string, url string, data ...interface{}) *gvar.Var { + response, err := c.DoRequest(method, url, data...) + if err != nil { + return gvar.New(nil) + } + defer response.Close() + return gvar.New(response.ReadAll()) +} diff --git a/net/ghttp/ghttp_request.go b/net/ghttp/ghttp_request.go index aab54b884..fa2fd6420 100644 --- a/net/ghttp/ghttp_request.go +++ b/net/ghttp/ghttp_request.go @@ -155,6 +155,7 @@ func (r *Request) IsAjaxRequest() bool { } // GetClientIp returns the client ip of this request without port. +// Note that this ip address might be modified by client header. func (r *Request) GetClientIp() string { if len(r.clientIp) == 0 { realIps := r.Header.Get("X-Forwarded-For") @@ -178,17 +179,21 @@ func (r *Request) GetClientIp() string { r.clientIp = r.Header.Get("X-Real-IP") } if r.clientIp == "" || strings.EqualFold("unknown", realIps) { - array, _ := gregex.MatchString(`(.+):(\d+)`, r.RemoteAddr) - if len(array) > 1 { - r.clientIp = array[1] - } else { - r.clientIp = r.RemoteAddr - } + r.clientIp = r.GetRemoteIp() } } return r.clientIp } +// GetRemoteIp returns the ip from RemoteAddr. +func (r *Request) GetRemoteIp() string { + array, _ := gregex.MatchString(`(.+):(\d+)`, r.RemoteAddr) + if len(array) > 1 { + return array[1] + } + return r.RemoteAddr +} + // GetUrl returns current URL of this request. func (r *Request) GetUrl() string { scheme := "http" diff --git a/net/ghttp/ghttp_unit_init_test.go b/net/ghttp/ghttp_unit_init_test.go index a1b24a8dd..b65689fe6 100644 --- a/net/ghttp/ghttp_unit_init_test.go +++ b/net/ghttp/ghttp_unit_init_test.go @@ -17,7 +17,7 @@ var ( func init() { genv.Set("UNDER_TEST", "1") - for i := 8000; i <= 9000; i++ { + for i := 50000; i <= 51000; i++ { ports.Append(i) } } diff --git a/net/ghttp/ghttp_unit_ip_test.go b/net/ghttp/ghttp_unit_ip_test.go new file mode 100644 index 000000000..d94ff6d59 --- /dev/null +++ b/net/ghttp/ghttp_unit_ip_test.go @@ -0,0 +1,41 @@ +// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +// static service testing. + +package ghttp_test + +import ( + "fmt" + "testing" + "time" + + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/net/ghttp" + "github.com/gogf/gf/test/gtest" +) + +func TestRequest_GetRemoteIp(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + p, _ := ports.PopRand() + s := g.Server(p) + s.BindHandler("/", func(r *ghttp.Request) { + r.Response.Write(r.GetRemoteIp()) + }) + s.SetDumpRouterMap(false) + s.SetPort(p) + s.Start() + defer s.Shutdown() + + time.Sleep(100 * time.Millisecond) + + client := ghttp.NewClient() + client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) + + t.Assert(client.GetContent("/"), "127.0.0.1") + }) + +} diff --git a/net/ghttp/ghttp_z_example_client_test.go b/net/ghttp/ghttp_z_example_client_test.go new file mode 100644 index 000000000..b1148d441 --- /dev/null +++ b/net/ghttp/ghttp_z_example_client_test.go @@ -0,0 +1,86 @@ +// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package ghttp_test + +import ( + "fmt" + + "github.com/gogf/gf/frame/g" +) + +func ExampleClient_Header() { + var ( + url = "http://127.0.0.1:8999/header" + header = g.MapStrStr{ + "Span-Id": "0.1", + "Trace-Id": "123456789", + } + ) + content := g.Client().Header(header).PostContent(url, g.Map{ + "id": 10000, + "name": "john", + }) + fmt.Println(content) + + // Output: + // Span-Id: 0.1, Trace-Id: 123456789 +} + +func ExampleClient_HeaderRaw() { + var ( + url = "http://127.0.0.1:8999/header" + headerRaw = ` +User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3950.0 Safari/537.36 +Span-Id: 0.1 +Trace-Id: 123456789 +` + ) + content := g.Client().HeaderRaw(headerRaw).PostContent(url, g.Map{ + "id": 10000, + "name": "john", + }) + fmt.Println(content) + + // Output: + // Span-Id: 0.1, Trace-Id: 123456789 +} + +func ExampleClient_Cookie() { + var ( + url = "http://127.0.0.1:8999/cookie" + cookie = g.MapStrStr{ + "SessionId": "123", + } + ) + content := g.Client().Cookie(cookie).PostContent(url, g.Map{ + "id": 10000, + "name": "john", + }) + fmt.Println(content) + + // Output: + // SessionId: 123 +} + +func ExampleClient_ContentJson() { + var ( + url = "http://127.0.0.1:8999/json" + jsonStr = `{"id":10000,"name":"john"}` + jsonMap = g.Map{ + "id": 10000, + "name": "john", + } + ) + // Post using JSON string. + fmt.Println(g.Client().ContentJson().PostContent(url, jsonStr)) + // Post using JSON map. + fmt.Println(g.Client().ContentJson().PostContent(url, jsonMap)) + + // Output: + // Content-Type: application/json, id: 10000 + // Content-Type: application/json, id: 10000 +} diff --git a/net/ghttp/ghttp_z_example_get_test.go b/net/ghttp/ghttp_z_example_get_test.go new file mode 100644 index 000000000..1505b37f1 --- /dev/null +++ b/net/ghttp/ghttp_z_example_get_test.go @@ -0,0 +1,89 @@ +// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package ghttp_test + +import ( + "fmt" + + "github.com/gogf/gf/frame/g" +) + +func ExampleClient_Get() { + url := "http://127.0.0.1:8999" + // Send with string parameter along with URL. + r1, err := g.Client().Get(url + "?id=10000&name=john") + if err != nil { + panic(err) + } + defer r1.Close() + fmt.Println(r1.ReadAllString()) + + // Send with string parameter in request body. + r2, err := g.Client().Get(url, "id=10000&name=john") + if err != nil { + panic(err) + } + defer r2.Close() + fmt.Println(r2.ReadAllString()) + + // Send with map parameter. + r3, err := g.Client().Get(url, g.Map{ + "id": 10000, + "name": "john", + }) + if err != nil { + panic(err) + } + defer r3.Close() + fmt.Println(r3.ReadAllString()) + + // Output: + // GET: query: 10000, john + // GET: query: 10000, john + // GET: query: 10000, john +} + +func ExampleClient_GetBytes() { + url := "http://127.0.0.1:8999" + fmt.Println(string(g.Client().GetBytes(url, g.Map{ + "id": 10000, + "name": "john", + }))) + + // Output: + // GET: query: 10000, john +} + +func ExampleClient_GetContent() { + url := "http://127.0.0.1:8999" + fmt.Println(g.Client().GetContent(url, g.Map{ + "id": 10000, + "name": "john", + })) + + // Output: + // GET: query: 10000, john +} + +func ExampleClient_GetVar() { + type User struct { + Id int + Name string + } + var ( + user *User + url = "http://127.0.0.1:8999/var/json" + ) + err := g.Client().GetVar(url).Scan(&user) + if err != nil { + panic(err) + } + fmt.Println(user) + + // Output: + // &{1 john} +} diff --git a/net/ghttp/ghttp_z_example_init_test.go b/net/ghttp/ghttp_z_example_init_test.go new file mode 100644 index 000000000..db741774f --- /dev/null +++ b/net/ghttp/ghttp_z_example_init_test.go @@ -0,0 +1,95 @@ +// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package ghttp_test + +import ( + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/net/ghttp" + "time" +) + +func init() { + p := 8999 + s := g.Server(p) + // HTTP method handlers. + s.Group("/", func(group *ghttp.RouterGroup) { + group.GET("/", func(r *ghttp.Request) { + r.Response.Writef( + "GET: query: %d, %s", + r.GetQueryInt("id"), + r.GetQueryString("name"), + ) + }) + group.PUT("/", func(r *ghttp.Request) { + r.Response.Writef( + "PUT: form: %d, %s", + r.GetFormInt("id"), + r.GetFormString("name"), + ) + }) + group.POST("/", func(r *ghttp.Request) { + r.Response.Writef( + "POST: form: %d, %s", + r.GetFormInt("id"), + r.GetFormString("name"), + ) + }) + group.DELETE("/", func(r *ghttp.Request) { + r.Response.Writef( + "DELETE: form: %d, %s", + r.GetFormInt("id"), + r.GetFormString("name"), + ) + }) + group.HEAD("/", func(r *ghttp.Request) { + r.Response.Write("head") + }) + group.OPTIONS("/", func(r *ghttp.Request) { + r.Response.Write("options") + }) + }) + // Client chaining operations handlers. + s.Group("/", func(group *ghttp.RouterGroup) { + group.ALL("/header", func(r *ghttp.Request) { + r.Response.Writef( + "Span-Id: %s, Trace-Id: %s", + r.Header.Get("Span-Id"), + r.Header.Get("Trace-Id"), + ) + }) + group.ALL("/cookie", func(r *ghttp.Request) { + r.Response.Writef( + "SessionId: %s", + r.Cookie.Get("SessionId"), + ) + }) + group.ALL("/json", func(r *ghttp.Request) { + r.Response.Writef( + "Content-Type: %s, id: %d", + r.Header.Get("Content-Type"), + r.GetInt("id"), + ) + }) + }) + // Other testing handlers. + s.Group("/var", func(group *ghttp.RouterGroup) { + group.ALL("/json", func(r *ghttp.Request) { + r.Response.Write(`{"id":1,"name":"john"}`) + }) + group.ALL("/jsons", func(r *ghttp.Request) { + r.Response.Write(`[{"id":1,"name":"john"}, {"id":2,"name":"smith"}]`) + }) + }) + s.SetAccessLogEnabled(false) + s.SetDumpRouterMap(false) + s.SetPort(p) + err := s.Start() + if err != nil { + panic(err) + } + time.Sleep(time.Millisecond * 500) +} diff --git a/net/ghttp/ghttp_z_example_post_test.go b/net/ghttp/ghttp_z_example_post_test.go new file mode 100644 index 000000000..9f9a69f50 --- /dev/null +++ b/net/ghttp/ghttp_z_example_post_test.go @@ -0,0 +1,79 @@ +// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package ghttp_test + +import ( + "fmt" + "github.com/gogf/gf/frame/g" +) + +func ExampleClient_Post() { + url := "http://127.0.0.1:8999" + // Send with string parameter in request body. + r1, err := g.Client().Post(url, "id=10000&name=john") + if err != nil { + panic(err) + } + defer r1.Close() + fmt.Println(r1.ReadAllString()) + + // Send with map parameter. + r2, err := g.Client().Post(url, g.Map{ + "id": 10000, + "name": "john", + }) + if err != nil { + panic(err) + } + defer r2.Close() + fmt.Println(r2.ReadAllString()) + + // Output: + // POST: form: 10000, john + // POST: form: 10000, john +} + +func ExampleClient_PostBytes() { + url := "http://127.0.0.1:8999" + fmt.Println(string(g.Client().PostBytes(url, g.Map{ + "id": 10000, + "name": "john", + }))) + + // Output: + // POST: form: 10000, john +} + +func ExampleClient_PostContent() { + url := "http://127.0.0.1:8999" + fmt.Println(g.Client().PostContent(url, g.Map{ + "id": 10000, + "name": "john", + })) + + // Output: + // POST: form: 10000, john +} + +func ExampleClient_PostVar() { + type User struct { + Id int + Name string + } + var ( + users []User + url = "http://127.0.0.1:8999/var/jsons" + ) + err := g.Client().PostVar(url).Scan(&users) + if err != nil { + panic(err) + } + fmt.Println(users) + + // Output: + // [{1 john} {2 smith}] +}