Skip to content

Commit

Permalink
Cleanup public types and add docs
Browse files Browse the repository at this point in the history
  • Loading branch information
MarcoPolo committed Aug 16, 2023
1 parent 14d809c commit 2345ec1
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 43 deletions.
64 changes: 31 additions & 33 deletions p2p/http/libp2phttp.go
Original file line number Diff line number Diff line change
@@ -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 (
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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 {
Expand All @@ -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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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")
Expand All @@ -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
}
Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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", "")

Expand Down Expand Up @@ -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()
}
Expand Down Expand Up @@ -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)
Expand All @@ -723,23 +721,23 @@ 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()
}
h.peerMetadata.Add(server, meta)
}

// 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
}
return h.peerMetadata.Get(server)
}

// 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
}
Expand Down
19 changes: 10 additions & 9 deletions p2p/http/libp2phttp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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}

Expand Down Expand Up @@ -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)
})
Expand Down Expand Up @@ -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",
Expand All @@ -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()
},
},
{
Expand All @@ -266,15 +267,15 @@ 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)

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
},
Expand All @@ -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)
})
Expand Down
8 changes: 7 additions & 1 deletion p2p/http/options.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
package libp2phttp

type RoundTripperOptsFn func(o roundTripperOpts) roundTripperOpts
type roundTripperOptsFn func(o roundTripperOpts) roundTripperOpts

type roundTripperOpts struct {
// todo SkipClientAuth bool
preferHTTPTransport bool
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
Expand Down

0 comments on commit 2345ec1

Please sign in to comment.