blob: d8db6f0db43888d5a0aeea391e4c2a6fef66f9dc [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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"
)
import (
"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" }