mirror of
https://github.com/kopia/kopia.git
synced 2026-01-24 22:38:00 -05:00
* cli: added a flag to create repository with v2 index features * content: plumb through compression.ID parameter to content.Manager.WriteContent() * content: expose content.Manager.SupportsContentCompression This allows object manager to decide whether to create compressed object or let the content manager do it. * object: if compression is requested and the repo supports it, pass compression ID to the content manager * cli: show compression status in 'repository status' * cli: output compression information in 'content list' and 'content stats' * content: compression and decompression support * content: unit tests for compression * object: compression tests * testing: added integration tests against v2 index * testing: run all e2e tests with and without content-level compression * htmlui: added UI for specifying index format on creation * cli: additional tests for 'content ls' and 'content stats' * applied pr suggestions
96 lines
2.4 KiB
Go
96 lines
2.4 KiB
Go
package server
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/gorilla/mux"
|
|
|
|
"github.com/kopia/kopia/internal/serverapi"
|
|
"github.com/kopia/kopia/repo"
|
|
"github.com/kopia/kopia/repo/compression"
|
|
"github.com/kopia/kopia/repo/content"
|
|
"github.com/kopia/kopia/repo/manifest"
|
|
)
|
|
|
|
func (s *Server) handleContentGet(ctx context.Context, r *http.Request, body []byte) (interface{}, *apiError) {
|
|
dr, ok := s.rep.(repo.DirectRepository)
|
|
if !ok {
|
|
return nil, notFoundError("content not found")
|
|
}
|
|
|
|
cid := content.ID(mux.Vars(r)["contentID"])
|
|
|
|
data, err := dr.ContentReader().GetContent(ctx, cid)
|
|
if errors.Is(err, content.ErrContentNotFound) {
|
|
return nil, notFoundError("content not found")
|
|
}
|
|
|
|
return data, nil
|
|
}
|
|
|
|
func (s *Server) handleContentInfo(ctx context.Context, r *http.Request, body []byte) (interface{}, *apiError) {
|
|
dr, ok := s.rep.(repo.DirectRepository)
|
|
if !ok {
|
|
return nil, notFoundError("content not found")
|
|
}
|
|
|
|
cid := content.ID(mux.Vars(r)["contentID"])
|
|
|
|
ci, err := dr.ContentReader().ContentInfo(ctx, cid)
|
|
|
|
switch {
|
|
case err == nil:
|
|
return ci, nil
|
|
|
|
case errors.Is(err, content.ErrContentNotFound):
|
|
return nil, notFoundError("content not found")
|
|
|
|
default:
|
|
return nil, internalServerError(err)
|
|
}
|
|
}
|
|
|
|
func (s *Server) handleContentPut(ctx context.Context, r *http.Request, data []byte) (interface{}, *apiError) {
|
|
dr, ok := s.rep.(repo.DirectRepositoryWriter)
|
|
if !ok {
|
|
return nil, repositoryNotWritableError()
|
|
}
|
|
|
|
cid := content.ID(mux.Vars(r)["contentID"])
|
|
prefix := cid.Prefix()
|
|
|
|
if strings.HasPrefix(string(prefix), manifest.ContentPrefix) {
|
|
// it's not allowed to create contents prefixed with 'm' since those could be mistaken for manifest contents.
|
|
return nil, accessDeniedError()
|
|
}
|
|
|
|
var comp compression.HeaderID
|
|
|
|
if c := r.URL.Query().Get("compression"); c != "" {
|
|
v, err := strconv.ParseInt(c, 16, 32)
|
|
if err != nil {
|
|
return nil, requestError(serverapi.ErrorMalformedRequest, "malformed compression ID")
|
|
}
|
|
|
|
comp = compression.HeaderID(v)
|
|
if _, ok := compression.ByHeaderID[comp]; !ok {
|
|
return nil, requestError(serverapi.ErrorMalformedRequest, "invalid compression ID")
|
|
}
|
|
}
|
|
|
|
actualCID, err := dr.ContentManager().WriteContent(ctx, data, prefix, comp)
|
|
if err != nil {
|
|
return nil, internalServerError(err)
|
|
}
|
|
|
|
if actualCID != cid {
|
|
return nil, requestError(serverapi.ErrorMalformedRequest, "mismatched content ID")
|
|
}
|
|
|
|
return &serverapi.Empty{}, nil
|
|
}
|