Skip to content

Commit

Permalink
Support for the Location to be a relative URL
Browse files Browse the repository at this point in the history
The location header value returned by a creation is a URL that
"MAY be absolute or relative" as quoted from the TUS specification:
https://tus.io/protocols/resumable-upload.html#creation

This patch supports the case where the location value is a relative
URL.
  • Loading branch information
hmalphettes committed Aug 7, 2020
1 parent 45c7ec8 commit b168d24
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 15 deletions.
34 changes: 20 additions & 14 deletions client.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package tus

import (
"fmt"
"io"
"io/ioutil"
"net/http"
Expand Down Expand Up @@ -90,27 +91,18 @@ func (c *Client) CreateUpload(u *Upload) (*Uploader, error) {

switch res.StatusCode {
case 201:
url := res.Header.Get("Location")
location := res.Header.Get("Location")

baseUrl, err := netUrl.Parse(c.Url)
newURL, err := c.resolveLocationURL(location)
if err != nil {
return nil, ErrUrlNotRecognized
}

newUrl, err := netUrl.Parse(url)
if err != nil {
return nil, ErrUrlNotRecognized
}
if newUrl.Scheme == "" {
newUrl.Scheme = baseUrl.Scheme
url = newUrl.String()
return nil, err
}

if c.Config.Resume {
c.Config.Store.Set(u.Fingerprint, url)
c.Config.Store.Set(u.Fingerprint, newURL.String())
}

return NewUploader(c, url, u, 0), nil
return NewUploader(c, newURL.String(), u, 0), nil
case 412:
return nil, ErrVersionMismatch
case 413:
Expand All @@ -120,6 +112,20 @@ func (c *Client) CreateUpload(u *Upload) (*Uploader, error) {
}
}

func (c *Client) resolveLocationURL(location string) (*netUrl.URL, error) {
baseURL, err := netUrl.Parse(c.Url)
if err != nil {
return nil, fmt.Errorf("Invalid URL '%s'", c.Url)
}

locationURL, err := netUrl.Parse(location)
if err != nil {
return nil, fmt.Errorf("Invalid Location header value for a Creation '%s': %s", location, err.Error())
}
newURL := baseURL.ResolveReference(locationURL)
return newURL, nil
}

// ResumeUpload resumes the upload if already created, otherwise it will return an error.
func (c *Client) ResumeUpload(u *Upload) (*Uploader, error) {
if u == nil {
Expand Down
38 changes: 38 additions & 0 deletions client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import (
"fmt"
"net/http"
"net/http/httptest"
netUrl "net/url"
"os"
"path"
"strings"
"sync"
"testing"
Expand Down Expand Up @@ -325,6 +327,42 @@ func (s *UploadTestSuite) TestResumeUpload() {
s.EqualValues(1048576*150, fi.Size)
}

func (s *UploadTestSuite) TestUploadLocation() {
client, err := NewClient(s.url, nil)
s.Nil(err)
sourceURL, err := netUrl.Parse(s.url)
s.Nil(err)

s.T().Run("Location is a full URL", func(t *testing.T) {
location := "https://serveit.com/upload/123"
resourceURL, err := client.resolveLocationURL(location)
s.Nil(err)
s.EqualValues(location, resourceURL.String())
})

s.T().Run("Location is a URL without scheme", func(t *testing.T) {
location := "//serveit.com/upload/123"
resourceURL, err := client.resolveLocationURL(location)
s.Nil(err)
s.EqualValues(sourceURL.Scheme+":"+location, resourceURL.String())
})

s.T().Run("Location is an absolute path", func(t *testing.T) {
location := "/upload/123"
resourceURL, err := client.resolveLocationURL(location)
s.Nil(err)
s.EqualValues(sourceURL.Scheme+"://"+sourceURL.Host+location, resourceURL.String())
})

s.T().Run("Location is a relative path", func(t *testing.T) {
location := "somewhere/123"
resourceURL, err := client.resolveLocationURL(location)
s.Nil(err)
s.EqualValues(sourceURL.Scheme+"://"+sourceURL.Host+path.Join(sourceURL.Path, location), resourceURL.String())
})

}

func TestUploadTestSuite(t *testing.T) {
suite.Run(t, new(UploadTestSuite))
}
Expand Down
1 change: 0 additions & 1 deletion errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ var (
ErrUploadNotFound = errors.New("upload not found.")
ErrResumeNotEnabled = errors.New("resuming not enabled.")
ErrFingerprintNotSet = errors.New("fingerprint not set.")
ErrUrlNotRecognized = errors.New("url not recognized")
)

type ClientError struct {
Expand Down

0 comments on commit b168d24

Please sign in to comment.