3
import (
4
"bytes"
5
"encoding/json"
6
+ "net/http"
7
+ "net/http/httptest"
8
+ "strings"
9
"testing"
10
"time"
11
142
expectNoProviders(t, cid, nodes[1:]...)
143
})
144
145
+ t.Run("manual provide fails when no libp2p peers and no custom HTTP router", func(t *testing.T) {
146
+ t.Parallel()
147
+
148
+ h := harness.NewT(t)
149
+ node := h.NewNode().Init()
150
+ apply(node)
151
+ node.SetIPFSConfig("Provide.Enabled", true)
152
+ node.StartDaemon()
153
+ defer node.StopDaemon()
154
+
155
+ cid := node.IPFSAddStr(time.Now().String())
156
+ res := node.RunIPFS("routing", "provide", cid)
157
+ assert.Contains(t, res.Stderr.Trimmed(), "cannot provide, no connected peers")
158
+ assert.Equal(t, 1, res.ExitCode())
159
+ })
160
+
161
+ t.Run("manual provide succeeds via custom HTTP router when no libp2p peers", func(t *testing.T) {
162
+ t.Parallel()
163
+
164
+ // Create a mock HTTP server that accepts provide requests.
165
+ // This simulates the undocumented API behavior described in
166
+ // https://discuss.ipfs.tech/t/only-peers-found-from-dht-seem-to-be-getting-used-as-relays-so-cant-use-http-routers/19545/9
167
+ // Note: This is NOT IPIP-378, which was not implemented.
168
+ mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
169
+ // Accept both PUT and POST requests to /routing/v1/providers and /routing/v1/ipns
170
+ if (r.Method == http.MethodPut || r.Method == http.MethodPost) &&
171
+ (strings.HasPrefix(r.URL.Path, "/routing/v1/providers") || strings.HasPrefix(r.URL.Path, "/routing/v1/ipns")) {
172
+ // Return HTTP 200 to indicate successful publishing
173
+ w.WriteHeader(http.StatusOK)
174
+ } else {
175
+ w.WriteHeader(http.StatusNotFound)
176
+ }
177
+ }))
178
+ defer mockServer.Close()
179
+
180
+ h := harness.NewT(t)
181
+ node := h.NewNode().Init()
182
+ apply(node)
183
+ node.SetIPFSConfig("Provide.Enabled", true)
184
+ // Configure a custom HTTP router for providing.
185
+ // Using our mock server that will accept the provide requests.
186
+ routingConf := map[string]any{
187
+ "Type": "custom", // https://github.com/ipfs/kubo/blob/master/docs/delegated-routing.md#configuration-file-example
188
+ "Methods": map[string]any{
189
+ "provide": map[string]any{"RouterName": "MyCustomRouter"},
190
+ "get-ipns": map[string]any{"RouterName": "MyCustomRouter"},
191
+ "put-ipns": map[string]any{"RouterName": "MyCustomRouter"},
192
+ "find-peers": map[string]any{"RouterName": "MyCustomRouter"},
193
+ "find-providers": map[string]any{"RouterName": "MyCustomRouter"},
194
+ },
195
+ "Routers": map[string]any{
196
+ "MyCustomRouter": map[string]any{
197
+ "Type": "http",
198
+ "Parameters": map[string]any{
199
+ // Use the mock server URL
200
+ "Endpoint": mockServer.URL,
201
+ },
202
+ },
203
+ },
204
+ }
205
+ node.SetIPFSConfig("Routing", routingConf)
206
+ node.StartDaemon()
207
+ defer node.StopDaemon()
208
+
209
+ cid := node.IPFSAddStr(time.Now().String())
210
+ // The command should successfully provide via HTTP even without libp2p peers
211
+ res := node.RunIPFS("routing", "provide", cid)
212
+ assert.Empty(t, res.Stderr.String(), "Should have no errors when providing via HTTP router")
213
+ assert.Equal(t, 0, res.ExitCode(), "Should succeed with exit code 0")
214
+ })
215
+
216
// Right now Provide and Reprovide are tied together
217
t.Run("Reprovide.Interval=0 disables announcement of new CID too", func(t *testing.T) {
218
t.Parallel()