mirror of
https://github.com/dutchcoders/transfer.sh.git
synced 2024-11-29 23:50:18 +01:00
3b777e1c19
* gpg encryption support * gpg encryption support * refinement * refactor using protonmail opengpg * warning * remove exception in linting * reduce I/O on decrypt * fix typo * streaming in encrypt * Update README.md do not reference server public hosting version in encrypt/decrypt headers example * linting * linting fix * increase default random-token-length to 10
119 lines
3 KiB
Go
119 lines
3 KiB
Go
package storage
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"regexp"
|
|
"strconv"
|
|
"time"
|
|
)
|
|
|
|
type Range struct {
|
|
Start uint64
|
|
Limit uint64
|
|
contentRange string
|
|
}
|
|
|
|
// Range Reconstructs Range header and returns it
|
|
func (r *Range) Range() string {
|
|
if r.Limit > 0 {
|
|
return fmt.Sprintf("bytes=%d-%d", r.Start, r.Start+r.Limit-1)
|
|
} else {
|
|
return fmt.Sprintf("bytes=%d-", r.Start)
|
|
}
|
|
}
|
|
|
|
// AcceptLength Tries to accept given range
|
|
// returns newContentLength if range was satisfied, otherwise returns given contentLength
|
|
func (r *Range) AcceptLength(contentLength uint64) (newContentLength uint64) {
|
|
newContentLength = contentLength
|
|
if r.Limit == 0 {
|
|
r.Limit = newContentLength - r.Start
|
|
}
|
|
if contentLength < r.Start {
|
|
return
|
|
}
|
|
if r.Limit > contentLength-r.Start {
|
|
return
|
|
}
|
|
r.contentRange = fmt.Sprintf("bytes %d-%d/%d", r.Start, r.Start+r.Limit-1, contentLength)
|
|
newContentLength = r.Limit
|
|
return
|
|
}
|
|
|
|
func (r *Range) SetContentRange(cr string) {
|
|
r.contentRange = cr
|
|
}
|
|
|
|
// Returns accepted Content-Range header. If range wasn't accepted empty string is returned
|
|
func (r *Range) ContentRange() string {
|
|
return r.contentRange
|
|
}
|
|
|
|
var rexp *regexp.Regexp = regexp.MustCompile(`^bytes=([0-9]+)-([0-9]*)$`)
|
|
|
|
// Parses HTTP Range header and returns struct on success
|
|
// only bytes=start-finish supported
|
|
func ParseRange(rng string) *Range {
|
|
if rng == "" {
|
|
return nil
|
|
}
|
|
|
|
matches := rexp.FindAllStringSubmatch(rng, -1)
|
|
if len(matches) != 1 || len(matches[0]) != 3 {
|
|
return nil
|
|
}
|
|
if len(matches[0][0]) != len(rng) || len(matches[0][1]) == 0 {
|
|
return nil
|
|
}
|
|
|
|
start, err := strconv.ParseUint(matches[0][1], 10, 64)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
|
|
if len(matches[0][2]) == 0 {
|
|
return &Range{Start: start, Limit: 0}
|
|
}
|
|
|
|
finish, err := strconv.ParseUint(matches[0][2], 10, 64)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
if finish < start || finish+1 < finish {
|
|
return nil
|
|
}
|
|
|
|
return &Range{Start: start, Limit: finish - start + 1}
|
|
}
|
|
|
|
// Storage is the interface for storage operation
|
|
type Storage interface {
|
|
// Get retrieves a file from storage
|
|
Get(ctx context.Context, token string, filename string, rng *Range) (reader io.ReadCloser, contentLength uint64, err error)
|
|
// Head retrieves content length of a file from storage
|
|
Head(ctx context.Context, token string, filename string) (contentLength uint64, err error)
|
|
// Put saves a file on storage
|
|
Put(ctx context.Context, token string, filename string, reader io.Reader, contentType string, contentLength uint64) error
|
|
// Delete removes a file from storage
|
|
Delete(ctx context.Context, token string, filename string) error
|
|
// IsNotExist indicates if a file doesn't exist on storage
|
|
IsNotExist(err error) bool
|
|
// Purge cleans up the storage
|
|
Purge(ctx context.Context, days time.Duration) error
|
|
// Whether storage supports Get with Range header
|
|
IsRangeSupported() bool
|
|
// Type returns the storage type
|
|
Type() string
|
|
}
|
|
|
|
func CloseCheck(c io.Closer) {
|
|
if c == nil {
|
|
return
|
|
}
|
|
|
|
if err := c.Close(); err != nil {
|
|
fmt.Println("Received close error:", err)
|
|
}
|
|
}
|