15
// storageBackend provides HTTP access to a defined path. The local
16
// provider otimally would use a much simpler Storage, but this
17
// code may be useful in storage-free environs. Here it requires
18
// additional authentication work before it's viable.
19
type storageBackend struct {
24
// ServeHTTP handles the HTTP requests to the container.
25
func (s *storageBackend) ServeHTTP(w http.ResponseWriter, req *http.Request) {
28
if strings.HasSuffix(req.URL.Path, "*") {
36
s.handleDelete(w, req)
38
http.Error(w, "method "+req.Method+" is not supported", http.StatusMethodNotAllowed)
42
// handleGet returns a storage file to the client.
43
func (s *storageBackend) handleGet(w http.ResponseWriter, req *http.Request) {
44
data, err := ioutil.ReadFile(filepath.Join(s.path, req.URL.Path))
46
http.Error(w, fmt.Sprintf("404 %v", err), http.StatusNotFound)
49
w.Header().Set("Content-Type", "application/octet-stream")
53
// handleList returns the file names in the storage to the client.
54
func (s *storageBackend) handleList(w http.ResponseWriter, req *http.Request) {
55
fp := filepath.Join(s.path, req.URL.Path)
56
dir, prefix := filepath.Split(fp)
57
names, err := readDirs(dir, prefix[:len(prefix)-1], len(s.path)+1)
59
http.Error(w, fmt.Sprintf("404 %v", err), http.StatusNotFound)
63
data := []byte(strings.Join(names, "\n"))
64
w.Header().Set("Content-Type", "application/octet-stream")
68
// readDirs reads the directory hierarchy and compares the found
69
// names with the given prefix.
70
func readDirs(dir, prefix string, start int) ([]string, error) {
72
fis, err := ioutil.ReadDir(dir)
76
for _, fi := range fis {
78
if strings.HasPrefix(name, prefix) {
80
dnames, err := readDirs(filepath.Join(dir, name), prefix, start)
84
names = append(names, dnames...)
87
fullname := filepath.Join(dir, name)[start:]
88
names = append(names, fullname)
94
// handlePut stores data from the client in the storage.
95
func (s *storageBackend) handlePut(w http.ResponseWriter, req *http.Request) {
96
fp := filepath.Join(s.path, req.URL.Path)
97
dir, _ := filepath.Split(fp)
98
err := os.MkdirAll(dir, 0777)
100
http.Error(w, fmt.Sprintf("500 %v", err), http.StatusInternalServerError)
103
out, err := os.Create(fp)
105
http.Error(w, fmt.Sprintf("500 %v", err), http.StatusInternalServerError)
109
if _, err := io.Copy(out, req.Body); err != nil {
110
http.Error(w, fmt.Sprintf("500 %v", err), http.StatusInternalServerError)
113
w.WriteHeader(http.StatusCreated)
116
// handleDelete removes a file from the storage.
117
func (s *storageBackend) handleDelete(w http.ResponseWriter, req *http.Request) {
118
fp := filepath.Join(s.path, req.URL.Path)
119
if err := os.Remove(fp); err != nil && !os.IsNotExist(err) {
120
http.Error(w, fmt.Sprintf("500 %v", err), http.StatusInternalServerError)
123
w.WriteHeader(http.StatusOK)
126
// listen starts an HTTP listener to serve the
128
func listen(dataPath, environName, ip string, port int) (net.Listener, error) {
129
backend := &storageBackend{
130
environName: environName,
131
path: filepath.Join(dataPath, environName),
133
if err := os.MkdirAll(backend.path, 0777); err != nil {
136
listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", ip, port))
138
return nil, fmt.Errorf("cannot start listener: %v", err)
140
mux := http.NewServeMux()
141
mux.Handle("/", backend)
143
go http.Serve(listener, mux)