дȫÏîĿÉó²éÎÊÌⲢ·¢²¼ v1.1.9
¸ÄÓñ¾»ú¶ÀÁ¢ CA£¬»ָ´ CLI TLS УÑ飬±£»¤ƾ¾ÝÓëÅäÖò¢·¢±£´档 д Cursor Êý¾ݡ¢»Ự·¾¶¡¢´úÀíÉúÃüÖÜÆڡ¢ģÐÍÇëÇóÓë Skills Îļþ²Ù×÷¡£ Ϊ¶ÀÁ¢ Tab ·þÎñÔö¼ӿͻ§¶˼øȨ£¬Éý¼¶ÒÀÀµÓ빤¾ßÁ´£¬ÍêÉƷ¢²¼ԪÐÅϢͬ²½¡£ Ñé֤£ºÁ½¸ö Go ģ¿éµIJâÊÔ/vet¡¢¹ؼüģ¿é race¡¢ǰ¶˲âÊÔ/Éú²ú¹¹½¨/Éó¼ơ¢ Go ¿ɴï©¶´ɨÃèÓë macOS/Linux ¿ÉÐÐģ¿齻²æ±àÒë¾ùͨ¹ý¡£ Windows ¸ôÀëÆô¶¯ͨ¹ý£»δÍê³ɵĵã»÷ðÑ̺ÍԭÉúƽ̨Ñé֤¼ûÉó²鱨¸档
This commit is contained in:
@@ -40,10 +40,10 @@ func InstallDefaultTransport() {
|
||||
// NewHTTPClient creates an HTTP client that follows environment and OS proxy
|
||||
// settings while preserving the caller's timeout choice.
|
||||
func NewHTTPClient(timeout time.Duration) *http.Client {
|
||||
return &http.Client{
|
||||
return CredentialSafeClient(&http.Client{
|
||||
Transport: NewTransport(nil),
|
||||
Timeout: timeout,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// NewTransport clones the given transport and installs proxy resolution on it.
|
||||
|
||||
43
internal/netproxy/redirect.go
Normal file
43
internal/netproxy/redirect.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package netproxy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func origin(u *url.URL) string {
|
||||
port := u.Port()
|
||||
if port == "" {
|
||||
if strings.EqualFold(u.Scheme, "https") {
|
||||
port = "443"
|
||||
} else {
|
||||
port = "80"
|
||||
}
|
||||
}
|
||||
return strings.ToLower(u.Scheme) + "://" + strings.ToLower(u.Hostname()) + ":" + port
|
||||
}
|
||||
|
||||
// CredentialSafeClient retains client settings, while never forwarding custom
|
||||
// authentication headers or POST bodies to a different origin via redirects.
|
||||
func CredentialSafeClient(client *http.Client) *http.Client {
|
||||
if client == nil {
|
||||
client = http.DefaultClient
|
||||
}
|
||||
next := *client
|
||||
previous := next.CheckRedirect
|
||||
next.CheckRedirect = func(req *http.Request, via []*http.Request) error {
|
||||
if len(via) >= 10 {
|
||||
return fmt.Errorf("too many redirects")
|
||||
}
|
||||
if len(via) > 0 && origin(req.URL) != origin(via[0].URL) {
|
||||
return http.ErrUseLastResponse
|
||||
}
|
||||
if previous != nil {
|
||||
return previous(req, via)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return &next
|
||||
}
|
||||
67
internal/netproxy/redirect_test.go
Normal file
67
internal/netproxy/redirect_test.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package netproxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestCrossOriginRedirectNeverReceivesCustomAuthentication(t *testing.T) {
|
||||
called := false
|
||||
target := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { called = true }))
|
||||
defer target.Close()
|
||||
redirect := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Redirect(w, r, target.URL, http.StatusTemporaryRedirect)
|
||||
}))
|
||||
defer redirect.Close()
|
||||
client := CredentialSafeClient(redirect.Client())
|
||||
r, _ := http.NewRequest(http.MethodPost, redirect.URL, strings.NewReader("private body"))
|
||||
r.Header.Set("x-api-key", "sentinel")
|
||||
r.Header.Set("Authorization", "Bearer sentinel")
|
||||
resp, err := client.Do(r)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
resp.Body.Close()
|
||||
if called || resp.StatusCode != 307 {
|
||||
t.Fatal("credentials crossed origin")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSameOriginRedirectAndCancellation(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path == "/" {
|
||||
http.Redirect(w, r, "/ok", 307)
|
||||
return
|
||||
}
|
||||
if r.Header.Get("x-api-key") != "sentinel" {
|
||||
t.Error("same-origin credential lost")
|
||||
}
|
||||
w.WriteHeader(204)
|
||||
}))
|
||||
defer server.Close()
|
||||
client := CredentialSafeClient(server.Client())
|
||||
r, _ := http.NewRequest(http.MethodGet, server.URL, nil)
|
||||
r.Header.Set("x-api-key", "sentinel")
|
||||
resp, err := client.Do(r)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
resp.Body.Close()
|
||||
if resp.StatusCode != 204 {
|
||||
t.Fatal(resp.StatusCode)
|
||||
}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
r, _ = http.NewRequestWithContext(ctx, http.MethodGet, server.URL, nil)
|
||||
if _, err = client.Do(r); err == nil {
|
||||
t.Fatal("cancellation ignored")
|
||||
}
|
||||
client.Timeout = time.Nanosecond
|
||||
if client.Timeout == 0 {
|
||||
t.Fatal("timeout setting lost")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user