// TODO(vishh): Refactor the code to avoid having to specify stream config as part of both create and start. func (s *containerRouter) postContainerExecStart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := httputils.ParseForm(r); err != nil { return err } version := httputils.VersionFromContext(ctx) if versions.GreaterThan(version, "1.21") { if err := httputils.CheckForJSON(r); err != nil { return err } } var ( execName = vars["name"] stdin, inStream io.ReadCloser stdout, stderr, outStream io.Writer ) execStartCheck := &types.ExecStartCheck{} if err := json.NewDecoder(r.Body).Decode(execStartCheck); err != nil { return err } if exists, err := s.backend.ExecExists(execName); !exists { return err } if !execStartCheck.Detach { var err error // Setting up the streaming http interface. inStream, outStream, err = httputils.HijackConnection(w) if err != nil { return err } defer httputils.CloseStreams(inStream, outStream) if _, ok := r.Header["Upgrade"]; ok { fmt.Fprintf(outStream, "HTTP/1.1 101 UPGRADED\r\nContent-Type: application/vnd.docker.raw-stream\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n\r\n") } else { fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n") } stdin = inStream stdout = outStream if !execStartCheck.Tty { stderr = stdcopy.NewStdWriter(outStream, stdcopy.Stderr) stdout = stdcopy.NewStdWriter(outStream, stdcopy.Stdout) } } // Now run the user process in container. if err := s.backend.ContainerExecStart(execName, stdin, stdout, stderr); err != nil { if execStartCheck.Detach { return err } stdout.Write([]byte(err.Error() + "\r\n")) logrus.Errorf("Error running exec in container: %v", err) } return nil }
func (v apiVersion) Compare(other string) (int, error) { if dockerapiversion.LessThan(string(v), other) { return -1, nil } else if dockerapiversion.GreaterThan(string(v), other) { return 1, nil } return 0, nil }
// MakeErrorHandler makes an HTTP handler that decodes a Docker error and // returns it in the response. func MakeErrorHandler(err error) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { statusCode := GetHTTPErrorStatusCode(err) vars := mux.Vars(r) if vars["version"] == "" || versions.GreaterThan(vars["version"], "1.23") { response := &types.ErrorResponse{ Message: err.Error(), } WriteJSON(w, statusCode, response) } else { http.Error(w, grpc.ErrorDesc(err), statusCode) } } }
// WrapHandler returns a new handler function wrapping the previous one in the request chain. func (v VersionMiddleware) WrapHandler(handler func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error) func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { apiVersion := vars["version"] if apiVersion == "" { apiVersion = v.defaultVersion } if versions.GreaterThan(apiVersion, v.defaultVersion) { return badRequestError{fmt.Errorf("client is newer than server (client API version: %s, server API version: %s)", apiVersion, v.defaultVersion)} } if versions.LessThan(apiVersion, v.minVersion) { return badRequestError{fmt.Errorf("client version %s is too old. Minimum supported API version is %s, please upgrade your client to a newer version", apiVersion, v.minVersion)} } header := fmt.Sprintf("Docker/%s (%s)", v.serverVersion, runtime.GOOS) w.Header().Set("Server", header) ctx = context.WithValue(ctx, "api-version", apiVersion) return handler(ctx, w, r, vars) } }
func (cli *Client) sendClientRequest(ctx context.Context, method, path string, query url.Values, body io.Reader, headers map[string][]string) (*serverResponse, error) { serverResp := &serverResponse{ body: nil, statusCode: -1, } expectedPayload := (method == "POST" || method == "PUT") if expectedPayload && body == nil { body = bytes.NewReader([]byte{}) } req, err := cli.newRequest(method, path, query, body, headers) if err != nil { return serverResp, err } if cli.proto == "unix" || cli.proto == "npipe" { // For local communications, it doesn't matter what the host is. We just // need a valid and meaningful host name. (See #189) req.Host = "docker" } req.URL.Host = cli.addr req.URL.Scheme = cli.transport.Scheme() if expectedPayload && req.Header.Get("Content-Type") == "" { req.Header.Set("Content-Type", "text/plain") } resp, err := cancellable.Do(ctx, cli.transport, req) if err != nil { if isTimeout(err) || strings.Contains(err.Error(), "connection refused") || strings.Contains(err.Error(), "dial unix") { return serverResp, ErrConnectionFailed } if !cli.transport.Secure() && strings.Contains(err.Error(), "malformed HTTP response") { return serverResp, fmt.Errorf("%v.\n* Are you trying to connect to a TLS-enabled daemon without TLS?", err) } if cli.transport.Secure() && strings.Contains(err.Error(), "bad certificate") { return serverResp, fmt.Errorf("The server probably has client authentication (--tlsverify) enabled. Please check your TLS client certification settings: %v", err) } return serverResp, fmt.Errorf("An error occurred trying to connect: %v", err) } if resp != nil { serverResp.statusCode = resp.StatusCode } if serverResp.statusCode < 200 || serverResp.statusCode >= 400 { body, err := ioutil.ReadAll(resp.Body) if err != nil { return serverResp, err } if len(body) == 0 { return serverResp, fmt.Errorf("Error: request returned %s for API route and version %s, check if the server supports the requested API version", http.StatusText(serverResp.statusCode), req.URL) } var errorMessage string if (cli.version == "" || versions.GreaterThan(cli.version, "1.23")) && resp.Header.Get("Content-Type") == "application/json" { var errorResponse types.ErrorResponse if err := json.Unmarshal(body, &errorResponse); err != nil { return serverResp, fmt.Errorf("Error reading JSON: %v", err) } errorMessage = errorResponse.Message } else { errorMessage = string(body) } return serverResp, fmt.Errorf("Error response from daemon: %s", strings.TrimSpace(errorMessage)) } serverResp.body = resp.Body serverResp.header = resp.Header return serverResp, nil }