From e0315b9835454ee19c4e3ac222b1b9215da22bda Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Wed, 5 Jun 2024 09:46:32 -0700 Subject: [PATCH 1/2] Add support for http-path --- multiaddr_test.go | 34 ++++++++++++++++++++++++++++++++++ protocols.go | 9 +++++++++ transcoders.go | 16 ++++++++++++++++ 3 files changed, 59 insertions(+) diff --git a/multiaddr_test.go b/multiaddr_test.go index b33e94d..8686cd9 100644 --- a/multiaddr_test.go +++ b/multiaddr_test.go @@ -627,6 +627,7 @@ func TestRoundTrip(t *testing.T) { "/ip4/127.0.0.1/udp/1234/quic-v1/webtransport/certhash/uEiDDq4_xNyDorZBH3TlGazyJdOWSwvo4PUo5YHFMrvDE8g", "/p2p/QmbHVEEepCi7rn7VL7Exxpd2Ci9NNB6ifvqwhsrbRMgQFP", "/p2p/QmbHVEEepCi7rn7VL7Exxpd2Ci9NNB6ifvqwhsrbRMgQFP/unix/a/b/c", + "/http-path/tmp%2Fbar", } { ma, err := NewMultiaddr(s) if err != nil { @@ -923,3 +924,36 @@ func TestDNS(t *testing.T) { t.Fatal("expected equality") } } + +func TestHTTPPath(t *testing.T) { + t.Run("bad addr", func(t *testing.T) { + badAddr := "/http-path/thisIsMissingAfullBytes%f" + _, err := NewMultiaddr(badAddr) + require.Error(t, err) + }) + + t.Run("round trip", func(t *testing.T) { + cases := []string{ + "/http-path/tmp%2Fbar", + "/http-path/tmp%2Fbar%2Fbaz", + "/http-path/foo", + } + for _, c := range cases { + m, err := NewMultiaddr(c) + require.NoError(t, err) + require.Equal(t, c, m.String()) + } + }) + + t.Run("value for protocol", func(t *testing.T) { + m := StringCast("/http-path/tmp%2Fbar") + v, err := m.ValueForProtocol(P_HTTP_PATH) + require.NoError(t, err) + // This gives us the url escaped version + require.Equal(t, "tmp%2Fbar", v) + + // If we want the raw unescaped version, we can use the component and read it + _, component := SplitLast(m) + require.Equal(t, "tmp/bar", string(component.RawValue())) + }) +} diff --git a/protocols.go b/protocols.go index b01e6cb..d3117b2 100644 --- a/protocols.go +++ b/protocols.go @@ -26,6 +26,7 @@ const ( P_P2P = 421 P_IPFS = P_P2P // alias for backwards compatibility P_HTTP = 480 + P_HTTP_PATH = 481 P_HTTPS = 443 // deprecated alias for /tls/http P_ONION = 444 // also for backwards compatibility P_ONION3 = 445 @@ -206,6 +207,13 @@ var ( Code: P_HTTP, VCode: CodeToVarint(P_HTTP), } + protoHTTPPath = Protocol{ + Name: "http-path", + Code: P_HTTP_PATH, + VCode: CodeToVarint(P_HTTP_PATH), + Size: LengthPrefixedVarSize, + Transcoder: TranscoderHTTPPath, + } protoHTTPS = Protocol{ Name: "https", Code: P_HTTPS, @@ -301,6 +309,7 @@ func init() { protoWEBTRANSPORT, protoCERTHASH, protoHTTP, + protoHTTPPath, protoHTTPS, protoP2P, protoUNIX, diff --git a/transcoders.go b/transcoders.go index 5387740..18d7a5a 100644 --- a/transcoders.go +++ b/transcoders.go @@ -8,6 +8,7 @@ import ( "errors" "fmt" "net" + "net/url" "strconv" "strings" @@ -454,3 +455,18 @@ func validateCertHash(b []byte) error { _, err := mh.Decode(b) return err } + +var TranscoderHTTPPath = NewTranscoderFromFunctions(httpPathStB, httpPathBtS, validateHTTPPath) + +func httpPathStB(s string) ([]byte, error) { + unescaped, err := url.QueryUnescape(s) + return []byte(unescaped), err +} + +func httpPathBtS(b []byte) (string, error) { + return url.QueryEscape(string(b)), nil +} + +func validateHTTPPath(b []byte) error { + return nil // We can represent any byte slice when we escape it. +} From d31f376555b7bc87935926ce456aeb15f609c0de Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Wed, 12 Jun 2024 16:37:38 -0700 Subject: [PATCH 2/2] PR comments --- multiaddr_test.go | 19 ++++++++++++++++++- transcoders.go | 12 ++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/multiaddr_test.go b/multiaddr_test.go index 8686cd9..6d160b4 100644 --- a/multiaddr_test.go +++ b/multiaddr_test.go @@ -192,6 +192,11 @@ var good = []string{ "/ip4/127.0.0.1/tcp/127/wss", "/ip4/127.0.0.1/tcp/127/webrtc-direct", "/ip4/127.0.0.1/tcp/127/webrtc", + "/http-path/tmp%2Fbar", + "/http-path/tmp%2Fbar%2Fbaz", + "/http-path/foo", + "/ip4/127.0.0.1/tcp/0/p2p/12D3KooWCryG7Mon9orvQxcS1rYZjotPgpwoJNHHKcLLfE4Hf5mV/http-path/foo", + "/ip4/127.0.0.1/tcp/443/tls/sni/example.com/http/http-path/foo", } func TestConstructSucceeds(t *testing.T) { @@ -927,16 +932,28 @@ func TestDNS(t *testing.T) { func TestHTTPPath(t *testing.T) { t.Run("bad addr", func(t *testing.T) { - badAddr := "/http-path/thisIsMissingAfullBytes%f" + badAddr := "/http-path/thisIsMissingAfullByte%f" _, err := NewMultiaddr(badAddr) require.Error(t, err) }) + t.Run("only reads the http-path part", func(t *testing.T) { + addr := "/http-path/tmp%2Fbar/p2p-circuit" // The http-path only reference the part immediately after it. It does not include the rest of the multiaddr (like the /path component sometimes does) + m, err := NewMultiaddr(addr) + require.NoError(t, err) + m.ValueForProtocol(P_HTTP_PATH) + v, err := m.ValueForProtocol(P_HTTP_PATH) + require.NoError(t, err) + require.Equal(t, "tmp%2Fbar", v) + }) + t.Run("round trip", func(t *testing.T) { cases := []string{ "/http-path/tmp%2Fbar", "/http-path/tmp%2Fbar%2Fbaz", "/http-path/foo", + "/ip4/127.0.0.1/tcp/0/p2p/12D3KooWCryG7Mon9orvQxcS1rYZjotPgpwoJNHHKcLLfE4Hf5mV/http-path/foo", + "/ip4/127.0.0.1/tcp/443/tls/sni/example.com/http/http-path/foo", } for _, c := range cases { m, err := NewMultiaddr(c) diff --git a/transcoders.go b/transcoders.go index 18d7a5a..0d0327e 100644 --- a/transcoders.go +++ b/transcoders.go @@ -460,13 +460,25 @@ var TranscoderHTTPPath = NewTranscoderFromFunctions(httpPathStB, httpPathBtS, va func httpPathStB(s string) ([]byte, error) { unescaped, err := url.QueryUnescape(s) + if err != nil { + return nil, err + } + if len(unescaped) == 0 { + return nil, fmt.Errorf("empty http path is not allowed") + } return []byte(unescaped), err } func httpPathBtS(b []byte) (string, error) { + if len(b) == 0 { + return "", fmt.Errorf("empty http path is not allowed") + } return url.QueryEscape(string(b)), nil } func validateHTTPPath(b []byte) error { + if len(b) == 0 { + return fmt.Errorf("empty http path is not allowed") + } return nil // We can represent any byte slice when we escape it. }