@cryptotaxi247 / kubo / commits / 88d431c81

feat: remove writable gateway (#9743)

Co-authored-by: Marcin Rataj <lidel@lidel.org>

Henrique Dias committed Mar 27, 2023 at 15:19 UTC 88d431c8129aeef2ea5076336412cb54161342ce
12 files changed +18 -464
cmd/ipfs/daemon.go
+7 -12
@@ -163,7 +163,7 @@ Headers.
163 cmds.StringOption(initProfileOptionKwd, "Configuration profiles to apply for --init. See ipfs init --help for more"),
164 cmds.StringOption(routingOptionKwd, "Overrides the routing option").WithDefault(routingOptionDefaultKwd),
165 cmds.BoolOption(mountKwd, "Mounts IPFS to the filesystem using FUSE (experimental)"),
166 - cmds.BoolOption(writableKwd, "Enable legacy Gateway.Writable (deprecated)"),
166 + cmds.BoolOption(writableKwd, "Enable legacy Gateway.Writable (REMOVED)"),
167 cmds.StringOption(ipfsMountKwd, "Path to the mountpoint for IPFS (if using --mount). Defaults to config setting."),
168 cmds.StringOption(ipnsMountKwd, "Path to the mountpoint for IPNS (if using --mount). Defaults to config setting."),
169 cmds.BoolOption(unrestrictedAPIAccessKwd, "Allow API access to unlisted hashes"),
@@ -679,7 +679,7 @@ func serveHTTPApi(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, error
679
680 for _, listener := range listeners {
681 // we might have listened to /tcp/0 - let's see what we are listing on
682 - fmt.Printf("API server listening on %s\n", listener.Multiaddr())
682 + fmt.Printf("RPC API server listening on %s\n", listener.Multiaddr())
683 // Browsers require TCP.
684 switch listener.Addr().Network() {
685 case "tcp", "tcp4", "tcp6":
@@ -692,9 +692,9 @@ func serveHTTPApi(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, error
692 // only the webui objects are allowed.
693 // if you know what you're doing, go ahead and pass --unrestricted-api.
694 unrestricted, _ := req.Options[unrestrictedAPIAccessKwd].(bool)
695 - gatewayOpt := corehttp.GatewayOption(false, corehttp.WebUIPaths...)
695 + gatewayOpt := corehttp.GatewayOption(corehttp.WebUIPaths...)
696 if unrestricted {
697 - gatewayOpt = corehttp.GatewayOption(true, "/ipfs", "/ipns")
697 + gatewayOpt = corehttp.GatewayOption("/ipfs", "/ipns")
698 }
699
700 var opts = []corehttp.ServeOption{
@@ -804,7 +804,7 @@ func serveHTTPGateway(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, e
804 }
805
806 if writable {
807 - log.Error("serveHTTPGateway: legacy Gateway.Writable is DEPRECATED and will be removed or changed in future versions. If you are still using this, provide feedback in https://github.com/ipfs/specs/issues/375")
807 + log.Fatalf("Support for Gateway.Writable and --writable has been REMOVED. Please remove it from your config file or CLI. Modern replacement tracked in https://github.com/ipfs/specs/issues/375")
808 }
809
810 listeners, err := sockets.TakeListeners("io.ipfs.gateway")
@@ -837,13 +837,8 @@ func serveHTTPGateway(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, e
837 }
838
839 // we might have listened to /tcp/0 - let's see what we are listing on
840 - gwType := "readonly"
841 - if writable {
842 - gwType = "writable"
843 - }
844 -
840 for _, listener := range listeners {
846 - fmt.Printf("Gateway (%s) server listening on %s\n", gwType, listener.Multiaddr())
841 + fmt.Printf("Gateway server listening on %s\n", listener.Multiaddr())
842 }
843
844 cmdctx := *cctx
@@ -852,7 +847,7 @@ func serveHTTPGateway(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, e
847 var opts = []corehttp.ServeOption{
848 corehttp.MetricsCollectionOption("gateway"),
849 corehttp.HostnameOption(),
855 - corehttp.GatewayOption(writable, "/ipfs", "/ipns"),
850 + corehttp.GatewayOption("/ipfs", "/ipns"),
851 corehttp.VersionOption(),
852 corehttp.CheckVersionOption(),
853 corehttp.CommandsROOption(cmdctx),
cmd/ipfswatch/main.go
+1 -1
@@ -94,7 +94,7 @@ func run(ipfsPath, watchPath string) error {
94 if *http {
95 addr := "/ip4/127.0.0.1/tcp/5001"
96 var opts = []corehttp.ServeOption{
97 - corehttp.GatewayOption(true, "/ipfs", "/ipns"),
97 + corehttp.GatewayOption("/ipfs", "/ipns"),
98 corehttp.WebUIOption,
99 corehttp.CommandsOption(cmdCtx(node, ipfsPath)),
100 }
config/gateway.go
+1 -2
@@ -38,8 +38,7 @@ type Gateway struct {
38 // should be redirected.
39 RootRedirect string
40
41 - // DEPRECATED: Enables legacy PUT/POST request handling.
42 - // Modern replacement tracked in https://github.com/ipfs/specs/issues/375
41 + // REMOVED: modern replacement tracked in https://github.com/ipfs/specs/issues/375
42 Writable Flag `json:",omitempty"`
43
44 // PathPrefixes was removed: https://github.com/ipfs/go-ipfs/issues/7702
core/corehttp/corehttp.go
+1 -1
@@ -75,7 +75,7 @@ func ListenAndServe(n *core.IpfsNode, listeningMultiAddr string, options ...Serv
75
76 // we might have listened to /tcp/0 - let's see what we are listing on
77 addr = list.Multiaddr()
78 - fmt.Printf("API server listening on %s\n", addr)
78 + fmt.Printf("RPC API server listening on %s\n", addr)
79
80 return Serve(n, manet.NetListener(list), options...)
81 }
core/corehttp/gateway.go
+1 -28
@@ -24,18 +24,13 @@ import (
24 "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
25 )
26
27 -func GatewayOption(writable bool, paths ...string) ServeOption {
27 +func GatewayOption(paths ...string) ServeOption {
28 return func(n *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) {
29 cfg, err := n.Repo.Config()
30 if err != nil {
31 return nil, err
32 }
33
34 - api, err := coreapi.NewCoreAPI(n, options.Api.FetchBlocks(!cfg.Gateway.NoFetch))
35 - if err != nil {
36 - return nil, err
37 - }
38 -
34 headers := make(map[string][]string, len(cfg.Gateway.HTTPHeaders))
35 for h, v := range cfg.Gateway.HTTPHeaders {
36 headers[http.CanonicalHeaderKey(h)] = v
@@ -58,28 +53,6 @@ func GatewayOption(writable bool, paths ...string) ServeOption {
53 // By default, our HTTP handler is the gateway handler.
54 handler := gw.ServeHTTP
55
61 - // If we have the writable gateway enabled, we have to replace our
62 - // http handler by a handler that takes care of the different methods.
63 - if writable {
64 - writableGw := &writableGatewayHandler{
65 - config: &gwConfig,
66 - api: api,
67 - }
68 -
69 - handler = func(w http.ResponseWriter, r *http.Request) {
70 - switch r.Method {
71 - case http.MethodPost:
72 - writableGw.postHandler(w, r)
73 - case http.MethodDelete:
74 - writableGw.deleteHandler(w, r)
75 - case http.MethodPut:
76 - writableGw.putHandler(w, r)
77 - default:
78 - gw.ServeHTTP(w, r)
79 - }
80 - }
81 - }
82 -
56 for _, p := range paths {
57 mux.HandleFunc(p+"/", handler)
58 }
core/corehttp/gateway_test.go
+1 -1
@@ -124,7 +124,7 @@ func newTestServerAndNode(t *testing.T, ns mockNamesys) (*httptest.Server, iface
124 dh.Handler, err = makeHandler(n,
125 ts.Listener,
126 HostnameOption(),
127 - GatewayOption(false, "/ipfs", "/ipns"),
127 + GatewayOption("/ipfs", "/ipns"),
128 VersionOption(),
129 )
130 if err != nil {
core/corehttp/gateway_writable.go deleted
-265
@@ -1,265 +0,0 @@
1 -package corehttp
2 -
3 -import (
4 - "context"
5 - "fmt"
6 - "net/http"
7 - "os"
8 - gopath "path"
9 -
10 - cid "github.com/ipfs/go-cid"
11 - ipld "github.com/ipfs/go-ipld-format"
12 - "github.com/ipfs/go-libipfs/files"
13 - "github.com/ipfs/go-libipfs/gateway"
14 - dag "github.com/ipfs/go-merkledag"
15 - "github.com/ipfs/go-mfs"
16 - path "github.com/ipfs/go-path"
17 - "github.com/ipfs/go-path/resolver"
18 - iface "github.com/ipfs/interface-go-ipfs-core"
19 - routing "github.com/libp2p/go-libp2p/core/routing"
20 -)
21 -
22 -const (
23 - ipfsPathPrefix = "/ipfs/"
24 -)
25 -
26 -type writableGatewayHandler struct {
27 - api iface.CoreAPI
28 - config *gateway.Config
29 -}
30 -
31 -func (i *writableGatewayHandler) addUserHeaders(w http.ResponseWriter) {
32 - for k, v := range i.config.Headers {
33 - w.Header()[http.CanonicalHeaderKey(k)] = v
34 - }
35 -}
36 -
37 -func (i *writableGatewayHandler) postHandler(w http.ResponseWriter, r *http.Request) {
38 - p, err := i.api.Unixfs().Add(r.Context(), files.NewReaderFile(r.Body))
39 - if err != nil {
40 - internalWebError(w, err)
41 - return
42 - }
43 -
44 - i.addUserHeaders(w) // ok, _now_ write user's headers.
45 - w.Header().Set("IPFS-Hash", p.Cid().String())
46 - log.Debugw("CID created, http redirect", "from", r.URL, "to", p, "status", http.StatusCreated)
47 - http.Redirect(w, r, p.String(), http.StatusCreated)
48 -}
49 -
50 -func (i *writableGatewayHandler) putHandler(w http.ResponseWriter, r *http.Request) {
51 - ctx := r.Context()
52 - ds := i.api.Dag()
53 -
54 - // Parse the path
55 - rootCid, newPath, err := parseIpfsPath(r.URL.Path)
56 - if err != nil {
57 - webError(w, "WritableGateway: failed to parse the path", err, http.StatusBadRequest)
58 - return
59 - }
60 - if newPath == "" || newPath == "/" {
61 - http.Error(w, "WritableGateway: empty path", http.StatusBadRequest)
62 - return
63 - }
64 - newDirectory, newFileName := gopath.Split(newPath)
65 -
66 - // Resolve the old root.
67 -
68 - rnode, err := ds.Get(ctx, rootCid)
69 - if err != nil {
70 - webError(w, "WritableGateway: Could not create DAG from request", err, http.StatusInternalServerError)
71 - return
72 - }
73 -
74 - pbnd, ok := rnode.(*dag.ProtoNode)
75 - if !ok {
76 - webError(w, "Cannot read non protobuf nodes through gateway", dag.ErrNotProtobuf, http.StatusBadRequest)
77 - return
78 - }
79 -
80 - // Create the new file.
81 - newFilePath, err := i.api.Unixfs().Add(ctx, files.NewReaderFile(r.Body))
82 - if err != nil {
83 - webError(w, "WritableGateway: could not create DAG from request", err, http.StatusInternalServerError)
84 - return
85 - }
86 -
87 - newFile, err := ds.Get(ctx, newFilePath.Cid())
88 - if err != nil {
89 - webError(w, "WritableGateway: failed to resolve new file", err, http.StatusInternalServerError)
90 - return
91 - }
92 -
93 - // Patch the new file into the old root.
94 -
95 - root, err := mfs.NewRoot(ctx, ds, pbnd, nil)
96 - if err != nil {
97 - webError(w, "WritableGateway: failed to create MFS root", err, http.StatusBadRequest)
98 - return
99 - }
100 -
101 - if newDirectory != "" {
102 - err := mfs.Mkdir(root, newDirectory, mfs.MkdirOpts{Mkparents: true, Flush: false})
103 - if err != nil {
104 - webError(w, "WritableGateway: failed to create MFS directory", err, http.StatusInternalServerError)
105 - return
106 - }
107 - }
108 - dirNode, err := mfs.Lookup(root, newDirectory)
109 - if err != nil {
110 - webError(w, "WritableGateway: failed to lookup directory", err, http.StatusInternalServerError)
111 - return
112 - }
113 - dir, ok := dirNode.(*mfs.Directory)
114 - if !ok {
115 - http.Error(w, "WritableGateway: target directory is not a directory", http.StatusBadRequest)
116 - return
117 - }
118 - err = dir.Unlink(newFileName)
119 - switch err {
120 - case os.ErrNotExist, nil:
121 - default:
122 - webError(w, "WritableGateway: failed to replace existing file", err, http.StatusBadRequest)
123 - return
124 - }
125 - err = dir.AddChild(newFileName, newFile)
126 - if err != nil {
127 - webError(w, "WritableGateway: failed to link file into directory", err, http.StatusInternalServerError)
128 - return
129 - }
130 - nnode, err := root.GetDirectory().GetNode()
131 - if err != nil {
132 - webError(w, "WritableGateway: failed to finalize", err, http.StatusInternalServerError)
133 - return
134 - }
135 - newcid := nnode.Cid()
136 -
137 - i.addUserHeaders(w) // ok, _now_ write user's headers.
138 - w.Header().Set("IPFS-Hash", newcid.String())
139 -
140 - redirectURL := gopath.Join(ipfsPathPrefix, newcid.String(), newPath)
141 - log.Debugw("CID replaced, redirect", "from", r.URL, "to", redirectURL, "status", http.StatusCreated)
142 - http.Redirect(w, r, redirectURL, http.StatusCreated)
143 -}
144 -
145 -func (i *writableGatewayHandler) deleteHandler(w http.ResponseWriter, r *http.Request) {
146 - ctx := r.Context()
147 -
148 - // parse the path
149 -
150 - rootCid, newPath, err := parseIpfsPath(r.URL.Path)
151 - if err != nil {
152 - webError(w, "WritableGateway: failed to parse the path", err, http.StatusBadRequest)
153 - return
154 - }
155 - if newPath == "" || newPath == "/" {
156 - http.Error(w, "WritableGateway: empty path", http.StatusBadRequest)
157 - return
158 - }
159 - directory, filename := gopath.Split(newPath)
160 -
161 - // lookup the root
162 -
163 - rootNodeIPLD, err := i.api.Dag().Get(ctx, rootCid)
164 - if err != nil {
165 - webError(w, "WritableGateway: failed to resolve root CID", err, http.StatusInternalServerError)
166 - return
167 - }
168 - rootNode, ok := rootNodeIPLD.(*dag.ProtoNode)
169 - if !ok {
170 - http.Error(w, "WritableGateway: empty path", http.StatusInternalServerError)
171 - return
172 - }
173 -
174 - // construct the mfs root
175 -
176 - root, err := mfs.NewRoot(ctx, i.api.Dag(), rootNode, nil)
177 - if err != nil {
178 - webError(w, "WritableGateway: failed to construct the MFS root", err, http.StatusBadRequest)
179 - return
180 - }
181 -
182 - // lookup the parent directory
183 -
184 - parentNode, err := mfs.Lookup(root, directory)
185 - if err != nil {
186 - webError(w, "WritableGateway: failed to look up parent", err, http.StatusInternalServerError)
187 - return
188 - }
189 -
190 - parent, ok := parentNode.(*mfs.Directory)
191 - if !ok {
192 - http.Error(w, "WritableGateway: parent is not a directory", http.StatusInternalServerError)
193 - return
194 - }
195 -
196 - // delete the file
197 -
198 - switch parent.Unlink(filename) {
199 - case nil, os.ErrNotExist:
200 - default:
201 - webError(w, "WritableGateway: failed to remove file", err, http.StatusInternalServerError)
202 - return
203 - }
204 -
205 - nnode, err := root.GetDirectory().GetNode()
206 - if err != nil {
207 - webError(w, "WritableGateway: failed to finalize", err, http.StatusInternalServerError)
208 - return
209 - }
210 - ncid := nnode.Cid()
211 -
212 - i.addUserHeaders(w) // ok, _now_ write user's headers.
213 - w.Header().Set("IPFS-Hash", ncid.String())
214 -
215 - redirectURL := gopath.Join(ipfsPathPrefix+ncid.String(), directory)
216 - // note: StatusCreated is technically correct here as we created a new resource.
217 - log.Debugw("CID deleted, redirect", "from", r.RequestURI, "to", redirectURL, "status", http.StatusCreated)
218 - http.Redirect(w, r, redirectURL, http.StatusCreated)
219 -}
220 -
221 -func parseIpfsPath(p string) (cid.Cid, string, error) {
222 - rootPath, err := path.ParsePath(p)
223 - if err != nil {
224 - return cid.Cid{}, "", err
225 - }
226 -
227 - // Check the path.
228 - rsegs := rootPath.Segments()
229 - if rsegs[0] != "ipfs" {
230 - return cid.Cid{}, "", fmt.Errorf("WritableGateway: only ipfs paths supported")
231 - }
232 -
233 - rootCid, err := cid.Decode(rsegs[1])
234 - if err != nil {
235 - return cid.Cid{}, "", err
236 - }
237 -
238 - return rootCid, path.Join(rsegs[2:]), nil
239 -}
240 -
241 -func webError(w http.ResponseWriter, message string, err error, defaultCode int) {
242 - if _, ok := err.(resolver.ErrNoLink); ok {
243 - webErrorWithCode(w, message, err, http.StatusNotFound)
244 - } else if err == routing.ErrNotFound {
245 - webErrorWithCode(w, message, err, http.StatusNotFound)
246 - } else if ipld.IsNotFound(err) {
247 - webErrorWithCode(w, message, err, http.StatusNotFound)
248 - } else if err == context.DeadlineExceeded {
249 - webErrorWithCode(w, message, err, http.StatusRequestTimeout)
250 - } else {
251 - webErrorWithCode(w, message, err, defaultCode)
252 - }
253 -}
254 -
255 -func webErrorWithCode(w http.ResponseWriter, message string, err error, code int) {
256 - http.Error(w, fmt.Sprintf("%s: %s", message, err), code)
257 - if code >= 500 {
258 - log.Warnf("server error: %s: %s", message, err)
259 - }
260 -}
261 -
262 -// return a 500 error and log
263 -func internalWebError(w http.ResponseWriter, err error) {
264 - webErrorWithCode(w, "internalWebError", err, http.StatusInternalServerError)
265 -}
docs/config.md
+2 -7
@@ -681,14 +681,9 @@ Type: `string` (url)
681
682 ### `Gateway.Writable`
683
684 -**DEPRECATED**: Enables legacy PUT/POST request handling.
684 +**REMOVED**: this option no longer available as of [Kubo 0.20](https://github.com/ipfs/kubo/blob/master/docs/changelogs/v0.20.md).
685
686 -This API is not standardized, and should not be used for new projects.
687 -We are working on a modern replacement. IPIP can be tracked in [ipfs/specs#375](https://github.com/ipfs/specs/issues/375).
688 -
689 -Default: `false`
690 -
691 -Type: `bool`
686 +We are working on developing a modern replacement. To support our efforts, please leave a comment describing your use case in [ipfs/specs#375](https://github.com/ipfs/specs/issues/375).
687
688 ### `Gateway.PathPrefixes`
689
test/cli/gateway_test.go
+1 -1
@@ -436,7 +436,7 @@ func TestGateway(t *testing.T) {
436
437 t.Run("verify gateway file", func(t *testing.T) {
438 t.Parallel()
439 - r := regexp.MustCompile(`Gateway \(readonly\) server listening on (?P<addr>.+)\s`)
439 + r := regexp.MustCompile(`Gateway server listening on (?P<addr>.+)\s`)
440 matches := r.FindStringSubmatch(node.Daemon.Stdout.String())
441 ma, err := multiaddr.NewMultiaddr(matches[1])
442 require.NoError(t, err)
test/sharness/lib/test-lib.sh
+1 -8
@@ -214,13 +214,6 @@ test_init_ipfs() {
214
215 }
216
217 -test_config_ipfs_gateway_writable() {
218 - test_expect_success "prepare config -- gateway writable" '
219 - test_config_set --bool Gateway.Writable true ||
220 - test_fsh cat "\"$IPFS_PATH/config\""
221 - '
222 -}
223 -
217 test_wait_for_file() {
218 loops=$1
219 delay=$2
@@ -247,7 +240,7 @@ test_set_address_vars() {
240 API_ADDR=$(convert_tcp_maddr $API_MADDR) &&
241 API_PORT=$(port_from_maddr $API_MADDR) &&
242
250 - GWAY_MADDR=$(sed -n "s/^Gateway (.*) server listening on //p" "$daemon_output") &&
243 + GWAY_MADDR=$(sed -n "s/^Gateway server listening on //p" "$daemon_output") &&
244 GWAY_ADDR=$(convert_tcp_maddr $GWAY_MADDR) &&
245 GWAY_PORT=$(port_from_maddr $GWAY_MADDR)
246 '
test/sharness/t0060-daemon.sh
+2 -2
@@ -89,9 +89,9 @@ test_expect_success "ipfs daemon output looks good" '
89 echo "" >>expected_daemon &&
90 sed "s/^/Swarm listening on /" listen_addrs >>expected_daemon &&
91 sed "s/^/Swarm announcing /" local_addrs >>expected_daemon &&
92 - echo "API server listening on '$API_MADDR'" >>expected_daemon &&
92 + echo "RPC API server listening on '$API_MADDR'" >>expected_daemon &&
93 echo "WebUI: http://'$API_ADDR'/webui" >>expected_daemon &&
94 - echo "Gateway (readonly) server listening on '$GWAY_MADDR'" >>expected_daemon &&
94 + echo "Gateway server listening on '$GWAY_MADDR'" >>expected_daemon &&
95 echo "Daemon is ready" >>expected_daemon &&
96 test_cmp expected_daemon actual_daemon
97 '
test/sharness/t0111-gateway-writeable.sh deleted
-136
@@ -1,136 +0,0 @@
1 -#!/usr/bin/env bash
2 -#
3 -# Copyright (c) 2014 Christian Couder
4 -# MIT Licensed; see the LICENSE file in this repository.
5 -#
6 -
7 -test_description="Test HTTP Gateway (Writable)"
8 -
9 -. lib/test-lib.sh
10 -
11 -test_init_ipfs
12 -
13 -test_launch_ipfs_daemon --writable
14 -test_expect_success "ipfs daemon --writable overrides config" '
15 - curl -v -X POST http://$GWAY_ADDR/ipfs/ 2> outfile &&
16 - grep "HTTP/1.1 201 Created" outfile &&
17 - grep "Location: /ipfs/QmbFMke1KXqnYyBBWxB74N4c5SBnJMVAiMNRcGu6x1AwQH" outfile
18 -'
19 -test_kill_ipfs_daemon
20 -
21 -test_config_ipfs_gateway_writable
22 -test_launch_ipfs_daemon --writable=false
23 -test_expect_success "ipfs daemon --writable=false overrides Writable=true config" '
24 - curl -v -X POST http://$GWAY_ADDR/ipfs/ 2> outfile &&
25 - grep "HTTP/1.1 405 Method Not Allowed" outfile
26 -'
27 -test_kill_ipfs_daemon
28 -test_launch_ipfs_daemon
29 -
30 -port=$GWAY_PORT
31 -
32 -test_expect_success "ipfs daemon up" '
33 - pollEndpoint -host $GWAY_MADDR -v -tout=1s -tries=60 2>poll_apierr > poll_apiout ||
34 - test_fsh cat poll_apierr || test_fsh cat poll_apiout
35 -'
36 -
37 -test_expect_success "deprecation notice is printed when Gateway.Writable=true" '
38 - test_should_contain "legacy Gateway.Writable is DEPRECATED and will be removed or changed in future versions. If you are still using this, provide feedback in https://github.com/ipfs/specs/issues/375" daemon_err
39 -'
40 -
41 -test_expect_success "HTTP gateway gives access to sample file" '
42 - curl -s -o welcome "http://$GWAY_ADDR/ipfs/$HASH_WELCOME_DOCS/readme" &&
43 - grep "Hello and Welcome to IPFS!" welcome
44 -'
45 -
46 -test_expect_success "HTTP POST file gives Hash" '
47 - echo "$RANDOM" >infile &&
48 - URL="http://127.0.0.1:$port/ipfs/" &&
49 - curl -svX POST --data-binary @infile "$URL" 2>curl_post.out &&
50 - grep "HTTP/1.1 201 Created" curl_post.out &&
51 - LOCATION=$(grep Location curl_post.out) &&
52 - HASH=$(echo $LOCATION | cut -d":" -f2- |tr -d " \n\r")
53 -'
54 -
55 -test_expect_success "We can HTTP GET file just created" '
56 - URL="http://127.0.0.1:${port}${HASH}" &&
57 - curl -so outfile "$URL" &&
58 - test_cmp infile outfile
59 -'
60 -
61 -test_expect_success "We got the correct hash" '
62 - ADD_HASH="/ipfs/$(ipfs add -q infile)" &&
63 - test "x$ADD_HASH" = "x$HASH" || test_fsh echo "$ADD_HASH != $HASH"
64 -'
65 -
66 -test_expect_success "HTTP GET empty directory" '
67 - URL="http://127.0.0.1:$port/ipfs/$HASH_EMPTY_DIR/" &&
68 - echo "GET $URL" &&
69 - curl -so outfile "$URL" 2>curl_getEmpty.out &&
70 - cat outfile | tr -s "\n" " " | grep "Index of /ipfs/<a href=\"/ipfs/$HASH_EMPTY_DIR\">$HASH_EMPTY_DIR</a>"
71 -'
72 -
73 -test_expect_success "HTTP PUT file to construct a hierarchy" '
74 - echo "$RANDOM" >infile &&
75 - URL="http://127.0.0.1:$port/ipfs/$HASH_EMPTY_DIR/test.txt" &&
76 - echo "PUT $URL" &&
77 - curl -svX PUT --data-binary @infile "$URL" 2>curl_put.out &&
78 - grep "HTTP/1.1 201 Created" curl_put.out &&
79 - LOCATION=$(grep Location curl_put.out) &&
80 - HASH=$(expr "$LOCATION" : "< Location: /ipfs/\(.*\)/test.txt")
81 -'
82 -
83 -test_expect_success "We can HTTP GET file just created" '
84 - URL="http://127.0.0.1:$port/ipfs/$HASH/test.txt" &&
85 - echo "GET $URL" &&
86 - curl -so outfile "$URL" &&
87 - test_cmp infile outfile
88 -'
89 -
90 -test_expect_success "HTTP PUT file to append to existing hierarchy" '
91 - echo "$RANDOM" >infile2 &&
92 - URL="http://127.0.0.1:$port/ipfs/$HASH/test/test.txt" &&
93 - echo "PUT $URL" &&
94 - curl -svX PUT --data-binary @infile2 "$URL" 2>curl_putAgain.out &&
95 - grep "HTTP/1.1 201 Created" curl_putAgain.out &&
96 - LOCATION=$(grep Location curl_putAgain.out) &&
97 - HASH=$(expr "$LOCATION" : "< Location: /ipfs/\(.*\)/test/test.txt")
98 -'
99 -
100 -
101 -test_expect_success "We can HTTP GET file just updated" '
102 - URL="http://127.0.0.1:$port/ipfs/$HASH/test/test.txt" &&
103 - echo "GET $URL" &&
104 - curl -svo outfile2 "$URL" 2>curl_getAgain.out &&
105 - test_cmp infile2 outfile2
106 -'
107 -
108 -test_expect_success "HTTP PUT to replace a directory" '
109 - echo "$RANDOM" >infile3 &&
110 - URL="http://127.0.0.1:$port/ipfs/$HASH/test" &&
111 - echo "PUT $URL" &&
112 - curl -svX PUT --data-binary @infile3 "$URL" 2>curl_putOverDirectory.out &&
113 - grep "HTTP/1.1 201 Created" curl_putOverDirectory.out &&
114 - LOCATION=$(grep Location curl_putOverDirectory.out) &&
115 - HASH=$(expr "$LOCATION" : "< Location: /ipfs/\(.*\)/test")
116 -'
117 -
118 -test_expect_success "We can HTTP GET file just put over a directory" '
119 - URL="http://127.0.0.1:$port/ipfs/$HASH/test" &&
120 - echo "GET $URL" &&
121 - curl -svo outfile3 "$URL" 2>curl_getOverDirectory.out &&
122 - test_cmp infile3 outfile3
123 -'
124 -
125 -test_expect_success "HTTP PUT to /ipns fails" '
126 - PEERID=`ipfs id --format="<id>"` &&
127 - URL="http://127.0.0.1:$port/ipns/$PEERID/test.txt" &&
128 - echo "PUT $URL" &&
129 - curl -svX PUT --data-binary @infile1 "$URL" 2>curl_putIpns.out &&
130 - grep "HTTP/1.1 400 Bad Request" curl_putIpns.out
131 -'
132 -
133 -
134 -test_kill_ipfs_daemon
135 -
136 -test_done