@@ -523,19 +523,18 @@ func (rt *streamRoundTripper) RoundTrip(r *http.Request) (*http.Response, error)
523
523
}
524
524
resp .Body = & streamReadCloser {resp .Body , s }
525
525
526
- locUrl , err := resp .Location ()
527
- if err == nil {
528
- // Location url in response. Is this a multiaddr uri? and is it relative?
529
- // If it's relative we want to convert it to an absolute multiaddr uri
530
- // so that the next request knows how to reach the endpoint.
531
- if locUrl .Scheme == "multiaddr" && resp .Request .URL .Scheme == "multiaddr" {
532
- // Check if it's a relative URI and turn it into an absolute one
533
- u , err := relativeMultiaddrURIToAbs (resp .Request .URL , locUrl )
534
- if err == nil {
535
- // It was a relative URI and we were able to convert it to an absolute one
536
- // Update the location header to be an absolute multiaddr uri
537
- resp .Header .Set ("Location" , u .String ())
526
+ if r .URL .Scheme == "multiaddr" {
527
+ // This was a multiaddr uri, we may need to convert relative URI
528
+ // references to absolute multiaddr ones so that the next request
529
+ // knows how to reach the endpoint.
530
+ locationHeader := resp .Header .Get ("Location" )
531
+ if locationHeader != "" {
532
+ u , err := locationHeaderToMultiaddrURI (r .URL , locationHeader )
533
+ if err != nil {
534
+ return nil , fmt .Errorf ("failed to convert location header (%s) from request (%s) to multiaddr uri: %w" , locationHeader , r .URL , err )
538
535
}
536
+ // Update the location header to be an absolute multiaddr uri
537
+ resp .Header .Set ("Location" , u .String ())
539
538
}
540
539
}
541
540
@@ -544,39 +543,68 @@ func (rt *streamRoundTripper) RoundTrip(r *http.Request) (*http.Response, error)
544
543
return resp , nil
545
544
}
546
545
547
- var errNotRelative = errors .New ("not relative" )
548
-
549
- // relativeMultiaddrURIToAbs takes a relative multiaddr URI and turns it into an
550
- // absolute one. Useful, for example, when a server gives us a relative URI for a redirect.
551
- // It allows the following request (the one after redirected) to reach the correct server.
552
- func relativeMultiaddrURIToAbs (original * url.URL , relative * url.URL ) (* url.URL , error ) {
553
- // Is this a relative uri? We know if it is because non-relative URI's of the form:
554
- // "multiaddr:/ip4/1.2.3.4/tcp/9899" when parsed by Go's url package will have url.OmitHost == true
555
- // But if it is relative (just a path to an http resource e.g. /here-instead)
556
- // a redirect will inherit the multiaddr scheme, but set url.OmitHost == false. It will also stringify as something like
557
- // multiaddr://here-instead.
558
- if relative .OmitHost {
559
- // Not relative (at least we can't tell). Nothing we can do here
560
- return nil , errNotRelative
546
+ // locationHeaderToMultiaddrURI takes our original URL and the response's Location header
547
+ // and, if the location header is relative, turns it into an absolute multiaddr uri.
548
+ // Refer to https://www.rfc-editor.org/rfc/rfc3986#section-4.2 for the
549
+ // definition of a Relative Reference.
550
+ func locationHeaderToMultiaddrURI (original * url.URL , locationHeader string ) (* url.URL , error ) {
551
+ if locationHeader == "" {
552
+ return nil , errors .New ("location header is empty" )
553
+ }
554
+ if strings .HasPrefix (locationHeader , "//" ) {
555
+ // This is a network path reference. We don't support these.
556
+ return nil , errors .New ("network path reference not supported" )
557
+ }
558
+
559
+ firstSegment := strings .SplitN (locationHeader , "/" , 2 )[0 ]
560
+ if strings .Contains (firstSegment , ":" ) {
561
+ // This location contains a scheme, so it's an absolute uri.
562
+ return url .Parse (locationHeader )
563
+ }
564
+
565
+ // It's a relative reference. We need to resolve it against the original URL.
566
+ if original .Scheme != "multiaddr" {
567
+ return nil , errors .New ("original uri is not a multiaddr" )
561
568
}
569
+
570
+ // Parse the original multiaddr
562
571
originalStr := original .RawPath
563
572
if originalStr == "" {
564
573
originalStr = original .Path
565
574
}
566
575
originalMa , err := ma .NewMultiaddr (originalStr )
567
576
if err != nil {
568
- return nil , errors .New ("original uri is not a multiaddr" )
577
+ return nil , fmt .Errorf ("original uri is not a valid multiaddr: %w" , err )
578
+ }
579
+
580
+ // Get the target http path
581
+ var targetHTTPPath string
582
+ for _ , c := range originalMa {
583
+ if c .Protocol ().Code == ma .P_HTTP_PATH {
584
+ targetHTTPPath = string (c .RawValue ())
585
+ break
586
+ }
587
+ }
588
+
589
+ // Resolve reference from targetURL and relativeURL
590
+ targetURL := url.URL {Path : targetHTTPPath }
591
+ relativeURL := url.URL {Path : locationHeader }
592
+ resolved := targetURL .ResolveReference (& relativeURL )
593
+
594
+ resolvedHTTPPath := resolved .Path
595
+ if len (resolvedHTTPPath ) > 0 && resolvedHTTPPath [0 ] == '/' {
596
+ resolvedHTTPPath = resolvedHTTPPath [1 :] // trim leading slash. It's implied by the http-path component
569
597
}
570
598
571
- relativePathComponent , err := ma .NewComponent ("http-path" , relative . Path )
599
+ resolvedHTTPPathComponent , err := ma .NewComponent ("http-path" , resolvedHTTPPath )
572
600
if err != nil {
573
- return nil , errors . New ("relative path is not a valid http-path" )
601
+ return nil , fmt . Errorf ("relative path is not a valid http-path: %w" , err )
574
602
}
575
603
576
604
withoutPath , afterAndIncludingPath := ma .SplitFunc (originalMa , func (c ma.Component ) bool {
577
605
return c .Protocol ().Code == ma .P_HTTP_PATH
578
606
})
579
- withNewPath := withoutPath .AppendComponent (relativePathComponent )
607
+ withNewPath := withoutPath .AppendComponent (resolvedHTTPPathComponent )
580
608
if len (afterAndIncludingPath ) > 1 {
581
609
// Include after path since it may include other parts
582
610
withNewPath = append (withNewPath , afterAndIncludingPath [1 :]... )
0 commit comments