Skip to content

Commit 7262bdc

Browse files
netshadeyurishkuro
authored andcommittedJul 27, 2018
Parse media types in the Content-Type header correctly (#964)
* Parse media types in the Content-Type header correctly Ensures that Content-Type calculation is based on the extracted Content-Type from the media type, rather than the raw Media Type value. Changes behavior in the Thrift HTTP endpoint and Zipkin V1 and V2 HTTP endpoints. Signed-off-by: Chris Zelenak <chris@zelenak.me> * Ensure Content-Type parse errors are visible Make sure that callers are informed of errors if they provide an incorrect media type. Signed-off-by: Chris Zelenak <chris@zelenak.me>
1 parent 809ffd8 commit 7262bdc

File tree

4 files changed

+53
-3
lines changed

4 files changed

+53
-3
lines changed
 

‎cmd/collector/app/http_handler.go

+8-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package app
1717
import (
1818
"fmt"
1919
"io/ioutil"
20+
"mime"
2021
"net/http"
2122
"time"
2223

@@ -66,7 +67,13 @@ func (aH *APIHandler) saveSpan(w http.ResponseWriter, r *http.Request) {
6667
return
6768
}
6869

69-
contentType := r.Header.Get("Content-Type")
70+
contentType, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
71+
72+
if err != nil {
73+
http.Error(w, fmt.Sprintf("Cannot parse content type: %v", err), http.StatusBadRequest)
74+
return
75+
}
76+
7077
if _, ok := acceptedThriftFormats[contentType]; !ok {
7178
http.Error(w, fmt.Sprintf("Unsupported content type: %v", contentType), http.StatusBadRequest)
7279
return

‎cmd/collector/app/http_handler_test.go

+14
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,11 @@ func TestThriftFormat(t *testing.T) {
8383
assert.EqualValues(t, http.StatusAccepted, statusCode)
8484
assert.EqualValues(t, "", resBodyStr)
8585

86+
statusCode, resBodyStr, err = postBytes("application/x-thrift; charset=utf-8", server.URL+`/api/traces`, someBytes)
87+
assert.NoError(t, err)
88+
assert.EqualValues(t, http.StatusAccepted, statusCode)
89+
assert.EqualValues(t, "", resBodyStr)
90+
8691
handler.jaegerBatchesHandler.(*mockJaegerHandler).err = fmt.Errorf("Bad times ahead")
8792
statusCode, resBodyStr, err = postBytes("application/vnd.apache.thrift.binary", server.URL+`/api/traces`, someBytes)
8893
assert.NoError(t, err)
@@ -143,6 +148,15 @@ func TestWrongFormat(t *testing.T) {
143148
assert.EqualValues(t, "Unsupported content type: nosoupforyou\n", resBodyStr)
144149
}
145150

151+
func TestMalformedFormat(t *testing.T) {
152+
server, _ := initializeTestServer(nil)
153+
defer server.Close()
154+
statusCode, resBodyStr, err := postBytes("application/json; =iammalformed", server.URL+`/api/traces`, []byte{})
155+
assert.NoError(t, err)
156+
assert.EqualValues(t, http.StatusBadRequest, statusCode)
157+
assert.EqualValues(t, "Cannot parse content type: mime: invalid media parameter\n", resBodyStr)
158+
}
159+
146160
func TestCannotReadBodyFromRequest(t *testing.T) {
147161
handler := NewAPIHandler(&mockJaegerHandler{})
148162
req, err := http.NewRequest(http.MethodPost, "whatever", &errReader{})

‎cmd/collector/app/zipkin/http_handler.go

+15-2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"fmt"
2020
"io"
2121
"io/ioutil"
22+
"mime"
2223
"net/http"
2324
"strings"
2425
"time"
@@ -79,7 +80,13 @@ func (aH *APIHandler) saveSpans(w http.ResponseWriter, r *http.Request) {
7980
return
8081
}
8182

82-
contentType := r.Header.Get("Content-Type")
83+
contentType, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
84+
85+
if err != nil {
86+
http.Error(w, fmt.Sprintf("Cannot parse Content-Type: %v", err), http.StatusBadRequest)
87+
return
88+
}
89+
8390
var tSpans []*zipkincore.Span
8491
if contentType == "application/x-thrift" {
8592
tSpans, err = deserializeThrift(bodyBytes)
@@ -121,7 +128,13 @@ func (aH *APIHandler) saveSpansV2(w http.ResponseWriter, r *http.Request) {
121128
return
122129
}
123130

124-
contentType := r.Header.Get("Content-Type")
131+
contentType, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
132+
133+
if err != nil {
134+
http.Error(w, fmt.Sprintf("Cannot parse Content-Type: %v", err), http.StatusBadRequest)
135+
return
136+
}
137+
125138
if contentType != "application/json" {
126139
http.Error(w, "Unsupported Content-Type", http.StatusBadRequest)
127140
return

‎cmd/collector/app/zipkin/http_handler_test.go

+16
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,11 @@ func TestJsonFormat(t *testing.T) {
131131
require.NotNil(t, recdSpan.Annotations[0].Host)
132132
assert.EqualValues(t, -1, recdSpan.Annotations[0].Host.Port, "Port 65535 must be represented as -1 in zipkin.thrift")
133133

134+
statusCode, resBodyStr, err = postBytes(server.URL+`/api/v1/spans`, []byte(spanJSON), createHeader("application/json; charset=utf-8"))
135+
assert.NoError(t, err)
136+
assert.EqualValues(t, http.StatusAccepted, statusCode)
137+
assert.EqualValues(t, "", resBodyStr)
138+
134139
endpErrJSON := createEndpoint("", "127.0.0.A", "", 80)
135140

136141
// error zipkinSpanHandler
@@ -193,6 +198,15 @@ func TestGzipBadBody(t *testing.T) {
193198
assert.EqualValues(t, "Unable to process request body: unexpected EOF\n", resBodyStr)
194199
}
195200

201+
func TestMalformedContentType(t *testing.T) {
202+
server, _ := initializeTestServer(nil)
203+
defer server.Close()
204+
statusCode, resBodyStr, err := postBytes(server.URL+`/api/v1/spans`, []byte{}, createHeader("application/json; =iammalformed;"))
205+
assert.NoError(t, err)
206+
assert.EqualValues(t, http.StatusBadRequest, statusCode)
207+
assert.EqualValues(t, "Cannot parse Content-Type: mime: invalid media parameter\n", resBodyStr)
208+
}
209+
196210
func TestUnsupportedContentType(t *testing.T) {
197211
server, _ := initializeTestServer(nil)
198212
defer server.Close()
@@ -245,8 +259,10 @@ func TestSaveSpansV2(t *testing.T) {
245259
headers map[string]string
246260
}{
247261
{body: []byte("[]"), code: http.StatusAccepted},
262+
{body: []byte("[]"), code: http.StatusAccepted, headers: map[string]string{"Content-Type": "application/json; charset=utf-8"}},
248263
{body: gzipEncode([]byte("[]")), code: http.StatusAccepted, headers: map[string]string{"Content-Encoding": "gzip"}},
249264
{body: []byte("[]"), code: http.StatusBadRequest, headers: map[string]string{"Content-Type": "text/html"}, resBody: "Unsupported Content-Type\n"},
265+
{body: []byte("[]"), code: http.StatusBadRequest, headers: map[string]string{"Content-Type": "application/json; =iammalformed;"}, resBody: "Cannot parse Content-Type: mime: invalid media parameter\n"},
250266
{body: []byte("[]"), code: http.StatusBadRequest, headers: map[string]string{"Content-Encoding": "gzip"}, resBody: "Unable to process request body: unexpected EOF\n"},
251267
{body: []byte("not good"), code: http.StatusBadRequest, resBody: "Unable to process request body: invalid character 'o' in literal null (expecting 'u')\n"},
252268
{body: []byte("[{}]"), code: http.StatusBadRequest, resBody: "Unable to process request body: validation failure list:\nid in body is required\ntraceId in body is required\n"},

0 commit comments

Comments
 (0)
Please sign in to comment.