| // Copyright 2021-2023 Buf Technologies, Inc. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| package triple_protocol_test |
| |
| import ( |
| "context" |
| "errors" |
| "net" |
| "net/http" |
| "net/http/httptest" |
| "sync" |
| |
| "dubbo.apache.org/dubbo-go/v3/protocol/triple/triple_protocol/internal/gen/proto/connect/ping/v1/pingv1connect" |
| ) |
| |
| var examplePingServer *inMemoryServer |
| |
| func init() { |
| // Generally, init functions are bad. |
| // |
| // To write testable examples that users can grok *and* can execute in the |
| // playground, where networking is disabled, we need an HTTP server that uses |
| // in-memory pipes instead of TCP. We don't want to pollute every example |
| // with this setup code. |
| // |
| // The least-awful option is to set up the server in init(). |
| mux := http.NewServeMux() |
| mux.Handle(pingv1connect.NewPingServiceHandler(pingServer{})) |
| examplePingServer = newInMemoryServer(mux) |
| } |
| |
| // inMemoryServer is an HTTP server that uses in-memory pipes instead of TCP. |
| // It supports HTTP/2 and has TLS enabled. |
| // |
| // The Go Playground panics if we try to start a TCP-backed server. If you're |
| // not familiar with the Playground's behavior, it looks like our examples are |
| // broken. This server lets us write examples that work in the playground |
| // without abstracting over HTTP. |
| type inMemoryServer struct { |
| server *httptest.Server |
| listener *memoryListener |
| } |
| |
| // newInMemoryServer constructs and starts an inMemoryServer. |
| func newInMemoryServer(handler http.Handler) *inMemoryServer { |
| lis := &memoryListener{ |
| conns: make(chan net.Conn), |
| closed: make(chan struct{}), |
| } |
| server := httptest.NewUnstartedServer(handler) |
| server.Listener = lis |
| server.EnableHTTP2 = true |
| server.StartTLS() |
| return &inMemoryServer{ |
| server: server, |
| listener: lis, |
| } |
| } |
| |
| // Client returns an HTTP client configured to trust the server's TLS |
| // certificate and use HTTP/2 over an in-memory pipe. Automatic HTTP-level gzip |
| // compression is disabled. It closes its idle connections when the server is |
| // closed. |
| func (s *inMemoryServer) Client() *http.Client { |
| client := s.server.Client() |
| if transport, ok := client.Transport.(*http.Transport); ok { |
| transport.DialContext = s.listener.DialContext |
| transport.DisableCompression = true |
| } |
| return client |
| } |
| |
| // URL is the server's URL. |
| func (s *inMemoryServer) URL() string { |
| return s.server.URL |
| } |
| |
| // Close shuts down the server, blocking until all outstanding requests have |
| // completed. |
| func (s *inMemoryServer) Close() { |
| s.server.Close() |
| } |
| |
| type memoryListener struct { |
| conns chan net.Conn |
| once sync.Once |
| closed chan struct{} |
| } |
| |
| // Accept implements net.Listener. |
| func (l *memoryListener) Accept() (net.Conn, error) { |
| select { |
| case conn := <-l.conns: |
| return conn, nil |
| case <-l.closed: |
| return nil, errors.New("listener closed") |
| } |
| } |
| |
| // Close implements net.Listener. |
| func (l *memoryListener) Close() error { |
| l.once.Do(func() { |
| close(l.closed) |
| }) |
| return nil |
| } |
| |
| // Addr implements net.Listener. |
| func (l *memoryListener) Addr() net.Addr { |
| return &memoryAddr{} |
| } |
| |
| // DialContext is the type expected by http.Transport.DialContext. |
| func (l *memoryListener) DialContext(ctx context.Context, network, addr string) (net.Conn, error) { |
| select { |
| case <-l.closed: |
| return nil, errors.New("listener closed") |
| default: |
| } |
| server, client := net.Pipe() |
| l.conns <- server |
| return client, nil |
| } |
| |
| type memoryAddr struct{} |
| |
| // Network implements net.Addr. |
| func (*memoryAddr) Network() string { return "memory" } |
| |
| // String implements io.Stringer, returning a value that matches the |
| // certificates used by net/http/httptest. |
| func (*memoryAddr) String() string { return "example.com" } |