github.com/jaegerpicker/docker@v0.7.7-0.20150325003727-22dba32b4dab/api/server/server.go (about)

     1  package server
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  
     7  	"encoding/base64"
     8  	"encoding/json"
     9  	"expvar"
    10  	"fmt"
    11  	"io"
    12  	"io/ioutil"
    13  	"net"
    14  	"net/http"
    15  	"net/http/pprof"
    16  	"os"
    17  	"strconv"
    18  	"strings"
    19  
    20  	"crypto/tls"
    21  	"crypto/x509"
    22  
    23  	"code.google.com/p/go.net/websocket"
    24  	"github.com/docker/libcontainer/user"
    25  	"github.com/gorilla/mux"
    26  
    27  	log "github.com/Sirupsen/logrus"
    28  	"github.com/docker/docker/api"
    29  	"github.com/docker/docker/api/types"
    30  	"github.com/docker/docker/daemon/networkdriver/portallocator"
    31  	"github.com/docker/docker/engine"
    32  	"github.com/docker/docker/pkg/listenbuffer"
    33  	"github.com/docker/docker/pkg/parsers"
    34  	"github.com/docker/docker/pkg/stdcopy"
    35  	"github.com/docker/docker/pkg/version"
    36  	"github.com/docker/docker/registry"
    37  	"github.com/docker/docker/utils"
    38  )
    39  
    40  var (
    41  	activationLock chan struct{}
    42  )
    43  
    44  type HttpServer struct {
    45  	srv *http.Server
    46  	l   net.Listener
    47  }
    48  
    49  func (s *HttpServer) Serve() error {
    50  	return s.srv.Serve(s.l)
    51  }
    52  func (s *HttpServer) Close() error {
    53  	return s.l.Close()
    54  }
    55  
    56  type HttpApiFunc func(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error
    57  
    58  func hijackServer(w http.ResponseWriter) (io.ReadCloser, io.Writer, error) {
    59  	conn, _, err := w.(http.Hijacker).Hijack()
    60  	if err != nil {
    61  		return nil, nil, err
    62  	}
    63  	// Flush the options to make sure the client sets the raw mode
    64  	conn.Write([]byte{})
    65  	return conn, conn, nil
    66  }
    67  
    68  func closeStreams(streams ...interface{}) {
    69  	for _, stream := range streams {
    70  		if tcpc, ok := stream.(interface {
    71  			CloseWrite() error
    72  		}); ok {
    73  			tcpc.CloseWrite()
    74  		} else if closer, ok := stream.(io.Closer); ok {
    75  			closer.Close()
    76  		}
    77  	}
    78  }
    79  
    80  // Check to make sure request's Content-Type is application/json
    81  func checkForJson(r *http.Request) error {
    82  	ct := r.Header.Get("Content-Type")
    83  
    84  	// No Content-Type header is ok as long as there's no Body
    85  	if ct == "" {
    86  		if r.Body == nil || r.ContentLength == 0 {
    87  			return nil
    88  		}
    89  	}
    90  
    91  	// Otherwise it better be json
    92  	if api.MatchesContentType(ct, "application/json") {
    93  		return nil
    94  	}
    95  	return fmt.Errorf("Content-Type specified (%s) must be 'application/json'", ct)
    96  }
    97  
    98  //If we don't do this, POST method without Content-type (even with empty body) will fail
    99  func parseForm(r *http.Request) error {
   100  	if r == nil {
   101  		return nil
   102  	}
   103  	if err := r.ParseForm(); err != nil && !strings.HasPrefix(err.Error(), "mime:") {
   104  		return err
   105  	}
   106  	return nil
   107  }
   108  
   109  func parseMultipartForm(r *http.Request) error {
   110  	if err := r.ParseMultipartForm(4096); err != nil && !strings.HasPrefix(err.Error(), "mime:") {
   111  		return err
   112  	}
   113  	return nil
   114  }
   115  
   116  func httpError(w http.ResponseWriter, err error) {
   117  	statusCode := http.StatusInternalServerError
   118  	// FIXME: this is brittle and should not be necessary.
   119  	// If we need to differentiate between different possible error types, we should
   120  	// create appropriate error types with clearly defined meaning.
   121  	errStr := strings.ToLower(err.Error())
   122  	if strings.Contains(errStr, "no such") {
   123  		statusCode = http.StatusNotFound
   124  	} else if strings.Contains(errStr, "bad parameter") {
   125  		statusCode = http.StatusBadRequest
   126  	} else if strings.Contains(errStr, "conflict") {
   127  		statusCode = http.StatusConflict
   128  	} else if strings.Contains(errStr, "impossible") {
   129  		statusCode = http.StatusNotAcceptable
   130  	} else if strings.Contains(errStr, "wrong login/password") {
   131  		statusCode = http.StatusUnauthorized
   132  	} else if strings.Contains(errStr, "hasn't been activated") {
   133  		statusCode = http.StatusForbidden
   134  	}
   135  
   136  	if err != nil {
   137  		log.Errorf("HTTP Error: statusCode=%d %v", statusCode, err)
   138  		http.Error(w, err.Error(), statusCode)
   139  	}
   140  }
   141  
   142  // writeJSONEnv writes the engine.Env values to the http response stream as a
   143  // json encoded body.
   144  func writeJSONEnv(w http.ResponseWriter, code int, v engine.Env) error {
   145  	w.Header().Set("Content-Type", "application/json")
   146  	w.WriteHeader(code)
   147  	return v.Encode(w)
   148  }
   149  
   150  // writeJSON writes the value v to the http response stream as json with standard
   151  // json encoding.
   152  func writeJSON(w http.ResponseWriter, code int, v interface{}) error {
   153  	w.Header().Set("Content-Type", "application/json")
   154  	w.WriteHeader(code)
   155  	return json.NewEncoder(w).Encode(v)
   156  }
   157  
   158  func streamJSON(job *engine.Job, w http.ResponseWriter, flush bool) {
   159  	w.Header().Set("Content-Type", "application/json")
   160  	if flush {
   161  		job.Stdout.Add(utils.NewWriteFlusher(w))
   162  	} else {
   163  		job.Stdout.Add(w)
   164  	}
   165  }
   166  
   167  func getBoolParam(value string) (bool, error) {
   168  	if value == "" {
   169  		return false, nil
   170  	}
   171  	ret, err := strconv.ParseBool(value)
   172  	if err != nil {
   173  		return false, fmt.Errorf("Bad parameter")
   174  	}
   175  	return ret, nil
   176  }
   177  
   178  func postAuth(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   179  	var (
   180  		authConfig, err = ioutil.ReadAll(r.Body)
   181  		job             = eng.Job("auth")
   182  		stdoutBuffer    = bytes.NewBuffer(nil)
   183  	)
   184  	if err != nil {
   185  		return err
   186  	}
   187  	job.Setenv("authConfig", string(authConfig))
   188  	job.Stdout.Add(stdoutBuffer)
   189  	if err = job.Run(); err != nil {
   190  		return err
   191  	}
   192  	if status := engine.Tail(stdoutBuffer, 1); status != "" {
   193  		var env engine.Env
   194  		env.Set("Status", status)
   195  		return writeJSON(w, http.StatusOK, &types.AuthResponse{
   196  			Status: status,
   197  		})
   198  	}
   199  	w.WriteHeader(http.StatusNoContent)
   200  	return nil
   201  }
   202  
   203  func getVersion(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   204  	w.Header().Set("Content-Type", "application/json")
   205  	eng.ServeHTTP(w, r)
   206  	return nil
   207  }
   208  
   209  func postContainersKill(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   210  	if vars == nil {
   211  		return fmt.Errorf("Missing parameter")
   212  	}
   213  	if err := parseForm(r); err != nil {
   214  		return err
   215  	}
   216  	job := eng.Job("kill", vars["name"])
   217  	if sig := r.Form.Get("signal"); sig != "" {
   218  		job.Args = append(job.Args, sig)
   219  	}
   220  	if err := job.Run(); err != nil {
   221  		return err
   222  	}
   223  	w.WriteHeader(http.StatusNoContent)
   224  	return nil
   225  }
   226  
   227  func postContainersPause(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   228  	if vars == nil {
   229  		return fmt.Errorf("Missing parameter")
   230  	}
   231  	if err := parseForm(r); err != nil {
   232  		return err
   233  	}
   234  	job := eng.Job("pause", vars["name"])
   235  	if err := job.Run(); err != nil {
   236  		return err
   237  	}
   238  	w.WriteHeader(http.StatusNoContent)
   239  	return nil
   240  }
   241  
   242  func postContainersUnpause(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   243  	if vars == nil {
   244  		return fmt.Errorf("Missing parameter")
   245  	}
   246  	if err := parseForm(r); err != nil {
   247  		return err
   248  	}
   249  	job := eng.Job("unpause", vars["name"])
   250  	if err := job.Run(); err != nil {
   251  		return err
   252  	}
   253  	w.WriteHeader(http.StatusNoContent)
   254  	return nil
   255  }
   256  
   257  func getContainersExport(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   258  	if vars == nil {
   259  		return fmt.Errorf("Missing parameter")
   260  	}
   261  	job := eng.Job("export", vars["name"])
   262  	job.Stdout.Add(w)
   263  	if err := job.Run(); err != nil {
   264  		return err
   265  	}
   266  	return nil
   267  }
   268  
   269  func getImagesJSON(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   270  	if err := parseForm(r); err != nil {
   271  		return err
   272  	}
   273  
   274  	var (
   275  		err  error
   276  		outs *engine.Table
   277  		job  = eng.Job("images")
   278  	)
   279  
   280  	job.Setenv("filters", r.Form.Get("filters"))
   281  	// FIXME this parameter could just be a match filter
   282  	job.Setenv("filter", r.Form.Get("filter"))
   283  	job.Setenv("all", r.Form.Get("all"))
   284  
   285  	if version.GreaterThanOrEqualTo("1.7") {
   286  		streamJSON(job, w, false)
   287  	} else if outs, err = job.Stdout.AddListTable(); err != nil {
   288  		return err
   289  	}
   290  
   291  	if err := job.Run(); err != nil {
   292  		return err
   293  	}
   294  
   295  	if version.LessThan("1.7") && outs != nil { // Convert to legacy format
   296  		outsLegacy := engine.NewTable("Created", 0)
   297  		for _, out := range outs.Data {
   298  			for _, repoTag := range out.GetList("RepoTags") {
   299  				repo, tag := parsers.ParseRepositoryTag(repoTag)
   300  				outLegacy := &engine.Env{}
   301  				outLegacy.Set("Repository", repo)
   302  				outLegacy.SetJson("Tag", tag)
   303  				outLegacy.Set("Id", out.Get("Id"))
   304  				outLegacy.SetInt64("Created", out.GetInt64("Created"))
   305  				outLegacy.SetInt64("Size", out.GetInt64("Size"))
   306  				outLegacy.SetInt64("VirtualSize", out.GetInt64("VirtualSize"))
   307  				outsLegacy.Add(outLegacy)
   308  			}
   309  		}
   310  		w.Header().Set("Content-Type", "application/json")
   311  		if _, err := outsLegacy.WriteListTo(w); err != nil {
   312  			return err
   313  		}
   314  	}
   315  	return nil
   316  }
   317  
   318  func getImagesViz(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   319  	if version.GreaterThan("1.6") {
   320  		w.WriteHeader(http.StatusNotFound)
   321  		return fmt.Errorf("This is now implemented in the client.")
   322  	}
   323  	eng.ServeHTTP(w, r)
   324  	return nil
   325  }
   326  
   327  func getInfo(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   328  	w.Header().Set("Content-Type", "application/json")
   329  	eng.ServeHTTP(w, r)
   330  	return nil
   331  }
   332  
   333  func getEvents(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   334  	if err := parseForm(r); err != nil {
   335  		return err
   336  	}
   337  
   338  	var job = eng.Job("events")
   339  	streamJSON(job, w, true)
   340  	job.Setenv("since", r.Form.Get("since"))
   341  	job.Setenv("until", r.Form.Get("until"))
   342  	job.Setenv("filters", r.Form.Get("filters"))
   343  	return job.Run()
   344  }
   345  
   346  func getImagesHistory(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   347  	if vars == nil {
   348  		return fmt.Errorf("Missing parameter")
   349  	}
   350  
   351  	var job = eng.Job("history", vars["name"])
   352  	streamJSON(job, w, false)
   353  
   354  	if err := job.Run(); err != nil {
   355  		return err
   356  	}
   357  	return nil
   358  }
   359  
   360  func getContainersChanges(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   361  	if vars == nil {
   362  		return fmt.Errorf("Missing parameter")
   363  	}
   364  	var job = eng.Job("container_changes", vars["name"])
   365  	streamJSON(job, w, false)
   366  
   367  	return job.Run()
   368  }
   369  
   370  func getContainersTop(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   371  	if version.LessThan("1.4") {
   372  		return fmt.Errorf("top was improved a lot since 1.3, Please upgrade your docker client.")
   373  	}
   374  	if vars == nil {
   375  		return fmt.Errorf("Missing parameter")
   376  	}
   377  	if err := parseForm(r); err != nil {
   378  		return err
   379  	}
   380  
   381  	job := eng.Job("top", vars["name"], r.Form.Get("ps_args"))
   382  	streamJSON(job, w, false)
   383  	return job.Run()
   384  }
   385  
   386  func getContainersJSON(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   387  	if err := parseForm(r); err != nil {
   388  		return err
   389  	}
   390  	var (
   391  		err  error
   392  		outs *engine.Table
   393  		job  = eng.Job("containers")
   394  	)
   395  
   396  	job.Setenv("all", r.Form.Get("all"))
   397  	job.Setenv("size", r.Form.Get("size"))
   398  	job.Setenv("since", r.Form.Get("since"))
   399  	job.Setenv("before", r.Form.Get("before"))
   400  	job.Setenv("limit", r.Form.Get("limit"))
   401  	job.Setenv("filters", r.Form.Get("filters"))
   402  
   403  	if version.GreaterThanOrEqualTo("1.5") {
   404  		streamJSON(job, w, false)
   405  	} else if outs, err = job.Stdout.AddTable(); err != nil {
   406  		return err
   407  	}
   408  	if err = job.Run(); err != nil {
   409  		return err
   410  	}
   411  	if version.LessThan("1.5") { // Convert to legacy format
   412  		for _, out := range outs.Data {
   413  			ports := engine.NewTable("", 0)
   414  			ports.ReadListFrom([]byte(out.Get("Ports")))
   415  			out.Set("Ports", api.DisplayablePorts(ports))
   416  		}
   417  		w.Header().Set("Content-Type", "application/json")
   418  		if _, err = outs.WriteListTo(w); err != nil {
   419  			return err
   420  		}
   421  	}
   422  	return nil
   423  }
   424  
   425  func getContainersStats(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   426  	if err := parseForm(r); err != nil {
   427  		return err
   428  	}
   429  	if vars == nil {
   430  		return fmt.Errorf("Missing parameter")
   431  	}
   432  	name := vars["name"]
   433  	job := eng.Job("container_stats", name)
   434  	streamJSON(job, w, true)
   435  	return job.Run()
   436  }
   437  
   438  func getContainersLogs(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   439  	if err := parseForm(r); err != nil {
   440  		return err
   441  	}
   442  	if vars == nil {
   443  		return fmt.Errorf("Missing parameter")
   444  	}
   445  
   446  	var (
   447  		inspectJob = eng.Job("container_inspect", vars["name"])
   448  		logsJob    = eng.Job("logs", vars["name"])
   449  		c, err     = inspectJob.Stdout.AddEnv()
   450  	)
   451  	if err != nil {
   452  		return err
   453  	}
   454  	logsJob.Setenv("follow", r.Form.Get("follow"))
   455  	logsJob.Setenv("tail", r.Form.Get("tail"))
   456  	logsJob.Setenv("stdout", r.Form.Get("stdout"))
   457  	logsJob.Setenv("stderr", r.Form.Get("stderr"))
   458  	logsJob.Setenv("timestamps", r.Form.Get("timestamps"))
   459  	// Validate args here, because we can't return not StatusOK after job.Run() call
   460  	stdout, stderr := logsJob.GetenvBool("stdout"), logsJob.GetenvBool("stderr")
   461  	if !(stdout || stderr) {
   462  		return fmt.Errorf("Bad parameters: you must choose at least one stream")
   463  	}
   464  	if err = inspectJob.Run(); err != nil {
   465  		return err
   466  	}
   467  
   468  	var outStream, errStream io.Writer
   469  	outStream = utils.NewWriteFlusher(w)
   470  
   471  	if c.GetSubEnv("Config") != nil && !c.GetSubEnv("Config").GetBool("Tty") && version.GreaterThanOrEqualTo("1.6") {
   472  		errStream = stdcopy.NewStdWriter(outStream, stdcopy.Stderr)
   473  		outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout)
   474  	} else {
   475  		errStream = outStream
   476  	}
   477  
   478  	logsJob.Stdout.Add(outStream)
   479  	logsJob.Stderr.Set(errStream)
   480  	if err := logsJob.Run(); err != nil {
   481  		fmt.Fprintf(outStream, "Error running logs job: %s\n", err)
   482  	}
   483  	return nil
   484  }
   485  
   486  func postImagesTag(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   487  	if err := parseForm(r); err != nil {
   488  		return err
   489  	}
   490  	if vars == nil {
   491  		return fmt.Errorf("Missing parameter")
   492  	}
   493  
   494  	job := eng.Job("tag", vars["name"], r.Form.Get("repo"), r.Form.Get("tag"))
   495  	job.Setenv("force", r.Form.Get("force"))
   496  	if err := job.Run(); err != nil {
   497  		return err
   498  	}
   499  	w.WriteHeader(http.StatusCreated)
   500  	return nil
   501  }
   502  
   503  func postCommit(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   504  	if err := parseForm(r); err != nil {
   505  		return err
   506  	}
   507  	var (
   508  		config       engine.Env
   509  		env          engine.Env
   510  		job          = eng.Job("commit", r.Form.Get("container"))
   511  		stdoutBuffer = bytes.NewBuffer(nil)
   512  	)
   513  
   514  	if err := checkForJson(r); err != nil {
   515  		return err
   516  	}
   517  
   518  	if err := config.Decode(r.Body); err != nil {
   519  		log.Errorf("%s", err)
   520  	}
   521  
   522  	if r.FormValue("pause") == "" && version.GreaterThanOrEqualTo("1.13") {
   523  		job.Setenv("pause", "1")
   524  	} else {
   525  		job.Setenv("pause", r.FormValue("pause"))
   526  	}
   527  
   528  	job.Setenv("repo", r.Form.Get("repo"))
   529  	job.Setenv("tag", r.Form.Get("tag"))
   530  	job.Setenv("author", r.Form.Get("author"))
   531  	job.Setenv("comment", r.Form.Get("comment"))
   532  	job.SetenvList("changes", r.Form["changes"])
   533  	job.SetenvSubEnv("config", &config)
   534  
   535  	job.Stdout.Add(stdoutBuffer)
   536  	if err := job.Run(); err != nil {
   537  		return err
   538  	}
   539  	env.Set("Id", engine.Tail(stdoutBuffer, 1))
   540  	return writeJSONEnv(w, http.StatusCreated, env)
   541  }
   542  
   543  // Creates an image from Pull or from Import
   544  func postImagesCreate(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   545  	if err := parseForm(r); err != nil {
   546  		return err
   547  	}
   548  
   549  	var (
   550  		image = r.Form.Get("fromImage")
   551  		repo  = r.Form.Get("repo")
   552  		tag   = r.Form.Get("tag")
   553  		job   *engine.Job
   554  	)
   555  	authEncoded := r.Header.Get("X-Registry-Auth")
   556  	authConfig := &registry.AuthConfig{}
   557  	if authEncoded != "" {
   558  		authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
   559  		if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
   560  			// for a pull it is not an error if no auth was given
   561  			// to increase compatibility with the existing api it is defaulting to be empty
   562  			authConfig = &registry.AuthConfig{}
   563  		}
   564  	}
   565  	if image != "" { //pull
   566  		if tag == "" {
   567  			image, tag = parsers.ParseRepositoryTag(image)
   568  		}
   569  		metaHeaders := map[string][]string{}
   570  		for k, v := range r.Header {
   571  			if strings.HasPrefix(k, "X-Meta-") {
   572  				metaHeaders[k] = v
   573  			}
   574  		}
   575  		job = eng.Job("pull", image, tag)
   576  		job.SetenvBool("parallel", version.GreaterThan("1.3"))
   577  		job.SetenvJson("metaHeaders", metaHeaders)
   578  		job.SetenvJson("authConfig", authConfig)
   579  	} else { //import
   580  		if tag == "" {
   581  			repo, tag = parsers.ParseRepositoryTag(repo)
   582  		}
   583  		job = eng.Job("import", r.Form.Get("fromSrc"), repo, tag)
   584  		job.Stdin.Add(r.Body)
   585  		job.SetenvList("changes", r.Form["changes"])
   586  	}
   587  
   588  	if version.GreaterThan("1.0") {
   589  		job.SetenvBool("json", true)
   590  		streamJSON(job, w, true)
   591  	} else {
   592  		job.Stdout.Add(utils.NewWriteFlusher(w))
   593  	}
   594  	if err := job.Run(); err != nil {
   595  		if !job.Stdout.Used() {
   596  			return err
   597  		}
   598  		sf := utils.NewStreamFormatter(version.GreaterThan("1.0"))
   599  		w.Write(sf.FormatError(err))
   600  	}
   601  
   602  	return nil
   603  }
   604  
   605  func getImagesSearch(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   606  	if err := parseForm(r); err != nil {
   607  		return err
   608  	}
   609  	var (
   610  		authEncoded = r.Header.Get("X-Registry-Auth")
   611  		authConfig  = &registry.AuthConfig{}
   612  		metaHeaders = map[string][]string{}
   613  	)
   614  
   615  	if authEncoded != "" {
   616  		authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
   617  		if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
   618  			// for a search it is not an error if no auth was given
   619  			// to increase compatibility with the existing api it is defaulting to be empty
   620  			authConfig = &registry.AuthConfig{}
   621  		}
   622  	}
   623  	for k, v := range r.Header {
   624  		if strings.HasPrefix(k, "X-Meta-") {
   625  			metaHeaders[k] = v
   626  		}
   627  	}
   628  
   629  	var job = eng.Job("search", r.Form.Get("term"))
   630  	job.SetenvJson("metaHeaders", metaHeaders)
   631  	job.SetenvJson("authConfig", authConfig)
   632  	streamJSON(job, w, false)
   633  
   634  	return job.Run()
   635  }
   636  
   637  func postImagesPush(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   638  	if vars == nil {
   639  		return fmt.Errorf("Missing parameter")
   640  	}
   641  
   642  	metaHeaders := map[string][]string{}
   643  	for k, v := range r.Header {
   644  		if strings.HasPrefix(k, "X-Meta-") {
   645  			metaHeaders[k] = v
   646  		}
   647  	}
   648  	if err := parseForm(r); err != nil {
   649  		return err
   650  	}
   651  	authConfig := &registry.AuthConfig{}
   652  
   653  	authEncoded := r.Header.Get("X-Registry-Auth")
   654  	if authEncoded != "" {
   655  		// the new format is to handle the authConfig as a header
   656  		authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
   657  		if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
   658  			// to increase compatibility to existing api it is defaulting to be empty
   659  			authConfig = &registry.AuthConfig{}
   660  		}
   661  	} else {
   662  		// the old format is supported for compatibility if there was no authConfig header
   663  		if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil {
   664  			return err
   665  		}
   666  	}
   667  
   668  	job := eng.Job("push", vars["name"])
   669  	job.SetenvJson("metaHeaders", metaHeaders)
   670  	job.SetenvJson("authConfig", authConfig)
   671  	job.Setenv("tag", r.Form.Get("tag"))
   672  	if version.GreaterThan("1.0") {
   673  		job.SetenvBool("json", true)
   674  		streamJSON(job, w, true)
   675  	} else {
   676  		job.Stdout.Add(utils.NewWriteFlusher(w))
   677  	}
   678  
   679  	if err := job.Run(); err != nil {
   680  		if !job.Stdout.Used() {
   681  			return err
   682  		}
   683  		sf := utils.NewStreamFormatter(version.GreaterThan("1.0"))
   684  		w.Write(sf.FormatError(err))
   685  	}
   686  	return nil
   687  }
   688  
   689  func getImagesGet(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   690  	if vars == nil {
   691  		return fmt.Errorf("Missing parameter")
   692  	}
   693  	if err := parseForm(r); err != nil {
   694  		return err
   695  	}
   696  	if version.GreaterThan("1.0") {
   697  		w.Header().Set("Content-Type", "application/x-tar")
   698  	}
   699  	var job *engine.Job
   700  	if name, ok := vars["name"]; ok {
   701  		job = eng.Job("image_export", name)
   702  	} else {
   703  		job = eng.Job("image_export", r.Form["names"]...)
   704  	}
   705  	job.Stdout.Add(w)
   706  	return job.Run()
   707  }
   708  
   709  func postImagesLoad(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   710  	job := eng.Job("load")
   711  	job.Stdin.Add(r.Body)
   712  	return job.Run()
   713  }
   714  
   715  func postContainersCreate(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   716  	if err := parseForm(r); err != nil {
   717  		return nil
   718  	}
   719  	if err := checkForJson(r); err != nil {
   720  		return err
   721  	}
   722  	var (
   723  		job          = eng.Job("create", r.Form.Get("name"))
   724  		outWarnings  []string
   725  		stdoutBuffer = bytes.NewBuffer(nil)
   726  		warnings     = bytes.NewBuffer(nil)
   727  	)
   728  
   729  	if err := job.DecodeEnv(r.Body); err != nil {
   730  		return err
   731  	}
   732  	// Read container ID from the first line of stdout
   733  	job.Stdout.Add(stdoutBuffer)
   734  	// Read warnings from stderr
   735  	job.Stderr.Add(warnings)
   736  	if err := job.Run(); err != nil {
   737  		return err
   738  	}
   739  	// Parse warnings from stderr
   740  	scanner := bufio.NewScanner(warnings)
   741  	for scanner.Scan() {
   742  		outWarnings = append(outWarnings, scanner.Text())
   743  	}
   744  	return writeJSON(w, http.StatusCreated, &types.ContainerCreateResponse{
   745  		ID:       engine.Tail(stdoutBuffer, 1),
   746  		Warnings: outWarnings,
   747  	})
   748  }
   749  
   750  func postContainersRestart(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   751  	if err := parseForm(r); err != nil {
   752  		return err
   753  	}
   754  	if vars == nil {
   755  		return fmt.Errorf("Missing parameter")
   756  	}
   757  	job := eng.Job("restart", vars["name"])
   758  	job.Setenv("t", r.Form.Get("t"))
   759  	if err := job.Run(); err != nil {
   760  		return err
   761  	}
   762  	w.WriteHeader(http.StatusNoContent)
   763  	return nil
   764  }
   765  
   766  func postContainerRename(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   767  	if err := parseForm(r); err != nil {
   768  		return err
   769  	}
   770  	if vars == nil {
   771  		return fmt.Errorf("Missing parameter")
   772  	}
   773  
   774  	newName := r.URL.Query().Get("name")
   775  	job := eng.Job("container_rename", vars["name"], newName)
   776  	job.Setenv("t", r.Form.Get("t"))
   777  	if err := job.Run(); err != nil {
   778  		return err
   779  	}
   780  	w.WriteHeader(http.StatusNoContent)
   781  	return nil
   782  }
   783  
   784  func deleteContainers(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   785  	if err := parseForm(r); err != nil {
   786  		return err
   787  	}
   788  	if vars == nil {
   789  		return fmt.Errorf("Missing parameter")
   790  	}
   791  	job := eng.Job("rm", vars["name"])
   792  
   793  	job.Setenv("forceRemove", r.Form.Get("force"))
   794  
   795  	job.Setenv("removeVolume", r.Form.Get("v"))
   796  	job.Setenv("removeLink", r.Form.Get("link"))
   797  	if err := job.Run(); err != nil {
   798  		return err
   799  	}
   800  	w.WriteHeader(http.StatusNoContent)
   801  	return nil
   802  }
   803  
   804  func deleteImages(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   805  	if err := parseForm(r); err != nil {
   806  		return err
   807  	}
   808  	if vars == nil {
   809  		return fmt.Errorf("Missing parameter")
   810  	}
   811  	var job = eng.Job("image_delete", vars["name"])
   812  	streamJSON(job, w, false)
   813  	job.Setenv("force", r.Form.Get("force"))
   814  	job.Setenv("noprune", r.Form.Get("noprune"))
   815  
   816  	return job.Run()
   817  }
   818  
   819  func postContainersStart(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   820  	if vars == nil {
   821  		return fmt.Errorf("Missing parameter")
   822  	}
   823  	var (
   824  		name = vars["name"]
   825  		job  = eng.Job("start", name)
   826  	)
   827  
   828  	// If contentLength is -1, we can assumed chunked encoding
   829  	// or more technically that the length is unknown
   830  	// http://golang.org/src/pkg/net/http/request.go#L139
   831  	// net/http otherwise seems to swallow any headers related to chunked encoding
   832  	// including r.TransferEncoding
   833  	// allow a nil body for backwards compatibility
   834  	if r.Body != nil && (r.ContentLength > 0 || r.ContentLength == -1) {
   835  		if err := checkForJson(r); err != nil {
   836  			return err
   837  		}
   838  
   839  		if err := job.DecodeEnv(r.Body); err != nil {
   840  			return err
   841  		}
   842  	}
   843  
   844  	if err := job.Run(); err != nil {
   845  		if err.Error() == "Container already started" {
   846  			w.WriteHeader(http.StatusNotModified)
   847  			return nil
   848  		}
   849  		return err
   850  	}
   851  	w.WriteHeader(http.StatusNoContent)
   852  	return nil
   853  }
   854  
   855  func postContainersStop(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   856  	if err := parseForm(r); err != nil {
   857  		return err
   858  	}
   859  	if vars == nil {
   860  		return fmt.Errorf("Missing parameter")
   861  	}
   862  	job := eng.Job("stop", vars["name"])
   863  	job.Setenv("t", r.Form.Get("t"))
   864  	if err := job.Run(); err != nil {
   865  		if err.Error() == "Container already stopped" {
   866  			w.WriteHeader(http.StatusNotModified)
   867  			return nil
   868  		}
   869  		return err
   870  	}
   871  	w.WriteHeader(http.StatusNoContent)
   872  	return nil
   873  }
   874  
   875  func postContainersWait(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   876  	if vars == nil {
   877  		return fmt.Errorf("Missing parameter")
   878  	}
   879  	var (
   880  		env          engine.Env
   881  		stdoutBuffer = bytes.NewBuffer(nil)
   882  		job          = eng.Job("wait", vars["name"])
   883  	)
   884  	job.Stdout.Add(stdoutBuffer)
   885  	if err := job.Run(); err != nil {
   886  		return err
   887  	}
   888  
   889  	env.Set("StatusCode", engine.Tail(stdoutBuffer, 1))
   890  	return writeJSONEnv(w, http.StatusOK, env)
   891  }
   892  
   893  func postContainersResize(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   894  	if err := parseForm(r); err != nil {
   895  		return err
   896  	}
   897  	if vars == nil {
   898  		return fmt.Errorf("Missing parameter")
   899  	}
   900  	if err := eng.Job("resize", vars["name"], r.Form.Get("h"), r.Form.Get("w")).Run(); err != nil {
   901  		return err
   902  	}
   903  	return nil
   904  }
   905  
   906  func postContainersAttach(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   907  	if err := parseForm(r); err != nil {
   908  		return err
   909  	}
   910  	if vars == nil {
   911  		return fmt.Errorf("Missing parameter")
   912  	}
   913  
   914  	var (
   915  		job    = eng.Job("container_inspect", vars["name"])
   916  		c, err = job.Stdout.AddEnv()
   917  	)
   918  	if err != nil {
   919  		return err
   920  	}
   921  	if err = job.Run(); err != nil {
   922  		return err
   923  	}
   924  
   925  	inStream, outStream, err := hijackServer(w)
   926  	if err != nil {
   927  		return err
   928  	}
   929  	defer closeStreams(inStream, outStream)
   930  
   931  	var errStream io.Writer
   932  
   933  	if _, ok := r.Header["Upgrade"]; ok {
   934  		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")
   935  	} else {
   936  		fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
   937  	}
   938  
   939  	if c.GetSubEnv("Config") != nil && !c.GetSubEnv("Config").GetBool("Tty") && version.GreaterThanOrEqualTo("1.6") {
   940  		errStream = stdcopy.NewStdWriter(outStream, stdcopy.Stderr)
   941  		outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout)
   942  	} else {
   943  		errStream = outStream
   944  	}
   945  
   946  	job = eng.Job("attach", vars["name"])
   947  	job.Setenv("logs", r.Form.Get("logs"))
   948  	job.Setenv("stream", r.Form.Get("stream"))
   949  	job.Setenv("stdin", r.Form.Get("stdin"))
   950  	job.Setenv("stdout", r.Form.Get("stdout"))
   951  	job.Setenv("stderr", r.Form.Get("stderr"))
   952  	job.Stdin.Add(inStream)
   953  	job.Stdout.Add(outStream)
   954  	job.Stderr.Set(errStream)
   955  	if err := job.Run(); err != nil {
   956  		fmt.Fprintf(outStream, "Error attaching: %s\n", err)
   957  
   958  	}
   959  	return nil
   960  }
   961  
   962  func wsContainersAttach(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   963  	if err := parseForm(r); err != nil {
   964  		return err
   965  	}
   966  	if vars == nil {
   967  		return fmt.Errorf("Missing parameter")
   968  	}
   969  
   970  	if err := eng.Job("container_inspect", vars["name"]).Run(); err != nil {
   971  		return err
   972  	}
   973  
   974  	h := websocket.Handler(func(ws *websocket.Conn) {
   975  		defer ws.Close()
   976  		job := eng.Job("attach", vars["name"])
   977  		job.Setenv("logs", r.Form.Get("logs"))
   978  		job.Setenv("stream", r.Form.Get("stream"))
   979  		job.Setenv("stdin", r.Form.Get("stdin"))
   980  		job.Setenv("stdout", r.Form.Get("stdout"))
   981  		job.Setenv("stderr", r.Form.Get("stderr"))
   982  		job.Stdin.Add(ws)
   983  		job.Stdout.Add(ws)
   984  		job.Stderr.Set(ws)
   985  		if err := job.Run(); err != nil {
   986  			log.Errorf("Error attaching websocket: %s", err)
   987  		}
   988  	})
   989  	h.ServeHTTP(w, r)
   990  
   991  	return nil
   992  }
   993  
   994  func getContainersByName(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   995  	if vars == nil {
   996  		return fmt.Errorf("Missing parameter")
   997  	}
   998  	var job = eng.Job("container_inspect", vars["name"])
   999  	if version.LessThan("1.12") {
  1000  		job.SetenvBool("raw", true)
  1001  	}
  1002  	streamJSON(job, w, false)
  1003  	return job.Run()
  1004  }
  1005  
  1006  func getExecByID(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  1007  	if vars == nil {
  1008  		return fmt.Errorf("Missing parameter 'id'")
  1009  	}
  1010  	var job = eng.Job("execInspect", vars["id"])
  1011  	streamJSON(job, w, false)
  1012  	return job.Run()
  1013  }
  1014  
  1015  func getImagesByName(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  1016  	if vars == nil {
  1017  		return fmt.Errorf("Missing parameter")
  1018  	}
  1019  	var job = eng.Job("image_inspect", vars["name"])
  1020  	if version.LessThan("1.12") {
  1021  		job.SetenvBool("raw", true)
  1022  	}
  1023  	streamJSON(job, w, false)
  1024  	return job.Run()
  1025  }
  1026  
  1027  func postBuild(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  1028  	if version.LessThan("1.3") {
  1029  		return fmt.Errorf("Multipart upload for build is no longer supported. Please upgrade your docker client.")
  1030  	}
  1031  	var (
  1032  		authEncoded       = r.Header.Get("X-Registry-Auth")
  1033  		authConfig        = &registry.AuthConfig{}
  1034  		configFileEncoded = r.Header.Get("X-Registry-Config")
  1035  		configFile        = &registry.ConfigFile{}
  1036  		job               = eng.Job("build")
  1037  	)
  1038  
  1039  	// This block can be removed when API versions prior to 1.9 are deprecated.
  1040  	// Both headers will be parsed and sent along to the daemon, but if a non-empty
  1041  	// ConfigFile is present, any value provided as an AuthConfig directly will
  1042  	// be overridden. See BuildFile::CmdFrom for details.
  1043  	if version.LessThan("1.9") && authEncoded != "" {
  1044  		authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
  1045  		if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
  1046  			// for a pull it is not an error if no auth was given
  1047  			// to increase compatibility with the existing api it is defaulting to be empty
  1048  			authConfig = &registry.AuthConfig{}
  1049  		}
  1050  	}
  1051  
  1052  	if configFileEncoded != "" {
  1053  		configFileJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(configFileEncoded))
  1054  		if err := json.NewDecoder(configFileJson).Decode(configFile); err != nil {
  1055  			// for a pull it is not an error if no auth was given
  1056  			// to increase compatibility with the existing api it is defaulting to be empty
  1057  			configFile = &registry.ConfigFile{}
  1058  		}
  1059  	}
  1060  
  1061  	if version.GreaterThanOrEqualTo("1.8") {
  1062  		job.SetenvBool("json", true)
  1063  		streamJSON(job, w, true)
  1064  	} else {
  1065  		job.Stdout.Add(utils.NewWriteFlusher(w))
  1066  	}
  1067  
  1068  	if r.FormValue("forcerm") == "1" && version.GreaterThanOrEqualTo("1.12") {
  1069  		job.Setenv("rm", "1")
  1070  	} else if r.FormValue("rm") == "" && version.GreaterThanOrEqualTo("1.12") {
  1071  		job.Setenv("rm", "1")
  1072  	} else {
  1073  		job.Setenv("rm", r.FormValue("rm"))
  1074  	}
  1075  	if r.FormValue("pull") == "1" && version.GreaterThanOrEqualTo("1.16") {
  1076  		job.Setenv("pull", "1")
  1077  	}
  1078  	job.Stdin.Add(r.Body)
  1079  	job.Setenv("remote", r.FormValue("remote"))
  1080  	job.Setenv("dockerfile", r.FormValue("dockerfile"))
  1081  	job.Setenv("t", r.FormValue("t"))
  1082  	job.Setenv("q", r.FormValue("q"))
  1083  	job.Setenv("nocache", r.FormValue("nocache"))
  1084  	job.Setenv("forcerm", r.FormValue("forcerm"))
  1085  	job.SetenvJson("authConfig", authConfig)
  1086  	job.SetenvJson("configFile", configFile)
  1087  	job.Setenv("memswap", r.FormValue("memswap"))
  1088  	job.Setenv("memory", r.FormValue("memory"))
  1089  	job.Setenv("cpusetcpus", r.FormValue("cpusetcpus"))
  1090  	job.Setenv("cpushares", r.FormValue("cpushares"))
  1091  
  1092  	// Job cancellation. Note: not all job types support this.
  1093  	if closeNotifier, ok := w.(http.CloseNotifier); ok {
  1094  		finished := make(chan struct{})
  1095  		defer close(finished)
  1096  		go func() {
  1097  			select {
  1098  			case <-finished:
  1099  			case <-closeNotifier.CloseNotify():
  1100  				log.Infof("Client disconnected, cancelling job: %v", job)
  1101  				job.Cancel()
  1102  			}
  1103  		}()
  1104  	}
  1105  
  1106  	if err := job.Run(); err != nil {
  1107  		if !job.Stdout.Used() {
  1108  			return err
  1109  		}
  1110  		sf := utils.NewStreamFormatter(version.GreaterThanOrEqualTo("1.8"))
  1111  		w.Write(sf.FormatError(err))
  1112  	}
  1113  	return nil
  1114  }
  1115  
  1116  func postContainersCopy(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  1117  	if vars == nil {
  1118  		return fmt.Errorf("Missing parameter")
  1119  	}
  1120  
  1121  	var copyData engine.Env
  1122  
  1123  	if err := checkForJson(r); err != nil {
  1124  		return err
  1125  	}
  1126  
  1127  	if err := copyData.Decode(r.Body); err != nil {
  1128  		return err
  1129  	}
  1130  
  1131  	if copyData.Get("Resource") == "" {
  1132  		return fmt.Errorf("Path cannot be empty")
  1133  	}
  1134  
  1135  	origResource := copyData.Get("Resource")
  1136  
  1137  	if copyData.Get("Resource")[0] == '/' {
  1138  		copyData.Set("Resource", copyData.Get("Resource")[1:])
  1139  	}
  1140  
  1141  	job := eng.Job("container_copy", vars["name"], copyData.Get("Resource"))
  1142  	job.Stdout.Add(w)
  1143  	w.Header().Set("Content-Type", "application/x-tar")
  1144  	if err := job.Run(); err != nil {
  1145  		log.Errorf("%v", err)
  1146  		if strings.Contains(strings.ToLower(err.Error()), "no such id") {
  1147  			w.WriteHeader(http.StatusNotFound)
  1148  		} else if strings.Contains(err.Error(), "no such file or directory") {
  1149  			return fmt.Errorf("Could not find the file %s in container %s", origResource, vars["name"])
  1150  		}
  1151  	}
  1152  	return nil
  1153  }
  1154  
  1155  func postContainerExecCreate(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  1156  	if err := parseForm(r); err != nil {
  1157  		return nil
  1158  	}
  1159  	var (
  1160  		name         = vars["name"]
  1161  		job          = eng.Job("execCreate", name)
  1162  		stdoutBuffer = bytes.NewBuffer(nil)
  1163  		outWarnings  []string
  1164  		warnings     = bytes.NewBuffer(nil)
  1165  	)
  1166  
  1167  	if err := job.DecodeEnv(r.Body); err != nil {
  1168  		return err
  1169  	}
  1170  
  1171  	job.Stdout.Add(stdoutBuffer)
  1172  	// Read warnings from stderr
  1173  	job.Stderr.Add(warnings)
  1174  	// Register an instance of Exec in container.
  1175  	if err := job.Run(); err != nil {
  1176  		fmt.Fprintf(os.Stderr, "Error setting up exec command in container %s: %s\n", name, err)
  1177  		return err
  1178  	}
  1179  	// Parse warnings from stderr
  1180  	scanner := bufio.NewScanner(warnings)
  1181  	for scanner.Scan() {
  1182  		outWarnings = append(outWarnings, scanner.Text())
  1183  	}
  1184  
  1185  	return writeJSON(w, http.StatusCreated, &types.ContainerExecCreateResponse{
  1186  		ID:       engine.Tail(stdoutBuffer, 1),
  1187  		Warnings: outWarnings,
  1188  	})
  1189  }
  1190  
  1191  // TODO(vishh): Refactor the code to avoid having to specify stream config as part of both create and start.
  1192  func postContainerExecStart(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  1193  	if err := parseForm(r); err != nil {
  1194  		return nil
  1195  	}
  1196  	var (
  1197  		name             = vars["name"]
  1198  		job              = eng.Job("execStart", name)
  1199  		errOut io.Writer = os.Stderr
  1200  	)
  1201  
  1202  	if err := job.DecodeEnv(r.Body); err != nil {
  1203  		return err
  1204  	}
  1205  	if !job.GetenvBool("Detach") {
  1206  		// Setting up the streaming http interface.
  1207  		inStream, outStream, err := hijackServer(w)
  1208  		if err != nil {
  1209  			return err
  1210  		}
  1211  		defer closeStreams(inStream, outStream)
  1212  
  1213  		var errStream io.Writer
  1214  
  1215  		if _, ok := r.Header["Upgrade"]; ok {
  1216  			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")
  1217  		} else {
  1218  			fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
  1219  		}
  1220  
  1221  		if !job.GetenvBool("Tty") && version.GreaterThanOrEqualTo("1.6") {
  1222  			errStream = stdcopy.NewStdWriter(outStream, stdcopy.Stderr)
  1223  			outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout)
  1224  		} else {
  1225  			errStream = outStream
  1226  		}
  1227  		job.Stdin.Add(inStream)
  1228  		job.Stdout.Add(outStream)
  1229  		job.Stderr.Set(errStream)
  1230  		errOut = outStream
  1231  	}
  1232  	// Now run the user process in container.
  1233  	job.SetCloseIO(false)
  1234  	if err := job.Run(); err != nil {
  1235  		fmt.Fprintf(errOut, "Error starting exec command in container %s: %s\n", name, err)
  1236  		return err
  1237  	}
  1238  	w.WriteHeader(http.StatusNoContent)
  1239  
  1240  	return nil
  1241  }
  1242  
  1243  func postContainerExecResize(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  1244  	if err := parseForm(r); err != nil {
  1245  		return err
  1246  	}
  1247  	if vars == nil {
  1248  		return fmt.Errorf("Missing parameter")
  1249  	}
  1250  	if err := eng.Job("execResize", vars["name"], r.Form.Get("h"), r.Form.Get("w")).Run(); err != nil {
  1251  		return err
  1252  	}
  1253  	return nil
  1254  }
  1255  
  1256  func optionsHandler(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  1257  	w.WriteHeader(http.StatusOK)
  1258  	return nil
  1259  }
  1260  func writeCorsHeaders(w http.ResponseWriter, r *http.Request, corsHeaders string) {
  1261  	log.Debugf("CORS header is enabled and set to: %s", corsHeaders)
  1262  	w.Header().Add("Access-Control-Allow-Origin", corsHeaders)
  1263  	w.Header().Add("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, X-Registry-Auth")
  1264  	w.Header().Add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS")
  1265  }
  1266  
  1267  func ping(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  1268  	_, err := w.Write([]byte{'O', 'K'})
  1269  	return err
  1270  }
  1271  
  1272  func makeHttpHandler(eng *engine.Engine, logging bool, localMethod string, localRoute string, handlerFunc HttpApiFunc, corsHeaders string, dockerVersion version.Version) http.HandlerFunc {
  1273  	return func(w http.ResponseWriter, r *http.Request) {
  1274  		// log the request
  1275  		log.Debugf("Calling %s %s", localMethod, localRoute)
  1276  
  1277  		if logging {
  1278  			log.Infof("%s %s", r.Method, r.RequestURI)
  1279  		}
  1280  
  1281  		if strings.Contains(r.Header.Get("User-Agent"), "Docker-Client/") {
  1282  			userAgent := strings.Split(r.Header.Get("User-Agent"), "/")
  1283  			if len(userAgent) == 2 && !dockerVersion.Equal(version.Version(userAgent[1])) {
  1284  				log.Debugf("Warning: client and server don't have the same version (client: %s, server: %s)", userAgent[1], dockerVersion)
  1285  			}
  1286  		}
  1287  		version := version.Version(mux.Vars(r)["version"])
  1288  		if version == "" {
  1289  			version = api.APIVERSION
  1290  		}
  1291  		if corsHeaders != "" {
  1292  			writeCorsHeaders(w, r, corsHeaders)
  1293  		}
  1294  
  1295  		if version.GreaterThan(api.APIVERSION) {
  1296  			http.Error(w, fmt.Errorf("client and server don't have same version (client : %s, server: %s)", version, api.APIVERSION).Error(), http.StatusNotFound)
  1297  			return
  1298  		}
  1299  
  1300  		if err := handlerFunc(eng, version, w, r, mux.Vars(r)); err != nil {
  1301  			log.Errorf("Handler for %s %s returned error: %s", localMethod, localRoute, err)
  1302  			httpError(w, err)
  1303  		}
  1304  	}
  1305  }
  1306  
  1307  // Replicated from expvar.go as not public.
  1308  func expvarHandler(w http.ResponseWriter, r *http.Request) {
  1309  	w.Header().Set("Content-Type", "application/json; charset=utf-8")
  1310  	fmt.Fprintf(w, "{\n")
  1311  	first := true
  1312  	expvar.Do(func(kv expvar.KeyValue) {
  1313  		if !first {
  1314  			fmt.Fprintf(w, ",\n")
  1315  		}
  1316  		first = false
  1317  		fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value)
  1318  	})
  1319  	fmt.Fprintf(w, "\n}\n")
  1320  }
  1321  
  1322  func AttachProfiler(router *mux.Router) {
  1323  	router.HandleFunc("/debug/vars", expvarHandler)
  1324  	router.HandleFunc("/debug/pprof/", pprof.Index)
  1325  	router.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
  1326  	router.HandleFunc("/debug/pprof/profile", pprof.Profile)
  1327  	router.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
  1328  	router.HandleFunc("/debug/pprof/block", pprof.Handler("block").ServeHTTP)
  1329  	router.HandleFunc("/debug/pprof/heap", pprof.Handler("heap").ServeHTTP)
  1330  	router.HandleFunc("/debug/pprof/goroutine", pprof.Handler("goroutine").ServeHTTP)
  1331  	router.HandleFunc("/debug/pprof/threadcreate", pprof.Handler("threadcreate").ServeHTTP)
  1332  }
  1333  
  1334  // we keep enableCors just for legacy usage, need to be removed in the future
  1335  func createRouter(eng *engine.Engine, logging, enableCors bool, corsHeaders string, dockerVersion string) *mux.Router {
  1336  	r := mux.NewRouter()
  1337  	if os.Getenv("DEBUG") != "" {
  1338  		AttachProfiler(r)
  1339  	}
  1340  	m := map[string]map[string]HttpApiFunc{
  1341  		"GET": {
  1342  			"/_ping":                          ping,
  1343  			"/events":                         getEvents,
  1344  			"/info":                           getInfo,
  1345  			"/version":                        getVersion,
  1346  			"/images/json":                    getImagesJSON,
  1347  			"/images/viz":                     getImagesViz,
  1348  			"/images/search":                  getImagesSearch,
  1349  			"/images/get":                     getImagesGet,
  1350  			"/images/{name:.*}/get":           getImagesGet,
  1351  			"/images/{name:.*}/history":       getImagesHistory,
  1352  			"/images/{name:.*}/json":          getImagesByName,
  1353  			"/containers/ps":                  getContainersJSON,
  1354  			"/containers/json":                getContainersJSON,
  1355  			"/containers/{name:.*}/export":    getContainersExport,
  1356  			"/containers/{name:.*}/changes":   getContainersChanges,
  1357  			"/containers/{name:.*}/json":      getContainersByName,
  1358  			"/containers/{name:.*}/top":       getContainersTop,
  1359  			"/containers/{name:.*}/logs":      getContainersLogs,
  1360  			"/containers/{name:.*}/stats":     getContainersStats,
  1361  			"/containers/{name:.*}/attach/ws": wsContainersAttach,
  1362  			"/exec/{id:.*}/json":              getExecByID,
  1363  		},
  1364  		"POST": {
  1365  			"/auth":                         postAuth,
  1366  			"/commit":                       postCommit,
  1367  			"/build":                        postBuild,
  1368  			"/images/create":                postImagesCreate,
  1369  			"/images/load":                  postImagesLoad,
  1370  			"/images/{name:.*}/push":        postImagesPush,
  1371  			"/images/{name:.*}/tag":         postImagesTag,
  1372  			"/containers/create":            postContainersCreate,
  1373  			"/containers/{name:.*}/kill":    postContainersKill,
  1374  			"/containers/{name:.*}/pause":   postContainersPause,
  1375  			"/containers/{name:.*}/unpause": postContainersUnpause,
  1376  			"/containers/{name:.*}/restart": postContainersRestart,
  1377  			"/containers/{name:.*}/start":   postContainersStart,
  1378  			"/containers/{name:.*}/stop":    postContainersStop,
  1379  			"/containers/{name:.*}/wait":    postContainersWait,
  1380  			"/containers/{name:.*}/resize":  postContainersResize,
  1381  			"/containers/{name:.*}/attach":  postContainersAttach,
  1382  			"/containers/{name:.*}/copy":    postContainersCopy,
  1383  			"/containers/{name:.*}/exec":    postContainerExecCreate,
  1384  			"/exec/{name:.*}/start":         postContainerExecStart,
  1385  			"/exec/{name:.*}/resize":        postContainerExecResize,
  1386  			"/containers/{name:.*}/rename":  postContainerRename,
  1387  		},
  1388  		"DELETE": {
  1389  			"/containers/{name:.*}": deleteContainers,
  1390  			"/images/{name:.*}":     deleteImages,
  1391  		},
  1392  		"OPTIONS": {
  1393  			"": optionsHandler,
  1394  		},
  1395  	}
  1396  
  1397  	// If "api-cors-header" is not given, but "api-enable-cors" is true, we set cors to "*"
  1398  	// otherwise, all head values will be passed to HTTP handler
  1399  	if corsHeaders == "" && enableCors {
  1400  		corsHeaders = "*"
  1401  	}
  1402  
  1403  	for method, routes := range m {
  1404  		for route, fct := range routes {
  1405  			log.Debugf("Registering %s, %s", method, route)
  1406  			// NOTE: scope issue, make sure the variables are local and won't be changed
  1407  			localRoute := route
  1408  			localFct := fct
  1409  			localMethod := method
  1410  
  1411  			// build the handler function
  1412  			f := makeHttpHandler(eng, logging, localMethod, localRoute, localFct, corsHeaders, version.Version(dockerVersion))
  1413  
  1414  			// add the new route
  1415  			if localRoute == "" {
  1416  				r.Methods(localMethod).HandlerFunc(f)
  1417  			} else {
  1418  				r.Path("/v{version:[0-9.]+}" + localRoute).Methods(localMethod).HandlerFunc(f)
  1419  				r.Path(localRoute).Methods(localMethod).HandlerFunc(f)
  1420  			}
  1421  		}
  1422  	}
  1423  
  1424  	return r
  1425  }
  1426  
  1427  // ServeRequest processes a single http request to the docker remote api.
  1428  // FIXME: refactor this to be part of Server and not require re-creating a new
  1429  // router each time. This requires first moving ListenAndServe into Server.
  1430  func ServeRequest(eng *engine.Engine, apiversion version.Version, w http.ResponseWriter, req *http.Request) {
  1431  	router := createRouter(eng, false, true, "", "")
  1432  	// Insert APIVERSION into the request as a convenience
  1433  	req.URL.Path = fmt.Sprintf("/v%s%s", apiversion, req.URL.Path)
  1434  	router.ServeHTTP(w, req)
  1435  }
  1436  
  1437  func lookupGidByName(nameOrGid string) (int, error) {
  1438  	groupFile, err := user.GetGroupPath()
  1439  	if err != nil {
  1440  		return -1, err
  1441  	}
  1442  	groups, err := user.ParseGroupFileFilter(groupFile, func(g user.Group) bool {
  1443  		return g.Name == nameOrGid || strconv.Itoa(g.Gid) == nameOrGid
  1444  	})
  1445  	if err != nil {
  1446  		return -1, err
  1447  	}
  1448  	if groups != nil && len(groups) > 0 {
  1449  		return groups[0].Gid, nil
  1450  	}
  1451  	gid, err := strconv.Atoi(nameOrGid)
  1452  	if err == nil {
  1453  		log.Warnf("Could not find GID %d", gid)
  1454  		return gid, nil
  1455  	}
  1456  	return -1, fmt.Errorf("Group %s not found", nameOrGid)
  1457  }
  1458  
  1459  func setupTls(cert, key, ca string, l net.Listener) (net.Listener, error) {
  1460  	tlsCert, err := tls.LoadX509KeyPair(cert, key)
  1461  	if err != nil {
  1462  		if os.IsNotExist(err) {
  1463  			return nil, fmt.Errorf("Could not load X509 key pair (%s, %s): %v", cert, key, err)
  1464  		}
  1465  		return nil, fmt.Errorf("Error reading X509 key pair (%s, %s): %q. Make sure the key is encrypted.",
  1466  			cert, key, err)
  1467  	}
  1468  	tlsConfig := &tls.Config{
  1469  		NextProtos:   []string{"http/1.1"},
  1470  		Certificates: []tls.Certificate{tlsCert},
  1471  		// Avoid fallback on insecure SSL protocols
  1472  		MinVersion: tls.VersionTLS10,
  1473  	}
  1474  
  1475  	if ca != "" {
  1476  		certPool := x509.NewCertPool()
  1477  		file, err := ioutil.ReadFile(ca)
  1478  		if err != nil {
  1479  			return nil, fmt.Errorf("Could not read CA certificate: %v", err)
  1480  		}
  1481  		certPool.AppendCertsFromPEM(file)
  1482  		tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
  1483  		tlsConfig.ClientCAs = certPool
  1484  	}
  1485  
  1486  	return tls.NewListener(l, tlsConfig), nil
  1487  }
  1488  
  1489  func newListener(proto, addr string, bufferRequests bool) (net.Listener, error) {
  1490  	if bufferRequests {
  1491  		return listenbuffer.NewListenBuffer(proto, addr, activationLock)
  1492  	}
  1493  
  1494  	return net.Listen(proto, addr)
  1495  }
  1496  
  1497  func changeGroup(addr string, nameOrGid string) error {
  1498  	gid, err := lookupGidByName(nameOrGid)
  1499  	if err != nil {
  1500  		return err
  1501  	}
  1502  
  1503  	log.Debugf("%s group found. gid: %d", nameOrGid, gid)
  1504  	return os.Chown(addr, 0, gid)
  1505  }
  1506  
  1507  func setSocketGroup(addr, group string) error {
  1508  	if group == "" {
  1509  		return nil
  1510  	}
  1511  
  1512  	if err := changeGroup(addr, group); err != nil {
  1513  		if group != "docker" {
  1514  			return err
  1515  		}
  1516  		log.Debugf("Warning: could not chgrp %s to docker: %v", addr, err)
  1517  	}
  1518  
  1519  	return nil
  1520  }
  1521  
  1522  func allocateDaemonPort(addr string) error {
  1523  	host, port, err := net.SplitHostPort(addr)
  1524  	if err != nil {
  1525  		return err
  1526  	}
  1527  
  1528  	intPort, err := strconv.Atoi(port)
  1529  	if err != nil {
  1530  		return err
  1531  	}
  1532  
  1533  	var hostIPs []net.IP
  1534  	if parsedIP := net.ParseIP(host); parsedIP != nil {
  1535  		hostIPs = append(hostIPs, parsedIP)
  1536  	} else if hostIPs, err = net.LookupIP(host); err != nil {
  1537  		return fmt.Errorf("failed to lookup %s address in host specification", host)
  1538  	}
  1539  
  1540  	for _, hostIP := range hostIPs {
  1541  		if _, err := portallocator.RequestPort(hostIP, "tcp", intPort); err != nil {
  1542  			return fmt.Errorf("failed to allocate daemon listening port %d (err: %v)", intPort, err)
  1543  		}
  1544  	}
  1545  	return nil
  1546  }
  1547  
  1548  func setupTcpHttp(addr string, job *engine.Job) (*HttpServer, error) {
  1549  	if !job.GetenvBool("TlsVerify") {
  1550  		log.Infof("/!\\ DON'T BIND ON ANY IP ADDRESS WITHOUT setting -tlsverify IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\")
  1551  	}
  1552  
  1553  	r := createRouter(job.Eng, job.GetenvBool("Logging"), job.GetenvBool("EnableCors"), job.Getenv("CorsHeaders"), job.Getenv("Version"))
  1554  
  1555  	l, err := newListener("tcp", addr, job.GetenvBool("BufferRequests"))
  1556  	if err != nil {
  1557  		return nil, err
  1558  	}
  1559  
  1560  	if err := allocateDaemonPort(addr); err != nil {
  1561  		return nil, err
  1562  	}
  1563  
  1564  	if job.GetenvBool("Tls") || job.GetenvBool("TlsVerify") {
  1565  		var tlsCa string
  1566  		if job.GetenvBool("TlsVerify") {
  1567  			tlsCa = job.Getenv("TlsCa")
  1568  		}
  1569  		l, err = setupTls(job.Getenv("TlsCert"), job.Getenv("TlsKey"), tlsCa, l)
  1570  		if err != nil {
  1571  			return nil, err
  1572  		}
  1573  	}
  1574  	return &HttpServer{&http.Server{Addr: addr, Handler: r}, l}, nil
  1575  }
  1576  
  1577  type Server interface {
  1578  	Serve() error
  1579  	Close() error
  1580  }
  1581  
  1582  // ServeApi loops through all of the protocols sent in to docker and spawns
  1583  // off a go routine to setup a serving http.Server for each.
  1584  func ServeApi(job *engine.Job) engine.Status {
  1585  	if len(job.Args) == 0 {
  1586  		return job.Errorf("usage: %s PROTO://ADDR [PROTO://ADDR ...]", job.Name)
  1587  	}
  1588  	var (
  1589  		protoAddrs = job.Args
  1590  		chErrors   = make(chan error, len(protoAddrs))
  1591  	)
  1592  	activationLock = make(chan struct{})
  1593  
  1594  	for _, protoAddr := range protoAddrs {
  1595  		protoAddrParts := strings.SplitN(protoAddr, "://", 2)
  1596  		if len(protoAddrParts) != 2 {
  1597  			return job.Errorf("usage: %s PROTO://ADDR [PROTO://ADDR ...]", job.Name)
  1598  		}
  1599  		go func() {
  1600  			log.Infof("Listening for HTTP on %s (%s)", protoAddrParts[0], protoAddrParts[1])
  1601  			srv, err := NewServer(protoAddrParts[0], protoAddrParts[1], job)
  1602  			if err != nil {
  1603  				chErrors <- err
  1604  				return
  1605  			}
  1606  			job.Eng.OnShutdown(func() {
  1607  				if err := srv.Close(); err != nil {
  1608  					log.Error(err)
  1609  				}
  1610  			})
  1611  			if err = srv.Serve(); err != nil && strings.Contains(err.Error(), "use of closed network connection") {
  1612  				err = nil
  1613  			}
  1614  			chErrors <- err
  1615  		}()
  1616  	}
  1617  
  1618  	for i := 0; i < len(protoAddrs); i++ {
  1619  		err := <-chErrors
  1620  		if err != nil {
  1621  			return job.Error(err)
  1622  		}
  1623  	}
  1624  
  1625  	return engine.StatusOK
  1626  }