error handling, url matching

This commit is contained in:
Felix Geisendörfer 2013-05-03 11:42:46 +02:00
parent 0e87800ddc
commit 3abd71b6c0
2 changed files with 84 additions and 12 deletions

View File

@ -9,6 +9,8 @@ import (
"strconv" "strconv"
) )
const basePath = "/files/"
func main() { func main() {
log.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds) log.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds)
log.Printf("tusd started") log.Printf("tusd started")
@ -36,20 +38,34 @@ func main() {
} }
} }
config := tushttp.HandlerConfig{ tusConfig := tushttp.HandlerConfig{
Dir: dir, Dir: dir,
MaxSize: maxSize, MaxSize: maxSize,
BasePath: basePath,
} }
log.Printf("handler config: %+v", config) log.Printf("handler config: %+v", tusConfig)
handler, err := tushttp.NewHandler(config) tusHandler, err := tushttp.NewHandler(tusConfig)
if err != nil { if err != nil {
panic(err) panic(err)
} }
http.Handle(basePath, tusHandler)
go handleUploads(tusHandler)
log.Printf("servering clients at http://localhost%s", addr) log.Printf("servering clients at http://localhost%s", addr)
if err := http.ListenAndServe(addr, handler); err != nil { if err := http.ListenAndServe(addr, nil); err != nil {
panic(err) panic(err)
} }
} }
func handleUploads(tus *tushttp.Handler) {
for {
select {
case err := <-tus.Error:
log.Printf("error: %s", err)
}
}
}

View File

@ -1,12 +1,15 @@
package http package http
import ( import (
"log" "errors"
"io"
"net/http" "net/http"
"os" "os"
"strings"
) )
type HandlerConfig struct{ // HandlerConfig holds the configuration for a tus Handler.
type HandlerConfig struct {
// Dir points to a filesystem path used by tus to store uploaded and partial // Dir points to a filesystem path used by tus to store uploaded and partial
// files. Will be created if does not exist yet. Required. // files. Will be created if does not exist yet. Required.
Dir string Dir string
@ -15,23 +18,76 @@ type HandlerConfig struct{
// limit will cause the oldest upload files to be deleted until enough space // limit will cause the oldest upload files to be deleted until enough space
// is available again. Required. // is available again. Required.
MaxSize int64 MaxSize int64
// BasePath defines the url path used for handling uploads, e.g. "/files/".
// Must contain a trailling "/". Requests not matching this base path will
// cause a 404, so make sure you dispatch only appropriate requests to the
// handler. Required.
BasePath string
} }
// NewHandler returns an initialized Handler. An error may occur if the
// config.Dir is not writable.
func NewHandler(config HandlerConfig) (*Handler, error) { func NewHandler(config HandlerConfig) (*Handler, error) {
// Ensure the data store directory exists // Ensure the data store directory exists
if err := os.MkdirAll(config.Dir, 0777); err != nil { if err := os.MkdirAll(config.Dir, 0777); err != nil {
return nil, err return nil, err
} }
errChan := make(chan error)
return &Handler{ return &Handler{
store: newDataStore(config.Dir, config.MaxSize), store: newDataStore(config.Dir, config.MaxSize),
basePath: config.BasePath,
Error: errChan,
sendError: errChan,
}, nil }, nil
} }
type Handler struct{ // Handler is a http.Handler that implements tus resumable upload protocol.
store *DataStore type Handler struct {
store *DataStore
basePath string
// Error provides error events for logging purposes.
Error <-chan error
// same chan as Error, used for sending.
sendError chan<- error
} }
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
log.Printf("request: %s %s", r.Method, r.URL.RequestURI()) absPath := r.URL.Path
if !strings.HasPrefix(absPath, h.basePath) {
err := errors.New("invalid url path: " + absPath + " - does not match basePath: " + h.basePath)
h.err(err, w, http.StatusNotFound)
return
}
relPath := absPath[len(h.basePath)-1:]
// File creation request
if relPath == "/" {
// Must use POST method according to tus protocol
if r.Method != "POST" {
w.Header().Set("Allow", "POST")
err := errors.New(r.Method + " used against file creation url. Only POST is allowed.")
h.err(err, w, http.StatusMethodNotAllowed)
return
}
}
err := errors.New("invalid url path: " + absPath + " - does not match file pattern")
h.err(err, w, http.StatusNotFound)
}
// err sends a http error response and publishes to the Error channel.
func (h *Handler) err(err error, w http.ResponseWriter, status int) {
w.WriteHeader(status)
io.WriteString(w, err.Error()+"\n")
// non-blocking send
select {
case h.sendError <- err:
default:
}
} }