http.go 3.41 KB
Newer Older
Jeromy's avatar
Jeromy committed
1
// package http implements an http server that serves static content from ipfs
verokarhu's avatar
verokarhu committed
2 3 4
package http

import (
verokarhu's avatar
verokarhu committed
5
	"fmt"
verokarhu's avatar
verokarhu committed
6
	"io"
verokarhu's avatar
verokarhu committed
7
	"net/http"
8
	"strings"
verokarhu's avatar
verokarhu committed
9

10
	"github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gorilla/mux"
11
	ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
12
	manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net"
verokarhu's avatar
verokarhu committed
13
	mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash"
14

15
	cmds "github.com/jbenet/go-ipfs/commands"
verokarhu's avatar
verokarhu committed
16
	core "github.com/jbenet/go-ipfs/core"
17
	"github.com/jbenet/go-ipfs/core/commands"
verokarhu's avatar
verokarhu committed
18 19
)

20
type objectHandler struct {
21 22 23
	ipfs
}

24 25
type apiHandler struct{}

verokarhu's avatar
verokarhu committed
26
// Serve starts the http server
27
func Serve(address ma.Multiaddr, node *core.IpfsNode) error {
verokarhu's avatar
verokarhu committed
28
	r := mux.NewRouter()
29 30 31 32 33 34 35 36
	objectHandler := &objectHandler{&ipfsHandler{node}}
	apiHandler := &apiHandler{}

	r.PathPrefix("/api/v0/").Handler(apiHandler).Methods("GET", "POST")

	r.HandleFunc("/ipfs/", objectHandler.postHandler).Methods("POST")
	r.PathPrefix("/ipfs/").Handler(objectHandler).Methods("GET")

verokarhu's avatar
verokarhu committed
37 38
	http.Handle("/", r)

39
	_, host, err := manet.DialArgs(address)
40 41 42 43 44
	if err != nil {
		return err
	}

	return http.ListenAndServe(host, nil)
verokarhu's avatar
verokarhu committed
45 46
}

47
func (i *objectHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
48
	path := r.URL.Path[5:]
verokarhu's avatar
verokarhu committed
49

50
	nd, err := i.ResolvePath(path)
verokarhu's avatar
verokarhu committed
51 52
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
verokarhu's avatar
verokarhu committed
53 54 55 56 57 58 59 60 61
		fmt.Println(err)
		return
	}

	dr, err := i.NewDagReader(nd)
	if err != nil {
		// TODO: return json object containing the tree data if it's a directory (err == ErrIsDir)
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Println(err)
verokarhu's avatar
verokarhu committed
62 63 64
		return
	}

verokarhu's avatar
verokarhu committed
65
	io.Copy(w, dr)
verokarhu's avatar
verokarhu committed
66 67
}

68
func (i *objectHandler) postHandler(w http.ResponseWriter, r *http.Request) {
verokarhu's avatar
verokarhu committed
69
	nd, err := i.NewDagFromReader(r.Body)
verokarhu's avatar
verokarhu committed
70 71
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
verokarhu's avatar
verokarhu committed
72
		fmt.Println(err)
verokarhu's avatar
verokarhu committed
73 74
		return
	}
verokarhu's avatar
verokarhu committed
75

verokarhu's avatar
verokarhu committed
76
	k, err := i.AddNodeToDAG(nd)
verokarhu's avatar
verokarhu committed
77 78
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
verokarhu's avatar
verokarhu committed
79
		fmt.Println(err)
verokarhu's avatar
verokarhu committed
80 81 82 83 84
		return
	}

	//TODO: return json representation of list instead
	w.WriteHeader(http.StatusCreated)
verokarhu's avatar
verokarhu committed
85
	w.Write([]byte(mh.Multihash(k).B58String()))
verokarhu's avatar
verokarhu committed
86
}
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106

func (i *apiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	path := strings.Split(r.URL.Path, "/")[3:]
	opts := getOptions(r)

	// TODO: get args

	// ensure the requested command exists, otherwise 404
	_, err := commands.Root.Get(path)
	if err != nil {
		w.WriteHeader(http.StatusNotFound)
		w.Write([]byte("404 page not found"))
		return
	}

	// build the Request and call the command
	req := cmds.NewRequest(path, opts, nil, nil)
	res := commands.Root.Call(req)

	// if response contains an error, write an HTTP error status code
107
	if e := res.Error(); e != nil {
108 109 110 111 112 113 114
		if e.Code == cmds.ErrClient {
			w.WriteHeader(http.StatusBadRequest)
		} else {
			w.WriteHeader(http.StatusInternalServerError)
		}
	}

115 116 117 118
	_, err = io.Copy(w, res)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte(err.Error()))
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
	}
}

// getOptions returns the command options in the given HTTP request
// (from the querystring and request body)
func getOptions(r *http.Request) map[string]interface{} {
	opts := make(map[string]interface{})

	query := r.URL.Query()
	for k, v := range query {
		opts[k] = v[0]
	}

	// TODO: get more options from request body (formdata, json, etc)

134 135 136
	_, short := opts[cmds.EncShort]
	_, long := opts[cmds.EncLong]
	if !short && !long {
137 138 139 140 141
		opts[cmds.EncShort] = cmds.JSON
	}

	return opts
}