github.com/sbward/docker@v1.4.2-0.20150114010528-c9dab702bed3/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 getContainersLogs(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  
   422  	var (
   423  		inspectJob = eng.Job("container_inspect", vars["name"])
   424  		logsJob    = eng.Job("logs", vars["name"])
   425  		c, err     = inspectJob.Stdout.AddEnv()
   426  	)
   427  	if err != nil {
   428  		return err
   429  	}
   430  	logsJob.Setenv("follow", r.Form.Get("follow"))
   431  	logsJob.Setenv("tail", r.Form.Get("tail"))
   432  	logsJob.Setenv("stdout", r.Form.Get("stdout"))
   433  	logsJob.Setenv("stderr", r.Form.Get("stderr"))
   434  	logsJob.Setenv("timestamps", r.Form.Get("timestamps"))
   435  	// Validate args here, because we can't return not StatusOK after job.Run() call
   436  	stdout, stderr := logsJob.GetenvBool("stdout"), logsJob.GetenvBool("stderr")
   437  	if !(stdout || stderr) {
   438  		return fmt.Errorf("Bad parameters: you must choose at least one stream")
   439  	}
   440  	if err = inspectJob.Run(); err != nil {
   441  		return err
   442  	}
   443  
   444  	var outStream, errStream io.Writer
   445  	outStream = utils.NewWriteFlusher(w)
   446  
   447  	if c.GetSubEnv("Config") != nil && !c.GetSubEnv("Config").GetBool("Tty") && version.GreaterThanOrEqualTo("1.6") {
   448  		errStream = stdcopy.NewStdWriter(outStream, stdcopy.Stderr)
   449  		outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout)
   450  	} else {
   451  		errStream = outStream
   452  	}
   453  
   454  	logsJob.Stdout.Add(outStream)
   455  	logsJob.Stderr.Set(errStream)
   456  	if err := logsJob.Run(); err != nil {
   457  		fmt.Fprintf(outStream, "Error running logs job: %s\n", err)
   458  	}
   459  	return nil
   460  }
   461  
   462  func postImagesTag(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   463  	if err := parseForm(r); err != nil {
   464  		return err
   465  	}
   466  	if vars == nil {
   467  		return fmt.Errorf("Missing parameter")
   468  	}
   469  
   470  	job := eng.Job("tag", vars["name"], r.Form.Get("repo"), r.Form.Get("tag"))
   471  	job.Setenv("force", r.Form.Get("force"))
   472  	if err := job.Run(); err != nil {
   473  		return err
   474  	}
   475  	w.WriteHeader(http.StatusCreated)
   476  	return nil
   477  }
   478  
   479  func postCommit(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   480  	if err := parseForm(r); err != nil {
   481  		return err
   482  	}
   483  	var (
   484  		config       engine.Env
   485  		env          engine.Env
   486  		job          = eng.Job("commit", r.Form.Get("container"))
   487  		stdoutBuffer = bytes.NewBuffer(nil)
   488  	)
   489  
   490  	if err := checkForJson(r); err != nil {
   491  		return err
   492  	}
   493  
   494  	if err := config.Decode(r.Body); err != nil {
   495  		log.Errorf("%s", err)
   496  	}
   497  
   498  	if r.FormValue("pause") == "" && version.GreaterThanOrEqualTo("1.13") {
   499  		job.Setenv("pause", "1")
   500  	} else {
   501  		job.Setenv("pause", r.FormValue("pause"))
   502  	}
   503  
   504  	job.Setenv("repo", r.Form.Get("repo"))
   505  	job.Setenv("tag", r.Form.Get("tag"))
   506  	job.Setenv("author", r.Form.Get("author"))
   507  	job.Setenv("comment", r.Form.Get("comment"))
   508  	job.SetenvSubEnv("config", &config)
   509  
   510  	job.Stdout.Add(stdoutBuffer)
   511  	if err := job.Run(); err != nil {
   512  		return err
   513  	}
   514  	env.Set("Id", engine.Tail(stdoutBuffer, 1))
   515  	return writeJSON(w, http.StatusCreated, env)
   516  }
   517  
   518  // Creates an image from Pull or from Import
   519  func postImagesCreate(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   520  	if err := parseForm(r); err != nil {
   521  		return err
   522  	}
   523  
   524  	var (
   525  		image = r.Form.Get("fromImage")
   526  		repo  = r.Form.Get("repo")
   527  		tag   = r.Form.Get("tag")
   528  		job   *engine.Job
   529  	)
   530  	authEncoded := r.Header.Get("X-Registry-Auth")
   531  	authConfig := &registry.AuthConfig{}
   532  	if authEncoded != "" {
   533  		authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
   534  		if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
   535  			// for a pull it is not an error if no auth was given
   536  			// to increase compatibility with the existing api it is defaulting to be empty
   537  			authConfig = &registry.AuthConfig{}
   538  		}
   539  	}
   540  	if image != "" { //pull
   541  		if tag == "" {
   542  			image, tag = parsers.ParseRepositoryTag(image)
   543  		}
   544  		metaHeaders := map[string][]string{}
   545  		for k, v := range r.Header {
   546  			if strings.HasPrefix(k, "X-Meta-") {
   547  				metaHeaders[k] = v
   548  			}
   549  		}
   550  		job = eng.Job("pull", image, tag)
   551  		job.SetenvBool("parallel", version.GreaterThan("1.3"))
   552  		job.SetenvJson("metaHeaders", metaHeaders)
   553  		job.SetenvJson("authConfig", authConfig)
   554  	} else { //import
   555  		if tag == "" {
   556  			repo, tag = parsers.ParseRepositoryTag(repo)
   557  		}
   558  		job = eng.Job("import", r.Form.Get("fromSrc"), repo, tag)
   559  		job.Stdin.Add(r.Body)
   560  	}
   561  
   562  	if version.GreaterThan("1.0") {
   563  		job.SetenvBool("json", true)
   564  		streamJSON(job, w, true)
   565  	} else {
   566  		job.Stdout.Add(utils.NewWriteFlusher(w))
   567  	}
   568  	if err := job.Run(); err != nil {
   569  		if !job.Stdout.Used() {
   570  			return err
   571  		}
   572  		sf := utils.NewStreamFormatter(version.GreaterThan("1.0"))
   573  		w.Write(sf.FormatError(err))
   574  	}
   575  
   576  	return nil
   577  }
   578  
   579  func getImagesSearch(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   580  	if err := parseForm(r); err != nil {
   581  		return err
   582  	}
   583  	var (
   584  		authEncoded = r.Header.Get("X-Registry-Auth")
   585  		authConfig  = &registry.AuthConfig{}
   586  		metaHeaders = map[string][]string{}
   587  	)
   588  
   589  	if authEncoded != "" {
   590  		authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
   591  		if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
   592  			// for a search it is not an error if no auth was given
   593  			// to increase compatibility with the existing api it is defaulting to be empty
   594  			authConfig = &registry.AuthConfig{}
   595  		}
   596  	}
   597  	for k, v := range r.Header {
   598  		if strings.HasPrefix(k, "X-Meta-") {
   599  			metaHeaders[k] = v
   600  		}
   601  	}
   602  
   603  	var job = eng.Job("search", r.Form.Get("term"))
   604  	job.SetenvJson("metaHeaders", metaHeaders)
   605  	job.SetenvJson("authConfig", authConfig)
   606  	streamJSON(job, w, false)
   607  
   608  	return job.Run()
   609  }
   610  
   611  func postImagesPush(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   612  	if vars == nil {
   613  		return fmt.Errorf("Missing parameter")
   614  	}
   615  
   616  	metaHeaders := map[string][]string{}
   617  	for k, v := range r.Header {
   618  		if strings.HasPrefix(k, "X-Meta-") {
   619  			metaHeaders[k] = v
   620  		}
   621  	}
   622  	if err := parseForm(r); err != nil {
   623  		return err
   624  	}
   625  	authConfig := &registry.AuthConfig{}
   626  
   627  	authEncoded := r.Header.Get("X-Registry-Auth")
   628  	if authEncoded != "" {
   629  		// the new format is to handle the authConfig as a header
   630  		authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
   631  		if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
   632  			// to increase compatibility to existing api it is defaulting to be empty
   633  			authConfig = &registry.AuthConfig{}
   634  		}
   635  	} else {
   636  		// the old format is supported for compatibility if there was no authConfig header
   637  		if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil {
   638  			return err
   639  		}
   640  	}
   641  
   642  	job := eng.Job("push", vars["name"])
   643  	job.SetenvJson("metaHeaders", metaHeaders)
   644  	job.SetenvJson("authConfig", authConfig)
   645  	job.Setenv("tag", r.Form.Get("tag"))
   646  	if version.GreaterThan("1.0") {
   647  		job.SetenvBool("json", true)
   648  		streamJSON(job, w, true)
   649  	} else {
   650  		job.Stdout.Add(utils.NewWriteFlusher(w))
   651  	}
   652  
   653  	if err := job.Run(); err != nil {
   654  		if !job.Stdout.Used() {
   655  			return err
   656  		}
   657  		sf := utils.NewStreamFormatter(version.GreaterThan("1.0"))
   658  		w.Write(sf.FormatError(err))
   659  	}
   660  	return nil
   661  }
   662  
   663  func getImagesGet(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   664  	if vars == nil {
   665  		return fmt.Errorf("Missing parameter")
   666  	}
   667  	if err := parseForm(r); err != nil {
   668  		return err
   669  	}
   670  	if version.GreaterThan("1.0") {
   671  		w.Header().Set("Content-Type", "application/x-tar")
   672  	}
   673  	var job *engine.Job
   674  	if name, ok := vars["name"]; ok {
   675  		job = eng.Job("image_export", name)
   676  	} else {
   677  		job = eng.Job("image_export", r.Form["names"]...)
   678  	}
   679  	job.Stdout.Add(w)
   680  	return job.Run()
   681  }
   682  
   683  func postImagesLoad(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   684  	job := eng.Job("load")
   685  	job.Stdin.Add(r.Body)
   686  	return job.Run()
   687  }
   688  
   689  func postContainersCreate(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   690  	if err := parseForm(r); err != nil {
   691  		return nil
   692  	}
   693  	var (
   694  		out          engine.Env
   695  		job          = eng.Job("create", r.Form.Get("name"))
   696  		outWarnings  []string
   697  		stdoutBuffer = bytes.NewBuffer(nil)
   698  		warnings     = bytes.NewBuffer(nil)
   699  	)
   700  
   701  	if err := checkForJson(r); err != nil {
   702  		return err
   703  	}
   704  
   705  	if err := job.DecodeEnv(r.Body); err != nil {
   706  		return err
   707  	}
   708  	// Read container ID from the first line of stdout
   709  	job.Stdout.Add(stdoutBuffer)
   710  	// Read warnings from stderr
   711  	job.Stderr.Add(warnings)
   712  	if err := job.Run(); err != nil {
   713  		return err
   714  	}
   715  	// Parse warnings from stderr
   716  	scanner := bufio.NewScanner(warnings)
   717  	for scanner.Scan() {
   718  		outWarnings = append(outWarnings, scanner.Text())
   719  	}
   720  	out.Set("Id", engine.Tail(stdoutBuffer, 1))
   721  	out.SetList("Warnings", outWarnings)
   722  
   723  	return writeJSON(w, http.StatusCreated, out)
   724  }
   725  
   726  func postContainersRestart(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   727  	if err := parseForm(r); err != nil {
   728  		return err
   729  	}
   730  	if vars == nil {
   731  		return fmt.Errorf("Missing parameter")
   732  	}
   733  	job := eng.Job("restart", vars["name"])
   734  	job.Setenv("t", r.Form.Get("t"))
   735  	if err := job.Run(); err != nil {
   736  		return err
   737  	}
   738  	w.WriteHeader(http.StatusNoContent)
   739  	return nil
   740  }
   741  
   742  func postContainerRename(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   743  	if err := parseForm(r); err != nil {
   744  		return err
   745  	}
   746  	if vars == nil {
   747  		return fmt.Errorf("Missing parameter")
   748  	}
   749  
   750  	newName := r.URL.Query().Get("name")
   751  	job := eng.Job("container_rename", vars["name"], newName)
   752  	job.Setenv("t", r.Form.Get("t"))
   753  	if err := job.Run(); err != nil {
   754  		return err
   755  	}
   756  	w.WriteHeader(http.StatusNoContent)
   757  	return nil
   758  }
   759  
   760  func deleteContainers(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   761  	if err := parseForm(r); err != nil {
   762  		return err
   763  	}
   764  	if vars == nil {
   765  		return fmt.Errorf("Missing parameter")
   766  	}
   767  	job := eng.Job("rm", vars["name"])
   768  
   769  	job.Setenv("forceRemove", r.Form.Get("force"))
   770  
   771  	job.Setenv("removeVolume", r.Form.Get("v"))
   772  	job.Setenv("removeLink", r.Form.Get("link"))
   773  	if err := job.Run(); err != nil {
   774  		return err
   775  	}
   776  	w.WriteHeader(http.StatusNoContent)
   777  	return nil
   778  }
   779  
   780  func deleteImages(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   781  	if err := parseForm(r); err != nil {
   782  		return err
   783  	}
   784  	if vars == nil {
   785  		return fmt.Errorf("Missing parameter")
   786  	}
   787  	var job = eng.Job("image_delete", vars["name"])
   788  	streamJSON(job, w, false)
   789  	job.Setenv("force", r.Form.Get("force"))
   790  	job.Setenv("noprune", r.Form.Get("noprune"))
   791  
   792  	return job.Run()
   793  }
   794  
   795  func postContainersStart(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   796  	if vars == nil {
   797  		return fmt.Errorf("Missing parameter")
   798  	}
   799  	var (
   800  		name = vars["name"]
   801  		job  = eng.Job("start", name)
   802  	)
   803  
   804  	// If contentLength is -1, we can assumed chunked encoding
   805  	// or more technically that the length is unknown
   806  	// http://golang.org/src/pkg/net/http/request.go#L139
   807  	// net/http otherwise seems to swallow any headers related to chunked encoding
   808  	// including r.TransferEncoding
   809  	// allow a nil body for backwards compatibility
   810  	if r.Body != nil && (r.ContentLength > 0 || r.ContentLength == -1) {
   811  		if err := checkForJson(r); err != nil {
   812  			return err
   813  		}
   814  
   815  		if err := job.DecodeEnv(r.Body); err != nil {
   816  			return err
   817  		}
   818  	}
   819  
   820  	if err := job.Run(); err != nil {
   821  		if err.Error() == "Container already started" {
   822  			w.WriteHeader(http.StatusNotModified)
   823  			return nil
   824  		}
   825  		return err
   826  	}
   827  	w.WriteHeader(http.StatusNoContent)
   828  	return nil
   829  }
   830  
   831  func postContainersStop(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   832  	if err := parseForm(r); err != nil {
   833  		return err
   834  	}
   835  	if vars == nil {
   836  		return fmt.Errorf("Missing parameter")
   837  	}
   838  	job := eng.Job("stop", vars["name"])
   839  	job.Setenv("t", r.Form.Get("t"))
   840  	if err := job.Run(); err != nil {
   841  		if err.Error() == "Container already stopped" {
   842  			w.WriteHeader(http.StatusNotModified)
   843  			return nil
   844  		}
   845  		return err
   846  	}
   847  	w.WriteHeader(http.StatusNoContent)
   848  	return nil
   849  }
   850  
   851  func postContainersWait(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   852  	if vars == nil {
   853  		return fmt.Errorf("Missing parameter")
   854  	}
   855  	var (
   856  		env          engine.Env
   857  		stdoutBuffer = bytes.NewBuffer(nil)
   858  		job          = eng.Job("wait", vars["name"])
   859  	)
   860  	job.Stdout.Add(stdoutBuffer)
   861  	if err := job.Run(); err != nil {
   862  		return err
   863  	}
   864  
   865  	env.Set("StatusCode", engine.Tail(stdoutBuffer, 1))
   866  	return writeJSON(w, http.StatusOK, env)
   867  }
   868  
   869  func postContainersResize(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   870  	if err := parseForm(r); err != nil {
   871  		return err
   872  	}
   873  	if vars == nil {
   874  		return fmt.Errorf("Missing parameter")
   875  	}
   876  	if err := eng.Job("resize", vars["name"], r.Form.Get("h"), r.Form.Get("w")).Run(); err != nil {
   877  		return err
   878  	}
   879  	return nil
   880  }
   881  
   882  func postContainersAttach(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  
   890  	var (
   891  		job    = eng.Job("container_inspect", vars["name"])
   892  		c, err = job.Stdout.AddEnv()
   893  	)
   894  	if err != nil {
   895  		return err
   896  	}
   897  	if err = job.Run(); err != nil {
   898  		return err
   899  	}
   900  
   901  	inStream, outStream, err := hijackServer(w)
   902  	if err != nil {
   903  		return err
   904  	}
   905  	defer closeStreams(inStream, outStream)
   906  
   907  	var errStream io.Writer
   908  
   909  	if _, ok := r.Header["Upgrade"]; ok {
   910  		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")
   911  	} else {
   912  		fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
   913  	}
   914  
   915  	if c.GetSubEnv("Config") != nil && !c.GetSubEnv("Config").GetBool("Tty") && version.GreaterThanOrEqualTo("1.6") {
   916  		errStream = stdcopy.NewStdWriter(outStream, stdcopy.Stderr)
   917  		outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout)
   918  	} else {
   919  		errStream = outStream
   920  	}
   921  
   922  	job = eng.Job("attach", vars["name"])
   923  	job.Setenv("logs", r.Form.Get("logs"))
   924  	job.Setenv("stream", r.Form.Get("stream"))
   925  	job.Setenv("stdin", r.Form.Get("stdin"))
   926  	job.Setenv("stdout", r.Form.Get("stdout"))
   927  	job.Setenv("stderr", r.Form.Get("stderr"))
   928  	job.Stdin.Add(inStream)
   929  	job.Stdout.Add(outStream)
   930  	job.Stderr.Set(errStream)
   931  	if err := job.Run(); err != nil {
   932  		fmt.Fprintf(outStream, "Error attaching: %s\n", err)
   933  
   934  	}
   935  	return nil
   936  }
   937  
   938  func wsContainersAttach(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   939  	if err := parseForm(r); err != nil {
   940  		return err
   941  	}
   942  	if vars == nil {
   943  		return fmt.Errorf("Missing parameter")
   944  	}
   945  
   946  	if err := eng.Job("container_inspect", vars["name"]).Run(); err != nil {
   947  		return err
   948  	}
   949  
   950  	h := websocket.Handler(func(ws *websocket.Conn) {
   951  		defer ws.Close()
   952  		job := eng.Job("attach", vars["name"])
   953  		job.Setenv("logs", r.Form.Get("logs"))
   954  		job.Setenv("stream", r.Form.Get("stream"))
   955  		job.Setenv("stdin", r.Form.Get("stdin"))
   956  		job.Setenv("stdout", r.Form.Get("stdout"))
   957  		job.Setenv("stderr", r.Form.Get("stderr"))
   958  		job.Stdin.Add(ws)
   959  		job.Stdout.Add(ws)
   960  		job.Stderr.Set(ws)
   961  		if err := job.Run(); err != nil {
   962  			log.Errorf("Error attaching websocket: %s", err)
   963  		}
   964  	})
   965  	h.ServeHTTP(w, r)
   966  
   967  	return nil
   968  }
   969  
   970  func getContainersByName(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   971  	if vars == nil {
   972  		return fmt.Errorf("Missing parameter")
   973  	}
   974  	var job = eng.Job("container_inspect", vars["name"])
   975  	if version.LessThan("1.12") {
   976  		job.SetenvBool("raw", true)
   977  	}
   978  	streamJSON(job, w, false)
   979  	return job.Run()
   980  }
   981  
   982  func getExecByID(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   983  	if vars == nil {
   984  		return fmt.Errorf("Missing parameter 'id'")
   985  	}
   986  	var job = eng.Job("execInspect", vars["id"])
   987  	streamJSON(job, w, false)
   988  	return job.Run()
   989  }
   990  
   991  func getImagesByName(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   992  	if vars == nil {
   993  		return fmt.Errorf("Missing parameter")
   994  	}
   995  	var job = eng.Job("image_inspect", vars["name"])
   996  	if version.LessThan("1.12") {
   997  		job.SetenvBool("raw", true)
   998  	}
   999  	streamJSON(job, w, false)
  1000  	return job.Run()
  1001  }
  1002  
  1003  func postBuild(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  1004  	if version.LessThan("1.3") {
  1005  		return fmt.Errorf("Multipart upload for build is no longer supported. Please upgrade your docker client.")
  1006  	}
  1007  	var (
  1008  		authEncoded       = r.Header.Get("X-Registry-Auth")
  1009  		authConfig        = &registry.AuthConfig{}
  1010  		configFileEncoded = r.Header.Get("X-Registry-Config")
  1011  		configFile        = &registry.ConfigFile{}
  1012  		job               = eng.Job("build")
  1013  	)
  1014  
  1015  	// This block can be removed when API versions prior to 1.9 are deprecated.
  1016  	// Both headers will be parsed and sent along to the daemon, but if a non-empty
  1017  	// ConfigFile is present, any value provided as an AuthConfig directly will
  1018  	// be overridden. See BuildFile::CmdFrom for details.
  1019  	if version.LessThan("1.9") && authEncoded != "" {
  1020  		authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
  1021  		if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
  1022  			// for a pull it is not an error if no auth was given
  1023  			// to increase compatibility with the existing api it is defaulting to be empty
  1024  			authConfig = &registry.AuthConfig{}
  1025  		}
  1026  	}
  1027  
  1028  	if configFileEncoded != "" {
  1029  		configFileJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(configFileEncoded))
  1030  		if err := json.NewDecoder(configFileJson).Decode(configFile); err != nil {
  1031  			// for a pull it is not an error if no auth was given
  1032  			// to increase compatibility with the existing api it is defaulting to be empty
  1033  			configFile = &registry.ConfigFile{}
  1034  		}
  1035  	}
  1036  
  1037  	if version.GreaterThanOrEqualTo("1.8") {
  1038  		job.SetenvBool("json", true)
  1039  		streamJSON(job, w, true)
  1040  	} else {
  1041  		job.Stdout.Add(utils.NewWriteFlusher(w))
  1042  	}
  1043  
  1044  	if r.FormValue("forcerm") == "1" && version.GreaterThanOrEqualTo("1.12") {
  1045  		job.Setenv("rm", "1")
  1046  	} else if r.FormValue("rm") == "" && version.GreaterThanOrEqualTo("1.12") {
  1047  		job.Setenv("rm", "1")
  1048  	} else {
  1049  		job.Setenv("rm", r.FormValue("rm"))
  1050  	}
  1051  	if r.FormValue("pull") == "1" && version.GreaterThanOrEqualTo("1.16") {
  1052  		job.Setenv("pull", "1")
  1053  	}
  1054  	job.Stdin.Add(r.Body)
  1055  	job.Setenv("remote", r.FormValue("remote"))
  1056  	job.Setenv("dockerfile", r.FormValue("dockerfile"))
  1057  	job.Setenv("t", r.FormValue("t"))
  1058  	job.Setenv("q", r.FormValue("q"))
  1059  	job.Setenv("nocache", r.FormValue("nocache"))
  1060  	job.Setenv("forcerm", r.FormValue("forcerm"))
  1061  	job.SetenvJson("authConfig", authConfig)
  1062  	job.SetenvJson("configFile", configFile)
  1063  
  1064  	if err := job.Run(); err != nil {
  1065  		if !job.Stdout.Used() {
  1066  			return err
  1067  		}
  1068  		sf := utils.NewStreamFormatter(version.GreaterThanOrEqualTo("1.8"))
  1069  		w.Write(sf.FormatError(err))
  1070  	}
  1071  	return nil
  1072  }
  1073  
  1074  func postContainersCopy(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  1075  	if vars == nil {
  1076  		return fmt.Errorf("Missing parameter")
  1077  	}
  1078  
  1079  	var copyData engine.Env
  1080  
  1081  	if err := checkForJson(r); err != nil {
  1082  		return err
  1083  	}
  1084  
  1085  	if err := copyData.Decode(r.Body); err != nil {
  1086  		return err
  1087  	}
  1088  
  1089  	if copyData.Get("Resource") == "" {
  1090  		return fmt.Errorf("Path cannot be empty")
  1091  	}
  1092  
  1093  	origResource := copyData.Get("Resource")
  1094  
  1095  	if copyData.Get("Resource")[0] == '/' {
  1096  		copyData.Set("Resource", copyData.Get("Resource")[1:])
  1097  	}
  1098  
  1099  	job := eng.Job("container_copy", vars["name"], copyData.Get("Resource"))
  1100  	job.Stdout.Add(w)
  1101  	w.Header().Set("Content-Type", "application/x-tar")
  1102  	if err := job.Run(); err != nil {
  1103  		log.Errorf("%s", err.Error())
  1104  		if strings.Contains(strings.ToLower(err.Error()), "no such container") {
  1105  			w.WriteHeader(http.StatusNotFound)
  1106  		} else if strings.Contains(err.Error(), "no such file or directory") {
  1107  			return fmt.Errorf("Could not find the file %s in container %s", origResource, vars["name"])
  1108  		}
  1109  	}
  1110  	return nil
  1111  }
  1112  
  1113  func postContainerExecCreate(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  1114  	if err := parseForm(r); err != nil {
  1115  		return nil
  1116  	}
  1117  	var (
  1118  		out          engine.Env
  1119  		name         = vars["name"]
  1120  		job          = eng.Job("execCreate", name)
  1121  		stdoutBuffer = bytes.NewBuffer(nil)
  1122  	)
  1123  
  1124  	if err := job.DecodeEnv(r.Body); err != nil {
  1125  		return err
  1126  	}
  1127  
  1128  	job.Stdout.Add(stdoutBuffer)
  1129  	// Register an instance of Exec in container.
  1130  	if err := job.Run(); err != nil {
  1131  		fmt.Fprintf(os.Stderr, "Error setting up exec command in container %s: %s\n", name, err)
  1132  		return err
  1133  	}
  1134  	// Return the ID
  1135  	out.Set("Id", engine.Tail(stdoutBuffer, 1))
  1136  
  1137  	return writeJSON(w, http.StatusCreated, out)
  1138  }
  1139  
  1140  // TODO(vishh): Refactor the code to avoid having to specify stream config as part of both create and start.
  1141  func postContainerExecStart(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  1142  	if err := parseForm(r); err != nil {
  1143  		return nil
  1144  	}
  1145  	var (
  1146  		name             = vars["name"]
  1147  		job              = eng.Job("execStart", name)
  1148  		errOut io.Writer = os.Stderr
  1149  	)
  1150  
  1151  	if err := job.DecodeEnv(r.Body); err != nil {
  1152  		return err
  1153  	}
  1154  	if !job.GetenvBool("Detach") {
  1155  		// Setting up the streaming http interface.
  1156  		inStream, outStream, err := hijackServer(w)
  1157  		if err != nil {
  1158  			return err
  1159  		}
  1160  		defer closeStreams(inStream, outStream)
  1161  
  1162  		var errStream io.Writer
  1163  
  1164  		if _, ok := r.Header["Upgrade"]; ok {
  1165  			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")
  1166  		} else {
  1167  			fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
  1168  		}
  1169  
  1170  		if !job.GetenvBool("Tty") && version.GreaterThanOrEqualTo("1.6") {
  1171  			errStream = stdcopy.NewStdWriter(outStream, stdcopy.Stderr)
  1172  			outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout)
  1173  		} else {
  1174  			errStream = outStream
  1175  		}
  1176  		job.Stdin.Add(inStream)
  1177  		job.Stdout.Add(outStream)
  1178  		job.Stderr.Set(errStream)
  1179  		errOut = outStream
  1180  	}
  1181  	// Now run the user process in container.
  1182  	job.SetCloseIO(false)
  1183  	if err := job.Run(); err != nil {
  1184  		fmt.Fprintf(errOut, "Error starting exec command in container %s: %s\n", name, err)
  1185  		return err
  1186  	}
  1187  	w.WriteHeader(http.StatusNoContent)
  1188  
  1189  	return nil
  1190  }
  1191  
  1192  func postContainerExecResize(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 err
  1195  	}
  1196  	if vars == nil {
  1197  		return fmt.Errorf("Missing parameter")
  1198  	}
  1199  	if err := eng.Job("execResize", vars["name"], r.Form.Get("h"), r.Form.Get("w")).Run(); err != nil {
  1200  		return err
  1201  	}
  1202  	return nil
  1203  }
  1204  
  1205  func optionsHandler(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  1206  	w.WriteHeader(http.StatusOK)
  1207  	return nil
  1208  }
  1209  func writeCorsHeaders(w http.ResponseWriter, r *http.Request) {
  1210  	w.Header().Add("Access-Control-Allow-Origin", "*")
  1211  	w.Header().Add("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, X-Registry-Auth")
  1212  	w.Header().Add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS")
  1213  }
  1214  
  1215  func ping(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  1216  	_, err := w.Write([]byte{'O', 'K'})
  1217  	return err
  1218  }
  1219  
  1220  func makeHttpHandler(eng *engine.Engine, logging bool, localMethod string, localRoute string, handlerFunc HttpApiFunc, enableCors bool, dockerVersion version.Version) http.HandlerFunc {
  1221  	return func(w http.ResponseWriter, r *http.Request) {
  1222  		// log the request
  1223  		log.Debugf("Calling %s %s", localMethod, localRoute)
  1224  
  1225  		if logging {
  1226  			log.Infof("%s %s", r.Method, r.RequestURI)
  1227  		}
  1228  
  1229  		if strings.Contains(r.Header.Get("User-Agent"), "Docker-Client/") {
  1230  			userAgent := strings.Split(r.Header.Get("User-Agent"), "/")
  1231  			if len(userAgent) == 2 && !dockerVersion.Equal(version.Version(userAgent[1])) {
  1232  				log.Debugf("Warning: client and server don't have the same version (client: %s, server: %s)", userAgent[1], dockerVersion)
  1233  			}
  1234  		}
  1235  		version := version.Version(mux.Vars(r)["version"])
  1236  		if version == "" {
  1237  			version = api.APIVERSION
  1238  		}
  1239  		if enableCors {
  1240  			writeCorsHeaders(w, r)
  1241  		}
  1242  
  1243  		if version.GreaterThan(api.APIVERSION) {
  1244  			http.Error(w, fmt.Errorf("client and server don't have same version (client : %s, server: %s)", version, api.APIVERSION).Error(), http.StatusNotFound)
  1245  			return
  1246  		}
  1247  
  1248  		if err := handlerFunc(eng, version, w, r, mux.Vars(r)); err != nil {
  1249  			log.Errorf("Handler for %s %s returned error: %s", localMethod, localRoute, err)
  1250  			httpError(w, err)
  1251  		}
  1252  	}
  1253  }
  1254  
  1255  // Replicated from expvar.go as not public.
  1256  func expvarHandler(w http.ResponseWriter, r *http.Request) {
  1257  	w.Header().Set("Content-Type", "application/json; charset=utf-8")
  1258  	fmt.Fprintf(w, "{\n")
  1259  	first := true
  1260  	expvar.Do(func(kv expvar.KeyValue) {
  1261  		if !first {
  1262  			fmt.Fprintf(w, ",\n")
  1263  		}
  1264  		first = false
  1265  		fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value)
  1266  	})
  1267  	fmt.Fprintf(w, "\n}\n")
  1268  }
  1269  
  1270  func AttachProfiler(router *mux.Router) {
  1271  	router.HandleFunc("/debug/vars", expvarHandler)
  1272  	router.HandleFunc("/debug/pprof/", pprof.Index)
  1273  	router.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
  1274  	router.HandleFunc("/debug/pprof/profile", pprof.Profile)
  1275  	router.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
  1276  	router.HandleFunc("/debug/pprof/block", pprof.Handler("block").ServeHTTP)
  1277  	router.HandleFunc("/debug/pprof/heap", pprof.Handler("heap").ServeHTTP)
  1278  	router.HandleFunc("/debug/pprof/goroutine", pprof.Handler("goroutine").ServeHTTP)
  1279  	router.HandleFunc("/debug/pprof/threadcreate", pprof.Handler("threadcreate").ServeHTTP)
  1280  }
  1281  
  1282  func createRouter(eng *engine.Engine, logging, enableCors bool, dockerVersion string) *mux.Router {
  1283  	r := mux.NewRouter()
  1284  	if os.Getenv("DEBUG") != "" {
  1285  		AttachProfiler(r)
  1286  	}
  1287  	m := map[string]map[string]HttpApiFunc{
  1288  		"GET": {
  1289  			"/_ping":                          ping,
  1290  			"/events":                         getEvents,
  1291  			"/info":                           getInfo,
  1292  			"/version":                        getVersion,
  1293  			"/images/json":                    getImagesJSON,
  1294  			"/images/viz":                     getImagesViz,
  1295  			"/images/search":                  getImagesSearch,
  1296  			"/images/get":                     getImagesGet,
  1297  			"/images/{name:.*}/get":           getImagesGet,
  1298  			"/images/{name:.*}/history":       getImagesHistory,
  1299  			"/images/{name:.*}/json":          getImagesByName,
  1300  			"/containers/ps":                  getContainersJSON,
  1301  			"/containers/json":                getContainersJSON,
  1302  			"/containers/{name:.*}/export":    getContainersExport,
  1303  			"/containers/{name:.*}/changes":   getContainersChanges,
  1304  			"/containers/{name:.*}/json":      getContainersByName,
  1305  			"/containers/{name:.*}/top":       getContainersTop,
  1306  			"/containers/{name:.*}/logs":      getContainersLogs,
  1307  			"/containers/{name:.*}/attach/ws": wsContainersAttach,
  1308  			"/exec/{id:.*}/json":              getExecByID,
  1309  		},
  1310  		"POST": {
  1311  			"/auth":                         postAuth,
  1312  			"/commit":                       postCommit,
  1313  			"/build":                        postBuild,
  1314  			"/images/create":                postImagesCreate,
  1315  			"/images/load":                  postImagesLoad,
  1316  			"/images/{name:.*}/push":        postImagesPush,
  1317  			"/images/{name:.*}/tag":         postImagesTag,
  1318  			"/containers/create":            postContainersCreate,
  1319  			"/containers/{name:.*}/kill":    postContainersKill,
  1320  			"/containers/{name:.*}/pause":   postContainersPause,
  1321  			"/containers/{name:.*}/unpause": postContainersUnpause,
  1322  			"/containers/{name:.*}/restart": postContainersRestart,
  1323  			"/containers/{name:.*}/start":   postContainersStart,
  1324  			"/containers/{name:.*}/stop":    postContainersStop,
  1325  			"/containers/{name:.*}/wait":    postContainersWait,
  1326  			"/containers/{name:.*}/resize":  postContainersResize,
  1327  			"/containers/{name:.*}/attach":  postContainersAttach,
  1328  			"/containers/{name:.*}/copy":    postContainersCopy,
  1329  			"/containers/{name:.*}/exec":    postContainerExecCreate,
  1330  			"/exec/{name:.*}/start":         postContainerExecStart,
  1331  			"/exec/{name:.*}/resize":        postContainerExecResize,
  1332  			"/containers/{name:.*}/rename":  postContainerRename,
  1333  		},
  1334  		"DELETE": {
  1335  			"/containers/{name:.*}": deleteContainers,
  1336  			"/images/{name:.*}":     deleteImages,
  1337  		},
  1338  		"OPTIONS": {
  1339  			"": optionsHandler,
  1340  		},
  1341  	}
  1342  
  1343  	for method, routes := range m {
  1344  		for route, fct := range routes {
  1345  			log.Debugf("Registering %s, %s", method, route)
  1346  			// NOTE: scope issue, make sure the variables are local and won't be changed
  1347  			localRoute := route
  1348  			localFct := fct
  1349  			localMethod := method
  1350  
  1351  			// build the handler function
  1352  			f := makeHttpHandler(eng, logging, localMethod, localRoute, localFct, enableCors, version.Version(dockerVersion))
  1353  
  1354  			// add the new route
  1355  			if localRoute == "" {
  1356  				r.Methods(localMethod).HandlerFunc(f)
  1357  			} else {
  1358  				r.Path("/v{version:[0-9.]+}" + localRoute).Methods(localMethod).HandlerFunc(f)
  1359  				r.Path(localRoute).Methods(localMethod).HandlerFunc(f)
  1360  			}
  1361  		}
  1362  	}
  1363  
  1364  	return r
  1365  }
  1366  
  1367  // ServeRequest processes a single http request to the docker remote api.
  1368  // FIXME: refactor this to be part of Server and not require re-creating a new
  1369  // router each time. This requires first moving ListenAndServe into Server.
  1370  func ServeRequest(eng *engine.Engine, apiversion version.Version, w http.ResponseWriter, req *http.Request) {
  1371  	router := createRouter(eng, false, true, "")
  1372  	// Insert APIVERSION into the request as a convenience
  1373  	req.URL.Path = fmt.Sprintf("/v%s%s", apiversion, req.URL.Path)
  1374  	router.ServeHTTP(w, req)
  1375  }
  1376  
  1377  // serveFd creates an http.Server and sets it up to serve given a socket activated
  1378  // argument.
  1379  func serveFd(addr string, job *engine.Job) error {
  1380  	r := createRouter(job.Eng, job.GetenvBool("Logging"), job.GetenvBool("EnableCors"), job.Getenv("Version"))
  1381  
  1382  	ls, e := systemd.ListenFD(addr)
  1383  	if e != nil {
  1384  		return e
  1385  	}
  1386  
  1387  	chErrors := make(chan error, len(ls))
  1388  
  1389  	// We don't want to start serving on these sockets until the
  1390  	// daemon is initialized and installed. Otherwise required handlers
  1391  	// won't be ready.
  1392  	<-activationLock
  1393  
  1394  	// Since ListenFD will return one or more sockets we have
  1395  	// to create a go func to spawn off multiple serves
  1396  	for i := range ls {
  1397  		listener := ls[i]
  1398  		go func() {
  1399  			httpSrv := http.Server{Handler: r}
  1400  			chErrors <- httpSrv.Serve(listener)
  1401  		}()
  1402  	}
  1403  
  1404  	for i := 0; i < len(ls); i++ {
  1405  		err := <-chErrors
  1406  		if err != nil {
  1407  			return err
  1408  		}
  1409  	}
  1410  
  1411  	return nil
  1412  }
  1413  
  1414  func lookupGidByName(nameOrGid string) (int, error) {
  1415  	groupFile, err := user.GetGroupPath()
  1416  	if err != nil {
  1417  		return -1, err
  1418  	}
  1419  	groups, err := user.ParseGroupFileFilter(groupFile, func(g user.Group) bool {
  1420  		return g.Name == nameOrGid || strconv.Itoa(g.Gid) == nameOrGid
  1421  	})
  1422  	if err != nil {
  1423  		return -1, err
  1424  	}
  1425  	if groups != nil && len(groups) > 0 {
  1426  		return groups[0].Gid, nil
  1427  	}
  1428  	return -1, fmt.Errorf("Group %s not found", nameOrGid)
  1429  }
  1430  
  1431  func setupTls(cert, key, ca string, l net.Listener) (net.Listener, error) {
  1432  	tlsCert, err := tls.LoadX509KeyPair(cert, key)
  1433  	if err != nil {
  1434  		return nil, fmt.Errorf("Couldn't load X509 key pair (%s, %s): %s. Key encrypted?",
  1435  			cert, key, err)
  1436  	}
  1437  	tlsConfig := &tls.Config{
  1438  		NextProtos:   []string{"http/1.1"},
  1439  		Certificates: []tls.Certificate{tlsCert},
  1440  		// Avoid fallback on insecure SSL protocols
  1441  		MinVersion: tls.VersionTLS10,
  1442  	}
  1443  
  1444  	if ca != "" {
  1445  		certPool := x509.NewCertPool()
  1446  		file, err := ioutil.ReadFile(ca)
  1447  		if err != nil {
  1448  			return nil, fmt.Errorf("Couldn't read CA certificate: %s", err)
  1449  		}
  1450  		certPool.AppendCertsFromPEM(file)
  1451  		tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
  1452  		tlsConfig.ClientCAs = certPool
  1453  	}
  1454  
  1455  	return tls.NewListener(l, tlsConfig), nil
  1456  }
  1457  
  1458  func newListener(proto, addr string, bufferRequests bool) (net.Listener, error) {
  1459  	if bufferRequests {
  1460  		return listenbuffer.NewListenBuffer(proto, addr, activationLock)
  1461  	}
  1462  
  1463  	return net.Listen(proto, addr)
  1464  }
  1465  
  1466  func changeGroup(addr string, nameOrGid string) error {
  1467  	gid, err := lookupGidByName(nameOrGid)
  1468  	if err != nil {
  1469  		return err
  1470  	}
  1471  
  1472  	log.Debugf("%s group found. gid: %d", nameOrGid, gid)
  1473  	return os.Chown(addr, 0, gid)
  1474  }
  1475  
  1476  func setSocketGroup(addr, group string) error {
  1477  	if group == "" {
  1478  		return nil
  1479  	}
  1480  
  1481  	if err := changeGroup(addr, group); err != nil {
  1482  		if group != "docker" {
  1483  			return err
  1484  		}
  1485  		log.Debugf("Warning: could not chgrp %s to docker: %v", addr, err)
  1486  	}
  1487  
  1488  	return nil
  1489  }
  1490  
  1491  func setupUnixHttp(addr string, job *engine.Job) (*HttpServer, error) {
  1492  	r := createRouter(job.Eng, job.GetenvBool("Logging"), job.GetenvBool("EnableCors"), job.Getenv("Version"))
  1493  
  1494  	if err := syscall.Unlink(addr); err != nil && !os.IsNotExist(err) {
  1495  		return nil, err
  1496  	}
  1497  	mask := syscall.Umask(0777)
  1498  	defer syscall.Umask(mask)
  1499  
  1500  	l, err := newListener("unix", addr, job.GetenvBool("BufferRequests"))
  1501  	if err != nil {
  1502  		return nil, err
  1503  	}
  1504  
  1505  	if err := setSocketGroup(addr, job.Getenv("SocketGroup")); err != nil {
  1506  		return nil, err
  1507  	}
  1508  
  1509  	if err := os.Chmod(addr, 0660); err != nil {
  1510  		return nil, err
  1511  	}
  1512  
  1513  	return &HttpServer{&http.Server{Addr: addr, Handler: r}, l}, nil
  1514  }
  1515  
  1516  func allocateDaemonPort(addr string) error {
  1517  	host, port, err := net.SplitHostPort(addr)
  1518  	if err != nil {
  1519  		return err
  1520  	}
  1521  
  1522  	intPort, err := strconv.Atoi(port)
  1523  	if err != nil {
  1524  		return err
  1525  	}
  1526  
  1527  	var hostIPs []net.IP
  1528  	if parsedIP := net.ParseIP(host); parsedIP != nil {
  1529  		hostIPs = append(hostIPs, parsedIP)
  1530  	} else if hostIPs, err = net.LookupIP(host); err != nil {
  1531  		return fmt.Errorf("failed to lookup %s address in host specification", host)
  1532  	}
  1533  
  1534  	for _, hostIP := range hostIPs {
  1535  		if _, err := portallocator.RequestPort(hostIP, "tcp", intPort); err != nil {
  1536  			return fmt.Errorf("failed to allocate daemon listening port %d (err: %v)", intPort, err)
  1537  		}
  1538  	}
  1539  	return nil
  1540  }
  1541  
  1542  func setupTcpHttp(addr string, job *engine.Job) (*HttpServer, error) {
  1543  	if !strings.HasPrefix(addr, "127.0.0.1") && !job.GetenvBool("TlsVerify") {
  1544  		log.Infof("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\")
  1545  	}
  1546  
  1547  	r := createRouter(job.Eng, job.GetenvBool("Logging"), job.GetenvBool("EnableCors"), job.Getenv("Version"))
  1548  
  1549  	l, err := newListener("tcp", addr, job.GetenvBool("BufferRequests"))
  1550  	if err != nil {
  1551  		return nil, err
  1552  	}
  1553  
  1554  	if err := allocateDaemonPort(addr); err != nil {
  1555  		return nil, err
  1556  	}
  1557  
  1558  	if job.GetenvBool("Tls") || job.GetenvBool("TlsVerify") {
  1559  		var tlsCa string
  1560  		if job.GetenvBool("TlsVerify") {
  1561  			tlsCa = job.Getenv("TlsCa")
  1562  		}
  1563  		l, err = setupTls(job.Getenv("TlsCert"), job.Getenv("TlsKey"), tlsCa, l)
  1564  		if err != nil {
  1565  			return nil, err
  1566  		}
  1567  	}
  1568  	return &HttpServer{&http.Server{Addr: addr, Handler: r}, l}, nil
  1569  }
  1570  
  1571  // NewServer sets up the required Server and does protocol specific checking.
  1572  func NewServer(proto, addr string, job *engine.Job) (Server, error) {
  1573  	// Basic error and sanity checking
  1574  	switch proto {
  1575  	case "fd":
  1576  		return nil, serveFd(addr, job)
  1577  	case "tcp":
  1578  		return setupTcpHttp(addr, job)
  1579  	case "unix":
  1580  		return setupUnixHttp(addr, job)
  1581  	default:
  1582  		return nil, fmt.Errorf("Invalid protocol format.")
  1583  	}
  1584  }
  1585  
  1586  type Server interface {
  1587  	Serve() error
  1588  	Close() error
  1589  }
  1590  
  1591  // ServeApi loops through all of the protocols sent in to docker and spawns
  1592  // off a go routine to setup a serving http.Server for each.
  1593  func ServeApi(job *engine.Job) engine.Status {
  1594  	if len(job.Args) == 0 {
  1595  		return job.Errorf("usage: %s PROTO://ADDR [PROTO://ADDR ...]", job.Name)
  1596  	}
  1597  	var (
  1598  		protoAddrs = job.Args
  1599  		chErrors   = make(chan error, len(protoAddrs))
  1600  	)
  1601  	activationLock = make(chan struct{})
  1602  
  1603  	for _, protoAddr := range protoAddrs {
  1604  		protoAddrParts := strings.SplitN(protoAddr, "://", 2)
  1605  		if len(protoAddrParts) != 2 {
  1606  			return job.Errorf("usage: %s PROTO://ADDR [PROTO://ADDR ...]", job.Name)
  1607  		}
  1608  		go func() {
  1609  			log.Infof("Listening for HTTP on %s (%s)", protoAddrParts[0], protoAddrParts[1])
  1610  			srv, err := NewServer(protoAddrParts[0], protoAddrParts[1], job)
  1611  			if err != nil {
  1612  				chErrors <- err
  1613  				return
  1614  			}
  1615  			chErrors <- srv.Serve()
  1616  		}()
  1617  	}
  1618  
  1619  	for i := 0; i < len(protoAddrs); i++ {
  1620  		err := <-chErrors
  1621  		if err != nil {
  1622  			return job.Error(err)
  1623  		}
  1624  	}
  1625  
  1626  	return engine.StatusOK
  1627  }
  1628  
  1629  func AcceptConnections(job *engine.Job) engine.Status {
  1630  	// Tell the init daemon we are accepting requests
  1631  	go systemd.SdNotify("READY=1")
  1632  
  1633  	// close the lock so the listeners start accepting connections
  1634  	if activationLock != nil {
  1635  		close(activationLock)
  1636  	}
  1637  
  1638  	return engine.StatusOK
  1639  }