From 6f0fa81187b2a5a96855af423d24d68efd20d686 Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Thu, 20 Jul 2023 10:35:14 -0700 Subject: [PATCH] Initial self review --- p2p/http/auth.go | 14 +++++++------ p2p/http/libp2phttp.go | 39 ++++++++++++++++++++++++------------- p2p/http/libp2phttp_test.go | 2 +- 3 files changed, 35 insertions(+), 20 deletions(-) diff --git a/p2p/http/auth.go b/p2p/http/auth.go index 4b549bec44..458b45bb1a 100644 --- a/p2p/http/auth.go +++ b/p2p/http/auth.go @@ -134,11 +134,15 @@ func AuthenticateClient(hostKey crypto.PrivKey, responseHeader http.Header, requ return "", fmt.Errorf("error generating noise payload: %w", err) } - authInfoMsg, _, _, err := hs.WriteMessage(hbuf[:0], payload) + authInfoMsg, cs1, cs2, err := hs.WriteMessage(hbuf[:0], payload) if err != nil { return "", fmt.Errorf("error writing handshake message: %w", err) } + if cs1 == nil || cs2 == nil { + return "", errors.New("expected handshake to be complete") + } + authInfoMsgEncoded, err := multibase.Encode(multibase.Encodings["base32"], authInfoMsg) if err != nil { return "", fmt.Errorf("error encoding handshake message: %w", err) @@ -171,7 +175,7 @@ func (s AuthState) AuthenticateServer(expectedSNI string, responseHeader http.He } if cs1 == nil || cs2 == nil { - return "", errors.New("expected ciphersuites to be present") + return "", errors.New("expected handshake to be complete") } server, extensions, err := handleRemoteHandshakePayload(payload, s.hs.PeerStatic()) @@ -198,9 +202,7 @@ func (s AuthState) AuthenticateServer(expectedSNI string, responseHeader http.He } func generateNoisePayload(hostKey crypto.PrivKey, localStatic noise.DHKey, ext *pb.NoiseExtensions) ([]byte, error) { - // obtain the public key from the handshake session, so we can sign it with - // our libp2p secret key. - localKeyRaw, err := crypto.MarshalPublicKey(hostKey.GetPublic()) + localPubKeyRaw, err := crypto.MarshalPublicKey(hostKey.GetPublic()) if err != nil { return nil, fmt.Errorf("error serializing libp2p identity key: %w", err) } @@ -214,7 +216,7 @@ func generateNoisePayload(hostKey crypto.PrivKey, localStatic noise.DHKey, ext * // create payload payloadEnc, err := proto.Marshal(&pb.NoiseHandshakePayload{ - IdentityKey: localKeyRaw, + IdentityKey: localPubKeyRaw, IdentitySig: signedPayload, Extensions: ext, }) diff --git a/p2p/http/libp2phttp.go b/p2p/http/libp2phttp.go index 9da308c4d1..e5a7e27168 100644 --- a/p2p/http/libp2phttp.go +++ b/p2p/http/libp2phttp.go @@ -1,3 +1,5 @@ +// HTTP semantics with libp2p. Can use a libp2p stream transport or stock HTTP +// transports. This API is experimental and will likely change soon. Implements libp2p spec #508. package libp2phttp import ( @@ -39,7 +41,8 @@ type WellKnownProtocolMeta struct { type WellKnownProtoMap map[protocol.ID]WellKnownProtocolMeta -type wellKnownHandler struct { +// WellKnownHandler is an http.Handler that serves the .well-known/libp2p resource +type WellKnownHandler struct { wellknownMapMu sync.Mutex wellKnownMapping WellKnownProtoMap } @@ -49,11 +52,7 @@ func StreamHostListen(streamHost host.Host) (net.Listener, error) { return gostream.Listen(streamHost, ProtocolIDForMultistreamSelect) } -func NewWellKnownHandler() wellKnownHandler { - return wellKnownHandler{wellKnownMapping: make(map[protocol.ID]WellKnownProtocolMeta)} -} - -func (h *wellKnownHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { +func (h *WellKnownHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { // Check if the requests accepts JSON accepts := r.Header.Get("Accept") if accepts != "" && !(strings.Contains(accepts, "application/json") || strings.Contains(accepts, "*/*")) { @@ -78,21 +77,29 @@ func (h *wellKnownHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { w.Write(mapping) } -func (h *wellKnownHandler) AddProtocolMapping(p protocol.ID, path string) { +func (h *WellKnownHandler) AddProtocolMapping(p protocol.ID, path string) { h.wellknownMapMu.Lock() + if h.wellKnownMapping == nil { + h.wellKnownMapping = make(map[protocol.ID]WellKnownProtocolMeta) + } h.wellKnownMapping[p] = WellKnownProtocolMeta{Path: path} h.wellknownMapMu.Unlock() } -func (h *wellKnownHandler) RmProtocolMapping(p protocol.ID, path string) { +func (h *WellKnownHandler) RmProtocolMapping(p protocol.ID, path string) { h.wellknownMapMu.Lock() - delete(h.wellKnownMapping, p) + if h.wellKnownMapping != nil { + delete(h.wellKnownMapping, p) + } h.wellknownMapMu.Unlock() } +// HTTPHost is a libp2p host for request/responses with HTTP semantics. This is +// in contrast to a stream-oriented host like the host.Host interface. Warning, +// this is experimental. The API will likely change. type HTTPHost struct { rootHandler http.ServeMux - wk wellKnownHandler + wk WellKnownHandler httpRoundTripper http.RoundTripper recentHTTPAddrs *lru.Cache[peer.ID, httpAddr] peerMetadata *lru.Cache[peer.ID, WellKnownProtoMap] @@ -113,12 +120,12 @@ func New() *HTTPHost { recentHTTP, err := lru.New[peer.ID, httpAddr](recentConnsLimit) peerMetadata, err2 := lru.New[peer.ID, WellKnownProtoMap](PeerMetadataLRUSize) if err != nil || err2 != nil { - // Only happens if size is < 1. We set it to 32, so this should never happen. + // Only happens if size is < 1. We make sure to not do that, so this should never happen. panic(err) } h := &HTTPHost{ - wk: NewWellKnownHandler(), + wk: WellKnownHandler{}, rootHandler: http.ServeMux{}, httpRoundTripper: http.DefaultTransport, recentHTTPAddrs: recentHTTP, @@ -270,6 +277,9 @@ func RoundTripperPreferHTTPTransport(o roundTripperOpts) roundTripperOpts { type RoundTripperOptsFn func(o roundTripperOpts) roundTripperOpts +// NewRoundTripper returns an http.RoundTripper that can fulfill and HTTP +// request to the given server. It may use an HTTP transport or a stream based +// transport. It is valid to pass an empty server.ID and a nil streamHost. func (h *HTTPHost) NewRoundTripper(streamHost host.Host, server peer.AddrInfo, opts ...RoundTripperOptsFn) (http.RoundTripper, error) { options := roundTripperOpts{} for _, o := range opts { @@ -340,6 +350,9 @@ func (h *HTTPHost) NewRoundTripper(streamHost host.Host, server peer.AddrInfo, o } // Otherwise use a stream based transport + if streamHost == nil { + return nil, fmt.Errorf("no http addresses for peer, and no stream host provided") + } if !existingStreamConn { if server.ID == "" { return nil, fmt.Errorf("no http addresses for peer, and no server peer ID provided") @@ -398,7 +411,7 @@ func (h *HTTPHost) GetAndStorePeerProtoMap(roundtripper http.RoundTripper, serve } req.Header.Set("Accept", "application/json") - client := &http.Client{Transport: roundtripper} + client := http.Client{Transport: roundtripper} resp, err := client.Do(req) if err != nil { return nil, err diff --git a/p2p/http/libp2phttp_test.go b/p2p/http/libp2phttp_test.go index c3246b097d..6fcbe7c6e2 100644 --- a/p2p/http/libp2phttp_test.go +++ b/p2p/http/libp2phttp_test.go @@ -216,7 +216,7 @@ func TestRoundTrippers(t *testing.T) { // TODO test with a native Go HTTP server func TestPlainOldHTTPServer(t *testing.T) { mux := http.NewServeMux() - wk := libp2phttp.NewWellKnownHandler() + wk := libp2phttp.WellKnownHandler{} mux.Handle("/.well-known/libp2p", &wk) mux.HandleFunc("/ping/", libp2phttp.Ping)