Browse code

Add flag to enable cross domain requests in Api

Add the -api-enable-cors flag when running docker
in daemon mode to allow CORS requests to be made to
the Remote Api. The default value is false for this
flag to not allow cross origin request to be made.

Also added a handler for OPTIONS requests the standard
for cross domain requests is to initially make an
OPTIONS request to the api.

Michael Crosby authored on 2013/06/04 10:39:00
Showing 5 changed files
... ...
@@ -703,6 +703,11 @@ func postBuild(srv *Server, version float64, w http.ResponseWriter, r *http.Requ
703 703
 	return nil
704 704
 }
705 705
 
706
+func writeCorsHeaders(w http.ResponseWriter, r *http.Request) {
707
+	w.Header().Add("Access-Control-Allow-Origin", "*")
708
+	w.Header().Add("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept")
709
+}
710
+
706 711
 func ListenAndServe(addr string, srv *Server, logging bool) error {
707 712
 	r := mux.NewRouter()
708 713
 	log.Printf("Listening for HTTP on %s\n", addr)
... ...
@@ -773,12 +778,20 @@ func ListenAndServe(addr string, srv *Server, logging bool) error {
773 773
 					w.WriteHeader(http.StatusNotFound)
774 774
 					return
775 775
 				}
776
+				if srv.enableCors {
777
+					writeCorsHeaders(w, r)
778
+				}
776 779
 				if err := localFct(srv, version, w, r, mux.Vars(r)); err != nil {
777 780
 					httpError(w, err)
778 781
 				}
779 782
 			}
780 783
 			r.Path("/v{version:[0-9.]+}" + localRoute).Methods(localMethod).HandlerFunc(f)
781 784
 			r.Path(localRoute).Methods(localMethod).HandlerFunc(f)
785
+			r.Methods("OPTIONS").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
786
+				if srv.enableCors {
787
+					writeCorsHeaders(w, r)
788
+				}
789
+			})
782 790
 		}
783 791
 	}
784 792
 	return http.ListenAndServe(addr, r)
... ...
@@ -1239,6 +1239,32 @@ func TestDeleteContainers(t *testing.T) {
1239 1239
 	}
1240 1240
 }
1241 1241
 
1242
+func TestGetEnabledCors(t *testing.T) {
1243
+	runtime, err := newTestRuntime()
1244
+	if err != nil {
1245
+		t.Fatal(err)
1246
+	}
1247
+	defer nuke(runtime)
1248
+
1249
+	srv := &Server{runtime: runtime, enableCors: true}
1250
+
1251
+	r := httptest.NewRecorder()
1252
+
1253
+	if err := getVersion(srv, API_VERSION, r, nil, nil); err != nil {
1254
+		t.Fatal(err)
1255
+	}
1256
+
1257
+	allowOrigin := r.Header().Get("Access-Control-Allow-Origin")
1258
+	allowHeaders := r.Header().Get("Access-Control-Allow-Headers")
1259
+
1260
+	if allowOrigin != "*" {
1261
+		t.Errorf("Expected header Access-Control-Allow-Origin to be \"*\", %s found.", allowOrigin)
1262
+	}
1263
+	if allowHeaders != "Origin, X-Requested-With, Content-Type, Accept" {
1264
+		t.Errorf("Expected header Access-Control-Allow-Headers to be \"Origin, X-Requested-With, Content-Type, Accept\", %s found.", allowHeaders)
1265
+	}
1266
+}
1267
+
1242 1268
 func TestDeleteImages(t *testing.T) {
1243 1269
 	//FIXME: Implement this test
1244 1270
 	t.Log("Test not implemented")
... ...
@@ -33,6 +33,7 @@ func main() {
33 33
 	bridgeName := flag.String("b", "", "Attach containers to a pre-existing network bridge")
34 34
 	pidfile := flag.String("p", "/var/run/docker.pid", "File containing process PID")
35 35
 	flHost := flag.String("H", fmt.Sprintf("%s:%d", host, port), "Host:port to bind/connect to")
36
+	flEnableCors := flag.Bool("api-enable-cors", false, "Enable CORS requests in the remote api.")
36 37
 	flag.Parse()
37 38
 	if *bridgeName != "" {
38 39
 		docker.NetworkBridgeIface = *bridgeName
... ...
@@ -65,7 +66,7 @@ func main() {
65 65
 			flag.Usage()
66 66
 			return
67 67
 		}
68
-		if err := daemon(*pidfile, host, port, *flAutoRestart); err != nil {
68
+		if err := daemon(*pidfile, host, port, *flAutoRestart, *flEnableCors); err != nil {
69 69
 			log.Fatal(err)
70 70
 			os.Exit(-1)
71 71
 		}
... ...
@@ -104,7 +105,7 @@ func removePidFile(pidfile string) {
104 104
 	}
105 105
 }
106 106
 
107
-func daemon(pidfile, addr string, port int, autoRestart bool) error {
107
+func daemon(pidfile, addr string, port int, autoRestart, enableCors bool) error {
108 108
 	if addr != "127.0.0.1" {
109 109
 		log.Println("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\")
110 110
 	}
... ...
@@ -122,7 +123,7 @@ func daemon(pidfile, addr string, port int, autoRestart bool) error {
122 122
 		os.Exit(0)
123 123
 	}()
124 124
 
125
-	server, err := docker.NewServer(autoRestart)
125
+	server, err := docker.NewServer(autoRestart, enableCors)
126 126
 	if err != nil {
127 127
 		return err
128 128
 	}
... ...
@@ -1056,3 +1056,11 @@ Here are the steps of 'docker run' :
1056 1056
 
1057 1057
 In this first version of the API, some of the endpoints, like /attach, /pull or /push uses hijacking to transport stdin,
1058 1058
 stdout and stderr on the same socket. This might change in the future.
1059
+
1060
+
1061
+3.3 CORS Requests
1062
+-----------------
1063
+
1064
+To enable cross origin requests to the remote api add the flag "-api-enable-cors" when running docker in daemon mode.
1065
+    
1066
+    docker -d -H="192.168.1.9:4243" -api-enable-cors
... ...
@@ -870,7 +870,7 @@ func (srv *Server) ImageInspect(name string) (*Image, error) {
870 870
 	return nil, fmt.Errorf("No such image: %s", name)
871 871
 }
872 872
 
873
-func NewServer(autoRestart bool) (*Server, error) {
873
+func NewServer(autoRestart, enableCors bool) (*Server, error) {
874 874
 	if runtime.GOARCH != "amd64" {
875 875
 		log.Fatalf("The docker runtime currently only supports amd64 (not %s). This will change in the future. Aborting.", runtime.GOARCH)
876 876
 	}
... ...
@@ -879,12 +879,14 @@ func NewServer(autoRestart bool) (*Server, error) {
879 879
 		return nil, err
880 880
 	}
881 881
 	srv := &Server{
882
-		runtime: runtime,
882
+		runtime:    runtime,
883
+		enableCors: enableCors,
883 884
 	}
884 885
 	runtime.srv = srv
885 886
 	return srv, nil
886 887
 }
887 888
 
888 889
 type Server struct {
889
-	runtime *Runtime
890
+	runtime    *Runtime
891
+	enableCors bool
890 892
 }