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