2016-12-21 21:36:32 +08:00
package api
import (
"context"
2017-02-15 21:29:20 +08:00
"crypto/tls"
2016-12-21 21:36:32 +08:00
"errors"
"fmt"
2017-04-27 14:54:21 +08:00
"net"
2016-12-21 21:36:32 +08:00
"net/http"
"os"
"path"
2017-08-23 19:31:26 +08:00
"time"
2016-12-21 21:36:32 +08:00
"github.com/grafana/grafana/pkg/api/live"
2019-01-16 21:53:59 +08:00
"github.com/grafana/grafana/pkg/api/routing"
2016-12-21 21:36:32 +08:00
httpstatic "github.com/grafana/grafana/pkg/api/static"
2017-04-25 23:17:45 +08:00
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
2019-06-13 16:55:38 +08:00
"github.com/grafana/grafana/pkg/infra/localcache"
2019-05-13 14:45:54 +08:00
"github.com/grafana/grafana/pkg/infra/log"
2019-04-08 19:31:46 +08:00
"github.com/grafana/grafana/pkg/infra/remotecache"
2016-12-21 21:36:32 +08:00
"github.com/grafana/grafana/pkg/middleware"
2017-04-25 23:17:45 +08:00
"github.com/grafana/grafana/pkg/models"
2016-12-21 21:36:32 +08:00
"github.com/grafana/grafana/pkg/plugins"
2018-05-01 21:51:15 +08:00
"github.com/grafana/grafana/pkg/registry"
2018-10-26 16:40:33 +08:00
"github.com/grafana/grafana/pkg/services/datasources"
2018-10-12 17:26:42 +08:00
"github.com/grafana/grafana/pkg/services/hooks"
2019-06-13 22:47:52 +08:00
"github.com/grafana/grafana/pkg/services/login"
2019-02-12 04:12:01 +08:00
"github.com/grafana/grafana/pkg/services/quota"
2018-05-24 21:26:27 +08:00
"github.com/grafana/grafana/pkg/services/rendering"
2016-12-21 21:36:32 +08:00
"github.com/grafana/grafana/pkg/setting"
2019-11-18 16:49:08 +08:00
"github.com/grafana/grafana/pkg/util/errutil"
2019-01-16 21:53:59 +08:00
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
2019-05-27 23:47:29 +08:00
macaron "gopkg.in/macaron.v1"
2016-12-21 21:36:32 +08:00
)
2018-05-01 21:51:15 +08:00
func init ( ) {
2018-07-01 22:01:43 +08:00
registry . Register ( & registry . Descriptor {
Name : "HTTPServer" ,
Instance : & HTTPServer { } ,
InitPriority : registry . High ,
} )
2018-05-01 21:51:15 +08:00
}
2019-04-30 19:32:18 +08:00
type ProvisioningService interface {
ProvisionDatasources ( ) error
ProvisionNotifications ( ) error
ProvisionDashboards ( ) error
GetDashboardProvisionerResolvedPath ( name string ) string
2019-10-31 21:27:31 +08:00
GetAllowUiUpdatesFromConfig ( name string ) bool
2019-04-30 19:32:18 +08:00
}
2018-03-23 05:13:46 +08:00
type HTTPServer struct {
2016-12-21 21:36:32 +08:00
log log . Logger
macaron * macaron . Macaron
context context . Context
streamManager * live . StreamManager
2018-04-30 22:21:04 +08:00
httpSrv * http . Server
2017-02-06 16:40:07 +08:00
2019-04-30 19:32:18 +08:00
RouteRegister routing . RouteRegister ` inject:"" `
Bus bus . Bus ` inject:"" `
RenderService rendering . Service ` inject:"" `
Cfg * setting . Cfg ` inject:"" `
HooksService * hooks . HooksService ` inject:"" `
2019-06-13 16:55:38 +08:00
CacheService * localcache . CacheService ` inject:"" `
2019-04-30 19:32:18 +08:00
DatasourceCache datasources . CacheService ` inject:"" `
AuthTokenService models . UserTokenService ` inject:"" `
QuotaService * quota . QuotaService ` inject:"" `
RemoteCacheService * remotecache . RemoteCache ` inject:"" `
ProvisioningService ProvisioningService ` inject:"" `
2019-06-13 22:47:52 +08:00
Login * login . LoginService ` inject:"" `
2019-11-01 21:56:12 +08:00
License models . Licensing ` inject:"" `
2016-12-21 21:36:32 +08:00
}
2018-05-01 21:51:15 +08:00
func ( hs * HTTPServer ) Init ( ) error {
2018-04-27 19:41:58 +08:00
hs . log = log . New ( "http.server" )
2018-05-01 21:51:15 +08:00
2018-07-01 22:01:43 +08:00
hs . streamManager = live . NewStreamManager ( )
hs . macaron = hs . newMacaron ( )
hs . registerRoutes ( )
2018-05-01 21:51:15 +08:00
return nil
2016-12-21 21:36:32 +08:00
}
2018-05-01 21:51:15 +08:00
func ( hs * HTTPServer ) Run ( ctx context . Context ) error {
2016-12-21 21:36:32 +08:00
var err error
hs . context = ctx
2018-07-02 14:35:50 +08:00
2018-07-01 22:01:43 +08:00
hs . applyRoutes ( )
2016-12-21 21:36:32 +08:00
hs . streamManager . Run ( ctx )
listenAddr := fmt . Sprintf ( "%s:%s" , setting . HttpAddr , setting . HttpPort )
2019-10-22 19:07:43 +08:00
listener , err := net . Listen ( "tcp" , listenAddr )
if err != nil {
2019-11-18 16:49:08 +08:00
return errutil . Wrapf ( err , "failed to open listener on address %s" , listenAddr )
2019-10-22 19:07:43 +08:00
}
hs . log . Info ( "HTTP Server Listen" , "address" , listener . Addr ( ) . String ( ) , "protocol" , setting . Protocol , "subUrl" , setting . AppSubUrl , "socket" , setting . SocketPath )
2016-12-21 21:36:32 +08:00
2017-02-06 16:40:07 +08:00
hs . httpSrv = & http . Server { Addr : listenAddr , Handler : hs . macaron }
2018-05-01 20:13:38 +08:00
// handle http shutdown on server context done
go func ( ) {
<- ctx . Done ( )
2018-05-03 00:10:21 +08:00
// Hacky fix for race condition between ListenAndServe and Shutdown
time . Sleep ( time . Millisecond * 100 )
2018-05-01 20:13:38 +08:00
if err := hs . httpSrv . Shutdown ( context . Background ( ) ) ; err != nil {
hs . log . Error ( "Failed to shutdown server" , "error" , err )
}
} ( )
2016-12-21 21:36:32 +08:00
switch setting . Protocol {
case setting . HTTP :
2019-10-22 19:07:43 +08:00
err = hs . httpSrv . Serve ( listener )
2017-02-06 16:40:07 +08:00
if err == http . ErrServerClosed {
hs . log . Debug ( "server was shutdown gracefully" )
return nil
}
2019-08-16 23:06:54 +08:00
case setting . HTTP2 :
2019-10-22 19:07:43 +08:00
err = hs . listenAndServeH2TLS ( listener , setting . CertFile , setting . KeyFile )
2019-08-16 23:06:54 +08:00
if err == http . ErrServerClosed {
hs . log . Debug ( "server was shutdown gracefully" )
return nil
}
2016-12-21 21:36:32 +08:00
case setting . HTTPS :
2019-10-22 19:07:43 +08:00
err = hs . listenAndServeTLS ( listener , setting . CertFile , setting . KeyFile )
2017-02-06 16:40:07 +08:00
if err == http . ErrServerClosed {
hs . log . Debug ( "server was shutdown gracefully" )
return nil
}
2017-04-27 14:54:21 +08:00
case setting . SOCKET :
2018-03-22 21:14:44 +08:00
ln , err := net . ListenUnix ( "unix" , & net . UnixAddr { Name : setting . SocketPath , Net : "unix" } )
2017-04-27 14:54:21 +08:00
if err != nil {
2019-10-09 00:57:53 +08:00
hs . log . Debug ( "server was shutdown gracefully" , "err" , err )
2017-04-27 14:54:21 +08:00
return nil
}
2018-03-22 21:14:44 +08:00
// Make socket writable by group
2019-10-09 00:57:53 +08:00
if err := os . Chmod ( setting . SocketPath , 0660 ) ; err != nil {
hs . log . Debug ( "server was shutdown gracefully" , "err" , err )
return nil
}
2018-03-22 21:14:44 +08:00
2017-04-27 14:54:21 +08:00
err = hs . httpSrv . Serve ( ln )
if err != nil {
2019-10-09 00:57:53 +08:00
hs . log . Debug ( "server was shutdown gracefully" , "err" , err )
2017-04-27 14:54:21 +08:00
return nil
}
2016-12-21 21:36:32 +08:00
default :
hs . log . Error ( "Invalid protocol" , "protocol" , setting . Protocol )
err = errors . New ( "Invalid Protocol" )
}
return err
}
2019-10-22 19:07:43 +08:00
func ( hs * HTTPServer ) listenAndServeTLS ( listener net . Listener , certfile , keyfile string ) error {
2016-12-21 21:36:32 +08:00
if certfile == "" {
return fmt . Errorf ( "cert_file cannot be empty when using HTTPS" )
}
if keyfile == "" {
return fmt . Errorf ( "cert_key cannot be empty when using HTTPS" )
}
if _ , err := os . Stat ( setting . CertFile ) ; os . IsNotExist ( err ) {
return fmt . Errorf ( ` Cannot find SSL cert_file at %v ` , setting . CertFile )
}
if _ , err := os . Stat ( setting . KeyFile ) ; os . IsNotExist ( err ) {
return fmt . Errorf ( ` Cannot find SSL key_file at %v ` , setting . KeyFile )
}
2017-02-15 21:29:20 +08:00
tlsCfg := & tls . Config {
MinVersion : tls . VersionTLS12 ,
PreferServerCipherSuites : true ,
CipherSuites : [ ] uint16 {
tls . TLS_RSA_WITH_AES_128_CBC_SHA ,
tls . TLS_RSA_WITH_AES_256_CBC_SHA ,
tls . TLS_RSA_WITH_AES_128_GCM_SHA256 ,
tls . TLS_RSA_WITH_AES_256_GCM_SHA384 ,
tls . TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA ,
tls . TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA ,
tls . TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA ,
tls . TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA ,
tls . TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 ,
tls . TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 ,
tls . TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 ,
tls . TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 ,
} ,
}
2017-06-18 05:10:12 +08:00
hs . httpSrv . TLSConfig = tlsCfg
2018-04-17 02:22:12 +08:00
hs . httpSrv . TLSNextProto = make ( map [ string ] func ( * http . Server , * tls . Conn , http . Handler ) )
2017-06-18 05:10:12 +08:00
2019-10-22 19:07:43 +08:00
return hs . httpSrv . ServeTLS ( listener , setting . CertFile , setting . KeyFile )
2016-12-21 21:36:32 +08:00
}
2019-10-22 19:07:43 +08:00
func ( hs * HTTPServer ) listenAndServeH2TLS ( listener net . Listener , certfile , keyfile string ) error {
2019-08-16 23:06:54 +08:00
if certfile == "" {
return fmt . Errorf ( "cert_file cannot be empty when using HTTP2" )
}
if keyfile == "" {
return fmt . Errorf ( "cert_key cannot be empty when using HTTP2" )
}
if _ , err := os . Stat ( setting . CertFile ) ; os . IsNotExist ( err ) {
return fmt . Errorf ( ` Cannot find SSL cert_file at %v ` , setting . CertFile )
}
if _ , err := os . Stat ( setting . KeyFile ) ; os . IsNotExist ( err ) {
return fmt . Errorf ( ` Cannot find SSL key_file at %v ` , setting . KeyFile )
}
tlsCfg := & tls . Config {
MinVersion : tls . VersionTLS12 ,
PreferServerCipherSuites : false ,
CipherSuites : [ ] uint16 {
tls . TLS_CHACHA20_POLY1305_SHA256 ,
tls . TLS_AES_128_GCM_SHA256 ,
tls . TLS_AES_256_GCM_SHA384 ,
tls . TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305 ,
tls . TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 ,
tls . TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 ,
tls . TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305 ,
tls . TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 ,
tls . TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 ,
} ,
NextProtos : [ ] string { "h2" , "http/1.1" } ,
}
hs . httpSrv . TLSConfig = tlsCfg
2019-10-22 19:07:43 +08:00
return hs . httpSrv . ServeTLS ( listener , setting . CertFile , setting . KeyFile )
2019-08-16 23:06:54 +08:00
}
2018-03-23 05:13:46 +08:00
func ( hs * HTTPServer ) newMacaron ( ) * macaron . Macaron {
2016-12-21 21:36:32 +08:00
macaron . Env = setting . Env
m := macaron . New ( )
2018-07-02 14:35:50 +08:00
// automatically set HEAD for every GET
m . SetAutoHead ( true )
return m
}
func ( hs * HTTPServer ) applyRoutes ( ) {
// start with middlewares & static routes
hs . addMiddlewaresAndStaticRoutes ( )
// then add view routes & api routes
hs . RouteRegister . Register ( hs . macaron )
// then custom app proxy routes
hs . initAppPluginRoutes ( hs . macaron )
// lastly not found route
2018-10-12 17:26:42 +08:00
hs . macaron . NotFound ( hs . NotFoundHandler )
2018-07-02 14:35:50 +08:00
}
func ( hs * HTTPServer ) addMiddlewaresAndStaticRoutes ( ) {
m := hs . macaron
2016-12-21 21:36:32 +08:00
m . Use ( middleware . Logger ( ) )
if setting . EnableGzip {
m . Use ( middleware . Gziper ( ) )
}
2017-11-15 20:51:15 +08:00
m . Use ( middleware . Recovery ( ) )
2016-12-21 21:36:32 +08:00
for _ , route := range plugins . StaticRoutes {
pluginRoute := path . Join ( "/public/plugins/" , route . PluginId )
2017-10-12 21:29:01 +08:00
hs . log . Debug ( "Plugins: Adding route" , "route" , pluginRoute , "dir" , route . Directory )
2018-07-02 14:35:50 +08:00
hs . mapStatic ( hs . macaron , route . Directory , "" , pluginRoute )
2016-12-21 21:36:32 +08:00
}
2018-05-08 19:54:00 +08:00
hs . mapStatic ( m , setting . StaticRootPath , "build" , "public/build" )
2016-12-21 21:36:32 +08:00
hs . mapStatic ( m , setting . StaticRootPath , "" , "public" )
hs . mapStatic ( m , setting . StaticRootPath , "robots.txt" , "robots.txt" )
2017-12-31 06:01:51 +08:00
if setting . ImageUploadProvider == "local" {
2018-05-24 21:26:27 +08:00
hs . mapStatic ( m , hs . Cfg . ImagesDir , "" , "/public/img/attachments" )
2017-12-31 06:01:51 +08:00
}
2019-05-06 15:22:59 +08:00
m . Use ( middleware . AddDefaultResponseHeaders ( ) )
2019-05-27 23:47:29 +08:00
if setting . ServeFromSubPath && setting . AppSubUrl != "" {
m . SetURLPrefix ( setting . AppSubUrl )
}
2016-12-21 21:36:32 +08:00
m . Use ( macaron . Renderer ( macaron . RenderOptions {
Directory : path . Join ( setting . StaticRootPath , "views" ) ,
IndentJSON : macaron . Env != macaron . PROD ,
Delims : macaron . Delims { Left : "[[" , Right : "]]" } ,
} ) )
2017-04-25 23:17:45 +08:00
m . Use ( hs . healthHandler )
2017-09-07 04:24:10 +08:00
m . Use ( hs . metricsEndpoint )
2019-04-08 19:31:46 +08:00
m . Use ( middleware . GetContextHandler (
hs . AuthTokenService ,
hs . RemoteCacheService ,
) )
2017-02-17 22:02:14 +08:00
m . Use ( middleware . OrgRedirect ( ) )
2016-12-21 21:36:32 +08:00
// needs to be after context handler
if setting . EnforceDomain {
m . Use ( middleware . ValidateHostHeader ( setting . Domain ) )
}
2018-10-26 16:40:33 +08:00
m . Use ( middleware . HandleNoCacheHeader ( ) )
2016-12-21 21:36:32 +08:00
}
2018-03-23 05:13:46 +08:00
func ( hs * HTTPServer ) metricsEndpoint ( ctx * macaron . Context ) {
2018-09-13 20:36:16 +08:00
if ! hs . Cfg . MetricsEndpointEnabled {
return
}
2019-06-12 13:27:47 +08:00
if ctx . Req . Method != http . MethodGet || ctx . Req . URL . Path != "/metrics" {
2017-09-07 04:24:10 +08:00
return
}
2018-11-20 02:15:18 +08:00
if hs . metricsEndpointBasicAuthEnabled ( ) && ! BasicAuthenticatedRequest ( ctx . Req , hs . Cfg . MetricsEndpointBasicAuthUsername , hs . Cfg . MetricsEndpointBasicAuthPassword ) {
2018-11-15 06:37:32 +08:00
ctx . Resp . WriteHeader ( http . StatusUnauthorized )
return
2018-11-15 04:42:47 +08:00
}
2019-07-15 13:33:48 +08:00
promhttp .
HandlerFor ( prometheus . DefaultGatherer , promhttp . HandlerOpts { } ) .
2017-10-23 15:35:46 +08:00
ServeHTTP ( ctx . Resp , ctx . Req . Request )
2017-09-07 04:24:10 +08:00
}
2018-03-23 05:13:46 +08:00
func ( hs * HTTPServer ) healthHandler ( ctx * macaron . Context ) {
2017-11-21 22:01:59 +08:00
notHeadOrGet := ctx . Req . Method != http . MethodGet && ctx . Req . Method != http . MethodHead
if notHeadOrGet || ctx . Req . URL . Path != "/api/health" {
2017-04-25 23:17:45 +08:00
return
}
data := simplejson . New ( )
2017-04-25 23:23:38 +08:00
data . Set ( "database" , "ok" )
2017-04-25 23:17:45 +08:00
data . Set ( "version" , setting . BuildVersion )
data . Set ( "commit" , setting . BuildCommit )
if err := bus . Dispatch ( & models . GetDBHealthQuery { } ) ; err != nil {
2017-04-25 23:24:36 +08:00
data . Set ( "database" , "failing" )
2017-05-10 21:23:59 +08:00
ctx . Resp . Header ( ) . Set ( "Content-Type" , "application/json; charset=UTF-8" )
ctx . Resp . WriteHeader ( 503 )
} else {
ctx . Resp . Header ( ) . Set ( "Content-Type" , "application/json; charset=UTF-8" )
ctx . Resp . WriteHeader ( 200 )
2017-04-25 23:17:45 +08:00
}
dataBytes , _ := data . EncodePretty ( )
2019-10-09 00:57:53 +08:00
if _ , err := ctx . Resp . Write ( dataBytes ) ; err != nil {
hs . log . Error ( "Failed to write to response" , "err" , err )
}
2017-04-25 23:17:45 +08:00
}
2018-03-23 05:13:46 +08:00
func ( hs * HTTPServer ) mapStatic ( m * macaron . Macaron , rootDir string , dir string , prefix string ) {
2016-12-21 21:36:32 +08:00
headers := func ( c * macaron . Context ) {
c . Resp . Header ( ) . Set ( "Cache-Control" , "public, max-age=3600" )
}
2018-05-08 19:54:00 +08:00
if prefix == "public/build" {
headers = func ( c * macaron . Context ) {
c . Resp . Header ( ) . Set ( "Cache-Control" , "public, max-age=31536000" )
}
}
2016-12-21 21:36:32 +08:00
if setting . Env == setting . DEV {
headers = func ( c * macaron . Context ) {
c . Resp . Header ( ) . Set ( "Cache-Control" , "max-age=0, must-revalidate, no-cache" )
}
}
m . Use ( httpstatic . Static (
path . Join ( rootDir , dir ) ,
httpstatic . StaticOptions {
SkipLogging : true ,
Prefix : prefix ,
AddHeaders : headers ,
} ,
) )
}
2018-11-20 02:15:18 +08:00
func ( hs * HTTPServer ) metricsEndpointBasicAuthEnabled ( ) bool {
return hs . Cfg . MetricsEndpointBasicAuthUsername != "" && hs . Cfg . MetricsEndpointBasicAuthPassword != ""
}