github.com/stchris/docker@v1.4.2-0.20150106053530-1510a324dbd5/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 deleteContainers(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  	job := eng.Job("rm", vars["name"])
   750  
   751  	job.Setenv("forceRemove", r.Form.Get("force"))
   752  
   753  	job.Setenv("removeVolume", r.Form.Get("v"))
   754  	job.Setenv("removeLink", r.Form.Get("link"))
   755  	if err := job.Run(); err != nil {
   756  		return err
   757  	}
   758  	w.WriteHeader(http.StatusNoContent)
   759  	return nil
   760  }
   761  
   762  func deleteImages(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   763  	if err := parseForm(r); err != nil {
   764  		return err
   765  	}
   766  	if vars == nil {
   767  		return fmt.Errorf("Missing parameter")
   768  	}
   769  	var job = eng.Job("image_delete", vars["name"])
   770  	streamJSON(job, w, false)
   771  	job.Setenv("force", r.Form.Get("force"))
   772  	job.Setenv("noprune", r.Form.Get("noprune"))
   773  
   774  	return job.Run()
   775  }
   776  
   777  func postContainersStart(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   778  	if vars == nil {
   779  		return fmt.Errorf("Missing parameter")
   780  	}
   781  	var (
   782  		name = vars["name"]
   783  		job  = eng.Job("start", name)
   784  	)
   785  
   786  	// If contentLength is -1, we can assumed chunked encoding
   787  	// or more technically that the length is unknown
   788  	// http://golang.org/src/pkg/net/http/request.go#L139
   789  	// net/http otherwise seems to swallow any headers related to chunked encoding
   790  	// including r.TransferEncoding
   791  	// allow a nil body for backwards compatibility
   792  	if r.Body != nil && (r.ContentLength > 0 || r.ContentLength == -1) {
   793  		if err := checkForJson(r); err != nil {
   794  			return err
   795  		}
   796  
   797  		if err := job.DecodeEnv(r.Body); err != nil {
   798  			return err
   799  		}
   800  	}
   801  
   802  	if err := job.Run(); err != nil {
   803  		if err.Error() == "Container already started" {
   804  			w.WriteHeader(http.StatusNotModified)
   805  			return nil
   806  		}
   807  		return err
   808  	}
   809  	w.WriteHeader(http.StatusNoContent)
   810  	return nil
   811  }
   812  
   813  func postContainersStop(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   814  	if err := parseForm(r); err != nil {
   815  		return err
   816  	}
   817  	if vars == nil {
   818  		return fmt.Errorf("Missing parameter")
   819  	}
   820  	job := eng.Job("stop", vars["name"])
   821  	job.Setenv("t", r.Form.Get("t"))
   822  	if err := job.Run(); err != nil {
   823  		if err.Error() == "Container already stopped" {
   824  			w.WriteHeader(http.StatusNotModified)
   825  			return nil
   826  		}
   827  		return err
   828  	}
   829  	w.WriteHeader(http.StatusNoContent)
   830  	return nil
   831  }
   832  
   833  func postContainersWait(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   834  	if vars == nil {
   835  		return fmt.Errorf("Missing parameter")
   836  	}
   837  	var (
   838  		env          engine.Env
   839  		stdoutBuffer = bytes.NewBuffer(nil)
   840  		job          = eng.Job("wait", vars["name"])
   841  	)
   842  	job.Stdout.Add(stdoutBuffer)
   843  	if err := job.Run(); err != nil {
   844  		return err
   845  	}
   846  
   847  	env.Set("StatusCode", engine.Tail(stdoutBuffer, 1))
   848  	return writeJSON(w, http.StatusOK, env)
   849  }
   850  
   851  func postContainersResize(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   852  	if err := parseForm(r); err != nil {
   853  		return err
   854  	}
   855  	if vars == nil {
   856  		return fmt.Errorf("Missing parameter")
   857  	}
   858  	if err := eng.Job("resize", vars["name"], r.Form.Get("h"), r.Form.Get("w")).Run(); err != nil {
   859  		return err
   860  	}
   861  	return nil
   862  }
   863  
   864  func postContainersAttach(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   865  	if err := parseForm(r); err != nil {
   866  		return err
   867  	}
   868  	if vars == nil {
   869  		return fmt.Errorf("Missing parameter")
   870  	}
   871  
   872  	var (
   873  		job    = eng.Job("container_inspect", vars["name"])
   874  		c, err = job.Stdout.AddEnv()
   875  	)
   876  	if err != nil {
   877  		return err
   878  	}
   879  	if err = job.Run(); err != nil {
   880  		return err
   881  	}
   882  
   883  	inStream, outStream, err := hijackServer(w)
   884  	if err != nil {
   885  		return err
   886  	}
   887  	defer closeStreams(inStream, outStream)
   888  
   889  	var errStream io.Writer
   890  
   891  	if _, ok := r.Header["Upgrade"]; ok {
   892  		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")
   893  	} else {
   894  		fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
   895  	}
   896  
   897  	if c.GetSubEnv("Config") != nil && !c.GetSubEnv("Config").GetBool("Tty") && version.GreaterThanOrEqualTo("1.6") {
   898  		errStream = stdcopy.NewStdWriter(outStream, stdcopy.Stderr)
   899  		outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout)
   900  	} else {
   901  		errStream = outStream
   902  	}
   903  
   904  	job = eng.Job("attach", vars["name"])
   905  	job.Setenv("logs", r.Form.Get("logs"))
   906  	job.Setenv("stream", r.Form.Get("stream"))
   907  	job.Setenv("stdin", r.Form.Get("stdin"))
   908  	job.Setenv("stdout", r.Form.Get("stdout"))
   909  	job.Setenv("stderr", r.Form.Get("stderr"))
   910  	job.Stdin.Add(inStream)
   911  	job.Stdout.Add(outStream)
   912  	job.Stderr.Set(errStream)
   913  	if err := job.Run(); err != nil {
   914  		fmt.Fprintf(outStream, "Error attaching: %s\n", err)
   915  
   916  	}
   917  	return nil
   918  }
   919  
   920  func wsContainersAttach(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   921  	if err := parseForm(r); err != nil {
   922  		return err
   923  	}
   924  	if vars == nil {
   925  		return fmt.Errorf("Missing parameter")
   926  	}
   927  
   928  	if err := eng.Job("container_inspect", vars["name"]).Run(); err != nil {
   929  		return err
   930  	}
   931  
   932  	h := websocket.Handler(func(ws *websocket.Conn) {
   933  		defer ws.Close()
   934  		job := eng.Job("attach", vars["name"])
   935  		job.Setenv("logs", r.Form.Get("logs"))
   936  		job.Setenv("stream", r.Form.Get("stream"))
   937  		job.Setenv("stdin", r.Form.Get("stdin"))
   938  		job.Setenv("stdout", r.Form.Get("stdout"))
   939  		job.Setenv("stderr", r.Form.Get("stderr"))
   940  		job.Stdin.Add(ws)
   941  		job.Stdout.Add(ws)
   942  		job.Stderr.Set(ws)
   943  		if err := job.Run(); err != nil {
   944  			log.Errorf("Error attaching websocket: %s", err)
   945  		}
   946  	})
   947  	h.ServeHTTP(w, r)
   948  
   949  	return nil
   950  }
   951  
   952  func getContainersByName(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   953  	if vars == nil {
   954  		return fmt.Errorf("Missing parameter")
   955  	}
   956  	var job = eng.Job("container_inspect", vars["name"])
   957  	if version.LessThan("1.12") {
   958  		job.SetenvBool("raw", true)
   959  	}
   960  	streamJSON(job, w, false)
   961  	return job.Run()
   962  }
   963  
   964  func getExecByID(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   965  	if vars == nil {
   966  		return fmt.Errorf("Missing parameter 'id'")
   967  	}
   968  	var job = eng.Job("execInspect", vars["id"])
   969  	streamJSON(job, w, false)
   970  	return job.Run()
   971  }
   972  
   973  func getImagesByName(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   974  	if vars == nil {
   975  		return fmt.Errorf("Missing parameter")
   976  	}
   977  	var job = eng.Job("image_inspect", vars["name"])
   978  	if version.LessThan("1.12") {
   979  		job.SetenvBool("raw", true)
   980  	}
   981  	streamJSON(job, w, false)
   982  	return job.Run()
   983  }
   984  
   985  func postBuild(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   986  	if version.LessThan("1.3") {
   987  		return fmt.Errorf("Multipart upload for build is no longer supported. Please upgrade your docker client.")
   988  	}
   989  	var (
   990  		authEncoded       = r.Header.Get("X-Registry-Auth")
   991  		authConfig        = &registry.AuthConfig{}
   992  		configFileEncoded = r.Header.Get("X-Registry-Config")
   993  		configFile        = &registry.ConfigFile{}
   994  		job               = eng.Job("build")
   995  	)
   996  
   997  	// This block can be removed when API versions prior to 1.9 are deprecated.
   998  	// Both headers will be parsed and sent along to the daemon, but if a non-empty
   999  	// ConfigFile is present, any value provided as an AuthConfig directly will
  1000  	// be overridden. See BuildFile::CmdFrom for details.
  1001  	if version.LessThan("1.9") && authEncoded != "" {
  1002  		authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
  1003  		if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
  1004  			// for a pull it is not an error if no auth was given
  1005  			// to increase compatibility with the existing api it is defaulting to be empty
  1006  			authConfig = &registry.AuthConfig{}
  1007  		}
  1008  	}
  1009  
  1010  	if configFileEncoded != "" {
  1011  		configFileJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(configFileEncoded))
  1012  		if err := json.NewDecoder(configFileJson).Decode(configFile); err != nil {
  1013  			// for a pull it is not an error if no auth was given
  1014  			// to increase compatibility with the existing api it is defaulting to be empty
  1015  			configFile = &registry.ConfigFile{}
  1016  		}
  1017  	}
  1018  
  1019  	if version.GreaterThanOrEqualTo("1.8") {
  1020  		job.SetenvBool("json", true)
  1021  		streamJSON(job, w, true)
  1022  	} else {
  1023  		job.Stdout.Add(utils.NewWriteFlusher(w))
  1024  	}
  1025  
  1026  	if r.FormValue("forcerm") == "1" && version.GreaterThanOrEqualTo("1.12") {
  1027  		job.Setenv("rm", "1")
  1028  	} else if r.FormValue("rm") == "" && version.GreaterThanOrEqualTo("1.12") {
  1029  		job.Setenv("rm", "1")
  1030  	} else {
  1031  		job.Setenv("rm", r.FormValue("rm"))
  1032  	}
  1033  	if r.FormValue("pull") == "1" && version.GreaterThanOrEqualTo("1.16") {
  1034  		job.Setenv("pull", "1")
  1035  	}
  1036  	job.Stdin.Add(r.Body)
  1037  	job.Setenv("remote", r.FormValue("remote"))
  1038  	job.Setenv("t", r.FormValue("t"))
  1039  	job.Setenv("q", r.FormValue("q"))
  1040  	job.Setenv("nocache", r.FormValue("nocache"))
  1041  	job.Setenv("forcerm", r.FormValue("forcerm"))
  1042  	job.SetenvJson("authConfig", authConfig)
  1043  	job.SetenvJson("configFile", configFile)
  1044  
  1045  	if err := job.Run(); err != nil {
  1046  		if !job.Stdout.Used() {
  1047  			return err
  1048  		}
  1049  		sf := utils.NewStreamFormatter(version.GreaterThanOrEqualTo("1.8"))
  1050  		w.Write(sf.FormatError(err))
  1051  	}
  1052  	return nil
  1053  }
  1054  
  1055  func postContainersCopy(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  1056  	if vars == nil {
  1057  		return fmt.Errorf("Missing parameter")
  1058  	}
  1059  
  1060  	var copyData engine.Env
  1061  
  1062  	if err := checkForJson(r); err != nil {
  1063  		return err
  1064  	}
  1065  
  1066  	if err := copyData.Decode(r.Body); err != nil {
  1067  		return err
  1068  	}
  1069  
  1070  	if copyData.Get("Resource") == "" {
  1071  		return fmt.Errorf("Path cannot be empty")
  1072  	}
  1073  
  1074  	origResource := copyData.Get("Resource")
  1075  
  1076  	if copyData.Get("Resource")[0] == '/' {
  1077  		copyData.Set("Resource", copyData.Get("Resource")[1:])
  1078  	}
  1079  
  1080  	job := eng.Job("container_copy", vars["name"], copyData.Get("Resource"))
  1081  	job.Stdout.Add(w)
  1082  	w.Header().Set("Content-Type", "application/x-tar")
  1083  	if err := job.Run(); err != nil {
  1084  		log.Errorf("%s", err.Error())
  1085  		if strings.Contains(strings.ToLower(err.Error()), "no such container") {
  1086  			w.WriteHeader(http.StatusNotFound)
  1087  		} else if strings.Contains(err.Error(), "no such file or directory") {
  1088  			return fmt.Errorf("Could not find the file %s in container %s", origResource, vars["name"])
  1089  		}
  1090  	}
  1091  	return nil
  1092  }
  1093  
  1094  func postContainerExecCreate(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  1095  	if err := parseForm(r); err != nil {
  1096  		return nil
  1097  	}
  1098  	var (
  1099  		out          engine.Env
  1100  		name         = vars["name"]
  1101  		job          = eng.Job("execCreate", name)
  1102  		stdoutBuffer = bytes.NewBuffer(nil)
  1103  	)
  1104  
  1105  	if err := job.DecodeEnv(r.Body); err != nil {
  1106  		return err
  1107  	}
  1108  
  1109  	job.Stdout.Add(stdoutBuffer)
  1110  	// Register an instance of Exec in container.
  1111  	if err := job.Run(); err != nil {
  1112  		fmt.Fprintf(os.Stderr, "Error setting up exec command in container %s: %s\n", name, err)
  1113  		return err
  1114  	}
  1115  	// Return the ID
  1116  	out.Set("Id", engine.Tail(stdoutBuffer, 1))
  1117  
  1118  	return writeJSON(w, http.StatusCreated, out)
  1119  }
  1120  
  1121  // TODO(vishh): Refactor the code to avoid having to specify stream config as part of both create and start.
  1122  func postContainerExecStart(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  1123  	if err := parseForm(r); err != nil {
  1124  		return nil
  1125  	}
  1126  	var (
  1127  		name             = vars["name"]
  1128  		job              = eng.Job("execStart", name)
  1129  		errOut io.Writer = os.Stderr
  1130  	)
  1131  
  1132  	if err := job.DecodeEnv(r.Body); err != nil {
  1133  		return err
  1134  	}
  1135  	if !job.GetenvBool("Detach") {
  1136  		// Setting up the streaming http interface.
  1137  		inStream, outStream, err := hijackServer(w)
  1138  		if err != nil {
  1139  			return err
  1140  		}
  1141  		defer closeStreams(inStream, outStream)
  1142  
  1143  		var errStream io.Writer
  1144  
  1145  		if _, ok := r.Header["Upgrade"]; ok {
  1146  			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")
  1147  		} else {
  1148  			fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
  1149  		}
  1150  
  1151  		if !job.GetenvBool("Tty") && version.GreaterThanOrEqualTo("1.6") {
  1152  			errStream = stdcopy.NewStdWriter(outStream, stdcopy.Stderr)
  1153  			outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout)
  1154  		} else {
  1155  			errStream = outStream
  1156  		}
  1157  		job.Stdin.Add(inStream)
  1158  		job.Stdout.Add(outStream)
  1159  		job.Stderr.Set(errStream)
  1160  		errOut = outStream
  1161  	}
  1162  	// Now run the user process in container.
  1163  	job.SetCloseIO(false)
  1164  	if err := job.Run(); err != nil {
  1165  		fmt.Fprintf(errOut, "Error starting exec command in container %s: %s\n", name, err)
  1166  		return err
  1167  	}
  1168  	w.WriteHeader(http.StatusNoContent)
  1169  
  1170  	return nil
  1171  }
  1172  
  1173  func postContainerExecResize(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  1174  	if err := parseForm(r); err != nil {
  1175  		return err
  1176  	}
  1177  	if vars == nil {
  1178  		return fmt.Errorf("Missing parameter")
  1179  	}
  1180  	if err := eng.Job("execResize", vars["name"], r.Form.Get("h"), r.Form.Get("w")).Run(); err != nil {
  1181  		return err
  1182  	}
  1183  	return nil
  1184  }
  1185  
  1186  func optionsHandler(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  1187  	w.WriteHeader(http.StatusOK)
  1188  	return nil
  1189  }
  1190  func writeCorsHeaders(w http.ResponseWriter, r *http.Request) {
  1191  	w.Header().Add("Access-Control-Allow-Origin", "*")
  1192  	w.Header().Add("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, X-Registry-Auth")
  1193  	w.Header().Add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS")
  1194  }
  1195  
  1196  func ping(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  1197  	_, err := w.Write([]byte{'O', 'K'})
  1198  	return err
  1199  }
  1200  
  1201  func makeHttpHandler(eng *engine.Engine, logging bool, localMethod string, localRoute string, handlerFunc HttpApiFunc, enableCors bool, dockerVersion version.Version) http.HandlerFunc {
  1202  	return func(w http.ResponseWriter, r *http.Request) {
  1203  		// log the request
  1204  		log.Debugf("Calling %s %s", localMethod, localRoute)
  1205  
  1206  		if logging {
  1207  			log.Infof("%s %s", r.Method, r.RequestURI)
  1208  		}
  1209  
  1210  		if strings.Contains(r.Header.Get("User-Agent"), "Docker-Client/") {
  1211  			userAgent := strings.Split(r.Header.Get("User-Agent"), "/")
  1212  			if len(userAgent) == 2 && !dockerVersion.Equal(version.Version(userAgent[1])) {
  1213  				log.Debugf("Warning: client and server don't have the same version (client: %s, server: %s)", userAgent[1], dockerVersion)
  1214  			}
  1215  		}
  1216  		version := version.Version(mux.Vars(r)["version"])
  1217  		if version == "" {
  1218  			version = api.APIVERSION
  1219  		}
  1220  		if enableCors {
  1221  			writeCorsHeaders(w, r)
  1222  		}
  1223  
  1224  		if version.GreaterThan(api.APIVERSION) {
  1225  			http.Error(w, fmt.Errorf("client and server don't have same version (client : %s, server: %s)", version, api.APIVERSION).Error(), http.StatusNotFound)
  1226  			return
  1227  		}
  1228  
  1229  		if err := handlerFunc(eng, version, w, r, mux.Vars(r)); err != nil {
  1230  			log.Errorf("Handler for %s %s returned error: %s", localMethod, localRoute, err)
  1231  			httpError(w, err)
  1232  		}
  1233  	}
  1234  }
  1235  
  1236  // Replicated from expvar.go as not public.
  1237  func expvarHandler(w http.ResponseWriter, r *http.Request) {
  1238  	w.Header().Set("Content-Type", "application/json; charset=utf-8")
  1239  	fmt.Fprintf(w, "{\n")
  1240  	first := true
  1241  	expvar.Do(func(kv expvar.KeyValue) {
  1242  		if !first {
  1243  			fmt.Fprintf(w, ",\n")
  1244  		}
  1245  		first = false
  1246  		fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value)
  1247  	})
  1248  	fmt.Fprintf(w, "\n}\n")
  1249  }
  1250  
  1251  func AttachProfiler(router *mux.Router) {
  1252  	router.HandleFunc("/debug/vars", expvarHandler)
  1253  	router.HandleFunc("/debug/pprof/", pprof.Index)
  1254  	router.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
  1255  	router.HandleFunc("/debug/pprof/profile", pprof.Profile)
  1256  	router.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
  1257  	router.HandleFunc("/debug/pprof/block", pprof.Handler("block").ServeHTTP)
  1258  	router.HandleFunc("/debug/pprof/heap", pprof.Handler("heap").ServeHTTP)
  1259  	router.HandleFunc("/debug/pprof/goroutine", pprof.Handler("goroutine").ServeHTTP)
  1260  	router.HandleFunc("/debug/pprof/threadcreate", pprof.Handler("threadcreate").ServeHTTP)
  1261  }
  1262  
  1263  func createRouter(eng *engine.Engine, logging, enableCors bool, dockerVersion string) (*mux.Router, error) {
  1264  	r := mux.NewRouter()
  1265  	if os.Getenv("DEBUG") != "" {
  1266  		AttachProfiler(r)
  1267  	}
  1268  	m := map[string]map[string]HttpApiFunc{
  1269  		"GET": {
  1270  			"/_ping":                          ping,
  1271  			"/events":                         getEvents,
  1272  			"/info":                           getInfo,
  1273  			"/version":                        getVersion,
  1274  			"/images/json":                    getImagesJSON,
  1275  			"/images/viz":                     getImagesViz,
  1276  			"/images/search":                  getImagesSearch,
  1277  			"/images/get":                     getImagesGet,
  1278  			"/images/{name:.*}/get":           getImagesGet,
  1279  			"/images/{name:.*}/history":       getImagesHistory,
  1280  			"/images/{name:.*}/json":          getImagesByName,
  1281  			"/containers/ps":                  getContainersJSON,
  1282  			"/containers/json":                getContainersJSON,
  1283  			"/containers/{name:.*}/export":    getContainersExport,
  1284  			"/containers/{name:.*}/changes":   getContainersChanges,
  1285  			"/containers/{name:.*}/json":      getContainersByName,
  1286  			"/containers/{name:.*}/top":       getContainersTop,
  1287  			"/containers/{name:.*}/logs":      getContainersLogs,
  1288  			"/containers/{name:.*}/attach/ws": wsContainersAttach,
  1289  			"/exec/{id:.*}/json":              getExecByID,
  1290  		},
  1291  		"POST": {
  1292  			"/auth":                         postAuth,
  1293  			"/commit":                       postCommit,
  1294  			"/build":                        postBuild,
  1295  			"/images/create":                postImagesCreate,
  1296  			"/images/load":                  postImagesLoad,
  1297  			"/images/{name:.*}/push":        postImagesPush,
  1298  			"/images/{name:.*}/tag":         postImagesTag,
  1299  			"/containers/create":            postContainersCreate,
  1300  			"/containers/{name:.*}/kill":    postContainersKill,
  1301  			"/containers/{name:.*}/pause":   postContainersPause,
  1302  			"/containers/{name:.*}/unpause": postContainersUnpause,
  1303  			"/containers/{name:.*}/restart": postContainersRestart,
  1304  			"/containers/{name:.*}/start":   postContainersStart,
  1305  			"/containers/{name:.*}/stop":    postContainersStop,
  1306  			"/containers/{name:.*}/wait":    postContainersWait,
  1307  			"/containers/{name:.*}/resize":  postContainersResize,
  1308  			"/containers/{name:.*}/attach":  postContainersAttach,
  1309  			"/containers/{name:.*}/copy":    postContainersCopy,
  1310  			"/containers/{name:.*}/exec":    postContainerExecCreate,
  1311  			"/exec/{name:.*}/start":         postContainerExecStart,
  1312  			"/exec/{name:.*}/resize":        postContainerExecResize,
  1313  		},
  1314  		"DELETE": {
  1315  			"/containers/{name:.*}": deleteContainers,
  1316  			"/images/{name:.*}":     deleteImages,
  1317  		},
  1318  		"OPTIONS": {
  1319  			"": optionsHandler,
  1320  		},
  1321  	}
  1322  
  1323  	for method, routes := range m {
  1324  		for route, fct := range routes {
  1325  			log.Debugf("Registering %s, %s", method, route)
  1326  			// NOTE: scope issue, make sure the variables are local and won't be changed
  1327  			localRoute := route
  1328  			localFct := fct
  1329  			localMethod := method
  1330  
  1331  			// build the handler function
  1332  			f := makeHttpHandler(eng, logging, localMethod, localRoute, localFct, enableCors, version.Version(dockerVersion))
  1333  
  1334  			// add the new route
  1335  			if localRoute == "" {
  1336  				r.Methods(localMethod).HandlerFunc(f)
  1337  			} else {
  1338  				r.Path("/v{version:[0-9.]+}" + localRoute).Methods(localMethod).HandlerFunc(f)
  1339  				r.Path(localRoute).Methods(localMethod).HandlerFunc(f)
  1340  			}
  1341  		}
  1342  	}
  1343  
  1344  	return r, nil
  1345  }
  1346  
  1347  // ServeRequest processes a single http request to the docker remote api.
  1348  // FIXME: refactor this to be part of Server and not require re-creating a new
  1349  // router each time. This requires first moving ListenAndServe into Server.
  1350  func ServeRequest(eng *engine.Engine, apiversion version.Version, w http.ResponseWriter, req *http.Request) error {
  1351  	router, err := createRouter(eng, false, true, "")
  1352  	if err != nil {
  1353  		return err
  1354  	}
  1355  	// Insert APIVERSION into the request as a convenience
  1356  	req.URL.Path = fmt.Sprintf("/v%s%s", apiversion, req.URL.Path)
  1357  	router.ServeHTTP(w, req)
  1358  	return nil
  1359  }
  1360  
  1361  // serveFd creates an http.Server and sets it up to serve given a socket activated
  1362  // argument.
  1363  func serveFd(addr string, job *engine.Job) error {
  1364  	r, err := createRouter(job.Eng, job.GetenvBool("Logging"), job.GetenvBool("EnableCors"), job.Getenv("Version"))
  1365  	if err != nil {
  1366  		return err
  1367  	}
  1368  
  1369  	ls, e := systemd.ListenFD(addr)
  1370  	if e != nil {
  1371  		return e
  1372  	}
  1373  
  1374  	chErrors := make(chan error, len(ls))
  1375  
  1376  	// We don't want to start serving on these sockets until the
  1377  	// daemon is initialized and installed. Otherwise required handlers
  1378  	// won't be ready.
  1379  	<-activationLock
  1380  
  1381  	// Since ListenFD will return one or more sockets we have
  1382  	// to create a go func to spawn off multiple serves
  1383  	for i := range ls {
  1384  		listener := ls[i]
  1385  		go func() {
  1386  			httpSrv := http.Server{Handler: r}
  1387  			chErrors <- httpSrv.Serve(listener)
  1388  		}()
  1389  	}
  1390  
  1391  	for i := 0; i < len(ls); i++ {
  1392  		err := <-chErrors
  1393  		if err != nil {
  1394  			return err
  1395  		}
  1396  	}
  1397  
  1398  	return nil
  1399  }
  1400  
  1401  func lookupGidByName(nameOrGid string) (int, error) {
  1402  	groupFile, err := user.GetGroupPath()
  1403  	if err != nil {
  1404  		return -1, err
  1405  	}
  1406  	groups, err := user.ParseGroupFileFilter(groupFile, func(g user.Group) bool {
  1407  		return g.Name == nameOrGid || strconv.Itoa(g.Gid) == nameOrGid
  1408  	})
  1409  	if err != nil {
  1410  		return -1, err
  1411  	}
  1412  	if groups != nil && len(groups) > 0 {
  1413  		return groups[0].Gid, nil
  1414  	}
  1415  	return -1, fmt.Errorf("Group %s not found", nameOrGid)
  1416  }
  1417  
  1418  func setupTls(cert, key, ca string, l net.Listener) (net.Listener, error) {
  1419  	tlsCert, err := tls.LoadX509KeyPair(cert, key)
  1420  	if err != nil {
  1421  		return nil, fmt.Errorf("Couldn't load X509 key pair (%s, %s): %s. Key encrypted?",
  1422  			cert, key, err)
  1423  	}
  1424  	tlsConfig := &tls.Config{
  1425  		NextProtos:   []string{"http/1.1"},
  1426  		Certificates: []tls.Certificate{tlsCert},
  1427  		// Avoid fallback on insecure SSL protocols
  1428  		MinVersion: tls.VersionTLS10,
  1429  	}
  1430  
  1431  	if ca != "" {
  1432  		certPool := x509.NewCertPool()
  1433  		file, err := ioutil.ReadFile(ca)
  1434  		if err != nil {
  1435  			return nil, fmt.Errorf("Couldn't read CA certificate: %s", err)
  1436  		}
  1437  		certPool.AppendCertsFromPEM(file)
  1438  		tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
  1439  		tlsConfig.ClientCAs = certPool
  1440  	}
  1441  
  1442  	return tls.NewListener(l, tlsConfig), nil
  1443  }
  1444  
  1445  func newListener(proto, addr string, bufferRequests bool) (net.Listener, error) {
  1446  	if bufferRequests {
  1447  		return listenbuffer.NewListenBuffer(proto, addr, activationLock)
  1448  	}
  1449  
  1450  	return net.Listen(proto, addr)
  1451  }
  1452  
  1453  func changeGroup(addr string, nameOrGid string) error {
  1454  	gid, err := lookupGidByName(nameOrGid)
  1455  	if err != nil {
  1456  		return err
  1457  	}
  1458  
  1459  	log.Debugf("%s group found. gid: %d", nameOrGid, gid)
  1460  	return os.Chown(addr, 0, gid)
  1461  }
  1462  
  1463  func setSocketGroup(addr, group string) error {
  1464  	if group == "" {
  1465  		return nil
  1466  	}
  1467  
  1468  	if err := changeGroup(addr, group); err != nil {
  1469  		if group != "docker" {
  1470  			return err
  1471  		}
  1472  		log.Debugf("Warning: could not chgrp %s to docker: %v", addr, err)
  1473  	}
  1474  
  1475  	return nil
  1476  }
  1477  
  1478  func setupUnixHttp(addr string, job *engine.Job) (*HttpServer, error) {
  1479  	r, err := createRouter(job.Eng, job.GetenvBool("Logging"), job.GetenvBool("EnableCors"), job.Getenv("Version"))
  1480  	if err != nil {
  1481  		return nil, err
  1482  	}
  1483  
  1484  	if err := syscall.Unlink(addr); err != nil && !os.IsNotExist(err) {
  1485  		return nil, err
  1486  	}
  1487  	mask := syscall.Umask(0777)
  1488  	defer syscall.Umask(mask)
  1489  
  1490  	l, err := newListener("unix", addr, job.GetenvBool("BufferRequests"))
  1491  	if err != nil {
  1492  		return nil, err
  1493  	}
  1494  
  1495  	if err := setSocketGroup(addr, job.Getenv("SocketGroup")); err != nil {
  1496  		return nil, err
  1497  	}
  1498  
  1499  	if err := os.Chmod(addr, 0660); err != nil {
  1500  		return nil, err
  1501  	}
  1502  
  1503  	return &HttpServer{&http.Server{Addr: addr, Handler: r}, l}, nil
  1504  }
  1505  
  1506  func allocateDaemonPort(addr string) error {
  1507  	host, port, err := net.SplitHostPort(addr)
  1508  	if err != nil {
  1509  		return err
  1510  	}
  1511  
  1512  	intPort, err := strconv.Atoi(port)
  1513  	if err != nil {
  1514  		return err
  1515  	}
  1516  
  1517  	var hostIPs []net.IP
  1518  	if parsedIP := net.ParseIP(host); parsedIP != nil {
  1519  		hostIPs = append(hostIPs, parsedIP)
  1520  	} else if hostIPs, err = net.LookupIP(host); err != nil {
  1521  		return fmt.Errorf("failed to lookup %s address in host specification", host)
  1522  	}
  1523  
  1524  	for _, hostIP := range hostIPs {
  1525  		if _, err := portallocator.RequestPort(hostIP, "tcp", intPort); err != nil {
  1526  			return fmt.Errorf("failed to allocate daemon listening port %d (err: %v)", intPort, err)
  1527  		}
  1528  	}
  1529  	return nil
  1530  }
  1531  
  1532  func setupTcpHttp(addr string, job *engine.Job) (*HttpServer, error) {
  1533  	if !strings.HasPrefix(addr, "127.0.0.1") && !job.GetenvBool("TlsVerify") {
  1534  		log.Infof("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\")
  1535  	}
  1536  
  1537  	r, err := createRouter(job.Eng, job.GetenvBool("Logging"), job.GetenvBool("EnableCors"), job.Getenv("Version"))
  1538  	if err != nil {
  1539  		return nil, err
  1540  	}
  1541  
  1542  	l, err := newListener("tcp", addr, job.GetenvBool("BufferRequests"))
  1543  	if err != nil {
  1544  		return nil, err
  1545  	}
  1546  
  1547  	if err := allocateDaemonPort(addr); err != nil {
  1548  		return nil, err
  1549  	}
  1550  
  1551  	if job.GetenvBool("Tls") || job.GetenvBool("TlsVerify") {
  1552  		var tlsCa string
  1553  		if job.GetenvBool("TlsVerify") {
  1554  			tlsCa = job.Getenv("TlsCa")
  1555  		}
  1556  		l, err = setupTls(job.Getenv("TlsCert"), job.Getenv("TlsKey"), tlsCa, l)
  1557  		if err != nil {
  1558  			return nil, err
  1559  		}
  1560  	}
  1561  	return &HttpServer{&http.Server{Addr: addr, Handler: r}, l}, nil
  1562  }
  1563  
  1564  // NewServer sets up the required Server and does protocol specific checking.
  1565  func NewServer(proto, addr string, job *engine.Job) (Server, error) {
  1566  	// Basic error and sanity checking
  1567  	switch proto {
  1568  	case "fd":
  1569  		return nil, serveFd(addr, job)
  1570  	case "tcp":
  1571  		return setupTcpHttp(addr, job)
  1572  	case "unix":
  1573  		return setupUnixHttp(addr, job)
  1574  	default:
  1575  		return nil, fmt.Errorf("Invalid protocol format.")
  1576  	}
  1577  }
  1578  
  1579  type Server interface {
  1580  	Serve() error
  1581  	Close() error
  1582  }
  1583  
  1584  // ServeApi loops through all of the protocols sent in to docker and spawns
  1585  // off a go routine to setup a serving http.Server for each.
  1586  func ServeApi(job *engine.Job) engine.Status {
  1587  	if len(job.Args) == 0 {
  1588  		return job.Errorf("usage: %s PROTO://ADDR [PROTO://ADDR ...]", job.Name)
  1589  	}
  1590  	var (
  1591  		protoAddrs = job.Args
  1592  		chErrors   = make(chan error, len(protoAddrs))
  1593  	)
  1594  	activationLock = make(chan struct{})
  1595  
  1596  	for _, protoAddr := range protoAddrs {
  1597  		protoAddrParts := strings.SplitN(protoAddr, "://", 2)
  1598  		if len(protoAddrParts) != 2 {
  1599  			return job.Errorf("usage: %s PROTO://ADDR [PROTO://ADDR ...]", job.Name)
  1600  		}
  1601  		go func() {
  1602  			log.Infof("Listening for HTTP on %s (%s)", protoAddrParts[0], protoAddrParts[1])
  1603  			srv, err := NewServer(protoAddrParts[0], protoAddrParts[1], job)
  1604  			if err != nil {
  1605  				chErrors <- err
  1606  				return
  1607  			}
  1608  			chErrors <- srv.Serve()
  1609  		}()
  1610  	}
  1611  
  1612  	for i := 0; i < len(protoAddrs); i++ {
  1613  		err := <-chErrors
  1614  		if err != nil {
  1615  			return job.Error(err)
  1616  		}
  1617  	}
  1618  
  1619  	return engine.StatusOK
  1620  }
  1621  
  1622  func AcceptConnections(job *engine.Job) engine.Status {
  1623  	// Tell the init daemon we are accepting requests
  1624  	go systemd.SdNotify("READY=1")
  1625  
  1626  	// close the lock so the listeners start accepting connections
  1627  	if activationLock != nil {
  1628  		close(activationLock)
  1629  	}
  1630  
  1631  	return engine.StatusOK
  1632  }