2014-03-28 22:59:29 +00:00
package server
2013-04-10 19:48:21 -07:00
import (
2015-05-07 09:49:07 -07:00
"crypto/tls"
2013-04-18 03:13:43 +02:00
"encoding/json"
2013-04-19 15:24:37 +02:00
"fmt"
2013-05-07 18:06:49 -07:00
"io"
2013-06-18 18:59:56 +00:00
"net"
2013-04-10 19:48:21 -07:00
"net/http"
2013-06-18 18:59:56 +00:00
"os"
2015-04-27 23:11:29 +02:00
"runtime"
2013-04-19 15:24:37 +02:00
"strings"
2014-03-28 22:59:29 +00:00
2014-07-24 13:37:44 -07:00
"github.com/gorilla/mux"
2014-05-02 21:43:51 +00:00
2015-03-26 23:22:04 +01:00
"github.com/Sirupsen/logrus"
2014-07-24 22:19:50 +00:00
"github.com/docker/docker/api"
2015-04-16 21:48:04 +02:00
"github.com/docker/docker/autogen/dockerversion"
2015-03-31 16:21:37 -07:00
"github.com/docker/docker/daemon"
2015-05-20 16:48:39 -07:00
"github.com/docker/docker/pkg/sockets"
2014-07-24 22:19:50 +00:00
"github.com/docker/docker/pkg/version"
2013-04-10 19:48:21 -07:00
)
2015-07-21 05:15:44 +00:00
// Config provides the configuration for the API server
type Config struct {
2015-04-16 21:48:04 +02:00
Logging bool
EnableCors bool
CorsHeaders string
Version string
SocketGroup string
2015-05-07 09:49:07 -07:00
TLSConfig * tls . Config
2015-04-16 21:48:04 +02:00
}
2015-07-21 05:15:44 +00:00
// Server contains instance details for the server
2015-04-17 14:32:18 -07:00
type Server struct {
2015-04-27 23:11:29 +02:00
daemon * daemon . Daemon
2015-07-21 05:15:44 +00:00
cfg * Config
2015-04-27 23:11:29 +02:00
router * mux . Router
start chan struct { }
servers [ ] serverCloser
2015-04-17 14:32:18 -07:00
}
2015-07-21 05:15:44 +00:00
// New returns a new instance of the server based on the specified configuration.
func New ( cfg * Config ) * Server {
2015-04-17 15:18:28 -07:00
srv := & Server {
cfg : cfg ,
start : make ( chan struct { } ) ,
2015-04-17 14:32:18 -07:00
}
2015-04-27 23:11:29 +02:00
r := createRouter ( srv )
2015-04-17 15:18:28 -07:00
srv . router = r
return srv
2015-04-17 14:32:18 -07:00
}
2015-07-21 05:15:44 +00:00
// Close closes servers and thus stop receiving requests
2015-04-27 23:11:29 +02:00
func ( s * Server ) Close ( ) {
for _ , srv := range s . servers {
if err := srv . Close ( ) ; err != nil {
logrus . Error ( err )
}
}
}
2015-04-17 14:32:18 -07:00
type serverCloser interface {
Serve ( ) error
Close ( ) error
}
2015-07-21 05:15:44 +00:00
// ServeAPI loops through all of the protocols sent in to docker and spawns
2015-04-17 14:32:18 -07:00
// off a go routine to setup a serving http.Server for each.
2015-07-21 05:15:44 +00:00
func ( s * Server ) ServeAPI ( protoAddrs [ ] string ) error {
2015-04-17 14:32:18 -07:00
var chErrors = make ( chan error , len ( protoAddrs ) )
for _ , protoAddr := range protoAddrs {
protoAddrParts := strings . SplitN ( protoAddr , "://" , 2 )
if len ( protoAddrParts ) != 2 {
return fmt . Errorf ( "bad format, expected PROTO://ADDR" )
}
2015-04-27 23:11:29 +02:00
srv , err := s . newServer ( protoAddrParts [ 0 ] , protoAddrParts [ 1 ] )
if err != nil {
return err
}
2015-05-28 12:15:03 -07:00
s . servers = append ( s . servers , srv ... )
2015-04-27 23:11:29 +02:00
2015-05-28 12:15:03 -07:00
for _ , s := range srv {
logrus . Infof ( "Listening for HTTP on %s (%s)" , protoAddrParts [ 0 ] , protoAddrParts [ 1 ] )
go func ( s serverCloser ) {
if err := s . Serve ( ) ; err != nil && strings . Contains ( err . Error ( ) , "use of closed network connection" ) {
err = nil
}
chErrors <- err
} ( s )
}
2015-04-17 14:32:18 -07:00
}
for i := 0 ; i < len ( protoAddrs ) ; i ++ {
err := <- chErrors
if err != nil {
return err
}
}
return nil
}
2015-07-21 05:15:44 +00:00
// HTTPServer contains an instance of http server and the listener.
// srv *http.Server, contains configuration to create a http server and a mux router with all api end points.
// l net.Listener, is a TCP or Socket listener that dispatches incoming request to the router.
type HTTPServer struct {
2014-11-07 15:21:19 -05:00
srv * http . Server
l net . Listener
}
2015-07-21 05:15:44 +00:00
// Serve starts listening for inbound requests.
func ( s * HTTPServer ) Serve ( ) error {
2014-11-07 15:21:19 -05:00
return s . srv . Serve ( s . l )
}
2015-07-21 05:15:44 +00:00
// Close closes the HTTPServer from listening for the inbound requests.
func ( s * HTTPServer ) Close ( ) error {
2014-11-07 15:21:19 -05:00
return s . l . Close ( )
}
2015-07-21 05:15:44 +00:00
// HTTPAPIFunc is an adapter to allow the use of ordinary functions as Docker API endpoints.
// Any function that has the appropriate signature can be register as a API endpoint (e.g. getVersion).
type HTTPAPIFunc func ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error
2013-07-13 16:59:07 +02:00
2013-05-07 18:06:49 -07:00
func hijackServer ( w http . ResponseWriter ) ( io . ReadCloser , io . Writer , error ) {
conn , _ , err := w . ( http . Hijacker ) . Hijack ( )
2013-05-06 11:31:22 +02:00
if err != nil {
return nil , nil , err
}
// Flush the options to make sure the client sets the raw mode
2013-05-07 18:06:49 -07:00
conn . Write ( [ ] byte { } )
return conn , conn , nil
2013-05-06 11:31:22 +02:00
}
2014-12-09 20:21:21 +01:00
func closeStreams ( streams ... interface { } ) {
for _ , stream := range streams {
if tcpc , ok := stream . ( interface {
CloseWrite ( ) error
} ) ; ok {
tcpc . CloseWrite ( )
} else if closer , ok := stream . ( io . Closer ) ; ok {
closer . Close ( )
}
}
}
2015-07-21 05:15:44 +00:00
// checkForJSON makes sure that the request's Content-Type is application/json.
func checkForJSON ( r * http . Request ) error {
2014-09-25 08:24:41 -07:00
ct := r . Header . Get ( "Content-Type" )
// No Content-Type header is ok as long as there's no Body
if ct == "" {
if r . Body == nil || r . ContentLength == 0 {
return nil
}
}
// Otherwise it better be json
if api . MatchesContentType ( ct , "application/json" ) {
return nil
}
return fmt . Errorf ( "Content-Type specified (%s) must be 'application/json'" , ct )
}
2013-05-08 18:52:01 +02:00
//If we don't do this, POST method without Content-type (even with empty body) will fail
func parseForm ( r * http . Request ) error {
2013-10-08 19:15:29 +00:00
if r == nil {
return nil
}
2013-05-08 18:52:01 +02:00
if err := r . ParseForm ( ) ; err != nil && ! strings . HasPrefix ( err . Error ( ) , "mime:" ) {
return err
}
return nil
}
2013-05-22 20:07:26 -07:00
func parseMultipartForm ( r * http . Request ) error {
if err := r . ParseMultipartForm ( 4096 ) ; err != nil && ! strings . HasPrefix ( err . Error ( ) , "mime:" ) {
return err
}
return nil
}
2013-05-06 11:31:22 +02:00
func httpError ( w http . ResponseWriter , err error ) {
2015-04-20 17:49:51 +08:00
if err == nil || w == nil {
logrus . WithFields ( logrus . Fields { "error" : err , "writer" : w } ) . Error ( "unexpected HTTP error handling" )
return
}
2013-07-11 12:21:43 +00:00
statusCode := http . StatusInternalServerError
2013-11-14 23:53:43 +00:00
// FIXME: this is brittle and should not be necessary.
// If we need to differentiate between different possible error types, we should
// create appropriate error types with clearly defined meaning.
2014-10-23 13:34:06 -04:00
errStr := strings . ToLower ( err . Error ( ) )
2015-04-23 19:37:47 +08:00
for keyword , status := range map [ string ] int {
"not found" : http . StatusNotFound ,
"no such" : http . StatusNotFound ,
"bad parameter" : http . StatusBadRequest ,
"conflict" : http . StatusConflict ,
"impossible" : http . StatusNotAcceptable ,
"wrong login/password" : http . StatusUnauthorized ,
"hasn't been activated" : http . StatusForbidden ,
} {
if strings . Contains ( errStr , keyword ) {
statusCode = status
break
}
2013-10-16 17:08:14 -07:00
}
2015-04-20 17:49:51 +08:00
logrus . WithFields ( logrus . Fields { "statusCode" : statusCode , "err" : err } ) . Error ( "HTTP Error" )
http . Error ( w , err . Error ( ) , statusCode )
2013-05-06 11:31:22 +02:00
}
2015-02-24 11:12:47 -08:00
// writeJSON writes the value v to the http response stream as json with standard
// json encoding.
func writeJSON ( w http . ResponseWriter , code int , v interface { } ) error {
w . Header ( ) . Set ( "Content-Type" , "application/json" )
w . WriteHeader ( code )
return json . NewEncoder ( w ) . Encode ( v )
}
2015-04-27 23:11:29 +02:00
func ( s * Server ) optionsHandler ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-06-10 16:10:40 -09:00
w . WriteHeader ( http . StatusOK )
return nil
}
2015-02-09 15:15:07 +08:00
func writeCorsHeaders ( w http . ResponseWriter , r * http . Request , corsHeaders string ) {
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "CORS header is enabled and set to: %s" , corsHeaders )
2015-02-09 15:15:07 +08:00
w . Header ( ) . Add ( "Access-Control-Allow-Origin" , corsHeaders )
2014-11-01 19:22:28 +03:00
w . Header ( ) . Add ( "Access-Control-Allow-Headers" , "Origin, X-Requested-With, Content-Type, Accept, X-Registry-Auth" )
2015-08-24 05:57:22 -04:00
w . Header ( ) . Add ( "Access-Control-Allow-Methods" , "HEAD, GET, POST, DELETE, PUT, OPTIONS" )
2013-06-03 21:39:00 -04:00
}
2015-04-27 23:11:29 +02:00
func ( s * Server ) ping ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2014-05-02 22:03:59 +00:00
_ , err := w . Write ( [ ] byte { 'O' , 'K' } )
return err
2014-05-02 21:43:51 +00:00
}
2015-07-21 05:15:44 +00:00
func ( s * Server ) initTCPSocket ( addr string ) ( l net . Listener , err error ) {
2015-05-07 09:49:07 -07:00
if s . cfg . TLSConfig == nil || s . cfg . TLSConfig . ClientAuth != tls . RequireAndVerifyClientCert {
2015-05-20 16:48:39 -07:00
logrus . Warn ( "/!\\ DON'T BIND ON ANY IP ADDRESS WITHOUT setting -tlsverify IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\" )
}
2015-07-25 10:35:07 +02:00
if l , err = sockets . NewTCPSocket ( addr , s . cfg . TLSConfig , s . start ) ; err != nil {
2015-05-20 16:48:39 -07:00
return nil , err
}
if err := allocateDaemonPort ( addr ) ; err != nil {
return nil , err
}
return
}
2015-07-21 05:15:44 +00:00
func makeHTTPHandler ( logging bool , localMethod string , localRoute string , handlerFunc HTTPAPIFunc , corsHeaders string , dockerVersion version . Version ) http . HandlerFunc {
2013-07-13 16:59:07 +02:00
return func ( w http . ResponseWriter , r * http . Request ) {
// log the request
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "Calling %s %s" , localMethod , localRoute )
2013-07-13 16:59:07 +02:00
if logging {
2015-03-26 23:22:04 +01:00
logrus . Infof ( "%s %s" , r . Method , r . RequestURI )
2013-07-13 16:59:07 +02:00
}
if strings . Contains ( r . Header . Get ( "User-Agent" ) , "Docker-Client/" ) {
userAgent := strings . Split ( r . Header . Get ( "User-Agent" ) , "/" )
2015-06-04 10:29:57 -07:00
// v1.20 onwards includes the GOOS of the client after the version
// such as Docker/1.7.0 (linux)
if len ( userAgent ) == 2 && strings . Contains ( userAgent [ 1 ] , " " ) {
userAgent [ 1 ] = strings . Split ( userAgent [ 1 ] , " " ) [ 0 ]
}
2014-04-01 15:46:52 -07:00
if len ( userAgent ) == 2 && ! dockerVersion . Equal ( version . Version ( userAgent [ 1 ] ) ) {
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "Warning: client and server don't have the same version (client: %s, server: %s)" , userAgent [ 1 ] , dockerVersion )
2013-07-13 16:59:07 +02:00
}
}
2014-02-21 23:15:28 +00:00
version := version . Version ( mux . Vars ( r ) [ "version" ] )
2014-02-14 22:53:53 +00:00
if version == "" {
2015-06-17 23:57:32 +02:00
version = api . Version
2013-07-13 16:59:07 +02:00
}
2015-02-09 15:15:07 +08:00
if corsHeaders != "" {
writeCorsHeaders ( w , r , corsHeaders )
2013-07-13 16:59:07 +02:00
}
2015-06-17 23:57:32 +02:00
if version . GreaterThan ( api . Version ) {
http . Error ( w , fmt . Errorf ( "client is newer than server (client API version: %s, server API version: %s)" , version , api . Version ) . Error ( ) , http . StatusBadRequest )
return
}
if version . LessThan ( api . MinVersion ) {
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 )
2013-07-13 16:59:07 +02:00
return
}
2015-06-04 06:30:14 -07:00
w . Header ( ) . Set ( "Server" , "Docker/" + dockerversion . VERSION + " (" + runtime . GOOS + ")" )
2015-04-27 23:11:29 +02:00
if err := handlerFunc ( version , w , r , mux . Vars ( r ) ) ; err != nil {
2015-03-26 23:22:04 +01:00
logrus . Errorf ( "Handler for %s %s returned error: %s" , localMethod , localRoute , err )
2013-07-13 16:59:07 +02:00
httpError ( w , err )
}
}
}
2015-02-09 15:15:07 +08:00
// we keep enableCors just for legacy usage, need to be removed in the future
2015-04-27 23:11:29 +02:00
func createRouter ( s * Server ) * mux . Router {
2013-05-07 16:33:12 -07:00
r := mux . NewRouter ( )
2013-11-19 14:25:17 -05:00
if os . Getenv ( "DEBUG" ) != "" {
2015-07-21 05:15:44 +00:00
profilerSetup ( r , "/debug/" )
2013-11-19 14:25:17 -05:00
}
2015-07-21 05:15:44 +00:00
m := map [ string ] map [ string ] HTTPAPIFunc {
2015-05-13 15:01:51 -07:00
"HEAD" : {
"/containers/{name:.*}/archive" : s . headContainersArchive ,
} ,
2013-05-07 16:33:12 -07:00
"GET" : {
2015-04-17 15:18:28 -07:00
"/_ping" : s . ping ,
"/events" : s . getEvents ,
"/info" : s . getInfo ,
"/version" : s . getVersion ,
"/images/json" : s . getImagesJSON ,
"/images/search" : s . getImagesSearch ,
"/images/get" : s . getImagesGet ,
"/images/{name:.*}/get" : s . getImagesGet ,
"/images/{name:.*}/history" : s . getImagesHistory ,
"/images/{name:.*}/json" : s . getImagesByName ,
"/containers/json" : s . getContainersJSON ,
"/containers/{name:.*}/export" : s . getContainersExport ,
"/containers/{name:.*}/changes" : s . getContainersChanges ,
"/containers/{name:.*}/json" : s . getContainersByName ,
"/containers/{name:.*}/top" : s . getContainersTop ,
"/containers/{name:.*}/logs" : s . getContainersLogs ,
"/containers/{name:.*}/stats" : s . getContainersStats ,
"/containers/{name:.*}/attach/ws" : s . wsContainersAttach ,
"/exec/{id:.*}/json" : s . getExecByID ,
2015-05-13 15:01:51 -07:00
"/containers/{name:.*}/archive" : s . getContainersArchive ,
2015-06-12 09:25:32 -04:00
"/volumes" : s . getVolumesList ,
"/volumes/{name:.*}" : s . getVolumeByName ,
2013-05-07 16:33:12 -07:00
} ,
"POST" : {
2015-04-17 15:18:28 -07:00
"/auth" : s . postAuth ,
"/commit" : s . postCommit ,
"/build" : s . postBuild ,
"/images/create" : s . postImagesCreate ,
"/images/load" : s . postImagesLoad ,
"/images/{name:.*}/push" : s . postImagesPush ,
"/images/{name:.*}/tag" : s . postImagesTag ,
"/containers/create" : s . postContainersCreate ,
"/containers/{name:.*}/kill" : s . postContainersKill ,
"/containers/{name:.*}/pause" : s . postContainersPause ,
"/containers/{name:.*}/unpause" : s . postContainersUnpause ,
"/containers/{name:.*}/restart" : s . postContainersRestart ,
"/containers/{name:.*}/start" : s . postContainersStart ,
"/containers/{name:.*}/stop" : s . postContainersStop ,
"/containers/{name:.*}/wait" : s . postContainersWait ,
"/containers/{name:.*}/resize" : s . postContainersResize ,
"/containers/{name:.*}/attach" : s . postContainersAttach ,
"/containers/{name:.*}/copy" : s . postContainersCopy ,
"/containers/{name:.*}/exec" : s . postContainerExecCreate ,
"/exec/{name:.*}/start" : s . postContainerExecStart ,
"/exec/{name:.*}/resize" : s . postContainerExecResize ,
"/containers/{name:.*}/rename" : s . postContainerRename ,
2015-06-12 09:25:32 -04:00
"/volumes" : s . postVolumesCreate ,
2013-05-07 16:33:12 -07:00
} ,
2015-05-13 15:01:51 -07:00
"PUT" : {
"/containers/{name:.*}/archive" : s . putContainersArchive ,
} ,
2013-05-07 16:33:12 -07:00
"DELETE" : {
2015-04-17 15:18:28 -07:00
"/containers/{name:.*}" : s . deleteContainers ,
"/images/{name:.*}" : s . deleteImages ,
2015-06-12 09:25:32 -04:00
"/volumes/{name:.*}" : s . deleteVolumes ,
2013-05-07 16:33:12 -07:00
} ,
2013-06-10 16:10:40 -09:00
"OPTIONS" : {
2015-04-17 15:18:28 -07:00
"" : s . optionsHandler ,
2013-06-10 16:10:40 -09:00
} ,
2013-05-07 16:33:12 -07:00
}
2015-02-09 15:15:07 +08:00
// If "api-cors-header" is not given, but "api-enable-cors" is true, we set cors to "*"
// otherwise, all head values will be passed to HTTP handler
2015-04-17 15:18:28 -07:00
corsHeaders := s . cfg . CorsHeaders
if corsHeaders == "" && s . cfg . EnableCors {
2015-02-09 15:15:07 +08:00
corsHeaders = "*"
}
2013-05-07 16:33:12 -07:00
for method , routes := range m {
for route , fct := range routes {
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "Registering %s, %s" , method , route )
2013-05-07 16:33:12 -07:00
// NOTE: scope issue, make sure the variables are local and won't be changed
localRoute := route
localFct := fct
2013-07-13 16:59:07 +02:00
localMethod := method
2013-06-20 18:18:36 -07:00
2013-07-13 16:59:07 +02:00
// build the handler function
2015-07-21 05:15:44 +00:00
f := makeHTTPHandler ( s . cfg . Logging , localMethod , localRoute , localFct , corsHeaders , version . Version ( s . cfg . Version ) )
2013-06-10 16:10:40 -09:00
2013-07-13 16:59:07 +02:00
// add the new route
2013-06-10 16:10:40 -09:00
if localRoute == "" {
r . Methods ( localMethod ) . HandlerFunc ( f )
} else {
r . Path ( "/v{version:[0-9.]+}" + localRoute ) . Methods ( localMethod ) . HandlerFunc ( f )
r . Path ( localRoute ) . Methods ( localMethod ) . HandlerFunc ( f )
}
2013-04-29 17:46:41 +02:00
}
2013-05-07 16:33:12 -07:00
}
2013-07-05 19:55:15 +02:00
2015-01-13 22:41:34 +01:00
return r
2013-06-10 13:02:40 -09:00
}