github.com/zignig/go-ipfs@v0.0.0-20141111235910-c9e5fdf55a52/commands/http/handler.go (about)

     1  package http
     2  
     3  import (
     4  	"errors"
     5  	"io"
     6  	"net/http"
     7  
     8  	cmds "github.com/jbenet/go-ipfs/commands"
     9  	u "github.com/jbenet/go-ipfs/util"
    10  )
    11  
    12  var log = u.Logger("commands/http")
    13  
    14  type Handler struct {
    15  	ctx  cmds.Context
    16  	root *cmds.Command
    17  }
    18  
    19  var ErrNotFound = errors.New("404 page not found")
    20  
    21  const streamHeader = "X-Stream-Output"
    22  
    23  var mimeTypes = map[string]string{
    24  	cmds.JSON: "application/json",
    25  	cmds.XML:  "application/xml",
    26  	cmds.Text: "text/plain",
    27  }
    28  
    29  func NewHandler(ctx cmds.Context, root *cmds.Command) *Handler {
    30  	return &Handler{ctx, root}
    31  }
    32  
    33  func (i Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    34  	log.Debug("Incoming API request: ", r.URL)
    35  
    36  	req, err := Parse(r, i.root)
    37  	if err != nil {
    38  		if err == ErrNotFound {
    39  			w.WriteHeader(http.StatusNotFound)
    40  		} else {
    41  			w.WriteHeader(http.StatusBadRequest)
    42  		}
    43  		w.Write([]byte(err.Error()))
    44  		return
    45  	}
    46  	req.SetContext(i.ctx)
    47  
    48  	// call the command
    49  	res := i.root.Call(req)
    50  
    51  	// set the Content-Type based on res output
    52  	if _, ok := res.Output().(io.Reader); ok {
    53  		// we don't set the Content-Type for streams, so that browsers can MIME-sniff the type themselves
    54  		// we set this header so clients have a way to know this is an output stream
    55  		// (not marshalled command output)
    56  		// TODO: set a specific Content-Type if the command response needs it to be a certain type
    57  		w.Header().Set(streamHeader, "1")
    58  
    59  	} else {
    60  		enc, err := req.Option(cmds.EncShort).String()
    61  		if err != nil || len(enc) == 0 {
    62  			w.WriteHeader(http.StatusInternalServerError)
    63  			return
    64  		}
    65  		mime := mimeTypes[enc]
    66  		w.Header().Set("Content-Type", mime)
    67  	}
    68  
    69  	// if response contains an error, write an HTTP error status code
    70  	if e := res.Error(); e != nil {
    71  		if e.Code == cmds.ErrClient {
    72  			w.WriteHeader(http.StatusBadRequest)
    73  		} else {
    74  			w.WriteHeader(http.StatusInternalServerError)
    75  		}
    76  	}
    77  
    78  	out, err := res.Reader()
    79  	if err != nil {
    80  		w.WriteHeader(http.StatusInternalServerError)
    81  		w.Header().Set("Content-Type", "text/plain")
    82  		w.Write([]byte(err.Error()))
    83  		return
    84  	}
    85  
    86  	io.Copy(w, out)
    87  }