github.phpd.cn/thought-machine/please@v12.2.0+incompatible/tools/cache/server/http_server.go (about) 1 package server 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "io/ioutil" 8 "mime/multipart" 9 "net/http" 10 "os" 11 "path" 12 "path/filepath" 13 "strings" 14 15 "github.com/gorilla/mux" 16 "gopkg.in/op/go-logging.v1" 17 ) 18 19 var log = logging.MustGetLogger("server") 20 21 type httpServer struct { 22 cache *Cache 23 } 24 25 // The pingHandler will return a 200 Accepted status 26 // This handler will handle ping endpoint requests, in order to confirm whether the server can be accessed 27 func (s *httpServer) pingHandler(w http.ResponseWriter, r *http.Request) { 28 fmt.Fprintf(w, "Server connection established successfully.") 29 } 30 31 // The getHandler function handles the GET endpoint for the artifact path. 32 // It calls the RetrieveArtifact function, and then either returns the found artifact, or logs the error 33 // returned by RetrieveArtifact. 34 func (s *httpServer) getHandler(w http.ResponseWriter, r *http.Request) { 35 log.Debug("GET %s", r.URL.Path) 36 artifactPath := strings.TrimPrefix(r.URL.Path, "/artifact/") 37 38 arts, err := s.cache.RetrieveArtifact(artifactPath) 39 if err != nil && os.IsNotExist(err) { 40 w.WriteHeader(http.StatusNotFound) 41 log.Debug("%s doesn't exist in http cache", artifactPath) 42 return 43 } else if err != nil { 44 log.Errorf("Failed to retrieve artifact %s: %s", artifactPath, err) 45 w.WriteHeader(http.StatusInternalServerError) 46 return 47 } 48 49 // In order to handle directories we use multipart encoding. 50 // Note that we don't bother on the upload because the client knows all the parts and can 51 // send individually; here they don't know what they'll need to expect. 52 // We could use it for upload too which might be faster and would be more symmetric, but 53 // multipart is a bit fiddly so for now we're not bothering. 54 mw := multipart.NewWriter(w) 55 defer mw.Close() 56 w.Header().Set("Content-Type", mw.FormDataContentType()) 57 for _, art := range arts { 58 if part, err := mw.CreateFormFile(art.File, art.File); err != nil { 59 log.Errorf("Failed to create form file %s: %s", art.File, err) 60 w.WriteHeader(http.StatusInternalServerError) 61 } else if _, err := io.Copy(part, bytes.NewReader(art.Body)); err != nil { 62 log.Errorf("Failed to write form file %s: %s", art.File, err) 63 w.WriteHeader(http.StatusInternalServerError) 64 } 65 } 66 } 67 68 // The postHandler function handles the POST endpoint for the artifact path. 69 // It reads the request body and sends it to the StoreArtifact function, along with the path where it should 70 // be stored. 71 // The handler will either return an error or display a message confirming the file has been created. 72 func (s *httpServer) postHandler(w http.ResponseWriter, r *http.Request) { 73 log.Debug("POST %s", r.URL.Path) 74 artifact, err := ioutil.ReadAll(r.Body) 75 filePath, fileName := path.Split(strings.TrimPrefix(r.URL.Path, "/artifact")) 76 if err == nil { 77 // N.B. We cannot store symlinks here. 78 if err := s.cache.StoreArtifact(strings.TrimPrefix(r.URL.Path, "/artifact"), artifact, ""); err != nil { 79 w.WriteHeader(http.StatusInternalServerError) 80 log.Errorf("Failed to store artifact %s: %s", fileName, err) 81 return 82 } 83 absPath, _ := filepath.Abs(filePath) 84 fmt.Fprintf(w, "%s was created in %s.", fileName, absPath) 85 log.Notice("%s was stored in the http cache.", fileName) 86 } else { 87 w.WriteHeader(http.StatusInternalServerError) 88 log.Errorf("Failed to store artifact %s: %s", fileName, err) 89 } 90 } 91 92 // The deleteAllHandler function handles the DELETE endpoint for the general server path. 93 // It calls the DeleteAllArtifacts function. 94 // The handler will either return an error or display a message confirming the files have been removed. 95 func (s *httpServer) deleteAllHandler(w http.ResponseWriter, r *http.Request) { 96 if err := s.cache.DeleteAllArtifacts(); err != nil { 97 w.WriteHeader(http.StatusInternalServerError) 98 log.Errorf("Failed to clean http cache: %s", err) 99 return 100 } 101 log.Notice("The http cache has been cleaned.") 102 fmt.Fprintf(w, "The http cache has been cleaned.") 103 } 104 105 // The deleteHandler function handles the DELETE endpoint for the artifact path. 106 // It calls the DeleteArtifact function, sending the path of the artifact as a parameter. 107 // The handler will either return an error or display a message confirming the artifact has been removed. 108 func (s *httpServer) deleteHandler(w http.ResponseWriter, r *http.Request) { 109 artifactPath := strings.TrimPrefix(r.URL.Path, "/artifact") 110 if err := s.cache.DeleteArtifact(artifactPath); err != nil { 111 w.WriteHeader(http.StatusInternalServerError) 112 log.Errorf("Failed to remove %s from http cache: %s", artifactPath, err) 113 return 114 } 115 log.Notice("%s was removed from the http cache.", artifactPath) 116 fmt.Fprintf(w, "%s artifact was removed from cache.", artifactPath) 117 } 118 119 // BuildRouter creates a router, sets the base FileServer directory and the Handler Functions 120 // for each endpoint, and then returns the router. 121 func BuildRouter(cache *Cache) *mux.Router { 122 s := &httpServer{cache: cache} 123 r := mux.NewRouter() 124 r.HandleFunc("/ping", s.pingHandler).Methods("GET") 125 r.HandleFunc("/artifact/{os_name}/{artifact:.*}", s.getHandler).Methods("GET") 126 r.HandleFunc("/artifact/{os_name}/{artifact:.*}", s.postHandler).Methods("POST") 127 r.HandleFunc("/artifact/{artifact:.*}", s.deleteHandler).Methods("DELETE") 128 r.HandleFunc("/", s.deleteAllHandler).Methods("DELETE") 129 return r 130 }