返回 DeepSeek-Reasonix
transport_diagnostic_structural_test.go
根目录 / internal / provider / transport_diagnostic_structural_test.go
1 package provider
2
3 import (
4 "context"
5 "crypto/tls"
6 "errors"
7 "io"
8 "net/http"
9 "net/http/httptest"
10 "testing"
11 "time"
12
13 "golang.org/x/net/http2"
14 )
15
16 type opaqueTransportWrapper struct{ cause error }
17
18 func (opaqueTransportWrapper) Error() string { panic("classification must not read error wording") }
19 func (e opaqueTransportWrapper) Unwrap() error { return e.cause }
20
21 type http2ConnectionError uint32
22
23 func (http2ConnectionError) Error() string { return "connection error: PROTOCOL_ERROR" }
24
25 func TestHTTP2ClassificationUsesTypeAndCode(t *testing.T) {
26 for _, cause := range []error{
27 http2.ConnectionError(http2.ErrCodeFrameSize),
28 http2.StreamError{StreamID: 1, Code: http2.ErrCodeFrameSize},
29 http2.GoAwayError{ErrCode: http2.ErrCodeFrameSize},
30 new(http2.ConnectionError(http2.ErrCodeFrameSize)),
31 &http2.StreamError{StreamID: 1, Code: http2.ErrCodeFrameSize},
32 &http2.GoAwayError{ErrCode: http2.ErrCodeFrameSize},
33 } {
34 if got := HTTP2TransportCode(opaqueTransportWrapper{errors.Join(errors.New("unrelated"), cause)}); got != "FRAME_SIZE_ERROR" {
35 t.Fatalf("typed code = %q", got)
36 }
37 }
38 for _, cause := range []error{http2ConnectionError(1), errors.New("connection error: PROTOCOL_ERROR"), http2.ConnectionError(999)} {
39 if got := HTTP2TransportCode(opaqueTransportWrapper{cause}); got != "" {
40 t.Fatalf("classified foreign type, wording, or unknown code: %q", got)
41 }
42 }
43 }
44
45 func TestHTTP2StdlibStreamAndGoAwayTypes(t *testing.T) {
46 for _, goAway := range []bool{false, true} {
47 t.Run(map[bool]string{false: "stream", true: "goaway"}[goAway], func(t *testing.T) {
48 code := http2.ErrCodeFrameSize
49 if goAway {
50 code = http2.ErrCodeProtocol
51 }
52 server := httptest.NewUnstartedServer(http.NotFoundHandler())
53 server.EnableHTTP2 = true
54 server.Config.TLSNextProto = map[string]func(*http.Server, *tls.Conn, http.Handler){
55 "h2": func(_ *http.Server, conn *tls.Conn, _ http.Handler) {
56 defer conn.Close()
57 _ = conn.SetDeadline(time.Now().Add(5 * time.Second))
58 if _, err := io.ReadFull(conn, make([]byte, len(http2.ClientPreface))); err != nil {
59 return
60 }
61 framer := http2.NewFramer(conn, conn)
62 if err := framer.WriteSettings(); err != nil {
63 return
64 }
65 for {
66 frame, err := framer.ReadFrame()
67 if err != nil {
68 return
69 }
70 if headers, ok := frame.(*http2.HeadersFrame); ok {
71 if goAway {
72 _ = framer.WriteGoAway(headers.StreamID, code, nil)
73 } else {
74 // A peer PROTOCOL_ERROR triggers net/http's internal retry;
75 // use a non-retryable code to inspect the private stream type.
76 _ = framer.WriteRSTStream(headers.StreamID, code)
77 }
78 // Drain late client frames before closing: a Windows close
79 // with unread inbound data sends RST, clobbering the frame
80 // just written (upstream #11152 flake). Exits early on EOF.
81 _ = conn.SetReadDeadline(time.Now().Add(50 * time.Millisecond))
82 for {
83 if _, err := framer.ReadFrame(); err != nil {
84 break
85 }
86 }
87 return
88 }
89 }
90 },
91 }
92 server.StartTLS()
93 defer server.Close()
94 ctx, cancel := context.WithTimeout(t.Context(), 5*time.Second)
95 defer cancel()
96 req, err := http.NewRequestWithContext(ctx, http.MethodPost, server.URL, nil)
97 if err != nil {
98 t.Fatal(err)
99 }
100 response, err := server.Client().Do(req)
101 if response != nil {
102 response.Body.Close()
103 }
104 if got := HTTP2TransportCode(opaqueTransportWrapper{err}); got != code.String() {
105 t.Fatalf("code=%q, err=%v", got, err)
106 }
107 })
108 }
109 }
110
110 lines GO