ohsnap/api.go

114 lines
2.7 KiB
Go

package main
import (
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"log"
"net/http"
"strings"
)
type AuthFunction func(*User, []string) bool
type DispatchFunction func(*User, []string, []byte) (interface{}, error)
var apiRoutes = map[string]*(map[string]struct {
AuthFunction
DispatchFunction
}){
"version": &ApiVersionRouting,
"images": &ApiImagesRouting,
"users": &ApiUsersRouting,
}
type apiRouting struct{}
func ApiHandler() http.Handler {
return apiRouting{}
}
func (a apiRouting) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Authenticate the user if any
var user *User
if auth := r.Header.Get("Authorization"); strings.HasPrefix(auth, "Basic ") {
if c, err := base64.StdEncoding.DecodeString(auth[len("Basic "):]); err == nil {
cs := string(c)
if is := strings.IndexByte(cs, ':'); is >= 0 {
if data, err := base64.StdEncoding.DecodeString(cs[is+1:]); err == nil {
user = Authenticate(cs[:is], data)
}
}
}
}
log.Printf("Handling %s request from %s: %s %v [%s]\n", r.Method, r.RemoteAddr, r.URL.Path, user, r.UserAgent())
// Extract URL arguments
var sURL = strings.Split(r.URL.Path, "/")
if sURL[len(sURL)-1] == "" && len(sURL) > 0 {
// Remove trailing /
sURL = sURL[:len(sURL)-1]
}
w.Header().Set("Content-Type", "application/json")
var ret interface{}
var err error = nil
// Read the body
if r.ContentLength < 0 || r.ContentLength > 10485760 {
http.Error(w, "{errmsg:\"Request too large or request size unknown\"}", http.StatusRequestEntityTooLarge)
return
}
var body []byte
if r.ContentLength > 0 {
tmp := make([]byte, 1024)
for {
n, err := r.Body.Read(tmp)
for j := 0; j < n; j++ {
body = append(body, tmp[j])
}
if err != nil || n <= 0 {
break
}
}
}
// Route request
if len(sURL) > 0 {
if h, ok := apiRoutes[sURL[0]]; ok {
if f, ok := (*h)[r.Method]; ok {
if f.AuthFunction(user, sURL[1:]) {
ret, err = f.DispatchFunction(user, sURL[1:], body)
} else {
http.Error(w, "{errmsg:\"You are not allowed to do this request.\"}", http.StatusUnauthorized)
}
} else {
err = errors.New(fmt.Sprintf("Invalid action (%s) provided for %s.", r.Method, sURL[0]))
}
}
} else {
err = errors.New(fmt.Sprintf("No action provided"))
}
// Format response
resStatus := http.StatusOK
if err != nil {
ret = map[string]string{"errmsg": err.Error()}
resStatus = http.StatusBadRequest
}
if ret == nil {
ret = map[string]string{"errmsg": "Page not found"}
resStatus = http.StatusNotFound
}
if j, err := json.Marshal(ret); err != nil {
http.Error(w, fmt.Sprintf("{errmsg:%q}", err), http.StatusInternalServerError)
} else {
w.WriteHeader(resStatus)
w.Write(j)
}
}