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  }