Browse code

Add checks for app/json - issue #2515

Signed-off-by: Doug Davis <dug@us.ibm.com>

Doug Davis authored on 2014/09/26 00:24:41
Showing 2 changed files
... ...
@@ -51,6 +51,24 @@ func hijackServer(w http.ResponseWriter) (io.ReadCloser, io.Writer, error) {
51 51
 	return conn, conn, nil
52 52
 }
53 53
 
54
+// Check to make sure request's Content-Type is application/json
55
+func checkForJson(r *http.Request) error {
56
+	ct := r.Header.Get("Content-Type")
57
+
58
+	// No Content-Type header is ok as long as there's no Body
59
+	if ct == "" {
60
+		if r.Body == nil || r.ContentLength == 0 {
61
+			return nil
62
+		}
63
+	}
64
+
65
+	// Otherwise it better be json
66
+	if api.MatchesContentType(ct, "application/json") {
67
+		return nil
68
+	}
69
+	return fmt.Errorf("Content-Type specified (%s) must be 'application/json'", ct)
70
+}
71
+
54 72
 //If we don't do this, POST method without Content-type (even with empty body) will fail
55 73
 func parseForm(r *http.Request) error {
56 74
 	if r == nil {
... ...
@@ -445,6 +463,11 @@ func postCommit(eng *engine.Engine, version version.Version, w http.ResponseWrit
445 445
 		job          = eng.Job("commit", r.Form.Get("container"))
446 446
 		stdoutBuffer = bytes.NewBuffer(nil)
447 447
 	)
448
+
449
+	if err := checkForJson(r); err != nil {
450
+		return err
451
+	}
452
+
448 453
 	if err := config.Decode(r.Body); err != nil {
449 454
 		log.Errorf("%s", err)
450 455
 	}
... ...
@@ -651,6 +674,11 @@ func postContainersCreate(eng *engine.Engine, version version.Version, w http.Re
651 651
 		stdoutBuffer = bytes.NewBuffer(nil)
652 652
 		warnings     = bytes.NewBuffer(nil)
653 653
 	)
654
+
655
+	if err := checkForJson(r); err != nil {
656
+		return err
657
+	}
658
+
654 659
 	if err := job.DecodeEnv(r.Body); err != nil {
655 660
 		return err
656 661
 	}
... ...
@@ -734,8 +762,8 @@ func postContainersStart(eng *engine.Engine, version version.Version, w http.Res
734 734
 
735 735
 	// allow a nil body for backwards compatibility
736 736
 	if r.Body != nil && r.ContentLength > 0 {
737
-		if !api.MatchesContentType(r.Header.Get("Content-Type"), "application/json") {
738
-			return fmt.Errorf("Content-Type of application/json is required")
737
+		if err := checkForJson(r); err != nil {
738
+			return err
739 739
 		}
740 740
 
741 741
 		if err := job.DecodeEnv(r.Body); err != nil {
... ...
@@ -1001,12 +1029,12 @@ func postContainersCopy(eng *engine.Engine, version version.Version, w http.Resp
1001 1001
 
1002 1002
 	var copyData engine.Env
1003 1003
 
1004
-	if contentType := r.Header.Get("Content-Type"); api.MatchesContentType(contentType, "application/json") {
1005
-		if err := copyData.Decode(r.Body); err != nil {
1006
-			return err
1007
-		}
1008
-	} else {
1009
-		return fmt.Errorf("Content-Type not supported: %s", contentType)
1004
+	if err := checkForJson(r); err != nil {
1005
+		return err
1006
+	}
1007
+
1008
+	if err := copyData.Decode(r.Body); err != nil {
1009
+		return err
1010 1010
 	}
1011 1011
 
1012 1012
 	if copyData.Get("Resource") == "" {
... ...
@@ -1043,6 +1071,7 @@ func postContainerExecCreate(eng *engine.Engine, version version.Version, w http
1043 1043
 		job          = eng.Job("execCreate", name)
1044 1044
 		stdoutBuffer = bytes.NewBuffer(nil)
1045 1045
 	)
1046
+
1046 1047
 	if err := job.DecodeEnv(r.Body); err != nil {
1047 1048
 		return err
1048 1049
 	}
... ...
@@ -1068,6 +1097,7 @@ func postContainerExecStart(eng *engine.Engine, version version.Version, w http.
1068 1068
 		job              = eng.Job("execStart", name)
1069 1069
 		errOut io.Writer = os.Stderr
1070 1070
 	)
1071
+
1071 1072
 	if err := job.DecodeEnv(r.Body); err != nil {
1072 1073
 		return err
1073 1074
 	}
... ...
@@ -9,6 +9,7 @@ import (
9 9
 	"net"
10 10
 	"net/http"
11 11
 	"net/http/httptest"
12
+	"strings"
12 13
 	"testing"
13 14
 	"time"
14 15
 
... ...
@@ -356,6 +357,8 @@ func TestPostContainersCreate(t *testing.T) {
356 356
 		t.Fatal(err)
357 357
 	}
358 358
 
359
+	req.Header.Set("Content-Type", "application/json")
360
+
359 361
 	r := httptest.NewRecorder()
360 362
 	if err := server.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
361 363
 		t.Fatal(err)
... ...
@@ -379,6 +382,49 @@ func TestPostContainersCreate(t *testing.T) {
379 379
 	}
380 380
 }
381 381
 
382
+func TestPostJsonVerify(t *testing.T) {
383
+	eng := NewTestEngine(t)
384
+	defer mkDaemonFromEngine(eng, t).Nuke()
385
+
386
+	configJSON, err := json.Marshal(&runconfig.Config{
387
+		Image:  unitTestImageID,
388
+		Memory: 33554432,
389
+		Cmd:    []string{"touch", "/test"},
390
+	})
391
+	if err != nil {
392
+		t.Fatal(err)
393
+	}
394
+
395
+	req, err := http.NewRequest("POST", "/containers/create", bytes.NewReader(configJSON))
396
+	if err != nil {
397
+		t.Fatal(err)
398
+	}
399
+
400
+	r := httptest.NewRecorder()
401
+
402
+	if err := server.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
403
+		t.Fatal(err)
404
+	}
405
+
406
+	// Don't add Content-Type header
407
+	// req.Header.Set("Content-Type", "application/json")
408
+
409
+	err = server.ServeRequest(eng, api.APIVERSION, r, req)
410
+	if r.Code != http.StatusInternalServerError || !strings.Contains(((*r.Body).String()), "application/json") {
411
+		t.Fatal("Create should have failed due to no Content-Type header - got:", r)
412
+	}
413
+
414
+	// Now add header but with wrong type and retest
415
+	req.Header.Set("Content-Type", "application/xml")
416
+
417
+	if err := server.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
418
+		t.Fatal(err)
419
+	}
420
+	if r.Code != http.StatusInternalServerError || !strings.Contains(((*r.Body).String()), "application/json") {
421
+		t.Fatal("Create should have failed due to wrong Content-Type header - got:", r)
422
+	}
423
+}
424
+
382 425
 func TestPostContainersKill(t *testing.T) {
383 426
 	eng := NewTestEngine(t)
384 427
 	defer mkDaemonFromEngine(eng, t).Nuke()