From 73353824e91c62623b6bc6c3681b6a6dfda9e484 Mon Sep 17 00:00:00 2001
From: Matt Bell <mappum@gmail.com>
Date: Wed, 22 Oct 2014 15:26:18 -0700
Subject: [PATCH] server/http: Added HTTP API handler

---
 server/http/http.go | 94 ++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 88 insertions(+), 6 deletions(-)

diff --git a/server/http/http.go b/server/http/http.go
index 3c5992c6e..4b5a6ac3c 100644
--- a/server/http/http.go
+++ b/server/http/http.go
@@ -5,25 +5,35 @@ import (
 	"fmt"
 	"io"
 	"net/http"
+	"strings"
 
 	"github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gorilla/mux"
 	ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
 	manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net"
 	mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash"
 
+	cmds "github.com/jbenet/go-ipfs/commands"
 	core "github.com/jbenet/go-ipfs/core"
+	"github.com/jbenet/go-ipfs/core/commands"
 )
 
-type handler struct {
+type objectHandler struct {
 	ipfs
 }
 
+type apiHandler struct{}
+
 // Serve starts the http server
 func Serve(address ma.Multiaddr, node *core.IpfsNode) error {
 	r := mux.NewRouter()
-	handler := &handler{&ipfsHandler{node}}
-	r.HandleFunc("/ipfs/", handler.postHandler).Methods("POST")
-	r.PathPrefix("/ipfs/").Handler(handler).Methods("GET")
+	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")
+
 	http.Handle("/", r)
 
 	_, host, err := manet.DialArgs(address)
@@ -34,7 +44,7 @@ func Serve(address ma.Multiaddr, node *core.IpfsNode) error {
 	return http.ListenAndServe(host, nil)
 }
 
-func (i *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+func (i *objectHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	path := r.URL.Path[5:]
 
 	nd, err := i.ResolvePath(path)
@@ -55,7 +65,7 @@ func (i *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	io.Copy(w, dr)
 }
 
-func (i *handler) postHandler(w http.ResponseWriter, r *http.Request) {
+func (i *objectHandler) postHandler(w http.ResponseWriter, r *http.Request) {
 	nd, err := i.NewDagFromReader(r.Body)
 	if err != nil {
 		w.WriteHeader(http.StatusInternalServerError)
@@ -74,3 +84,75 @@ func (i *handler) postHandler(w http.ResponseWriter, r *http.Request) {
 	w.WriteHeader(http.StatusCreated)
 	w.Write([]byte(mh.Multihash(k).B58String()))
 }
+
+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
+	if err = res.Error(); err != nil {
+		e := err.(cmds.Error)
+
+		if e.Code == cmds.ErrClient {
+			w.WriteHeader(http.StatusBadRequest)
+		} else {
+			w.WriteHeader(http.StatusInternalServerError)
+		}
+	}
+
+	val := res.Value()
+
+	// if the output value is a io.Reader, stream its output in the request body
+	if stream, ok := val.(io.Reader); ok {
+		io.Copy(w, stream)
+		return
+	}
+
+	// otherwise, marshall and output the response value or error
+	if val != nil || res.Error() != nil {
+		output, err := res.Marshal()
+
+		if err != nil {
+			w.WriteHeader(http.StatusInternalServerError)
+			fmt.Println(err)
+			return
+		}
+
+		if output != nil {
+			w.Write(output)
+		}
+	}
+}
+
+// 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)
+
+	if _, exists := opts[cmds.EncShort]; !exists {
+		opts[cmds.EncShort] = cmds.JSON
+	}
+
+	return opts
+}
-- 
GitLab