perkeep/server/go/blobserver/camlistored.go

191 lines
5.0 KiB
Go
Raw Normal View History

2010-06-12 21:45:58 +00:00
// Copyright 2010 Brad Fitzpatrick <brad@danga.com>
//
// See LICENSE.
package main
import (
2010-11-29 04:06:22 +00:00
"camli/auth"
2010-12-06 06:34:46 +00:00
"camli/httputil"
"camli/webserver"
"camli/blobserver"
"camli/blobserver/localdisk"
"camli/blobserver/handlers"
"camli/mysqlindexer" // TODO: temporary for testing; wrong place kinda
"flag"
"fmt"
"http"
"log"
"strings"
"os"
)
2010-07-26 05:18:21 +00:00
var flagStorageRoot *string = flag.String("root", "/tmp/camliroot", "Root directory to store files")
var flagRequestLog *bool = flag.Bool("reqlog", false, "Log incoming requests")
2010-06-12 21:45:58 +00:00
var storage blobserver.Storage
var indexerStorage blobserver.Storage
const camliPrefix = "/camli/"
const partitionPrefix = "/partition-"
var InvalidCamliPath = os.NewError("Invalid Camlistore request path")
func parseCamliPath(path string) (partition blobserver.Partition, action string, err os.Error) {
camIdx := strings.Index(path, camliPrefix)
if camIdx == -1 {
err = InvalidCamliPath
return
}
action = path[camIdx+len(camliPrefix):]
if camIdx == 0 {
return
}
if !strings.HasPrefix(path, partitionPrefix) {
err = InvalidCamliPath
return
}
name := path[len(partitionPrefix):camIdx]
if !isValidPartitionName(name) {
err = InvalidCamliPath
return
}
partition = blobserver.Partition(name)
return
}
func pickPartitionHandlerMaybe(req *http.Request) (handler http.HandlerFunc, intercept bool) {
if !strings.HasPrefix(req.URL.Path, partitionPrefix) {
intercept = false
return
}
return http.HandlerFunc(handleCamli), true
}
func unsupportedHandler(conn http.ResponseWriter, req *http.Request) {
httputil.BadRequestError(conn, "Unsupported camlistore path or method.")
}
2010-10-04 15:28:14 +00:00
func handleCamli(conn http.ResponseWriter, req *http.Request) {
partition, action, err := parseCamliPath(req.URL.Path)
if err != nil {
log.Printf("Invalid request for method %q, path %q",
req.Method, req.URL.Path)
unsupportedHandler(conn, req)
return
2010-07-18 18:08:45 +00:00
}
handleCamliUsingStorage(conn, req, action, partition, storage)
}
func handleIndexRequest(conn http.ResponseWriter, req *http.Request) {
const prefix = "/indexer"
if !strings.HasPrefix(req.URL.Path, prefix) {
panic("bogus request")
return
}
path := req.URL.Path[len(prefix):]
partition, action, err := parseCamliPath(path)
if err != nil {
log.Printf("Invalid request for method %q, path %q",
req.Method, req.URL.Path)
unsupportedHandler(conn, req)
return
}
if partition != "" {
conn.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(conn, "Indexer doesn't support partitions.")
return
}
handleCamliUsingStorage(conn, req, action, partition, indexerStorage)
}
func handleCamliUsingStorage(conn http.ResponseWriter, req *http.Request,
action string, partition blobserver.Partition, storage blobserver.Storage) {
handler := unsupportedHandler
if *flagRequestLog {
log.Printf("method %q; partition %q; action %q", req.Method, partition, action)
}
switch req.Method {
case "GET":
switch action {
case "enumerate-blobs":
handler = auth.RequireAuth(handlers.CreateEnumerateHandler(storage, partition))
case "stat":
handler = auth.RequireAuth(handlers.CreateStatHandler(storage, partition))
2010-07-26 05:18:21 +00:00
default:
handler = handlers.CreateGetHandler(storage)
2010-07-26 05:18:21 +00:00
}
case "POST":
switch action {
case "stat":
handler = auth.RequireAuth(handlers.CreateStatHandler(storage, partition))
case "upload":
handler = auth.RequireAuth(handlers.CreateUploadHandler(storage))
case "remove":
// Currently only allows removing from a non-main partition.
handler = auth.RequireAuth(handlers.CreateRemoveHandler(storage, partition))
}
case "PUT": // no longer part of spec
handler = auth.RequireAuth(handlers.CreateNonStandardPutHandler(storage))
2010-06-13 00:15:49 +00:00
}
2010-07-18 18:08:45 +00:00
handler(conn, req)
2010-06-12 21:45:58 +00:00
}
2010-10-04 15:28:14 +00:00
func handleRoot(conn http.ResponseWriter, req *http.Request) {
fmt.Fprintf(conn, "<html><body>This is camlistored, a <a href='http://camlistore.org'>Camlistore</a> storage daemon.</body></html>\n")
}
func exitFailure(pattern string, args ...interface{}) {
if !strings.HasSuffix(pattern, "\n") {
pattern = pattern + "\n"
}
fmt.Fprintf(os.Stderr, pattern, args...)
os.Exit(1)
2010-06-12 21:45:58 +00:00
}
func main() {
flag.Parse()
auth.AccessPassword = os.Getenv("CAMLI_PASSWORD")
if len(auth.AccessPassword) == 0 {
exitFailure("No CAMLI_PASSWORD environment variable set.")
2010-06-12 21:45:58 +00:00
}
rootPrefix := func(s string) bool {
return strings.HasPrefix(*flagStorageRoot, s)
}
switch {
case *flagStorageRoot == "":
exitFailure("No storage root specified in --root")
case rootPrefix("s3:"):
// TODO: support Amazon, etc.
default:
var err os.Error
storage, err = localdisk.New(*flagStorageRoot)
if err != nil {
exitFailure("Error for --root of %q: %v", *flagStorageRoot, err)
2010-06-12 21:45:58 +00:00
}
}
if storage == nil {
exitFailure("Unsupported storage root type %q", *flagStorageRoot)
}
ws := webserver.New()
ws.RegisterPreMux(webserver.HandlerPicker(pickPartitionHandlerMaybe))
ws.HandleFunc("/", handleRoot)
ws.HandleFunc("/camli/", handleCamli)
// TODO: temporary
if true {
indexerStorage = &mysqlindexer.Indexer{}
ws.HandleFunc("/indexer/", handleIndexRequest)
}
ws.Handle("/js/", http.FileServer("../../clients/js", "/js/"))
ws.Serve()
2010-06-12 21:45:58 +00:00
}