Skip to content

Commit

Permalink
Merge pull request #56 from ncw/fix-54-header-encoding
Browse files Browse the repository at this point in the history
Encode Dropbox-API-Arg according to the specification
  • Loading branch information
karandeep-johar authored Nov 15, 2019
2 parents 3eae5e5 + f23ef52 commit 373d22d
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 17 deletions.
24 changes: 12 additions & 12 deletions dropbox/files/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,7 @@ func (dbx *apiImpl) AlphaUpload(arg *CommitInfoWithProperties, content io.Reader

headers := map[string]string{
"Content-Type": "application/octet-stream",
"Dropbox-API-Arg": string(b),
"Dropbox-API-Arg": dropbox.HTTPHeaderSafeJSON(b),
}
if dbx.Config.AsMemberID != "" {
headers["Dropbox-API-Select-User"] = dbx.Config.AsMemberID
Expand Down Expand Up @@ -1640,7 +1640,7 @@ func (dbx *apiImpl) Download(arg *DownloadArg) (res *FileMetadata, content io.Re
}

headers := map[string]string{
"Dropbox-API-Arg": string(b),
"Dropbox-API-Arg": dropbox.HTTPHeaderSafeJSON(b),
}
for k, v := range arg.ExtraHeaders {
headers[k] = v
Expand Down Expand Up @@ -1710,7 +1710,7 @@ func (dbx *apiImpl) DownloadZip(arg *DownloadZipArg) (res *DownloadZipResult, co
}

headers := map[string]string{
"Dropbox-API-Arg": string(b),
"Dropbox-API-Arg": dropbox.HTTPHeaderSafeJSON(b),
}
if dbx.Config.AsMemberID != "" {
headers["Dropbox-API-Select-User"] = dbx.Config.AsMemberID
Expand Down Expand Up @@ -1777,7 +1777,7 @@ func (dbx *apiImpl) Export(arg *ExportArg) (res *ExportResult, content io.ReadCl
}

headers := map[string]string{
"Dropbox-API-Arg": string(b),
"Dropbox-API-Arg": dropbox.HTTPHeaderSafeJSON(b),
}
if dbx.Config.AsMemberID != "" {
headers["Dropbox-API-Select-User"] = dbx.Config.AsMemberID
Expand Down Expand Up @@ -1921,7 +1921,7 @@ func (dbx *apiImpl) GetPreview(arg *PreviewArg) (res *FileMetadata, content io.R
}

headers := map[string]string{
"Dropbox-API-Arg": string(b),
"Dropbox-API-Arg": dropbox.HTTPHeaderSafeJSON(b),
}
if dbx.Config.AsMemberID != "" {
headers["Dropbox-API-Select-User"] = dbx.Config.AsMemberID
Expand Down Expand Up @@ -2120,7 +2120,7 @@ func (dbx *apiImpl) GetThumbnail(arg *ThumbnailArg) (res *FileMetadata, content
}

headers := map[string]string{
"Dropbox-API-Arg": string(b),
"Dropbox-API-Arg": dropbox.HTTPHeaderSafeJSON(b),
}
if dbx.Config.AsMemberID != "" {
headers["Dropbox-API-Select-User"] = dbx.Config.AsMemberID
Expand Down Expand Up @@ -2187,7 +2187,7 @@ func (dbx *apiImpl) GetThumbnailBatch(arg *GetThumbnailBatchArg) (res *GetThumbn
}

headers := map[string]string{
"Dropbox-API-Arg": string(b),
"Dropbox-API-Arg": dropbox.HTTPHeaderSafeJSON(b),
}
if dbx.Config.AsMemberID != "" {
headers["Dropbox-API-Select-User"] = dbx.Config.AsMemberID
Expand Down Expand Up @@ -3696,7 +3696,7 @@ func (dbx *apiImpl) Upload(arg *CommitInfo, content io.Reader) (res *FileMetadat

headers := map[string]string{
"Content-Type": "application/octet-stream",
"Dropbox-API-Arg": string(b),
"Dropbox-API-Arg": dropbox.HTTPHeaderSafeJSON(b),
}
if dbx.Config.AsMemberID != "" {
headers["Dropbox-API-Select-User"] = dbx.Config.AsMemberID
Expand Down Expand Up @@ -3763,7 +3763,7 @@ func (dbx *apiImpl) UploadSessionAppendV2(arg *UploadSessionAppendArg, content i

headers := map[string]string{
"Content-Type": "application/octet-stream",
"Dropbox-API-Arg": string(b),
"Dropbox-API-Arg": dropbox.HTTPHeaderSafeJSON(b),
}
if dbx.Config.AsMemberID != "" {
headers["Dropbox-API-Select-User"] = dbx.Config.AsMemberID
Expand Down Expand Up @@ -3828,7 +3828,7 @@ func (dbx *apiImpl) UploadSessionAppend(arg *UploadSessionCursor, content io.Rea

headers := map[string]string{
"Content-Type": "application/octet-stream",
"Dropbox-API-Arg": string(b),
"Dropbox-API-Arg": dropbox.HTTPHeaderSafeJSON(b),
}
if dbx.Config.AsMemberID != "" {
headers["Dropbox-API-Select-User"] = dbx.Config.AsMemberID
Expand Down Expand Up @@ -3890,7 +3890,7 @@ func (dbx *apiImpl) UploadSessionFinish(arg *UploadSessionFinishArg, content io.

headers := map[string]string{
"Content-Type": "application/octet-stream",
"Dropbox-API-Arg": string(b),
"Dropbox-API-Arg": dropbox.HTTPHeaderSafeJSON(b),
}
if dbx.Config.AsMemberID != "" {
headers["Dropbox-API-Select-User"] = dbx.Config.AsMemberID
Expand Down Expand Up @@ -4089,7 +4089,7 @@ func (dbx *apiImpl) UploadSessionStart(arg *UploadSessionStartArg, content io.Re

headers := map[string]string{
"Content-Type": "application/octet-stream",
"Dropbox-API-Arg": string(b),
"Dropbox-API-Arg": dropbox.HTTPHeaderSafeJSON(b),
}
if dbx.Config.AsMemberID != "" {
headers["Dropbox-API-Select-User"] = dbx.Config.AsMemberID
Expand Down
6 changes: 3 additions & 3 deletions dropbox/paper/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ func (dbx *apiImpl) DocsCreate(arg *PaperDocCreateArgs, content io.Reader) (res

headers := map[string]string{
"Content-Type": "application/octet-stream",
"Dropbox-API-Arg": string(b),
"Dropbox-API-Arg": dropbox.HTTPHeaderSafeJSON(b),
}
if dbx.Config.AsMemberID != "" {
headers["Dropbox-API-Select-User"] = dbx.Config.AsMemberID
Expand Down Expand Up @@ -244,7 +244,7 @@ func (dbx *apiImpl) DocsDownload(arg *PaperDocExport) (res *PaperDocExportResult
}

headers := map[string]string{
"Dropbox-API-Arg": string(b),
"Dropbox-API-Arg": dropbox.HTTPHeaderSafeJSON(b),
}
if dbx.Config.AsMemberID != "" {
headers["Dropbox-API-Select-User"] = dbx.Config.AsMemberID
Expand Down Expand Up @@ -830,7 +830,7 @@ func (dbx *apiImpl) DocsUpdate(arg *PaperDocUpdateArgs, content io.Reader) (res

headers := map[string]string{
"Content-Type": "application/octet-stream",
"Dropbox-API-Arg": string(b),
"Dropbox-API-Arg": dropbox.HTTPHeaderSafeJSON(b),
}
if dbx.Config.AsMemberID != "" {
headers["Dropbox-API-Select-User"] = dbx.Config.AsMemberID
Expand Down
18 changes: 18 additions & 0 deletions dropbox/sdk.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
package dropbox

import (
"bytes"
"encoding/json"
"fmt"
"io"
Expand Down Expand Up @@ -221,3 +222,20 @@ func HandleCommonAPIErrors(c Config, resp *http.Response, body []byte) error {
}
return apiError
}

// HTTPHeaderSafeJSON encode the JSON passed in b []byte passed in in
// a way that is suitable for HTTP headers.
//
// See: https://www.dropbox.com/developers/reference/json-encoding
func HTTPHeaderSafeJSON(b []byte) string {
var s bytes.Buffer
s.Grow(len(b))
for _, r := range string(b) {
if r >= 0x007f {
fmt.Fprintf(&s, "\\u%04x", r)
} else {
s.WriteRune(r)
}
}
return s.String()
}
55 changes: 55 additions & 0 deletions dropbox/sdk_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
package dropbox_test

import (
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
Expand Down Expand Up @@ -169,3 +170,57 @@ func TestAccessError(t *testing.T) {
t.Errorf("Unexpected tag: %s\n", re.AccessError.PaperAccessDenied.Tag)
}
}

func TestHTTPHeaderSafeJSON(t *testing.T) {
for _, test := range []struct {
name string
in interface{}
want string
}{
{
name: "empty string",
in: ``,
want: `""`,
},
{
name: "integer",
in: 123,
want: `123`,
},
{
name: "normal string",
in: `Normal string!`,
want: `"Normal string!"`,
},
{
name: "unicode",
in: `üñîcødé`,
want: `"\u00fc\u00f1\u00eec\u00f8d\u00e9"`,
},
{
name: "7f",
in: "\x7f",
want: `"\u007f"`,
},
{
name: "example from the docs",
in: struct {
Field string `json:"field"`
}{
Field: "some_üñîcødé_and_\x7F",
},
want: `{"field":"some_\u00fc\u00f1\u00eec\u00f8d\u00e9_and_\u007f"}`,
},
} {
t.Run(test.name, func(t *testing.T) {
b, err := json.Marshal(test.in)
if err != nil {
t.Fatal(err)
}
got := dropbox.HTTPHeaderSafeJSON(b)
if got != test.want {
t.Errorf("Want %q got %q", test.want, got)
}
})
}
}
2 changes: 1 addition & 1 deletion dropbox/sharing/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -966,7 +966,7 @@ func (dbx *apiImpl) GetSharedLinkFile(arg *GetSharedLinkMetadataArg) (res IsShar
}

headers := map[string]string{
"Dropbox-API-Arg": string(b),
"Dropbox-API-Arg": dropbox.HTTPHeaderSafeJSON(b),
}
if dbx.Config.AsMemberID != "" {
headers["Dropbox-API-Select-User"] = dbx.Config.AsMemberID
Expand Down
2 changes: 1 addition & 1 deletion generator/go_client.stoneg.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ def _generate_request(self, namespace, route):
headers = {}
if not is_void_type(route.arg_data_type):
if host == 'content' or style in ['upload', 'download']:
headers["Dropbox-API-Arg"] = "string(b)"
headers["Dropbox-API-Arg"] = "dropbox.HTTPHeaderSafeJSON(b)"
else:
headers["Content-Type"] = '"application/json"'
if style == 'upload':
Expand Down
18 changes: 18 additions & 0 deletions generator/go_rsrc/sdk.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"io"
"log"
"net/http"
"strings"

"golang.org/x/net/context"
"golang.org/x/oauth2"
Expand Down Expand Up @@ -221,3 +222,20 @@ func HandleCommonAPIErrors(c Config, resp *http.Response, body []byte) error {
}
return apiError
}

// HTTPHeaderSafeJSON encode the JSON passed in b []byte passed in in
// a way that is suitable for HTTP headers.
//
// See: https://www.dropbox.com/developers/reference/json-encoding
func HTTPHeaderSafeJSON(b []byte) string {
var s strings.Builder
s.Grow(len(b))
for _, r := range string(b) {
if r >= 0x007f {
fmt.Fprintf(&s, "\\u%04x", r)
} else {
s.WriteRune(r)
}
}
return s.String()
}

0 comments on commit 373d22d

Please sign in to comment.