From 2345ec14f6a7dae96d6cc2dcb72a8d1b0c32ca37 Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Wed, 16 Aug 2023 13:23:17 -0700 Subject: [PATCH] Cleanup public types and add docs --- p2p/http/libp2phttp.go | 64 ++++++++++++++++++------------------- p2p/http/libp2phttp_test.go | 19 +++++------ p2p/http/options.go | 8 ++++- 3 files changed, 48 insertions(+), 43 deletions(-) diff --git a/p2p/http/libp2phttp.go b/p2p/http/libp2phttp.go index a6d0ce6f6d..c450ef3d9f 100644 --- a/p2p/http/libp2phttp.go +++ b/p2p/http/libp2phttp.go @@ -1,5 +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. +// transports. This API is experimental and will likely change soon. Implements [libp2p spec #508](https://github.com/libp2p/specs/pull/508). package libp2phttp import ( @@ -42,12 +42,12 @@ type ProtocolMeta struct { Path string `json:"path"` } -type ProtocolMetaMap map[protocol.ID]ProtocolMeta +type PeerMeta map[protocol.ID]ProtocolMeta // WellKnownHandler is an http.Handler that serves the .well-known/libp2p resource type WellKnownHandler struct { wellknownMapMu sync.Mutex - wellKnownMapping ProtocolMetaMap + wellKnownMapping PeerMeta } // streamHostListen retuns a net.Listener that listens on libp2p streams for HTTP/1.1 messages. @@ -121,7 +121,7 @@ type HTTPHost struct { wk WellKnownHandler // peerMetadata is an lru cache of a peer's well-known protocol map. - peerMetadata *lru.Cache[peer.ID, ProtocolMetaMap] + peerMetadata *lru.Cache[peer.ID, PeerMeta] // createHTTPTransport is used to lazily create the httpTransport in a thread-safe way. createHTTPTransport sync.Once httpTransport *httpTransport @@ -141,8 +141,8 @@ func newHTTPTransport() *httpTransport { } } -func newPeerMetadataCache() *lru.Cache[peer.ID, ProtocolMetaMap] { - peerMetadata, err := lru.New[peer.ID, ProtocolMetaMap](peerMetadataLRUSize) +func newPeerMetadataCache() *lru.Cache[peer.ID, PeerMeta] { + peerMetadata, err := lru.New[peer.ID, PeerMeta](peerMetadataLRUSize) if err != nil { // Only happens if size is < 1. We make sure to not do that, so this should never happen. panic(err) @@ -331,9 +331,9 @@ func (h *HTTPHost) SetHTTPHandlerAtPath(p protocol.ID, path string, handler http h.ServeMux.Handle(path, http.StripPrefix(path, handler)) } -// getPeerProtoMap lets RoundTrippers implement a specific way of caching a peer's protocol mapping. -type getPeerProtoMap interface { - GetPeerProtoMap() (ProtocolMetaMap, error) +// PeerMetadataGetter lets RoundTrippers implement a specific way of caching a peer's protocol mapping. +type PeerMetadataGetter interface { + GetPeerMetadata() (PeerMeta, error) } type streamRoundTripper struct { @@ -352,8 +352,8 @@ func (s *streamReadCloser) Close() error { return s.ReadCloser.Close() } -func (rt *streamRoundTripper) GetPeerProtoMap(server peer.ID) (ProtocolMetaMap, error) { - return rt.httpHost.GetAndStorePeerProtoMap(rt, rt.server) +func (rt *streamRoundTripper) GetPeerProtoMap() (PeerMeta, error) { + return rt.httpHost.getAndStorePeerMetadata(rt, rt.server) } // RoundTrip implements http.RoundTripper. @@ -390,25 +390,25 @@ type roundTripperForSpecificServer struct { targetServerAddr string sni string scheme string - cachedProtos ProtocolMetaMap + cachedProtos PeerMeta } -func (rt *roundTripperForSpecificServer) GetPeerProtoMap() (ProtocolMetaMap, error) { +func (rt *roundTripperForSpecificServer) GetPeerProtoMap() (PeerMeta, error) { // Do we already have the peer's protocol mapping? if rt.cachedProtos != nil { return rt.cachedProtos, nil } // if the underlying roundtripper implements getPeerProtoMap, use that - if g, ok := rt.RoundTripper.(getPeerProtoMap); ok { - wk, err := g.GetPeerProtoMap() + if g, ok := rt.RoundTripper.(PeerMetadataGetter); ok { + wk, err := g.GetPeerMetadata() if err == nil { rt.cachedProtos = wk return wk, nil } } - wk, err := rt.httpHost.GetAndStorePeerProtoMap(rt, rt.server) + wk, err := rt.httpHost.getAndStorePeerMetadata(rt, rt.server) if err == nil { rt.cachedProtos = wk return wk, nil @@ -449,9 +449,9 @@ type namespacedRoundTripper struct { protocolPrefixRaw string } -func (rt *namespacedRoundTripper) GetPeerProtoMap() (ProtocolMetaMap, error) { - if g, ok := rt.RoundTripper.(getPeerProtoMap); ok { - return g.GetPeerProtoMap() +func (rt *namespacedRoundTripper) GetPeerProtoMap() (PeerMeta, error) { + if g, ok := rt.RoundTripper.(PeerMetadataGetter); ok { + return g.GetPeerMetadata() } return nil, fmt.Errorf("can not get peer protocol map. Inner roundtripper does not implement getPeerProtoMap") @@ -471,7 +471,7 @@ func (rt *namespacedRoundTripper) RoundTrip(r *http.Request) (*http.Response, er // NamespaceRoundTripper returns an http.RoundTripper that are scoped to the given protocol on the given server. func (h *HTTPHost) NamespaceRoundTripper(roundtripper http.RoundTripper, p protocol.ID, server peer.ID) (namespacedRoundTripper, error) { - protos, err := h.GetAndStorePeerProtoMap(roundtripper, server) + protos, err := h.getAndStorePeerMetadata(roundtripper, server) if err != nil { return namespacedRoundTripper{}, err } @@ -502,8 +502,9 @@ func (h *HTTPHost) NamespaceRoundTripper(roundtripper http.RoundTripper, p proto // NamespacedClient returns an http.Client that is scoped to the given protocol // on the given server. It creates a new RoundTripper for each call. If you are // creating many namespaced clients, consider creating a round tripper directly -// and namespacing that yourself. -func (h *HTTPHost) NamespacedClient(p protocol.ID, server peer.AddrInfo, opts ...RoundTripperOptsFn) (http.Client, error) { +// and namespacing the roundripper yourself, then creating clients from the +// namespace round tripper. +func (h *HTTPHost) NamespacedClient(p protocol.ID, server peer.AddrInfo, opts ...roundTripperOptsFn) (http.Client, error) { rt, err := h.NewRoundTripper(server, opts...) if err != nil { return http.Client{}, err @@ -520,7 +521,7 @@ func (h *HTTPHost) NamespacedClient(p protocol.ID, server peer.AddrInfo, opts .. // 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. -func (h *HTTPHost) NewRoundTripper(server peer.AddrInfo, opts ...RoundTripperOptsFn) (http.RoundTripper, error) { +func (h *HTTPHost) NewRoundTripper(server peer.AddrInfo, opts ...roundTripperOptsFn) (http.RoundTripper, error) { options := roundTripperOpts{} for _, o := range opts { options = o(options) @@ -599,7 +600,7 @@ func (h *HTTPHost) NewRoundTripper(server peer.AddrInfo, opts ...RoundTripperOpt } } - return NewStreamRoundTripper(h.StreamHost, server.ID), nil + return &streamRoundTripper{h: h.StreamHost, server: server.ID, httpHost: h}, nil } type httpMultiaddr struct { @@ -632,10 +633,6 @@ func parseMultiaddr(addr ma.Multiaddr) httpMultiaddr { return out } -func NewStreamRoundTripper(streamHost host.Host, server peer.ID) http.RoundTripper { - return &streamRoundTripper{h: streamHost, server: server} -} - var httpComponent, _ = ma.NewComponent("http", "") var tlsComponent, _ = ma.NewComponent("tls", "") @@ -671,7 +668,8 @@ func normalizeHTTPMultiaddr(addr ma.Multiaddr) (ma.Multiaddr, bool) { // ProtocolPathPrefix looks up the protocol path in the well-known mapping and // returns it. Will only store the peer's protocol mapping if the server ID is // provided. -func (h *HTTPHost) GetAndStorePeerProtoMap(roundtripper http.RoundTripper, server peer.ID) (ProtocolMetaMap, error) { +func (h *HTTPHost) getAndStorePeerMetadata(roundtripper http.RoundTripper, server peer.ID) (PeerMeta, error) { + fmt.Println(h) if h.peerMetadata == nil { h.peerMetadata = newPeerMetadataCache() } @@ -712,7 +710,7 @@ func (h *HTTPHost) GetAndStorePeerProtoMap(roundtripper http.RoundTripper, serve } } - meta := ProtocolMetaMap{} + meta := PeerMeta{} json.Unmarshal(body[:bytesRead], &meta) if server != "" { h.peerMetadata.Add(server, meta) @@ -723,7 +721,7 @@ func (h *HTTPHost) GetAndStorePeerProtoMap(roundtripper http.RoundTripper, serve // AddPeerMetadata adds a peer's protocol metadata to the http host. Useful if // you have out-of-band knowledge of a peer's protocol mapping. -func (h *HTTPHost) AddPeerMetadata(server peer.ID, meta ProtocolMetaMap) { +func (h *HTTPHost) AddPeerMetadata(server peer.ID, meta PeerMeta) { if h.peerMetadata == nil { h.peerMetadata = newPeerMetadataCache() } @@ -731,7 +729,7 @@ func (h *HTTPHost) AddPeerMetadata(server peer.ID, meta ProtocolMetaMap) { } // GetPeerMetadata gets a peer's cached protocol metadata from the http host. -func (h *HTTPHost) GetPeerMetadata(server peer.ID) (ProtocolMetaMap, bool) { +func (h *HTTPHost) GetPeerMetadata(server peer.ID) (PeerMeta, bool) { if h.peerMetadata == nil { return nil, false } @@ -739,7 +737,7 @@ func (h *HTTPHost) GetPeerMetadata(server peer.ID) (ProtocolMetaMap, bool) { } // RemovePeerMetadata removes a peer's protocol metadata from the http host -func (h *HTTPHost) RemovePeerMetadata(server peer.ID, meta ProtocolMetaMap) { +func (h *HTTPHost) RemovePeerMetadata(server peer.ID, meta PeerMeta) { if h.peerMetadata == nil { return } diff --git a/p2p/http/libp2phttp_test.go b/p2p/http/libp2phttp_test.go index 1125c71396..6cf65da5f2 100644 --- a/p2p/http/libp2phttp_test.go +++ b/p2p/http/libp2phttp_test.go @@ -52,7 +52,8 @@ func TestHTTPOverStreams(t *testing.T) { Addrs: serverHost.Addrs(), }) - clientRT := libp2phttp.NewStreamRoundTripper(clientHost, serverHost.ID()) + clientRT, err := (&libp2phttp.HTTPHost{StreamHost: clientHost}).NewRoundTripper(peer.AddrInfo{ID: serverHost.ID()}) + require.NoError(t, err) client := &http.Client{Transport: clientRT} @@ -204,10 +205,10 @@ func TestRoundTrippers(t *testing.T) { } // Read the .well-known/libp2p resource - wk, err := clientHttpHost.GetAndStorePeerProtoMap(rt, serverHost.ID()) + wk, err := rt.(libp2phttp.PeerMetadataGetter).GetPeerMetadata() require.NoError(t, err) - expectedMap := make(libp2phttp.ProtocolMetaMap) + expectedMap := make(libp2phttp.PeerMeta) expectedMap["/hello"] = libp2phttp.ProtocolMeta{Path: "/hello/"} require.Equal(t, expectedMap, wk) }) @@ -237,7 +238,7 @@ func TestPlainOldHTTPServer(t *testing.T) { testCases := []struct { name string do func(*testing.T, *http.Request) (*http.Response, error) - getWellKnown func(*testing.T) (libp2phttp.ProtocolMetaMap, error) + getWellKnown func(*testing.T) (libp2phttp.PeerMeta, error) }{ { name: "using libp2phttp", @@ -249,11 +250,11 @@ func TestPlainOldHTTPServer(t *testing.T) { client := &http.Client{Transport: rt} return client.Do(request) }, - getWellKnown: func(t *testing.T) (libp2phttp.ProtocolMetaMap, error) { + getWellKnown: func(t *testing.T) (libp2phttp.PeerMeta, error) { var clientHttpHost libp2phttp.HTTPHost rt, err := clientHttpHost.NewRoundTripper(peer.AddrInfo{Addrs: []ma.Multiaddr{ma.StringCast("/ip4/127.0.0.1/tcp/" + serverAddrParts[1] + "/http")}}) require.NoError(t, err) - return clientHttpHost.GetAndStorePeerProtoMap(rt, "") + return rt.(libp2phttp.PeerMetadataGetter).GetPeerMetadata() }, }, { @@ -266,7 +267,7 @@ func TestPlainOldHTTPServer(t *testing.T) { client := http.Client{} return client.Do(request) }, - getWellKnown: func(t *testing.T) (libp2phttp.ProtocolMetaMap, error) { + getWellKnown: func(t *testing.T) (libp2phttp.PeerMeta, error) { client := http.Client{} resp, err := client.Get("http://" + l.Addr().String() + "/.well-known/libp2p") require.NoError(t, err) @@ -274,7 +275,7 @@ func TestPlainOldHTTPServer(t *testing.T) { b, err := io.ReadAll(resp.Body) require.NoError(t, err) - var out libp2phttp.ProtocolMetaMap + var out libp2phttp.PeerMeta err = json.Unmarshal(b, &out) return out, err }, @@ -300,7 +301,7 @@ func TestPlainOldHTTPServer(t *testing.T) { protoMap, err := tc.getWellKnown(t) require.NoError(t, err) - expectedMap := make(libp2phttp.ProtocolMetaMap) + expectedMap := make(libp2phttp.PeerMeta) expectedMap[httpping.PingProtocolID] = libp2phttp.ProtocolMeta{Path: "/ping/"} require.Equal(t, expectedMap, protoMap) }) diff --git a/p2p/http/options.go b/p2p/http/options.go index 3bf0356f81..6d3fd5caf4 100644 --- a/p2p/http/options.go +++ b/p2p/http/options.go @@ -1,6 +1,6 @@ package libp2phttp -type RoundTripperOptsFn func(o roundTripperOpts) roundTripperOpts +type roundTripperOptsFn func(o roundTripperOpts) roundTripperOpts type roundTripperOpts struct { // todo SkipClientAuth bool @@ -8,11 +8,17 @@ type roundTripperOpts struct { ServerMustAuthenticatePeerID bool } +// PreferHTTPTransport tells the roundtripper constructor to prefer using an +// HTTP transport (as opposed to a libp2p stream transport). Useful, for +// example, if you want to attempt to leverage HTTP caching. func PreferHTTPTransport(o roundTripperOpts) roundTripperOpts { o.preferHTTPTransport = true return o } +// ServerMustAuthenticatePeerID tells the roundtripper constructor that we MUST +// authenticate the Server's PeerID. Note: this currently means we can not use a +// native HTTP transport (HTTP peer id authentication is not yet implemented: https://github.com/libp2p/specs/pull/564). func ServerMustAuthenticatePeerID(o roundTripperOpts) roundTripperOpts { o.ServerMustAuthenticatePeerID = true return o