@@ -25,6 +25,7 @@ import (
25
25
"github.com/libp2p/go-libp2p/core/peer"
26
26
"github.com/libp2p/go-libp2p/core/peerstore"
27
27
"github.com/libp2p/go-libp2p/core/protocol"
28
+ httpauth "github.com/libp2p/go-libp2p/p2p/http/auth"
28
29
gostream "github.com/libp2p/go-libp2p/p2p/net/gostream"
29
30
ma "github.com/multiformats/go-multiaddr"
30
31
)
@@ -44,6 +45,23 @@ const LegacyWellKnownProtocols = "/.well-known/libp2p"
44
45
const peerMetadataLimit = 8 << 10 // 8KB
45
46
const peerMetadataLRUSize = 256 // How many different peer's metadata to keep in our LRU cache
46
47
48
+ type clientPeerIDContextKey struct {}
49
+ type serverPeerIDContextKey struct {}
50
+
51
+ func ClientPeerID (r * http.Request ) peer.ID {
52
+ if id , ok := r .Context ().Value (clientPeerIDContextKey {}).(peer.ID ); ok {
53
+ return id
54
+ }
55
+ return ""
56
+ }
57
+
58
+ func ServerPeerID (r * http.Response ) peer.ID {
59
+ if id , ok := r .Request .Context ().Value (serverPeerIDContextKey {}).(peer.ID ); ok {
60
+ return id
61
+ }
62
+ return ""
63
+ }
64
+
47
65
// ProtocolMeta is metadata about a protocol.
48
66
type ProtocolMeta struct {
49
67
// Path defines the HTTP Path prefix used for this protocol
@@ -134,6 +152,12 @@ type Host struct {
134
152
// InsecureAllowHTTP indicates if the server is allowed to serve unencrypted
135
153
// HTTP requests over TCP.
136
154
InsecureAllowHTTP bool
155
+
156
+ // ServerPeerIDAuth TODO add comment
157
+ ServerPeerIDAuth * httpauth.ServerPeerIDAuth
158
+ // ClientPeerIDAuth TODO add comment
159
+ ClientPeerIDAuth * httpauth.ClientPeerIDAuth
160
+
137
161
// ServeMux is the http.ServeMux used by the server to serve requests. If
138
162
// nil, a new serve mux will be created. Users may manually add handlers to
139
163
// this mux instead of using `SetHTTPHandler`, but if they do, they should
@@ -264,15 +288,18 @@ func (h *Host) setupListeners(listenerErrCh chan error) error {
264
288
if parsedAddr .useHTTPS {
265
289
go func () {
266
290
srv := http.Server {
267
- Handler : h . ServeMux ,
291
+ Handler : maybeDecorateContextWithAuthMiddleware ( h . ServerPeerIDAuth , h . ServeMux ) ,
268
292
TLSConfig : h .TLSConfig ,
269
293
}
270
294
listenerErrCh <- srv .ServeTLS (l , "" , "" )
271
295
}()
272
296
h .httpTransport .listenAddrs = append (h .httpTransport .listenAddrs , listenAddr )
273
297
} else if h .InsecureAllowHTTP {
274
298
go func () {
275
- listenerErrCh <- http .Serve (l , h .ServeMux )
299
+ srv := http.Server {
300
+ Handler : maybeDecorateContextWithAuthMiddleware (h .ServerPeerIDAuth , h .ServeMux ),
301
+ }
302
+ listenerErrCh <- srv .Serve (l )
276
303
}()
277
304
h .httpTransport .listenAddrs = append (h .httpTransport .listenAddrs , listenAddr )
278
305
} else {
@@ -332,7 +359,20 @@ func (h *Host) Serve() error {
332
359
h .httpTransport .listenAddrs = append (h .httpTransport .listenAddrs , h .StreamHost .Addrs ()... )
333
360
334
361
go func () {
335
- errCh <- http .Serve (listener , connectionCloseHeaderMiddleware (h .ServeMux ))
362
+ srv := & http.Server {
363
+ Handler : connectionCloseHeaderMiddleware (h .ServeMux ),
364
+ ConnContext : func (ctx context.Context , c net.Conn ) context.Context {
365
+ remote := c .RemoteAddr ()
366
+ if remote .Network () == gostream .Network {
367
+ remoteID , err := peer .Decode (remote .String ())
368
+ if err == nil {
369
+ return context .WithValue (ctx , clientPeerIDContextKey {}, remoteID )
370
+ }
371
+ }
372
+ return ctx
373
+ },
374
+ }
375
+ errCh <- srv .Serve (listener )
336
376
}()
337
377
}
338
378
@@ -497,6 +537,8 @@ func (rt *streamRoundTripper) RoundTrip(r *http.Request) (*http.Response, error)
497
537
}
498
538
}
499
539
540
+ ctxWithServerID := context .WithValue (r .Context (), serverPeerIDContextKey {}, rt .server )
541
+ resp .Request = resp .Request .WithContext (ctxWithServerID )
500
542
return resp , nil
501
543
}
502
544
@@ -529,10 +571,16 @@ func relativeMultiaddrURIToAbs(original *url.URL, relative *url.URL) (*url.URL,
529
571
return nil , errors .New ("relative path is not a valid http-path" )
530
572
}
531
573
532
- withoutPath , _ := ma .SplitFunc (originalMa , func (c ma.Component ) bool {
574
+ withoutPath , afterAndIncludingPath := ma .SplitFunc (originalMa , func (c ma.Component ) bool {
533
575
return c .Protocol ().Code == ma .P_HTTP_PATH
534
576
})
535
577
withNewPath := withoutPath .Encapsulate (relativePathComponent )
578
+ _ , afterPath := ma .SplitFirst (afterAndIncludingPath )
579
+
580
+ // Include after path since it may include other relevant parts (/p2p?)
581
+ if afterPath != nil {
582
+ withNewPath = withNewPath .Encapsulate (afterPath )
583
+ }
536
584
return url .Parse ("multiaddr:" + withNewPath .String ())
537
585
}
538
586
@@ -743,6 +791,35 @@ func (h *Host) RoundTrip(r *http.Request) (*http.Response, error) {
743
791
rt .TLSClientConfig .ServerName = parsed .sni
744
792
}
745
793
794
+ if parsed .peer != "" {
795
+ // The peer ID is present. We are making an authenticated request
796
+ if h .ClientPeerIDAuth == nil {
797
+ return nil , fmt .Errorf ("can not authenticate server. Host.ClientPeerIDAuth field is not set" )
798
+ }
799
+
800
+ if r .Host == "" {
801
+ // Missing a host header. Default to what we parsed earlier
802
+ r .Host = u .Host
803
+ }
804
+
805
+ // thin client as ClientPeerIDAuth expects an *http.Client
806
+ c := http.Client {Transport : rt }
807
+ serverID , resp , err := h .ClientPeerIDAuth .AuthenticatedDo (& c , r )
808
+ if err != nil {
809
+ return nil , err
810
+ }
811
+
812
+ if serverID != parsed .peer {
813
+ resp .Body .Close ()
814
+ return nil , fmt .Errorf ("authenticated server ID does not match expected server ID" )
815
+ }
816
+
817
+ ctxWithServerID := context .WithValue (r .Context (), serverPeerIDContextKey {}, serverID )
818
+ resp .Request = resp .Request .WithContext (ctxWithServerID )
819
+
820
+ return resp , nil
821
+ }
822
+
746
823
return rt .RoundTrip (r )
747
824
}
748
825
@@ -1086,3 +1163,19 @@ func connectionCloseHeaderMiddleware(next http.Handler) http.Handler {
1086
1163
next .ServeHTTP (w , r )
1087
1164
})
1088
1165
}
1166
+
1167
+ // maybeDecorateContextWithAuth decorates the request context with
1168
+ // authentication information if serverAuth is provided.
1169
+ func maybeDecorateContextWithAuthMiddleware (serverAuth * httpauth.ServerPeerIDAuth , next http.Handler ) http.Handler {
1170
+ if serverAuth == nil {
1171
+ return next
1172
+ }
1173
+ return http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
1174
+ if httpauth .HasAuthHeader (r ) {
1175
+ serverAuth .ServeHTTPWithNextHandler (w , r , func (p peer.ID , w http.ResponseWriter , r * http.Request ) {
1176
+ r = r .WithContext (context .WithValue (r .Context (), clientPeerIDContextKey {}, p ))
1177
+ next .ServeHTTP (w , r )
1178
+ })
1179
+ }
1180
+ })
1181
+ }
0 commit comments