-
Notifications
You must be signed in to change notification settings - Fork 103
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
HTTP retrieval proposal #747
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you @hsanjuan, would be extremely nice if we can pull it off with such small set of changes.
Once we have HTTP basics like user-agent, status code metrics, 503/429/Retry-After (details inline), this is worth testing on Rainbow staging (do A/B test with bitswap-only box and bitswap+http).
ps. Whatever we do, HTTP should be opt-in, with a big EXPERIMENTAL warning.
bitswap/network/http_multiaddr.go
Outdated
return nil, fmt.Errorf("failed to extract host: %w", err) | ||
} | ||
host = hostVal | ||
case "tcp": |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: include "udp" to future proof HTTP/3 deployments?
case "http", "https": | ||
schema = comp.Name |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This may not be enough due to resolved multiaddrs that have explicit /tls/http
or even /tls/sni/../
/ip4/..../tcp/../tls/http
/ip4/..../tcp/../tls/sni/example.com/http
(for example,libp2p.direct
peers could announce HTTP capability this way)- here we need to construct
https://example.com
(or manually ensure request is sent with correct SNI and Host header)
- here we need to construct
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mm to clarify... why wouldn't libp2p direct use a /dns/<libp2p-direct-hostname>/tcp/.../https
address?
// pick the first one. In general there should not be more than one | ||
// url per peer. FIXME: right? | ||
pingURL := urls[0] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm.. there could be ip4 and ip6 and if we are ip4-only trying ip6 will fail.
We should probe them in order and use first one that passed ping.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd expect dns resolution chooses which one to try in that case... but yeah we should try what we have. I would not ping though, just try to make the request directly, probably randomizing the list order.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought this code was somewhere else. IIRC ping is only used to record latency to prioritize...
The pinger code prioritizes ipv4 over ipv6 and chooses the first resolved-entry... I think it may be a bit too much to consider hosts that have significantly different latencies depending on ipv4 or 6.
Ultimately the question is : given a provider URL example.com
. How many providers is that? One or N?
i.e. example.com can resolve to multiple ipv4/ipv6s. Are each of them meant to be a different provider URL to which we trigger requests? or do we assume the request should go to the first result?
I think we should consider it a single provider. If they want to be considered separate providers they can by providing on different urls.
bitswap/network/httpnet/httpnet.go
Outdated
if resp.StatusCode == http.StatusNotFound { | ||
if entry.SendDontHave { | ||
bsresp.AddDontHave(entry.Cid) | ||
} | ||
continue |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd say we should add 403, 410, and 451 here as well.
bitswap/network/httpnet/httpnet.go
Outdated
} | ||
|
||
if resp.StatusCode != http.StatusOK { | ||
err := fmt.Errorf("%s -> %d: %s", sendURL, resp.StatusCode, string(body)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: escape random bytes from random people
err := fmt.Errorf("%s -> %d: %s", sendURL, resp.StatusCode, string(body)) | |
err := fmt.Errorf("%s -> %d: %q", sendURL, resp.StatusCode, string(body)) |
bitswap/network/httpnet/httpnet.go
Outdated
break | ||
} | ||
|
||
if resp.StatusCode != http.StatusOK { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: we need special handling of 429 Too Many Requests and 503 Service Unavailable responses, and stop hammering server if they send us such code.
- We should check if response has
Retry-After
and backoff. - If
Retry-After
is missing, have some default backoff, rather than hammering server that is blocking us.
bitswap/network/router.go
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: have HTTP disabled by default and behind opt-in config flag (or env variable)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need http-specific metrics for response size, duration and status codes to see if there are any issues.
* Support limit when reading blocks * Support using a user-agent (+default) * Support cooldown periods for URLs on error * Make use of connectEventManager * Detect connect/disconnect events * Better error handlinga and refactoring of SendMsg code. * Prioritize HTTP in router when possible
This is a proposal to add HTTP retrieval to Boxo. The current state is highly WIP, but I successfully retrieved something over HTTP, so posting to initiate a discussion over the approach and if we want to pursue it until the end.
Approach
The high-level idea is that most of what lives in
bitswap/client
is actually an "exchange" implementation, with the only real "Bitswap" thing being thatbitswap/network
sends HAS/GET requests over bitswap-protocol streams. As such, we should be able to complementbitswap/network
with an HTTP-retrieval implementation which, instead of fetching things over the bitswap protocol, calls HTTP endpoints as indicated by the provider's/http
addresses entries.Note that conceptually at least, this is not adding HTTP retrieval into bitswap, but promoting most of the bitswap code to be a reference "Exchange" implementation, which is re-usable for different retrieval protocols (bitswap, http...). That is, we would be talking of an "exchange network" component and not a "bitswap network" component. Renames to this extent are still missing.
Implementation
In order to introduce an http-retrieval "exchange network" we need to:
/http
provider.To this end:
/http
addresses in the peerstore of the given peer./http
endpoints when handling a WANT.In my tests plugging it to Kubo, the http-network can be used to retrieve content from a gateway over http. 🥳
The main advantange to this approach is that it is relatively clean to incorporate to the codebase, and keeps most of the code untouched, without having to duplicate any of the complex areas.
Challenges
Bitswap places a lot of importance on managing connectivity events to peers. We avoid requesting things from peers that have not signaled connectivity, we clean peers that have disconnected and re-queue things for peers that disconnect. Thus it seems we must support http-connectivity events. When a libp2p peer connects for bitswap, we know that the connection is setup, handshake has been performed and protocol negotiation has happened. For HTTP these things may not exist so we need to define what means "Connected" (i.e. in the case of https it would mean we have completed SSL handshakes).
Apart from that, the question is what are the elements in the current
bitswap/client
stack that do not apply to HTTP (peerqueues, messagequeues, broadcast, wantsending, prioritization etc.)... and why not? What if a peer disconnects from bitswap but not from http or vice-versa? What if Latency is much worse for bitswap than for http? Perhaps this is all logic for the network-router to know how to choose which network to use to send messages.Otherwise perhaps it is not possible to have a satisfactory implementation this way and we need to start thinking what to copy-paste into a separate "http-exchange" (at least the client part).
Related: #608