Skip to content

Commit 420daff

Browse files
authored
Add FCH (#139)
* Add FCH --------- Co-authored-by: Adam Thieme <adam@cs.ucla.edu>
1 parent dc4c145 commit 420daff

File tree

3 files changed

+152
-0
lines changed

3 files changed

+152
-0
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ require (
66
github.com/cespare/xxhash v1.1.0
77
github.com/dgraph-io/badger/v4 v4.7.0
88
github.com/goccy/go-yaml v1.17.1
9+
github.com/gorilla/schema v1.4.1
910
github.com/gorilla/websocket v1.5.3
1011
github.com/mattn/go-sqlite3 v1.14.28
1112
github.com/quic-go/quic-go v0.51.0

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX
7272
github.com/google/pprof v0.0.0-20250423184734-337e5dd93bb4 h1:gD0vax+4I+mAj+jEChEf25Ia07Jq7kYOFO5PPhAxFl4=
7373
github.com/google/pprof v0.0.0-20250423184734-337e5dd93bb4/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
7474
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
75+
github.com/gorilla/schema v1.4.1 h1:jUg5hUjCSDZpNGLuXQOgIWGdlgrIdYvgQ0wZtdK1M3E=
76+
github.com/gorilla/schema v1.4.1/go.mod h1:Dg5SSm5PV60mhF2NFaTV1xuYYj8tV8NOPRo4FggUMnM=
7577
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
7678
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
7779
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=

std/ndn/fch/fch.go

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
// Package fch provides a simple NDN-FCH client.
2+
// https://github.com/11th-ndn-hackathon/ndn-fch
3+
package fch
4+
5+
import (
6+
"bytes"
7+
"context"
8+
"encoding/json"
9+
"errors"
10+
"fmt"
11+
"io"
12+
"net"
13+
"net/http"
14+
"net/url"
15+
"strings"
16+
"time"
17+
18+
"github.com/gorilla/schema"
19+
)
20+
21+
var encoder = schema.NewEncoder()
22+
23+
const (
24+
DefaultServer = "https://fch.ndn.today"
25+
DefaultTransport = "udp"
26+
)
27+
28+
// Request represents an NDN-FCH request.
29+
type Request struct {
30+
// Server is NDN-FCH server base URI.
31+
Server string `schema:"-"`
32+
33+
// Transport specifies a transport protocol.
34+
Transport string `schema:"cap"`
35+
36+
// Count specifies number of requested routers.
37+
Count int `schema:"k"`
38+
39+
// Network specifies desired network operator.
40+
Network string `schema:"network,omitempty"`
41+
}
42+
43+
func (req *Request) applyDefaults() {
44+
if req.Server == "" {
45+
req.Server = DefaultServer
46+
}
47+
req.Count = max(1, req.Count)
48+
if req.Transport == "" {
49+
req.Transport = DefaultTransport
50+
}
51+
}
52+
53+
func (req *Request) toURL() (u *url.URL, e error) {
54+
if u, e = url.ParseRequestURI(req.Server); e != nil {
55+
return nil, e
56+
}
57+
qs := url.Values{}
58+
if e = encoder.Encode(req, qs); e != nil {
59+
return nil, e
60+
}
61+
u.RawQuery = qs.Encode()
62+
return u, nil
63+
}
64+
65+
// Response represents an NDN-FCH response.
66+
type Response struct {
67+
Updated int64 `json:"updated"`
68+
Routers []Router `json:"routers"`
69+
}
70+
71+
// UpdatedTime returns last updated time.
72+
// Returns zero value if last updated time is unknown.
73+
func (res Response) UpdatedTime() time.Time {
74+
if res.Updated == 0 {
75+
return time.Time{}
76+
}
77+
return time.UnixMilli(res.Updated)
78+
}
79+
80+
// Router describes a router in NDN-FCH response.
81+
type Router struct {
82+
Transport string `json:"transport"`
83+
Connect string `json:"connect"`
84+
Prefix string `json:"prefix,omitempty"`
85+
}
86+
87+
// Query performs an NDN-FCH query.
88+
func Query(ctx context.Context, req Request) (res Response, e error) {
89+
req.applyDefaults()
90+
u, e := req.toURL()
91+
if e != nil {
92+
return
93+
}
94+
95+
hReq, e := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil)
96+
if e != nil {
97+
return
98+
}
99+
hReq.Header.Set("Accept", "application/json, text/plain, */*")
100+
101+
hRes, e := http.DefaultClient.Do(hReq)
102+
if e != nil {
103+
return
104+
}
105+
defer hRes.Body.Close()
106+
107+
if hRes.StatusCode != http.StatusOK {
108+
return res, fmt.Errorf("HTTP %s", hRes.Status)
109+
}
110+
111+
body, e := io.ReadAll(hRes.Body)
112+
if e != nil {
113+
return
114+
}
115+
116+
if strings.HasPrefix(hRes.Header.Get("Content-Type"), "application/json") {
117+
e = json.Unmarshal(body, &res)
118+
return
119+
}
120+
121+
routers := bytes.Split(body, []byte{','})
122+
for _, router := range routers {
123+
if len(router) == 0 {
124+
return res, errors.New("empty response")
125+
}
126+
127+
connect := string(router)
128+
switch req.Transport {
129+
case "udp":
130+
if _, _, e := net.SplitHostPort(connect); e != nil {
131+
connect = net.JoinHostPort(connect, "6363")
132+
}
133+
case "wss":
134+
if _, e := url.ParseRequestURI(connect); e != nil {
135+
connect = (&url.URL{
136+
Scheme: "wss",
137+
Host: connect,
138+
Path: "/ws/",
139+
}).String()
140+
}
141+
}
142+
143+
res.Routers = append(res.Routers, Router{
144+
Transport: req.Transport,
145+
Connect: connect,
146+
})
147+
}
148+
return res, nil
149+
}

0 commit comments

Comments
 (0)