This repository has been archived by the owner on Mar 11, 2018. It is now read-only.
/
web.go
125 lines (106 loc) · 2.89 KB
/
web.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
package main
import (
"archive/zip"
"fmt"
"io"
"labix.org/v2/mgo/bson"
"net/http"
"os"
"os/exec"
"path/filepath"
"regexp"
"sync"
"time"
)
const urlPrefix = "/worlds/"
func worldsHandler(w http.ResponseWriter, r *http.Request) {
path := r.URL.Path[len(urlPrefix):]
// match /508b5c0ab75f04080000007b.zip
route := regexp.MustCompile(`^([\w]{24})\.zip$`)
if match := route.FindStringSubmatch(path); match != nil {
filename := r.URL.Query().Get("name")
if filename == "" {
filename = match[1]
}
streamWorld(w, r, match[1], filename)
fmt.Println("[GET]", r.URL.Path, "– 200")
} else {
fmt.Println("[GET]", r.URL.Path, "– 404")
http.NotFound(w, r)
}
}
func streamWorld(w http.ResponseWriter, r *http.Request, worldId string, filename string) {
w.Header().Add("Content-Type", "application/zip")
w.Header().Add("Content-Disposition",
fmt.Sprintf("attachment; filename=%s.zip", filename))
hexId := bson.ObjectIdHex(worldId)
url, err := readUrlForServer(hexId)
if err != nil {
fmt.Println("no url for world", hexId.Hex(), err)
http.NotFound(w, r)
}
tempPath, err := createTempDir(worldId)
if err != nil {
fmt.Println("failed to create tempdir", err)
http.Error(w, "failed to create tempdir", 500)
}
zip := zip.NewWriter(w)
defer zip.Close()
// Zip files as they are downloaded
watcher := NewWatcher(tempPath, 5*time.Second)
go watcher.Watch()
var wg sync.WaitGroup
wg.Add(1)
go zipFilesAsTheyAppear(tempPath, filename, watcher.C, zip, &wg)
// start the download
err = restoreDir(url, tempPath)
if err != nil {
fmt.Println("failed to download archive", err)
http.Error(w, "failed to download archive", 500)
}
watcher.Cancel()
wg.Wait()
}
func zipFilesAsTheyAppear(root, prefix string, files chan string, zip *zip.Writer, wg *sync.WaitGroup) {
defer wg.Done()
for path := range files {
fileName := path[len(root)+1:]
zipF, err := zip.Create(prefix + "/" + fileName)
if err != nil {
fmt.Println("failed to write zip header", fileName)
return
}
f, err := os.Open(path)
if err != nil {
fmt.Println("failed to open path", path, err)
return
}
_, err = io.Copy(zipF, f)
if err != nil {
fmt.Println("failed to zip path", path, err)
return
}
}
}
func createTempDir(name string) (string, error) {
path := filepath.Join(os.TempDir(), name)
err := exec.Command("rm", "-rf", path).Run()
if err != nil {
return "", err
}
return path, exec.Command("mkdir", "-p", path).Run()
}
func notFoundHandler(w http.ResponseWriter, r *http.Request) {
fmt.Println("[GET]", r.URL.Path, "– 404")
http.NotFound(w, r)
}
func main() {
port := os.Getenv("PORT")
if port == "" {
port = "5000"
}
http.HandleFunc(urlPrefix, worldsHandler) // redirect all urls to the handler function
http.HandleFunc("/", notFoundHandler)
fmt.Println("listening on " + port)
http.ListenAndServe(":"+port, nil) // listen for connections at port 9999 on the local machine
}