Skip to content

Commit

Permalink
WIP(x/net/http/client): Implement IdleConnPool
Browse files Browse the repository at this point in the history
  • Loading branch information
spongehah committed Sep 4, 2024
1 parent c4d7315 commit 0d8cc27
Show file tree
Hide file tree
Showing 12 changed files with 832 additions and 291 deletions.
2 changes: 1 addition & 1 deletion x/net/http/_demo/get/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ func main() {
fmt.Println(err)
return
}
defer resp.Body.Close()
fmt.Println(resp.Status, "read bytes: ", resp.ContentLength)
resp.PrintHeaders()
body, err := io.ReadAll(resp.Body)
Expand All @@ -21,5 +22,4 @@ func main() {
return
}
fmt.Println(string(body))
defer resp.Body.Close()
}
2 changes: 1 addition & 1 deletion x/net/http/_demo/headers/headers.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ func main() {
println(err.Error())
return
}
defer resp.Body.Close()
fmt.Println(resp.Status)
resp.PrintHeaders()
body, err := io.ReadAll(resp.Body)
Expand All @@ -44,5 +45,4 @@ func main() {
return
}
fmt.Println(string(body))
defer resp.Body.Close()
}
2 changes: 1 addition & 1 deletion x/net/http/_demo/maxConnsPerHost/maxConnsPerHost.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ func main() {
fmt.Println(err)
return
}
defer resp.Body.Close()
fmt.Println(resp.Status, "read bytes: ", resp.ContentLength)
fmt.Println(resp.Proto)
resp.PrintHeaders()
Expand All @@ -28,5 +29,4 @@ func main() {
return
}
fmt.Println(string(body))
defer resp.Body.Close()
}
2 changes: 1 addition & 1 deletion x/net/http/_demo/post/post.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ func main() {
fmt.Println(err)
return
}
defer resp.Body.Close()
fmt.Println(resp.Status)
body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(body))
defer resp.Body.Close()
}
2 changes: 0 additions & 2 deletions x/net/http/_demo/postform/postform.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,10 @@ func main() {
return
}
defer resp.Body.Close()

body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(body))
defer resp.Body.Close()
}
2 changes: 1 addition & 1 deletion x/net/http/_demo/redirect/redirect.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ func main() {
fmt.Println(err)
return
}
defer resp.Body.Close()
fmt.Println(resp.Status, "read bytes: ", resp.ContentLength)
fmt.Println(resp.Proto)
resp.PrintHeaders()
Expand All @@ -22,5 +23,4 @@ func main() {
return
}
fmt.Println(string(body))
defer resp.Body.Close()
}
42 changes: 42 additions & 0 deletions x/net/http/_demo/reuseConn/reuseConn.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package main

import (
"fmt"
"io"

"github.com/goplus/llgoexamples/x/net/http"
)

func main() {
// Send request first time
resp, err := http.Get("https://www.baidu.com")
if err != nil {
fmt.Println(err)
return
}
fmt.Println(resp.Status, "read bytes: ", resp.ContentLength)
resp.PrintHeaders()
body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(body))
resp.Body.Close()

// Send request second time
resp, err = http.Get("https://www.baidu.com")
if err != nil {
fmt.Println(err)
return
}
fmt.Println(resp.Status, "read bytes: ", resp.ContentLength)
resp.PrintHeaders()
body, err = io.ReadAll(resp.Body)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(body))
resp.Body.Close()
}
12 changes: 6 additions & 6 deletions x/net/http/_demo/timeout/timeout.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,24 @@ import (

func main() {
client := &http.Client{
//Timeout: time.Millisecond, // Set a small timeout to ensure it will time out
Timeout: time.Second * 5,
Timeout: time.Millisecond, // Set a small timeout to ensure it will time out
//Timeout: time.Second,
}
req, err := http.NewRequest("GET", "https://www.baidu.com", nil)
if err != nil {
fmt.Println(err.Error())
fmt.Println(err)
return
}
resp, err := client.Do(req)
if err != nil {
fmt.Println(err.Error())
fmt.Println(err)
return
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println(err.Error())
fmt.Println(err)
return
}
println(string(body))
defer resp.Body.Close()
}
8 changes: 5 additions & 3 deletions x/net/http/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,6 @@ func (c *Client) do(req *Request) (retres *Response, reterr error) {

// didTimeout is non-nil only if err != nil.
func (c *Client) send(req *Request, deadline time.Time) (resp *Response, didTimeout func() bool, err error) {
// TODO(spongehah) cookie(c.send)
if c.Jar != nil {
for _, cookie := range c.Jar.Cookies(req.URL) {
req.AddCookie(cookie)
Expand Down Expand Up @@ -309,13 +308,16 @@ func send(ireq *Request, rt RoundTripper, deadline time.Time) (resp *Response, d
}

// TODO(spongehah) timeout(send)
//stopTimer, didTimeout := setRequestCancel(req, rt, deadline)
req.timeoutch = make(chan struct{}, 1)
req.deadline = deadline
req.ctx.Done()
if deadline.IsZero() {
didTimeout = alwaysFalse
defer close(req.timeoutch)
} else {
didTimeout = func() bool { return req.timer.GetDueIn() == 0 }
}
//stopTimer, didTimeout := setRequestCancel(req, rt, deadline)

resp, err = rt.RoundTrip(req)
if err != nil {
Expand Down Expand Up @@ -488,7 +490,7 @@ func knownRoundTripperImpl(rt RoundTripper, req *Request) bool {
return knownRoundTripperImpl(altRT, req)
}
return true
// TODO(spongehah)
// TODO(spongehah) http2
//case *http2Transport, http2noDialH2RoundTripper:
// return true
}
Expand Down
73 changes: 48 additions & 25 deletions x/net/http/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,30 @@ type Request struct {

const defaultChunkSize = 8192

// NOTE: This is not intended to reflect the actual Go version being used.

Check warning on line 53 in x/net/http/request.go

View check run for this annotation

qiniu-x / note-check

x/net/http/request.go#L53

A Note is recommended to use "MARKER(uid): note body" format.
// It was changed at the time of Go 1.1 release because the former User-Agent
// had ended up blocked by some intrusion detection systems.
// See https://codereview.appspot.com/7532043.
const defaultUserAgent = "Go-http-client/1.1"

// errMissingHost is returned by Write when there is no Host or URL present in
// the Request.
var errMissingHost = errors.New("http: Request.Write on Request with no Host or URL set")

// Headers that Request.Write handles itself and should be skipped.
var reqWriteExcludeHeader = map[string]bool{
"Host": true, // not in Header map anyway
"User-Agent": true,
"Content-Length": true,
"Transfer-Encoding": true,
"Trailer": true,
}

// requestBodyReadError wraps an error from (*Request).write to indicate
// that the error came from a Read call on the Request.Body.
// This error type should not escape the net/http package to users.
type requestBodyReadError struct{ error }

// NewRequest wraps NewRequestWithContext using context.Background.
func NewRequest(method, url string, body io.Reader) (*Request, error) {

Check warning on line 78 in x/net/http/request.go

View check run for this annotation

qiniu-x / golangci-lint

x/net/http/request.go#L78

import-shadowing: The name 'url' shadows an import name (revive)
return NewRequestWithContext(context.Background(), method, url, body)
Expand Down Expand Up @@ -188,6 +212,22 @@ func (r *Request) closeBody() error {
return r.Body.Close()
}

func (r *Request) isReplayable() bool {
if r.Body == nil || r.Body == NoBody || r.GetBody != nil {
switch valueOrDefault(r.Method, "GET") {
case "GET", "HEAD", "OPTIONS", "TRACE":
return true
}
// The Idempotency-Key, while non-standard, is widely used to
// mean a POST or other request is idempotent. See
// https://golang.org/issue/19943#issuecomment-421092421
if r.Header.has("Idempotency-Key") || r.Header.has("X-Idempotency-Key") {
return true
}
}
return false
}

// Context returns the request's context. To change the context, use
// Clone or WithContext.
//
Expand Down Expand Up @@ -240,25 +280,6 @@ func (r *Request) ProtoAtLeast(major, minor int) bool {
r.ProtoMajor == major && r.ProtoMinor >= minor
}

// errMissingHost is returned by Write when there is no Host or URL present in
// the Request.
var errMissingHost = errors.New("http: Request.Write on Request with no Host or URL set")

// NOTE: This is not intended to reflect the actual Go version being used.
// It was changed at the time of Go 1.1 release because the former User-Agent
// had ended up blocked by some intrusion detection systems.
// See https://codereview.appspot.com/7532043.
const defaultUserAgent = "Go-http-client/1.1"

// Headers that Request.Write handles itself and should be skipped.
var reqWriteExcludeHeader = map[string]bool{
"Host": true, // not in Header map anyway
"User-Agent": true,
"Content-Length": true,
"Transfer-Encoding": true,
"Trailer": true,
}

// extraHeaders may be nil
// waitForContinue may be nil
// always closes body

Check warning on line 285 in x/net/http/request.go

View check run for this annotation

qiniu-x / golangci-lint

x/net/http/request.go#L285

Comment should end in a period (godot)
Expand All @@ -279,7 +300,6 @@ func (r *Request) write(client *hyper.ClientConn, taskData *taskData, exec *hype
}
// Send it!
sendTask := client.Send(hyperReq)
taskData.taskId = read
sendTask.SetUserdata(c.Pointer(taskData))
sendRes := exec.Push(sendTask)
if sendRes != hyper.OK {
Expand Down Expand Up @@ -482,11 +502,6 @@ func readCookies(h Header, filter string) []*Cookie {
return cookies
}

// requestBodyReadError wraps an error from (*Request).write to indicate
// that the error came from a Read call on the Request.Body.
// This error type should not escape the net/http package to users.
type requestBodyReadError struct{ error }

func idnaASCII(v string) (string, error) {
// TODO: Consider removing this check after verifying performance is okay.

Check warning on line 506 in x/net/http/request.go

View check run for this annotation

qiniu-x / note-check

x/net/http/request.go#L506

A Note is recommended to use "MARKER(uid): note body" format.
// Right now punycode verification, length checks, context checks, and the
Expand Down Expand Up @@ -519,3 +534,11 @@ func removeZone(host string) string {
}
return host[:j] + host[i:]
}

// Return value if nonempty, def otherwise.
func valueOrDefault(value, def string) string {
if value != "" {
return value
}
return def
}
Loading

0 comments on commit 0d8cc27

Please sign in to comment.