Quellcode durchsuchen

* Remote Api: Add flag to enable cross domain requests

Victor Vieux vor 12 Jahren
Ursprung
Commit
20bf0e00e8
5 geänderte Dateien mit 119 neuen und 10 gelöschten Zeilen
  1. 34 4
      api.go
  2. 67 0
      api_test.go
  3. 4 3
      docker/docker.go
  4. 9 0
      docs/sources/api/docker_remote_api.rst
  5. 5 3
      server.go

+ 34 - 4
api.go

@@ -703,9 +703,18 @@ func postBuild(srv *Server, version float64, w http.ResponseWriter, r *http.Requ
 	return nil
 }
 
-func ListenAndServe(addr string, srv *Server, logging bool) error {
+func optionsHandler(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+	w.WriteHeader(http.StatusOK)
+	return nil
+}
+func writeCorsHeaders(w http.ResponseWriter, r *http.Request) {
+	w.Header().Add("Access-Control-Allow-Origin", "*")
+	w.Header().Add("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept")
+	w.Header().Add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS")
+}
+
+func createRouter(srv *Server, logging bool) (*mux.Router, error) {
 	r := mux.NewRouter()
-	log.Printf("Listening for HTTP on %s\n", addr)
 
 	m := map[string]map[string]func(*Server, float64, http.ResponseWriter, *http.Request, map[string]string) error{
 		"GET": {
@@ -745,6 +754,9 @@ func ListenAndServe(addr string, srv *Server, logging bool) error {
 			"/containers/{name:.*}": deleteContainers,
 			"/images/{name:.*}":     deleteImages,
 		},
+		"OPTIONS": {
+			"": optionsHandler,
+		},
 	}
 
 	for method, routes := range m {
@@ -769,6 +781,9 @@ func ListenAndServe(addr string, srv *Server, logging bool) error {
 				if err != nil {
 					version = APIVERSION
 				}
+				if srv.enableCors {
+					writeCorsHeaders(w, r)
+				}
 				if version == 0 || version > APIVERSION {
 					w.WriteHeader(http.StatusNotFound)
 					return
@@ -777,9 +792,24 @@ func ListenAndServe(addr string, srv *Server, logging bool) error {
 					httpError(w, err)
 				}
 			}
-			r.Path("/v{version:[0-9.]+}" + localRoute).Methods(localMethod).HandlerFunc(f)
-			r.Path(localRoute).Methods(localMethod).HandlerFunc(f)
+
+			if localRoute == "" {
+				r.Methods(localMethod).HandlerFunc(f)
+			} else {
+				r.Path("/v{version:[0-9.]+}" + localRoute).Methods(localMethod).HandlerFunc(f)
+				r.Path(localRoute).Methods(localMethod).HandlerFunc(f)
+			}
 		}
 	}
+	return r, nil
+}
+
+func ListenAndServe(addr string, srv *Server, logging bool) error {
+	log.Printf("Listening for HTTP on %s\n", addr)
+
+	r, err := createRouter(srv, logging)
+	if err != nil {
+		return err
+	}
 	return http.ListenAndServe(addr, r)
 }

+ 67 - 0
api_test.go

@@ -1239,6 +1239,73 @@ func TestDeleteContainers(t *testing.T) {
 	}
 }
 
+func TestOptionsRoute(t *testing.T) {
+	runtime, err := newTestRuntime()
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer nuke(runtime)
+
+	srv := &Server{runtime: runtime, enableCors: true}
+
+	r := httptest.NewRecorder()
+	router, err := createRouter(srv, false)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	req, err := http.NewRequest("OPTIONS", "/", nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	router.ServeHTTP(r, req)
+	if r.Code != http.StatusOK {
+		t.Errorf("Expected response for OPTIONS request to be \"200\", %v found.", r.Code)
+	}
+}
+
+func TestGetEnabledCors(t *testing.T) {
+	runtime, err := newTestRuntime()
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer nuke(runtime)
+
+	srv := &Server{runtime: runtime, enableCors: true}
+
+	r := httptest.NewRecorder()
+
+	router, err := createRouter(srv, false)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	req, err := http.NewRequest("GET", "/version", nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	router.ServeHTTP(r, req)
+	if r.Code != http.StatusOK {
+		t.Errorf("Expected response for OPTIONS request to be \"200\", %v found.", r.Code)
+	}
+
+	allowOrigin := r.Header().Get("Access-Control-Allow-Origin")
+	allowHeaders := r.Header().Get("Access-Control-Allow-Headers")
+	allowMethods := r.Header().Get("Access-Control-Allow-Methods")
+
+	if allowOrigin != "*" {
+		t.Errorf("Expected header Access-Control-Allow-Origin to be \"*\", %s found.", allowOrigin)
+	}
+	if allowHeaders != "Origin, X-Requested-With, Content-Type, Accept" {
+		t.Errorf("Expected header Access-Control-Allow-Headers to be \"Origin, X-Requested-With, Content-Type, Accept\", %s found.", allowHeaders)
+	}
+	if allowMethods != "GET, POST, DELETE, PUT, OPTIONS" {
+		t.Errorf("Expected hearder Access-Control-Allow-Methods to be \"GET, POST, DELETE, PUT, OPTIONS\", %s found.", allowMethods)
+	}
+}
+
 func TestDeleteImages(t *testing.T) {
 	//FIXME: Implement this test
 	t.Log("Test not implemented")

+ 4 - 3
docker/docker.go

@@ -33,6 +33,7 @@ func main() {
 	bridgeName := flag.String("b", "", "Attach containers to a pre-existing network bridge")
 	pidfile := flag.String("p", "/var/run/docker.pid", "File containing process PID")
 	flHost := flag.String("H", fmt.Sprintf("%s:%d", host, port), "Host:port to bind/connect to")
+	flEnableCors := flag.Bool("api-enable-cors", false, "Enable CORS requests in the remote api.")
 	flag.Parse()
 	if *bridgeName != "" {
 		docker.NetworkBridgeIface = *bridgeName
@@ -65,7 +66,7 @@ func main() {
 			flag.Usage()
 			return
 		}
-		if err := daemon(*pidfile, host, port, *flAutoRestart); err != nil {
+		if err := daemon(*pidfile, host, port, *flAutoRestart, *flEnableCors); err != nil {
 			log.Fatal(err)
 			os.Exit(-1)
 		}
@@ -104,7 +105,7 @@ func removePidFile(pidfile string) {
 	}
 }
 
-func daemon(pidfile, addr string, port int, autoRestart bool) error {
+func daemon(pidfile, addr string, port int, autoRestart, enableCors bool) error {
 	if addr != "127.0.0.1" {
 		log.Println("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\")
 	}
@@ -122,7 +123,7 @@ func daemon(pidfile, addr string, port int, autoRestart bool) error {
 		os.Exit(0)
 	}()
 
-	server, err := docker.NewServer(autoRestart)
+	server, err := docker.NewServer(autoRestart, enableCors)
 	if err != nil {
 		return err
 	}

+ 9 - 0
docs/sources/api/docker_remote_api.rst

@@ -1057,6 +1057,14 @@ Here are the steps of 'docker run' :
 In this first version of the API, some of the endpoints, like /attach, /pull or /push uses hijacking to transport stdin,
 stdout and stderr on the same socket. This might change in the future.
 
+3.3 CORS Requests
+-----------------
+
+To enable cross origin requests to the remote api add the flag "-api-enable-cors" when running docker in daemon mode.
+    
+    docker -d -H="192.168.1.9:4243" -api-enable-cors
+
+
 ==================================
 Docker Remote API Client Libraries
 ==================================
@@ -1080,3 +1088,4 @@ and we will add the libraries here.
 | Javascript (Angular) | dockerui       | https://github.com/crosbymichael/dockerui  |
 | **WebUI**            |                |                                            |
 +----------------------+----------------+--------------------------------------------+
+

+ 5 - 3
server.go

@@ -879,7 +879,7 @@ func (srv *Server) ImageInspect(name string) (*Image, error) {
 	return nil, fmt.Errorf("No such image: %s", name)
 }
 
-func NewServer(autoRestart bool) (*Server, error) {
+func NewServer(autoRestart, enableCors bool) (*Server, error) {
 	if runtime.GOARCH != "amd64" {
 		log.Fatalf("The docker runtime currently only supports amd64 (not %s). This will change in the future. Aborting.", runtime.GOARCH)
 	}
@@ -888,12 +888,14 @@ func NewServer(autoRestart bool) (*Server, error) {
 		return nil, err
 	}
 	srv := &Server{
-		runtime: runtime,
+		runtime:    runtime,
+		enableCors: enableCors,
 	}
 	runtime.srv = srv
 	return srv, nil
 }
 
 type Server struct {
-	runtime *Runtime
+	runtime    *Runtime
+	enableCors bool
 }