package container // import "github.com/docker/docker/api/server/router/container" import ( "compress/flate" "compress/gzip" "context" "encoding/base64" "encoding/json" "errors" "io" "net/http" "github.com/docker/docker/api/server/httputils" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/versions" "github.com/docker/docker/errdefs" gddohttputil "github.com/golang/gddo/httputil" ) type pathError struct{} func (pathError) Error() string { return "Path cannot be empty" } func (pathError) InvalidParameter() {} // postContainersCopy is deprecated in favor of getContainersArchive. func (s *containerRouter) postContainersCopy(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { // Deprecated since 1.8, Errors out since 1.12 version := httputils.VersionFromContext(ctx) if versions.GreaterThanOrEqualTo(version, "1.24") { w.WriteHeader(http.StatusNotFound) return nil } if err := httputils.CheckForJSON(r); err != nil { return err } cfg := types.CopyConfig{} if err := json.NewDecoder(r.Body).Decode(&cfg); err != nil { if err == io.EOF { return errdefs.InvalidParameter(errors.New("got EOF while reading request body")) } return errdefs.InvalidParameter(err) } if cfg.Resource == "" { return pathError{} } data, err := s.backend.ContainerCopy(vars["name"], cfg.Resource) if err != nil { return err } defer data.Close() w.Header().Set("Content-Type", "application/x-tar") _, err = io.Copy(w, data) return err } // // Encode the stat to JSON, base64 encode, and place in a header. func setContainerPathStatHeader(stat *types.ContainerPathStat, header http.Header) error { statJSON, err := json.Marshal(stat) if err != nil { return err } header.Set( "X-Docker-Container-Path-Stat", base64.StdEncoding.EncodeToString(statJSON), ) return nil } func (s *containerRouter) headContainersArchive(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { v, err := httputils.ArchiveFormValues(r, vars) if err != nil { return err } stat, err := s.backend.ContainerStatPath(v.Name, v.Path) if err != nil { return err } return setContainerPathStatHeader(stat, w.Header()) } func writeCompressedResponse(w http.ResponseWriter, r *http.Request, body io.Reader) error { var cw io.Writer switch gddohttputil.NegotiateContentEncoding(r, []string{"gzip", "deflate"}) { case "gzip": gw := gzip.NewWriter(w) defer gw.Close() cw = gw w.Header().Set("Content-Encoding", "gzip") case "deflate": fw, err := flate.NewWriter(w, flate.DefaultCompression) if err != nil { return err } defer fw.Close() cw = fw w.Header().Set("Content-Encoding", "deflate") default: cw = w } _, err := io.Copy(cw, body) return err } func (s *containerRouter) getContainersArchive(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { v, err := httputils.ArchiveFormValues(r, vars) if err != nil { return err } tarArchive, stat, err := s.backend.ContainerArchivePath(v.Name, v.Path) if err != nil { return err } defer tarArchive.Close() if err := setContainerPathStatHeader(stat, w.Header()); err != nil { return err } w.Header().Set("Content-Type", "application/x-tar") return writeCompressedResponse(w, r, tarArchive) } func (s *containerRouter) putContainersArchive(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { v, err := httputils.ArchiveFormValues(r, vars) if err != nil { return err } noOverwriteDirNonDir := httputils.BoolValue(r, "noOverwriteDirNonDir") copyUIDGID := httputils.BoolValue(r, "copyUIDGID") return s.backend.ContainerExtractToDir(v.Name, v.Path, copyUIDGID, noOverwriteDirNonDir, r.Body) }