Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ The following emojis are used to highlight certain changes:

### Added

- `routing/http`: ✨ Support for HTTP(S) URLs alongside multiaddrs in Delegated Routing API ([IPIP-518](https://github.com/ipfs/specs/pull/518))
- The `Addrs` field in the Peer schema now accepts both multiaddr strings (starting with `/`) and HTTP(S) URLs
- Addresses are parsed with defensive programming: unsupported addresses are skipped, and processing continues with remaining addresses
- Special protocol filtering logic: `tls` filter matches both `/tls` multiaddrs and `https://` URLs, `http` filter matches both multiaddrs and URLs with HTTP semantics
- Schema-agnostic implementation allows any URI scheme (not just http/https) for future extensibility
- Includes `ToMultiaddr()` method for backward compatibility during transition period: converts HTTP(S) URLs to multiaddrs using `/https` (matching IPNI convention) and `/dns` (for dual-stack support)

### Changed

- upgrade to `go-libp2p` [v0.44.0](https://github.com/libp2p/go-libp2p/releases/tag/v0.44.0)
Expand Down
5 changes: 3 additions & 2 deletions routing/http/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ type Client struct {
accepts string

peerID peer.ID
addrs []types.Multiaddr
addrs types.Addresses
identity crypto.PrivKey

// Called immediately after signing a provide request. It is used
Expand Down Expand Up @@ -182,8 +182,9 @@ func WithUserAgent(ua string) Option {
func WithProviderInfo(peerID peer.ID, addrs []multiaddr.Multiaddr) Option {
return func(c *Client) error {
c.peerID = peerID
c.addrs = make(types.Addresses, 0, len(addrs))
for _, a := range addrs {
c.addrs = append(c.addrs, types.Multiaddr{Multiaddr: a})
c.addrs = append(c.addrs, *types.NewAddressFromMultiaddr(a))
}
return nil
}
Expand Down
15 changes: 9 additions & 6 deletions routing/http/client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,18 +146,21 @@ func makeCID() cid.Cid {
return c
}

func drAddrsToAddrs(drmas []types.Multiaddr) (addrs []multiaddr.Multiaddr) {
for _, a := range drmas {
addrs = append(addrs, a.Multiaddr)
func drAddrsToAddrs(draddrs types.Addresses) (addrs []multiaddr.Multiaddr) {
for _, a := range draddrs {
if a.IsMultiaddr() {
addrs = append(addrs, a.Multiaddr())
}
}
return
}

func addrsToDRAddrs(addrs []multiaddr.Multiaddr) (drmas []types.Multiaddr) {
func addrsToDRAddrs(addrs []multiaddr.Multiaddr) types.Addresses {
draddrs := make(types.Addresses, 0, len(addrs))
for _, a := range addrs {
drmas = append(drmas, types.Multiaddr{Multiaddr: a})
draddrs = append(draddrs, *types.NewAddressFromMultiaddr(a))
}
return
return draddrs
}

func makePeerRecord(protocols []string) types.PeerRecord {
Expand Down
18 changes: 15 additions & 3 deletions routing/http/contentrouter/contentrouter.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,11 @@ func readProviderResponses(ctx context.Context, iter iter.ResultIter[types.Recor

var addrs []multiaddr.Multiaddr
for _, a := range result.Addrs {
addrs = append(addrs, a.Multiaddr)
// Try to convert to multiaddr for backward compatibility
if ma := a.ToMultiaddr(); ma != nil {
addrs = append(addrs, ma)
}
// Note: Non-HTTP URLs are skipped as they can't be represented as multiaddrs
}

select {
Expand Down Expand Up @@ -167,7 +171,11 @@ func readProviderResponses(ctx context.Context, iter iter.ResultIter[types.Recor

var addrs []multiaddr.Multiaddr
for _, a := range result.Addrs {
addrs = append(addrs, a.Multiaddr)
// Try to convert to multiaddr for backward compatibility
if ma := a.ToMultiaddr(); ma != nil {
addrs = append(addrs, ma)
}
// Note: Non-HTTP URLs are skipped as they can't be represented as multiaddrs
}

select {
Expand Down Expand Up @@ -216,7 +224,11 @@ func (c *contentRouter) FindPeer(ctx context.Context, pid peer.ID) (peer.AddrInf

var addrs []multiaddr.Multiaddr
for _, a := range res.Val.Addrs {
addrs = append(addrs, a.Multiaddr)
// Try to convert to multiaddr for backward compatibility
if ma := a.ToMultiaddr(); ma != nil {
addrs = append(addrs, ma)
}
// Note: Non-HTTP URLs are skipped as they can't be represented as multiaddrs
}

// If there are no addresses there's nothing of value to return
Expand Down
2 changes: 1 addition & 1 deletion routing/http/contentrouter/contentrouter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ func TestFindPeer(t *testing.T) {
{
Schema: types.SchemaPeer,
ID: &p1,
Addrs: []types.Multiaddr{{Multiaddr: multiaddr.StringCast("/ip4/1.2.3.4/tcp/1234")}},
Addrs: types.Addresses{*types.NewAddressFromMultiaddr(multiaddr.StringCast("/ip4/1.2.3.4/tcp/1234"))},
Protocols: []string{"transport-bitswap"},
},
}
Expand Down
70 changes: 37 additions & 33 deletions routing/http/filters/filters.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"github.com/ipfs/boxo/routing/http/types"
"github.com/ipfs/boxo/routing/http/types/iter"
logging "github.com/ipfs/go-log/v2"
"github.com/multiformats/go-multiaddr"
)

var logger = logging.Logger("routing/http/filters")
Expand Down Expand Up @@ -159,76 +158,81 @@ func applyFilters(provider *types.PeerRecord, filterAddrs, filterProtocols []str
return provider
}

// applyAddrFilter filters a list of multiaddresses based on the provided filter query.
// applyAddrFilter filters a list of addresses based on the provided filter query.
//
// Parameters:
// - addrs: A slice of types.Multiaddr to be filtered.
// - addrs: A slice of types.Address to be filtered.
// - filterAddrsQuery: A slice of strings representing the filter criteria.
//
// The function supports both positive and negative filters:
// - Positive filters (e.g., "tcp", "udp") include addresses that match the specified protocols.
// - Positive filters (e.g., "tcp", "udp", "http") include addresses that match the specified protocols.
// - Negative filters (e.g., "!tcp", "!udp") exclude addresses that match the specified protocols.
// - "unknown" can be passed to include providers whose addresses are unknown or cannot be parsed.
//
// If no filters are provided, the original list of addresses is returned unchanged.
// If only negative filters are provided, addresses not matching any negative filter are included.
// If positive filters are provided, only addresses matching at least one positive filter (and no negative filters) are included.
// If both positive and negative filters are provided, the address must match at least one positive filter and no negative filters to be included.
//
// Returns:
// A new slice of types.Multiaddr containing only the addresses that pass the filter criteria.
func applyAddrFilter(addrs []types.Multiaddr, filterAddrsQuery []string) []types.Multiaddr {
// A new slice of types.Address containing only the addresses that pass the filter criteria.
func applyAddrFilter(addrs types.Addresses, filterAddrsQuery []string) types.Addresses {
if len(filterAddrsQuery) == 0 {
return addrs
}

var filteredAddrs []types.Multiaddr
var positiveFilters, negativeFilters []multiaddr.Protocol
var filteredAddrs types.Addresses
var positiveFilters, negativeFilters []string
var includeUnknown bool

// Separate positive and negative filters
for _, filter := range filterAddrsQuery {
if strings.HasPrefix(filter, "!") {
negativeFilters = append(negativeFilters, multiaddr.ProtocolWithName(filter[1:]))
if filter == "unknown" {
includeUnknown = true
} else if strings.HasPrefix(filter, "!") {
negativeFilters = append(negativeFilters, filter[1:])
} else {
positiveFilters = append(positiveFilters, multiaddr.ProtocolWithName(filter))
positiveFilters = append(positiveFilters, filter)
}
}

for _, addr := range addrs {
protocols := addr.Protocols()
// Handle unknown (unparseable) addresses
if !addr.IsValid() {
if includeUnknown {
filteredAddrs = append(filteredAddrs, addr)
}
continue
}

// Check negative filters
if containsAny(protocols, negativeFilters) {
shouldExclude := false
for _, filter := range negativeFilters {
if addr.HasProtocol(filter) {
shouldExclude = true
break
}
}
if shouldExclude {
continue
}

// If no positive filters or matches a positive filter, include the address
if len(positiveFilters) == 0 || containsAny(protocols, positiveFilters) {
if len(positiveFilters) == 0 {
filteredAddrs = append(filteredAddrs, addr)
} else {
for _, filter := range positiveFilters {
if addr.HasProtocol(filter) {
filteredAddrs = append(filteredAddrs, addr)
break
}
}
}
}

return filteredAddrs
}

// Helper function to check if protocols contain any of the filters
func containsAny(protocols []multiaddr.Protocol, filters []multiaddr.Protocol) bool {
for _, filter := range filters {
if containsProtocol(protocols, filter) {
return true
}
}
return false
}

func containsProtocol(protos []multiaddr.Protocol, proto multiaddr.Protocol) bool {
for _, p := range protos {
if p.Code == proto.Code {
return true
}
}
return false
}

// protocolsAllowed returns true if the peerProtocols are allowed by the filter protocols.
func protocolsAllowed(peerProtocols []string, filterProtocols []string) bool {
if len(filterProtocols) == 0 {
Expand Down
Loading
Loading