Skip to content

Commit 373d22d

Browse files
Merge pull request #56 from ncw/fix-54-header-encoding
Encode Dropbox-API-Arg according to the specification
2 parents 3eae5e5 + f23ef52 commit 373d22d

File tree

7 files changed

+108
-17
lines changed

7 files changed

+108
-17
lines changed

dropbox/files/client.go

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,7 @@ func (dbx *apiImpl) AlphaUpload(arg *CommitInfoWithProperties, content io.Reader
481481

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

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

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

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

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

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

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

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

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

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

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

40904090
headers := map[string]string{
40914091
"Content-Type": "application/octet-stream",
4092-
"Dropbox-API-Arg": string(b),
4092+
"Dropbox-API-Arg": dropbox.HTTPHeaderSafeJSON(b),
40934093
}
40944094
if dbx.Config.AsMemberID != "" {
40954095
headers["Dropbox-API-Select-User"] = dbx.Config.AsMemberID

dropbox/paper/client.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ func (dbx *apiImpl) DocsCreate(arg *PaperDocCreateArgs, content io.Reader) (res
178178

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

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

831831
headers := map[string]string{
832832
"Content-Type": "application/octet-stream",
833-
"Dropbox-API-Arg": string(b),
833+
"Dropbox-API-Arg": dropbox.HTTPHeaderSafeJSON(b),
834834
}
835835
if dbx.Config.AsMemberID != "" {
836836
headers["Dropbox-API-Select-User"] = dbx.Config.AsMemberID

dropbox/sdk.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
package dropbox
2222

2323
import (
24+
"bytes"
2425
"encoding/json"
2526
"fmt"
2627
"io"
@@ -221,3 +222,20 @@ func HandleCommonAPIErrors(c Config, resp *http.Response, body []byte) error {
221222
}
222223
return apiError
223224
}
225+
226+
// HTTPHeaderSafeJSON encode the JSON passed in b []byte passed in in
227+
// a way that is suitable for HTTP headers.
228+
//
229+
// See: https://www.dropbox.com/developers/reference/json-encoding
230+
func HTTPHeaderSafeJSON(b []byte) string {
231+
var s bytes.Buffer
232+
s.Grow(len(b))
233+
for _, r := range string(b) {
234+
if r >= 0x007f {
235+
fmt.Fprintf(&s, "\\u%04x", r)
236+
} else {
237+
s.WriteRune(r)
238+
}
239+
}
240+
return s.String()
241+
}

dropbox/sdk_test.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
package dropbox_test
2222

2323
import (
24+
"encoding/json"
2425
"fmt"
2526
"net/http"
2627
"net/http/httptest"
@@ -169,3 +170,57 @@ func TestAccessError(t *testing.T) {
169170
t.Errorf("Unexpected tag: %s\n", re.AccessError.PaperAccessDenied.Tag)
170171
}
171172
}
173+
174+
func TestHTTPHeaderSafeJSON(t *testing.T) {
175+
for _, test := range []struct {
176+
name string
177+
in interface{}
178+
want string
179+
}{
180+
{
181+
name: "empty string",
182+
in: ``,
183+
want: `""`,
184+
},
185+
{
186+
name: "integer",
187+
in: 123,
188+
want: `123`,
189+
},
190+
{
191+
name: "normal string",
192+
in: `Normal string!`,
193+
want: `"Normal string!"`,
194+
},
195+
{
196+
name: "unicode",
197+
in: `üñîcødé`,
198+
want: `"\u00fc\u00f1\u00eec\u00f8d\u00e9"`,
199+
},
200+
{
201+
name: "7f",
202+
in: "\x7f",
203+
want: `"\u007f"`,
204+
},
205+
{
206+
name: "example from the docs",
207+
in: struct {
208+
Field string `json:"field"`
209+
}{
210+
Field: "some_üñîcødé_and_\x7F",
211+
},
212+
want: `{"field":"some_\u00fc\u00f1\u00eec\u00f8d\u00e9_and_\u007f"}`,
213+
},
214+
} {
215+
t.Run(test.name, func(t *testing.T) {
216+
b, err := json.Marshal(test.in)
217+
if err != nil {
218+
t.Fatal(err)
219+
}
220+
got := dropbox.HTTPHeaderSafeJSON(b)
221+
if got != test.want {
222+
t.Errorf("Want %q got %q", test.want, got)
223+
}
224+
})
225+
}
226+
}

dropbox/sharing/client.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -966,7 +966,7 @@ func (dbx *apiImpl) GetSharedLinkFile(arg *GetSharedLinkMetadataArg) (res IsShar
966966
}
967967

968968
headers := map[string]string{
969-
"Dropbox-API-Arg": string(b),
969+
"Dropbox-API-Arg": dropbox.HTTPHeaderSafeJSON(b),
970970
}
971971
if dbx.Config.AsMemberID != "" {
972972
headers["Dropbox-API-Select-User"] = dbx.Config.AsMemberID

generator/go_client.stoneg.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ def _generate_request(self, namespace, route):
125125
headers = {}
126126
if not is_void_type(route.arg_data_type):
127127
if host == 'content' or style in ['upload', 'download']:
128-
headers["Dropbox-API-Arg"] = "string(b)"
128+
headers["Dropbox-API-Arg"] = "dropbox.HTTPHeaderSafeJSON(b)"
129129
else:
130130
headers["Content-Type"] = '"application/json"'
131131
if style == 'upload':

generator/go_rsrc/sdk.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"io"
2727
"log"
2828
"net/http"
29+
"strings"
2930

3031
"golang.org/x/net/context"
3132
"golang.org/x/oauth2"
@@ -221,3 +222,20 @@ func HandleCommonAPIErrors(c Config, resp *http.Response, body []byte) error {
221222
}
222223
return apiError
223224
}
225+
226+
// HTTPHeaderSafeJSON encode the JSON passed in b []byte passed in in
227+
// a way that is suitable for HTTP headers.
228+
//
229+
// See: https://www.dropbox.com/developers/reference/json-encoding
230+
func HTTPHeaderSafeJSON(b []byte) string {
231+
var s strings.Builder
232+
s.Grow(len(b))
233+
for _, r := range string(b) {
234+
if r >= 0x007f {
235+
fmt.Fprintf(&s, "\\u%04x", r)
236+
} else {
237+
s.WriteRune(r)
238+
}
239+
}
240+
return s.String()
241+
}

0 commit comments

Comments
 (0)