¸ÄÓñ¾»ú¶ÀÁ¢ CA£¬»ָ´ CLI TLS УÑ飬±£»¤ƾ¾ÝÓëÅäÖò¢·¢±£´档 д Cursor Êý¾ݡ¢»Ự·¾¶¡¢´úÀíÉúÃüÖÜÆڡ¢ģÐÍÇëÇóÓë Skills Îļþ²Ù×÷¡£ Ϊ¶ÀÁ¢ Tab ·þÎñÔö¼ӿͻ§¶˼øȨ£¬Éý¼¶ÒÀÀµÓ빤¾ßÁ´£¬ÍêÉƷ¢²¼ԪÐÅϢͬ²½¡£ Ñé֤£ºÁ½¸ö Go ģ¿éµIJâÊÔ/vet¡¢¹ؼüģ¿é race¡¢ǰ¶˲âÊÔ/Éú²ú¹¹½¨/Éó¼ơ¢ Go ¿ɴï©¶´ɨÃèÓë macOS/Linux ¿ÉÐÐģ¿齻²æ±àÒë¾ùͨ¹ý¡£ Windows ¸ôÀëÆô¶¯ͨ¹ý£»δÍê³ɵĵã»÷ðÑ̺ÍԭÉúƽ̨Ñé֤¼ûÉó²鱨¸档
132 lines
4.4 KiB
Go
132 lines
4.4 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestAuthorizationAndUpstreamTokenSeparation(t *testing.T) {
|
|
calls := 0
|
|
upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
calls++
|
|
if r.Header.Get("Authorization") != "Bearer upstream-secret" {
|
|
t.Error("wrong upstream token")
|
|
}
|
|
_, _ = w.Write([]byte("ok"))
|
|
}))
|
|
defer upstream.Close()
|
|
app := newServerApp(appConfig{Token: "upstream-secret", ClientToken: "client-secret"}, upstream.Client(), map[string]string{"/rpc": upstream.URL})
|
|
for _, token := range []string{"", "Bearer wrong", "Bearer upstream-secret", "Bearer client-secret"} {
|
|
r := httptest.NewRequest(http.MethodPost, "/rpc", strings.NewReader("request"))
|
|
r.Header.Set("Authorization", token)
|
|
w := httptest.NewRecorder()
|
|
app.ServeHTTP(w, r)
|
|
want := 401
|
|
if token == "Bearer client-secret" {
|
|
want = 200
|
|
}
|
|
if w.Code != want {
|
|
t.Fatalf("status=%d want=%d", w.Code, want)
|
|
}
|
|
}
|
|
if calls != 1 {
|
|
t.Fatal("unauthorized request reached upstream")
|
|
}
|
|
}
|
|
|
|
func TestOversizeRejectedBeforeUpstream(t *testing.T) {
|
|
app := newServerApp(appConfig{Token: "upstream", ClientToken: "client"}, nil, map[string]string{"/rpc": "http://127.0.0.1:1"})
|
|
r := httptest.NewRequest(http.MethodPost, "/rpc", strings.NewReader(""))
|
|
r.ContentLength = maxRequestBytes + 1
|
|
r.Header.Set("Authorization", "Bearer client")
|
|
w := httptest.NewRecorder()
|
|
app.ServeHTTP(w, r)
|
|
if w.Code != 413 {
|
|
t.Fatalf("status=%d", w.Code)
|
|
}
|
|
}
|
|
|
|
type testTransport func(*http.Request) (*http.Response, error)
|
|
|
|
func (t testTransport) RoundTrip(r *http.Request) (*http.Response, error) { return t(r) }
|
|
|
|
type brokenStream struct{ sent bool }
|
|
|
|
func (b *brokenStream) Read(p []byte) (int, error) {
|
|
if !b.sent {
|
|
b.sent = true
|
|
return copy(p, []byte("partial-event")), nil
|
|
}
|
|
return 0, errors.New("stream interrupted")
|
|
}
|
|
func (b *brokenStream) Close() error { return nil }
|
|
|
|
func TestStreamErrorNeverAppendsHTTPError(t *testing.T) {
|
|
client := &http.Client{Transport: testTransport(func(*http.Request) (*http.Response, error) {
|
|
return &http.Response{StatusCode: 200, Header: http.Header{}, Body: &brokenStream{}}, nil
|
|
})}
|
|
app := newServerApp(appConfig{Token: "upstream", ClientToken: "client"}, client, map[string]string{"/rpc": "https://upstream.invalid"})
|
|
r := httptest.NewRequest(http.MethodPost, "/rpc", nil)
|
|
r.Header.Set("Authorization", "Bearer client")
|
|
w := httptest.NewRecorder()
|
|
app.ServeHTTP(w, r)
|
|
if w.Code != 200 || w.Body.String() != "partial-event" {
|
|
t.Fatalf("response corrupted: %d %q", w.Code, w.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestCanceledRequestDoesNotCallUpstream(t *testing.T) {
|
|
client := &http.Client{Transport: testTransport(func(r *http.Request) (*http.Response, error) { return nil, r.Context().Err() })}
|
|
app := newServerApp(appConfig{Token: "upstream", ClientToken: "client"}, client, map[string]string{"/rpc": "https://upstream.invalid"})
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
cancel()
|
|
r := httptest.NewRequest(http.MethodPost, "/rpc", nil).WithContext(ctx)
|
|
r.Header.Set("Authorization", "Bearer client")
|
|
w := httptest.NewRecorder()
|
|
app.ServeHTTP(w, r)
|
|
if w.Code != 499 {
|
|
t.Fatalf("status=%d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestClientTokenRequiredAndDistinct(t *testing.T) {
|
|
p := filepath.Join(t.TempDir(), "config.yaml")
|
|
for _, content := range []string{"token: upstream\n", "token: same\nclientToken: same\n"} {
|
|
if err := os.WriteFile(p, []byte(content), 0600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := loadConfig(p); err == nil {
|
|
t.Fatal("insecure configuration accepted")
|
|
}
|
|
}
|
|
_ = os.WriteFile(p, []byte("token: upstream\nclientToken: client\n"), 0600)
|
|
if _, err := loadConfig(p); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestUpstreamRedirectDoesNotForwardSecret(t *testing.T) {
|
|
leaked := false
|
|
target := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { leaked = true; _, _ = io.WriteString(w, "bad") }))
|
|
defer target.Close()
|
|
redirect := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, target.URL, 307) }))
|
|
defer redirect.Close()
|
|
r, _ := http.NewRequest(http.MethodPost, redirect.URL, strings.NewReader(""))
|
|
r.Header.Set("Authorization", "Bearer secret")
|
|
resp, err := newHTTPClient().Do(r)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer resp.Body.Close()
|
|
if resp.StatusCode != 307 || leaked {
|
|
t.Fatal("redirect followed with credentials")
|
|
}
|
|
}
|