Skip to content

Commit 895b231

Browse files
authored
Merge pull request #14 from rogpeppe-contrib/053-better-json-content-parsing
recognise json suffix on content type
2 parents 8beede3 + debe44c commit 895b231

File tree

4 files changed

+120
-37
lines changed

4 files changed

+120
-37
lines changed

client_test.go

Lines changed: 55 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"net/http"
99
"net/http/httptest"
1010
"reflect"
11+
"regexp"
1112
"strings"
1213
"testing"
1314

@@ -490,21 +491,63 @@ func TestUnmarshalJSONResponseWithBodyReadError(t *testing.T) {
490491
assertDecodeResponseError(c, err, http.StatusOK, `{"one": "two"}`)
491492
}
492493

493-
func TestUnmarshalJSONResponseWithBadContentType(t *testing.T) {
494+
var unmarshalJSONResponseWithVariedJSONContentTypesTests = []struct {
495+
contentType string
496+
expectError bool
497+
}{{
498+
contentType: "application/json",
499+
}, {
500+
contentType: "application/json+other",
501+
}, {
502+
contentType: "application/vnd.schemaregistry.v1+json",
503+
}, {
504+
contentType: "other/json",
505+
expectError: true,
506+
}, {
507+
contentType: "other/jsonx",
508+
expectError: true,
509+
}, {
510+
contentType: "other/xjson",
511+
expectError: true,
512+
}, {
513+
contentType: "other/other+xjson",
514+
expectError: true,
515+
}, {
516+
contentType: "other/other+jsonx",
517+
expectError: true,
518+
}, {
519+
contentType: "application/other+json+foo",
520+
}, {
521+
contentType: "application/other+json",
522+
}, {
523+
contentType: "application/other+json+",
524+
}, {
525+
contentType: "application/+json+",
526+
}}
527+
528+
func TestUnmarshalJSONResponseWithVariedJSONContentTypes(t *testing.T) {
494529
c := qt.New(t)
495530

496-
resp := &http.Response{
497-
Header: http.Header{
498-
"Content-Type": {"foo/bar"},
499-
},
500-
StatusCode: http.StatusTeapot,
501-
Body: ioutil.NopCloser(strings.NewReader(`something or other`)),
531+
for _, test := range unmarshalJSONResponseWithVariedJSONContentTypesTests {
532+
c.Run(test.contentType, func(c *qt.C) {
533+
resp := &http.Response{
534+
Header: http.Header{
535+
"Content-Type": {test.contentType},
536+
},
537+
StatusCode: http.StatusTeapot,
538+
Body: ioutil.NopCloser(strings.NewReader(`{}`)),
539+
}
540+
var val map[string]string
541+
err := httprequest.UnmarshalJSONResponse(resp, &val)
542+
if !test.expectError {
543+
c.Assert(err, qt.IsNil)
544+
return
545+
}
546+
c.Assert(err, qt.ErrorMatches, `unexpected content type `+regexp.QuoteMeta(test.contentType)+`; want application/json; content: "{}"`)
547+
c.Assert(val, qt.IsNil)
548+
assertDecodeResponseError(c, err, http.StatusTeapot, `{}`)
549+
})
502550
}
503-
var val map[string]string
504-
err := httprequest.UnmarshalJSONResponse(resp, &val)
505-
c.Assert(err, qt.ErrorMatches, `unexpected content type foo/bar; want application/json; content: "something or other"`)
506-
c.Assert(val, qt.IsNil)
507-
assertDecodeResponseError(c, err, http.StatusTeapot, `something or other`)
508551
}
509552

510553
func TestUnmarshalJSONResponseWithErrorAndLargeBody(t *testing.T) {

fancyerror.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"io/ioutil"
88
"mime"
99
"net/http"
10+
"strings"
1011
"unicode"
1112

1213
"golang.org/x/net/html"
@@ -129,7 +130,23 @@ var maxErrorBodySize = 200 * 1024
129130
func isJSONMediaType(header http.Header) bool {
130131
contentType := header.Get("Content-Type")
131132
mediaType, _, _ := mime.ParseMediaType(contentType)
132-
return mediaType == "application/json"
133+
m := strings.TrimPrefix(mediaType, "application/")
134+
if len(m) == len(mediaType) {
135+
return false
136+
}
137+
// Look for +json suffix. See https://tools.ietf.org/html/rfc6838#section-4.2.8
138+
// We recognize multiple suffixes too (e.g. application/something+json+other)
139+
// as that seems to be a possibility.
140+
for {
141+
i := strings.Index(m, "+")
142+
if i == -1 {
143+
return m == "json"
144+
}
145+
if m[0:i] == "json" {
146+
return true
147+
}
148+
m = m[i+1:]
149+
}
133150
}
134151

135152
// Error implements error.Error by trying to produce a decent

go.mod

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
module gopkg.in/httprequest.v1
22

3+
go 1.15
4+
35
require (
4-
github.com/frankban/quicktest v1.1.0
5-
github.com/google/go-cmp v0.2.0
6-
github.com/juju/qthttptest v0.0.1
7-
github.com/julienschmidt/httprouter v1.2.0
8-
golang.org/x/net v0.0.0-20150829230318-ea47fc708ee3
9-
golang.org/x/tools v0.0.0-20181008205924-a2b3f7f249e9
6+
github.com/frankban/quicktest v1.10.0
7+
github.com/google/go-cmp v0.4.0
8+
github.com/juju/qthttptest v0.1.1
9+
github.com/julienschmidt/httprouter v1.3.0
10+
golang.org/x/net v0.0.0-20200505041828-1ed23360d12c
11+
golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8
1012
gopkg.in/errgo.v1 v1.0.0
1113
)

go.sum

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,49 @@
1-
github.com/frankban/quicktest v1.1.0 h1:Fw/voXLo2r0Tvu5uy/GV/W5XpT7LYfbrqottX3kz8YE=
2-
github.com/frankban/quicktest v1.1.0/go.mod h1:R98jIehRai+d1/3Hv2//jOVCTJhW1VBavT6B6CuGq2k=
3-
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
4-
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
5-
github.com/juju/qthttptest v0.0.1 h1:pR8nTl6Uo/iI6/ynQf5Cxy9FEICXzaa83NtrBdGMCVQ=
6-
github.com/juju/qthttptest v0.0.1/go.mod h1://LCf/Ls22/rPw2u1yWukUJvYtfPY4nYpWUl2uZhryo=
7-
github.com/julienschmidt/httprouter v0.0.0-20151013225520-77a895ad01eb h1:a8sYyruWLyKeMay8GPF+nwZ36xT7A0oNyn68Q6wJ5cc=
8-
github.com/julienschmidt/httprouter v0.0.0-20151013225520-77a895ad01eb/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
9-
github.com/julienschmidt/httprouter v1.2.0 h1:TDTW5Yz1mjftljbcKqRcrYhd4XeOoI98t+9HbQbYf7g=
10-
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
1+
github.com/frankban/quicktest v1.7.2/go.mod h1:jaStnuzAqU1AJdCO0l53JDCJrVDKcS03DbaAcR7Ks/o=
2+
github.com/frankban/quicktest v1.10.0 h1:Gfh+GAJZOAoKZsIZeZbdn2JF10kN1XHNvjsvQK8gVkE=
3+
github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y=
4+
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
5+
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
6+
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
7+
github.com/juju/qthttptest v0.1.1 h1:JPju5P5CDMCy8jmBJV2wGLjDItUsx2KKL514EfOYueM=
8+
github.com/juju/qthttptest v0.1.1/go.mod h1:aTlAv8TYaflIiTDIQYzxnl1QdPjAg8Q8qJMErpKy6A4=
9+
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
10+
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
1111
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
1212
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
13+
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
14+
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
1315
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
1416
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
1517
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
16-
golang.org/x/net v0.0.0-20150829230318-ea47fc708ee3 h1:vQKsY7JYxkBiIr0dkSCjLB4p7gONfkUsJHkLCH+pUQU=
17-
golang.org/x/net v0.0.0-20150829230318-ea47fc708ee3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
18-
golang.org/x/tools v0.0.0-20181008205924-a2b3f7f249e9 h1:T3nuFyXXDj5KXX9CqQm/r/YEL4Gua01s/ZEdfdLyJ2c=
19-
golang.org/x/tools v0.0.0-20181008205924-a2b3f7f249e9/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
18+
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
19+
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
20+
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
21+
golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
22+
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
23+
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
24+
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
25+
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
26+
golang.org/x/net v0.0.0-20200505041828-1ed23360d12c h1:zJ0mtu4jCalhKg6Oaukv6iIkb+cOvDrajDH9DH46Q4M=
27+
golang.org/x/net v0.0.0-20200505041828-1ed23360d12c/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
28+
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
29+
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
30+
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
31+
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
32+
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
33+
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
34+
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
35+
golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8 h1:BMFHd4OFnFtWX46Xj4DN6vvT1btiBxyq+s0orYBqcQY=
36+
golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
37+
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
38+
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
39+
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
40+
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
2041
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
2142
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
2243
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
2344
gopkg.in/errgo.v1 v1.0.0 h1:n+7XfCyygBFb8sEjg6692xjC6Us50TFRO54+xYUEwjE=
2445
gopkg.in/errgo.v1 v1.0.0/go.mod h1:CxwszS/Xz1C49Ucd2i6Zil5UToP1EmyrFhKaMVbg1mk=
25-
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce h1:xcEWjVhvbDy+nHP67nPDDpbYrY+ILlfndk4bRioVHaU=
26-
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
27-
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
28-
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
46+
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 h1:VpOs+IwYnYBaFnrNAeB8UUWtL3vEUnzSCL1nVjPhqrw=
47+
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
48+
gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
49+
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

0 commit comments

Comments
 (0)