github.com/cs3org/reva/v2@v2.27.7/pkg/storage/utils/downloader/downloader.go (about) 1 // Copyright 2018-2021 CERN 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 // 15 // In applying this license, CERN does not waive the privileges and immunities 16 // granted to it by virtue of its status as an Intergovernmental Organization 17 // or submit itself to any jurisdiction. 18 19 package downloader 20 21 import ( 22 "context" 23 "fmt" 24 "io" 25 "net/http" 26 27 gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" 28 rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" 29 provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" 30 "github.com/cs3org/reva/v2/internal/http/services/datagateway" 31 "github.com/cs3org/reva/v2/pkg/errtypes" 32 "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" 33 "github.com/cs3org/reva/v2/pkg/rhttp" 34 ) 35 36 // Downloader is the interface implemented by the objects that are able to 37 // download a path into a destination Writer 38 type Downloader interface { 39 Download(context.Context, *provider.ResourceId, io.Writer) error 40 } 41 42 type revaDownloader struct { 43 gatewaySelector pool.Selectable[gateway.GatewayAPIClient] 44 httpClient *http.Client 45 } 46 47 // NewDownloader creates a Downloader from the reva gateway 48 func NewDownloader(gatewaySelector pool.Selectable[gateway.GatewayAPIClient], options ...rhttp.Option) Downloader { 49 return &revaDownloader{ 50 gatewaySelector: gatewaySelector, 51 httpClient: rhttp.GetHTTPClient(options...), 52 } 53 } 54 55 func getDownloadProtocol(protocols []*gateway.FileDownloadProtocol, prot string) (*gateway.FileDownloadProtocol, error) { 56 for _, p := range protocols { 57 if p.Protocol == prot { 58 return p, nil 59 } 60 } 61 return nil, errtypes.InternalError(fmt.Sprintf("protocol %s not supported for downloading", prot)) 62 } 63 64 // Download downloads a resource given the path to the dst Writer 65 func (r *revaDownloader) Download(ctx context.Context, id *provider.ResourceId, dst io.Writer) error { 66 gatewayClient, err := r.gatewaySelector.Next() 67 if err != nil { 68 return err 69 } 70 downResp, err := gatewayClient.InitiateFileDownload(ctx, &provider.InitiateFileDownloadRequest{ 71 Ref: &provider.Reference{ 72 ResourceId: id, 73 Path: ".", 74 }, 75 }) 76 77 switch { 78 case err != nil: 79 return err 80 case downResp.Status.Code != rpc.Code_CODE_OK: 81 return errtypes.InternalError(downResp.Status.Message) 82 } 83 84 p, err := getDownloadProtocol(downResp.Protocols, "simple") 85 if err != nil { 86 p, err = getDownloadProtocol(downResp.Protocols, "spaces") 87 if err != nil { 88 return err 89 } 90 } 91 92 httpReq, err := rhttp.NewRequest(ctx, http.MethodGet, p.DownloadEndpoint, nil) 93 if err != nil { 94 return err 95 } 96 httpReq.Header.Set(datagateway.TokenTransportHeader, p.Token) 97 98 httpRes, err := r.httpClient.Do(httpReq) 99 if err != nil { 100 return err 101 } 102 defer httpRes.Body.Close() 103 104 if err := errtypes.NewErrtypeFromHTTPStatusCode(httpRes.StatusCode, id.String()); err != nil { 105 return err 106 } 107 108 _, err = io.Copy(dst, httpRes.Body) 109 return err 110 }