mirror of
https://github.com/libp2p/go-libp2p.git
synced 2025-10-06 16:47:02 +08:00

* Add SNI and HTTP_libp2p_token to Noise extensions
* Initial libp2phttp work
* Remove custom response writer
* Initial self review
* Add client for PingHTTP
* Support using a different sni from host
* Add WIP auth support
* Revert "Add WIP auth support"
This reverts commit 8a648d94f7
. Since
current spec doesn't use libp2p-noise-?X auth anymore.
* Remove libp2p-noise auth (removed from spec)
* wip notes
* Fix ordering of header writes
* Change api to have the host do more
* Add options
* Use stream host from option instead of parameter
* Nits
* Add AddPeerMetadata
* Add CustomRootHandler option
* Remove old todos
* Undo Noise changes
* Add comments
* Implement CloseIdleConnections to protect from surprising behavior
* Add todo
* Add ServerMustAuthenticatePeerID option
* WIP work on recent roundtripper logic
* Remove recentHTTPAddrs. We don't need it
* Move http ping to separate package
* Hide internal constants
* HTTPHost has a valid zero-value. Remove constructor and options
* Add https test
* Rename to following naming convention
* Add flag for insecure http
* Return after error
* Rename Rm to Remove
* Rename
* Refactor to always call closeAllListeners
* Rename
* Rename
* Automatically strip prefix when using SetHTTPHandler*
* Hide streamHostListen
* Cleanup public types and add docs
* Fix interface rename
* Remove debug
* PR comments
* Add examples
* Fix example name
* Cleanup unused example
* Add more examples
* Add well known handler example
* Handle empty path
* Fix typo
* Make RoundTripperOption public so users can allocate a slice of options
* Rename HTTPHost to Host
* Make the host.WellKnownHandler public
* Make Add merge PeerMetadata. Introduce SetPeerMetadata
* Rename AddProtocolMapping to AddProtocolMeta. Expand comment
* Expand comment on DefaultClientRoundTripper
And don't use the http.DefaultRoundTripper and cast
* Remove todo
Covered by https://github.com/libp2p/go-libp2p/issues/2511
* Fix comment typo
* Fix comment typo
* Create helper init fn
* Rename NewRoundTripper to NewConstrainedRoundTripper
* Use pointer for Host.ServeMux
* Don't ignore err
* json decode from reader
* Nits
* Move setupListeners to method
* Add comment for streamReadCloser
* Add more comments
* Add todo
* Defer connect until the round trip
* Rebase gostream
* Update p2p/http/libp2phttp.go
Co-authored-by: Andrew Gillis <gammazero@users.noreply.github.com>
---------
Co-authored-by: Andrew Gillis <gammazero@users.noreply.github.com>
354 lines
8.9 KiB
Go
354 lines
8.9 KiB
Go
package libp2phttp_test
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"net"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/libp2p/go-libp2p"
|
|
"github.com/libp2p/go-libp2p/core/peer"
|
|
libp2phttp "github.com/libp2p/go-libp2p/p2p/http"
|
|
ma "github.com/multiformats/go-multiaddr"
|
|
)
|
|
|
|
func ExampleHost_withAStockGoHTTPClient() {
|
|
server := libp2phttp.Host{
|
|
InsecureAllowHTTP: true, // For our example, we'll allow insecure HTTP
|
|
ListenAddrs: []ma.Multiaddr{ma.StringCast("/ip4/127.0.0.1/tcp/0/http")},
|
|
}
|
|
|
|
// A server with a simple echo protocol
|
|
server.SetHTTPHandler("/echo/1.0.0", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Add("Content-Type", "application/octet-stream")
|
|
io.Copy(w, r.Body)
|
|
}))
|
|
go server.Serve()
|
|
defer server.Close()
|
|
|
|
var serverHTTPPort string
|
|
var err error
|
|
for _, a := range server.Addrs() {
|
|
serverHTTPPort, err = a.ValueForProtocol(ma.P_TCP)
|
|
if err == nil {
|
|
break
|
|
}
|
|
}
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
// Make an HTTP request using the Go standard library.
|
|
resp, err := http.Post("http://127.0.0.1:"+serverHTTPPort+"/echo/1.0.0/", "application/octet-stream", strings.NewReader("Hello HTTP"))
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
fmt.Println(string(body))
|
|
|
|
// Output: Hello HTTP
|
|
}
|
|
|
|
func ExampleHost_listenOnHTTPTransportAndStreams() {
|
|
serverStreamHost, err := libp2p.New(libp2p.ListenAddrStrings("/ip4/127.0.0.1/udp/50124/quic-v1"))
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
server := libp2phttp.Host{
|
|
InsecureAllowHTTP: true, // For our example, we'll allow insecure HTTP
|
|
ListenAddrs: []ma.Multiaddr{ma.StringCast("/ip4/127.0.0.1/tcp/50124/http")},
|
|
StreamHost: serverStreamHost,
|
|
}
|
|
go server.Serve()
|
|
defer server.Close()
|
|
|
|
fmt.Println("Server listening on:", server.Addrs())
|
|
// Output: Server listening on: [/ip4/127.0.0.1/udp/50124/quic-v1 /ip4/127.0.0.1/tcp/50124/http]
|
|
}
|
|
|
|
func ExampleHost_overLibp2pStreams() {
|
|
serverStreamHost, err := libp2p.New(libp2p.ListenAddrStrings("/ip4/127.0.0.1/udp/0/quic-v1"))
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
server := libp2phttp.Host{
|
|
StreamHost: serverStreamHost,
|
|
}
|
|
|
|
// A server with a simple echo protocol
|
|
server.SetHTTPHandler("/echo/1.0.0", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Add("Content-Type", "application/octet-stream")
|
|
io.Copy(w, r.Body)
|
|
}))
|
|
go server.Serve()
|
|
defer server.Close()
|
|
|
|
clientStreamHost, err := libp2p.New(libp2p.NoListenAddrs)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
client := libp2phttp.Host{StreamHost: clientStreamHost}
|
|
|
|
// Make an HTTP request using the Go standard library, but over libp2p
|
|
// streams. If the server were listening on an HTTP transport, this could
|
|
// also make the request over the HTTP transport.
|
|
httpClient, err := client.NamespacedClient("/echo/1.0.0", peer.AddrInfo{ID: server.PeerID(), Addrs: server.Addrs()})
|
|
|
|
// Only need to Post to "/" because this client is namespaced to the "/echo/1.0.0" protocol.
|
|
resp, err := httpClient.Post("/", "application/octet-stream", strings.NewReader("Hello HTTP"))
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
fmt.Println(string(body))
|
|
|
|
// Output: Hello HTTP
|
|
}
|
|
|
|
func ExampleHost_Serve() {
|
|
server := libp2phttp.Host{
|
|
InsecureAllowHTTP: true, // For our example, we'll allow insecure HTTP
|
|
ListenAddrs: []ma.Multiaddr{ma.StringCast("/ip4/127.0.0.1/tcp/50221/http")},
|
|
}
|
|
|
|
go server.Serve()
|
|
defer server.Close()
|
|
|
|
fmt.Println(server.Addrs())
|
|
|
|
// Output: [/ip4/127.0.0.1/tcp/50221/http]
|
|
}
|
|
|
|
func ExampleHost_SetHTTPHandler() {
|
|
server := libp2phttp.Host{
|
|
InsecureAllowHTTP: true, // For our example, we'll allow insecure HTTP
|
|
ListenAddrs: []ma.Multiaddr{ma.StringCast("/ip4/127.0.0.1/tcp/50222/http")},
|
|
}
|
|
|
|
server.SetHTTPHandler("/hello/1", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Add("Content-Type", "text/plain")
|
|
w.Write([]byte("Hello World"))
|
|
}))
|
|
|
|
go server.Serve()
|
|
defer server.Close()
|
|
|
|
port, err := server.Addrs()[0].ValueForProtocol(ma.P_TCP)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
resp, err := http.Get("http://127.0.0.1:" + port + "/hello/1/")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
defer resp.Body.Close()
|
|
respBody, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
fmt.Println(string(respBody))
|
|
|
|
// Output: Hello World
|
|
}
|
|
|
|
func ExampleHost_SetHTTPHandlerAtPath() {
|
|
server := libp2phttp.Host{
|
|
InsecureAllowHTTP: true, // For our example, we'll allow insecure HTTP
|
|
ListenAddrs: []ma.Multiaddr{ma.StringCast("/ip4/127.0.0.1/tcp/50224/http")},
|
|
}
|
|
|
|
server.SetHTTPHandlerAtPath("/hello/1", "/other-place/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Add("Content-Type", "text/plain")
|
|
w.Write([]byte("Hello World"))
|
|
}))
|
|
|
|
go server.Serve()
|
|
defer server.Close()
|
|
|
|
port, err := server.Addrs()[0].ValueForProtocol(ma.P_TCP)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
resp, err := http.Get("http://127.0.0.1:" + port + "/other-place/")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
defer resp.Body.Close()
|
|
respBody, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
fmt.Println(string(respBody))
|
|
|
|
// Output: Hello World
|
|
}
|
|
|
|
func ExampleHost_NamespacedClient() {
|
|
var client libp2phttp.Host
|
|
|
|
// Create the server
|
|
server := libp2phttp.Host{
|
|
InsecureAllowHTTP: true, // For our example, we'll allow insecure HTTP
|
|
ListenAddrs: []ma.Multiaddr{ma.StringCast("/ip4/127.0.0.1/tcp/50221/http")},
|
|
}
|
|
|
|
server.SetHTTPHandlerAtPath("/hello/1", "/other-place/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Add("Content-Type", "text/plain")
|
|
w.Write([]byte("Hello World"))
|
|
}))
|
|
|
|
go server.Serve()
|
|
defer server.Close()
|
|
|
|
// Create an http.Client that is namespaced to this protocol.
|
|
httpClient, err := client.NamespacedClient("/hello/1", peer.AddrInfo{ID: server.PeerID(), Addrs: server.Addrs()})
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
resp, err := httpClient.Get("/")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
defer resp.Body.Close()
|
|
respBody, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
fmt.Println(string(respBody))
|
|
|
|
// Output: Hello World
|
|
}
|
|
|
|
func ExampleHost_NamespaceRoundTripper() {
|
|
var client libp2phttp.Host
|
|
|
|
// Create the server
|
|
server := libp2phttp.Host{
|
|
InsecureAllowHTTP: true, // For our example, we'll allow insecure HTTP
|
|
ListenAddrs: []ma.Multiaddr{ma.StringCast("/ip4/127.0.0.1/tcp/50223/http")},
|
|
}
|
|
|
|
server.SetHTTPHandler("/hello/1", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Add("Content-Type", "text/plain")
|
|
w.Write([]byte("Hello World"))
|
|
}))
|
|
|
|
go server.Serve()
|
|
defer server.Close()
|
|
|
|
// Create an http.Roundtripper for the server
|
|
rt, err := client.NewConstrainedRoundTripper(peer.AddrInfo{ID: server.PeerID(), Addrs: server.Addrs()})
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
// Namespace this roundtripper to a specific protocol
|
|
rt, err = client.NamespaceRoundTripper(rt, "/hello/1", server.PeerID())
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
resp, err := (&http.Client{Transport: rt}).Get("/")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
defer resp.Body.Close()
|
|
respBody, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
fmt.Println(string(respBody))
|
|
|
|
// Output: Hello World
|
|
}
|
|
|
|
func ExampleHost_NewConstrainedRoundTripper() {
|
|
var client libp2phttp.Host
|
|
|
|
// Create the server
|
|
server := libp2phttp.Host{
|
|
InsecureAllowHTTP: true, // For our example, we'll allow insecure HTTP
|
|
ListenAddrs: []ma.Multiaddr{ma.StringCast("/ip4/127.0.0.1/tcp/50225/http")},
|
|
}
|
|
|
|
server.SetHTTPHandler("/hello/1", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Add("Content-Type", "text/plain")
|
|
w.Write([]byte("Hello World"))
|
|
}))
|
|
|
|
go server.Serve()
|
|
defer server.Close()
|
|
|
|
// Create an http.Roundtripper for the server
|
|
rt, err := client.NewConstrainedRoundTripper(peer.AddrInfo{ID: server.PeerID(), Addrs: server.Addrs()})
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
resp, err := (&http.Client{Transport: rt}).Get("/hello/1")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
defer resp.Body.Close()
|
|
respBody, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
fmt.Println(string(respBody))
|
|
|
|
// Output: Hello World
|
|
}
|
|
|
|
func ExampleWellKnownHandler() {
|
|
var h libp2phttp.WellKnownHandler
|
|
h.AddProtocolMeta("/hello/1", libp2phttp.ProtocolMeta{
|
|
Path: "/hello-path/",
|
|
})
|
|
|
|
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
defer listener.Close()
|
|
// Serve `.well-known/libp2p`. Note, this is handled automatically if you use the libp2phttp.Host.
|
|
go http.Serve(listener, &h)
|
|
|
|
// Get the `.well-known/libp2p` resource
|
|
resp, err := http.Get("http://" + listener.Addr().String() + "/.well-known/libp2p")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
defer resp.Body.Close()
|
|
respBody, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
fmt.Println(string(respBody))
|
|
// Output: {"/hello/1":{"path":"/hello-path/"}}
|
|
|
|
}
|