server.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. package server
  2. import (
  3. "crypto/tls"
  4. "encoding/json"
  5. "fmt"
  6. "io"
  7. "net"
  8. "net/http"
  9. "os"
  10. "runtime"
  11. "strings"
  12. "github.com/gorilla/mux"
  13. "github.com/Sirupsen/logrus"
  14. "github.com/docker/docker/api"
  15. "github.com/docker/docker/autogen/dockerversion"
  16. "github.com/docker/docker/daemon"
  17. "github.com/docker/docker/pkg/sockets"
  18. "github.com/docker/docker/pkg/version"
  19. )
  20. // Config provides the configuration for the API server
  21. type Config struct {
  22. Logging bool
  23. EnableCors bool
  24. CorsHeaders string
  25. Version string
  26. SocketGroup string
  27. TLSConfig *tls.Config
  28. }
  29. // Server contains instance details for the server
  30. type Server struct {
  31. daemon *daemon.Daemon
  32. cfg *Config
  33. router *mux.Router
  34. start chan struct{}
  35. servers []serverCloser
  36. }
  37. // New returns a new instance of the server based on the specified configuration.
  38. func New(cfg *Config) *Server {
  39. srv := &Server{
  40. cfg: cfg,
  41. start: make(chan struct{}),
  42. }
  43. r := createRouter(srv)
  44. srv.router = r
  45. return srv
  46. }
  47. // Close closes servers and thus stop receiving requests
  48. func (s *Server) Close() {
  49. for _, srv := range s.servers {
  50. if err := srv.Close(); err != nil {
  51. logrus.Error(err)
  52. }
  53. }
  54. }
  55. type serverCloser interface {
  56. Serve() error
  57. Close() error
  58. }
  59. // ServeAPI loops through all of the protocols sent in to docker and spawns
  60. // off a go routine to setup a serving http.Server for each.
  61. func (s *Server) ServeAPI(protoAddrs []string) error {
  62. var chErrors = make(chan error, len(protoAddrs))
  63. for _, protoAddr := range protoAddrs {
  64. protoAddrParts := strings.SplitN(protoAddr, "://", 2)
  65. if len(protoAddrParts) != 2 {
  66. return fmt.Errorf("bad format, expected PROTO://ADDR")
  67. }
  68. srv, err := s.newServer(protoAddrParts[0], protoAddrParts[1])
  69. if err != nil {
  70. return err
  71. }
  72. s.servers = append(s.servers, srv...)
  73. for _, s := range srv {
  74. logrus.Infof("Listening for HTTP on %s (%s)", protoAddrParts[0], protoAddrParts[1])
  75. go func(s serverCloser) {
  76. if err := s.Serve(); err != nil && strings.Contains(err.Error(), "use of closed network connection") {
  77. err = nil
  78. }
  79. chErrors <- err
  80. }(s)
  81. }
  82. }
  83. for i := 0; i < len(protoAddrs); i++ {
  84. err := <-chErrors
  85. if err != nil {
  86. return err
  87. }
  88. }
  89. return nil
  90. }
  91. // HTTPServer contains an instance of http server and the listener.
  92. // srv *http.Server, contains configuration to create a http server and a mux router with all api end points.
  93. // l net.Listener, is a TCP or Socket listener that dispatches incoming request to the router.
  94. type HTTPServer struct {
  95. srv *http.Server
  96. l net.Listener
  97. }
  98. // Serve starts listening for inbound requests.
  99. func (s *HTTPServer) Serve() error {
  100. return s.srv.Serve(s.l)
  101. }
  102. // Close closes the HTTPServer from listening for the inbound requests.
  103. func (s *HTTPServer) Close() error {
  104. return s.l.Close()
  105. }
  106. // HTTPAPIFunc is an adapter to allow the use of ordinary functions as Docker API endpoints.
  107. // Any function that has the appropriate signature can be register as a API endpoint (e.g. getVersion).
  108. type HTTPAPIFunc func(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error
  109. func hijackServer(w http.ResponseWriter) (io.ReadCloser, io.Writer, error) {
  110. conn, _, err := w.(http.Hijacker).Hijack()
  111. if err != nil {
  112. return nil, nil, err
  113. }
  114. // Flush the options to make sure the client sets the raw mode
  115. conn.Write([]byte{})
  116. return conn, conn, nil
  117. }
  118. func closeStreams(streams ...interface{}) {
  119. for _, stream := range streams {
  120. if tcpc, ok := stream.(interface {
  121. CloseWrite() error
  122. }); ok {
  123. tcpc.CloseWrite()
  124. } else if closer, ok := stream.(io.Closer); ok {
  125. closer.Close()
  126. }
  127. }
  128. }
  129. // checkForJSON makes sure that the request's Content-Type is application/json.
  130. func checkForJSON(r *http.Request) error {
  131. ct := r.Header.Get("Content-Type")
  132. // No Content-Type header is ok as long as there's no Body
  133. if ct == "" {
  134. if r.Body == nil || r.ContentLength == 0 {
  135. return nil
  136. }
  137. }
  138. // Otherwise it better be json
  139. if api.MatchesContentType(ct, "application/json") {
  140. return nil
  141. }
  142. return fmt.Errorf("Content-Type specified (%s) must be 'application/json'", ct)
  143. }
  144. //If we don't do this, POST method without Content-type (even with empty body) will fail
  145. func parseForm(r *http.Request) error {
  146. if r == nil {
  147. return nil
  148. }
  149. if err := r.ParseForm(); err != nil && !strings.HasPrefix(err.Error(), "mime:") {
  150. return err
  151. }
  152. return nil
  153. }
  154. func parseMultipartForm(r *http.Request) error {
  155. if err := r.ParseMultipartForm(4096); err != nil && !strings.HasPrefix(err.Error(), "mime:") {
  156. return err
  157. }
  158. return nil
  159. }
  160. func httpError(w http.ResponseWriter, err error) {
  161. if err == nil || w == nil {
  162. logrus.WithFields(logrus.Fields{"error": err, "writer": w}).Error("unexpected HTTP error handling")
  163. return
  164. }
  165. statusCode := http.StatusInternalServerError
  166. // FIXME: this is brittle and should not be necessary.
  167. // If we need to differentiate between different possible error types, we should
  168. // create appropriate error types with clearly defined meaning.
  169. errStr := strings.ToLower(err.Error())
  170. for keyword, status := range map[string]int{
  171. "not found": http.StatusNotFound,
  172. "no such": http.StatusNotFound,
  173. "bad parameter": http.StatusBadRequest,
  174. "conflict": http.StatusConflict,
  175. "impossible": http.StatusNotAcceptable,
  176. "wrong login/password": http.StatusUnauthorized,
  177. "hasn't been activated": http.StatusForbidden,
  178. } {
  179. if strings.Contains(errStr, keyword) {
  180. statusCode = status
  181. break
  182. }
  183. }
  184. logrus.WithFields(logrus.Fields{"statusCode": statusCode, "err": err}).Error("HTTP Error")
  185. http.Error(w, err.Error(), statusCode)
  186. }
  187. // writeJSON writes the value v to the http response stream as json with standard
  188. // json encoding.
  189. func writeJSON(w http.ResponseWriter, code int, v interface{}) error {
  190. w.Header().Set("Content-Type", "application/json")
  191. w.WriteHeader(code)
  192. return json.NewEncoder(w).Encode(v)
  193. }
  194. func (s *Server) optionsHandler(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  195. w.WriteHeader(http.StatusOK)
  196. return nil
  197. }
  198. func writeCorsHeaders(w http.ResponseWriter, r *http.Request, corsHeaders string) {
  199. logrus.Debugf("CORS header is enabled and set to: %s", corsHeaders)
  200. w.Header().Add("Access-Control-Allow-Origin", corsHeaders)
  201. w.Header().Add("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, X-Registry-Auth")
  202. w.Header().Add("Access-Control-Allow-Methods", "HEAD, GET, POST, DELETE, PUT, OPTIONS")
  203. }
  204. func (s *Server) ping(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  205. _, err := w.Write([]byte{'O', 'K'})
  206. return err
  207. }
  208. func (s *Server) initTCPSocket(addr string) (l net.Listener, err error) {
  209. if s.cfg.TLSConfig == nil || s.cfg.TLSConfig.ClientAuth != tls.RequireAndVerifyClientCert {
  210. logrus.Warn("/!\\ DON'T BIND ON ANY IP ADDRESS WITHOUT setting -tlsverify IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\")
  211. }
  212. if l, err = sockets.NewTCPSocket(addr, s.cfg.TLSConfig, s.start); err != nil {
  213. return nil, err
  214. }
  215. if err := allocateDaemonPort(addr); err != nil {
  216. return nil, err
  217. }
  218. return
  219. }
  220. func makeHTTPHandler(logging bool, localMethod string, localRoute string, handlerFunc HTTPAPIFunc, corsHeaders string, dockerVersion version.Version) http.HandlerFunc {
  221. return func(w http.ResponseWriter, r *http.Request) {
  222. // log the request
  223. logrus.Debugf("Calling %s %s", localMethod, localRoute)
  224. if logging {
  225. logrus.Infof("%s %s", r.Method, r.RequestURI)
  226. }
  227. if strings.Contains(r.Header.Get("User-Agent"), "Docker-Client/") {
  228. userAgent := strings.Split(r.Header.Get("User-Agent"), "/")
  229. // v1.20 onwards includes the GOOS of the client after the version
  230. // such as Docker/1.7.0 (linux)
  231. if len(userAgent) == 2 && strings.Contains(userAgent[1], " ") {
  232. userAgent[1] = strings.Split(userAgent[1], " ")[0]
  233. }
  234. if len(userAgent) == 2 && !dockerVersion.Equal(version.Version(userAgent[1])) {
  235. logrus.Debugf("Warning: client and server don't have the same version (client: %s, server: %s)", userAgent[1], dockerVersion)
  236. }
  237. }
  238. version := version.Version(mux.Vars(r)["version"])
  239. if version == "" {
  240. version = api.Version
  241. }
  242. if corsHeaders != "" {
  243. writeCorsHeaders(w, r, corsHeaders)
  244. }
  245. if version.GreaterThan(api.Version) {
  246. http.Error(w, fmt.Errorf("client is newer than server (client API version: %s, server API version: %s)", version, api.Version).Error(), http.StatusBadRequest)
  247. return
  248. }
  249. if version.LessThan(api.MinVersion) {
  250. http.Error(w, fmt.Errorf("client is too old, minimum supported API version is %s, please upgrade your client to a newer version", api.MinVersion).Error(), http.StatusBadRequest)
  251. return
  252. }
  253. w.Header().Set("Server", "Docker/"+dockerversion.VERSION+" ("+runtime.GOOS+")")
  254. if err := handlerFunc(version, w, r, mux.Vars(r)); err != nil {
  255. logrus.Errorf("Handler for %s %s returned error: %s", localMethod, localRoute, err)
  256. httpError(w, err)
  257. }
  258. }
  259. }
  260. // we keep enableCors just for legacy usage, need to be removed in the future
  261. func createRouter(s *Server) *mux.Router {
  262. r := mux.NewRouter()
  263. if os.Getenv("DEBUG") != "" {
  264. profilerSetup(r, "/debug/")
  265. }
  266. m := map[string]map[string]HTTPAPIFunc{
  267. "HEAD": {
  268. "/containers/{name:.*}/archive": s.headContainersArchive,
  269. },
  270. "GET": {
  271. "/_ping": s.ping,
  272. "/events": s.getEvents,
  273. "/info": s.getInfo,
  274. "/version": s.getVersion,
  275. "/images/json": s.getImagesJSON,
  276. "/images/search": s.getImagesSearch,
  277. "/images/get": s.getImagesGet,
  278. "/images/{name:.*}/get": s.getImagesGet,
  279. "/images/{name:.*}/history": s.getImagesHistory,
  280. "/images/{name:.*}/json": s.getImagesByName,
  281. "/containers/ps": s.getContainersJSON,
  282. "/containers/json": s.getContainersJSON,
  283. "/containers/{name:.*}/export": s.getContainersExport,
  284. "/containers/{name:.*}/changes": s.getContainersChanges,
  285. "/containers/{name:.*}/json": s.getContainersByName,
  286. "/containers/{name:.*}/top": s.getContainersTop,
  287. "/containers/{name:.*}/logs": s.getContainersLogs,
  288. "/containers/{name:.*}/stats": s.getContainersStats,
  289. "/containers/{name:.*}/attach/ws": s.wsContainersAttach,
  290. "/exec/{id:.*}/json": s.getExecByID,
  291. "/containers/{name:.*}/archive": s.getContainersArchive,
  292. "/volumes": s.getVolumesList,
  293. "/volumes/{name:.*}": s.getVolumeByName,
  294. },
  295. "POST": {
  296. "/auth": s.postAuth,
  297. "/commit": s.postCommit,
  298. "/build": s.postBuild,
  299. "/images/create": s.postImagesCreate,
  300. "/images/load": s.postImagesLoad,
  301. "/images/{name:.*}/push": s.postImagesPush,
  302. "/images/{name:.*}/tag": s.postImagesTag,
  303. "/containers/create": s.postContainersCreate,
  304. "/containers/{name:.*}/kill": s.postContainersKill,
  305. "/containers/{name:.*}/pause": s.postContainersPause,
  306. "/containers/{name:.*}/unpause": s.postContainersUnpause,
  307. "/containers/{name:.*}/restart": s.postContainersRestart,
  308. "/containers/{name:.*}/start": s.postContainersStart,
  309. "/containers/{name:.*}/stop": s.postContainersStop,
  310. "/containers/{name:.*}/wait": s.postContainersWait,
  311. "/containers/{name:.*}/resize": s.postContainersResize,
  312. "/containers/{name:.*}/attach": s.postContainersAttach,
  313. "/containers/{name:.*}/copy": s.postContainersCopy,
  314. "/containers/{name:.*}/exec": s.postContainerExecCreate,
  315. "/exec/{name:.*}/start": s.postContainerExecStart,
  316. "/exec/{name:.*}/resize": s.postContainerExecResize,
  317. "/containers/{name:.*}/rename": s.postContainerRename,
  318. "/volumes": s.postVolumesCreate,
  319. },
  320. "PUT": {
  321. "/containers/{name:.*}/archive": s.putContainersArchive,
  322. },
  323. "DELETE": {
  324. "/containers/{name:.*}": s.deleteContainers,
  325. "/images/{name:.*}": s.deleteImages,
  326. "/volumes/{name:.*}": s.deleteVolumes,
  327. },
  328. "OPTIONS": {
  329. "": s.optionsHandler,
  330. },
  331. }
  332. // If "api-cors-header" is not given, but "api-enable-cors" is true, we set cors to "*"
  333. // otherwise, all head values will be passed to HTTP handler
  334. corsHeaders := s.cfg.CorsHeaders
  335. if corsHeaders == "" && s.cfg.EnableCors {
  336. corsHeaders = "*"
  337. }
  338. for method, routes := range m {
  339. for route, fct := range routes {
  340. logrus.Debugf("Registering %s, %s", method, route)
  341. // NOTE: scope issue, make sure the variables are local and won't be changed
  342. localRoute := route
  343. localFct := fct
  344. localMethod := method
  345. // build the handler function
  346. f := makeHTTPHandler(s.cfg.Logging, localMethod, localRoute, localFct, corsHeaders, version.Version(s.cfg.Version))
  347. // add the new route
  348. if localRoute == "" {
  349. r.Methods(localMethod).HandlerFunc(f)
  350. } else {
  351. r.Path("/v{version:[0-9.]+}" + localRoute).Methods(localMethod).HandlerFunc(f)
  352. r.Path(localRoute).Methods(localMethod).HandlerFunc(f)
  353. }
  354. }
  355. }
  356. return r
  357. }