2016-12-21 07:36:32 -06:00
package api
import (
"context"
2017-02-15 07:29:20 -06:00
"crypto/tls"
2016-12-21 07:36:32 -06:00
"errors"
"fmt"
2017-04-27 01:54:21 -05:00
"net"
2016-12-21 07:36:32 -06:00
"net/http"
"os"
"path"
2017-08-23 06:31:26 -05:00
"time"
2016-12-21 07:36:32 -06:00
"github.com/grafana/grafana/pkg/api/live"
2019-01-16 07:53:59 -06:00
"github.com/grafana/grafana/pkg/api/routing"
2016-12-21 07:36:32 -06:00
httpstatic "github.com/grafana/grafana/pkg/api/static"
2017-04-25 10:17:45 -05:00
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
2019-06-13 03:55:38 -05:00
"github.com/grafana/grafana/pkg/infra/localcache"
2019-05-13 01:45:54 -05:00
"github.com/grafana/grafana/pkg/infra/log"
2019-04-08 06:31:46 -05:00
"github.com/grafana/grafana/pkg/infra/remotecache"
2016-12-21 07:36:32 -06:00
"github.com/grafana/grafana/pkg/middleware"
2017-04-25 10:17:45 -05:00
"github.com/grafana/grafana/pkg/models"
2016-12-21 07:36:32 -06:00
"github.com/grafana/grafana/pkg/plugins"
2018-05-01 08:51:15 -05:00
"github.com/grafana/grafana/pkg/registry"
2018-10-26 03:40:33 -05:00
"github.com/grafana/grafana/pkg/services/datasources"
2018-10-12 04:26:42 -05:00
"github.com/grafana/grafana/pkg/services/hooks"
2019-06-13 09:47:52 -05:00
"github.com/grafana/grafana/pkg/services/login"
2019-02-11 14:12:01 -06:00
"github.com/grafana/grafana/pkg/services/quota"
2018-05-24 08:26:27 -05:00
"github.com/grafana/grafana/pkg/services/rendering"
2016-12-21 07:36:32 -06:00
"github.com/grafana/grafana/pkg/setting"
2019-11-18 02:49:08 -06:00
"github.com/grafana/grafana/pkg/util/errutil"
2019-01-16 07:53:59 -06:00
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
2019-05-27 10:47:29 -05:00
macaron "gopkg.in/macaron.v1"
2016-12-21 07:36:32 -06:00
)
2018-05-01 08:51:15 -05:00
func init ( ) {
2018-07-01 09:01:43 -05:00
registry . Register ( & registry . Descriptor {
Name : "HTTPServer" ,
Instance : & HTTPServer { } ,
InitPriority : registry . High ,
} )
2018-05-01 08:51:15 -05:00
}
2019-04-30 06:32:18 -05:00
type ProvisioningService interface {
ProvisionDatasources ( ) error
ProvisionNotifications ( ) error
ProvisionDashboards ( ) error
GetDashboardProvisionerResolvedPath ( name string ) string
2019-10-31 08:27:31 -05:00
GetAllowUiUpdatesFromConfig ( name string ) bool
2019-04-30 06:32:18 -05:00
}
2018-03-22 16:13:46 -05:00
type HTTPServer struct {
2016-12-21 07:36:32 -06:00
log log . Logger
macaron * macaron . Macaron
context context . Context
streamManager * live . StreamManager
2018-04-30 09:21:04 -05:00
httpSrv * http . Server
2017-02-06 02:40:07 -06:00
2019-04-30 06:32:18 -05:00
RouteRegister routing . RouteRegister ` inject:"" `
Bus bus . Bus ` inject:"" `
RenderService rendering . Service ` inject:"" `
Cfg * setting . Cfg ` inject:"" `
HooksService * hooks . HooksService ` inject:"" `
2019-06-13 03:55:38 -05:00
CacheService * localcache . CacheService ` inject:"" `
2019-04-30 06:32:18 -05:00
DatasourceCache datasources . CacheService ` inject:"" `
AuthTokenService models . UserTokenService ` inject:"" `
QuotaService * quota . QuotaService ` inject:"" `
RemoteCacheService * remotecache . RemoteCache ` inject:"" `
ProvisioningService ProvisioningService ` inject:"" `
2019-06-13 09:47:52 -05:00
Login * login . LoginService ` inject:"" `
2019-11-01 08:56:12 -05:00
License models . Licensing ` inject:"" `
2016-12-21 07:36:32 -06:00
}
2018-05-01 08:51:15 -05:00
func ( hs * HTTPServer ) Init ( ) error {
2018-04-27 06:41:58 -05:00
hs . log = log . New ( "http.server" )
2018-05-01 08:51:15 -05:00
2018-07-01 09:01:43 -05:00
hs . streamManager = live . NewStreamManager ( )
hs . macaron = hs . newMacaron ( )
hs . registerRoutes ( )
2018-05-01 08:51:15 -05:00
return nil
2016-12-21 07:36:32 -06:00
}
2018-05-01 08:51:15 -05:00
func ( hs * HTTPServer ) Run ( ctx context . Context ) error {
2016-12-21 07:36:32 -06:00
var err error
hs . context = ctx
2018-07-02 01:35:50 -05:00
2018-07-01 09:01:43 -05:00
hs . applyRoutes ( )
2016-12-21 07:36:32 -06:00
hs . streamManager . Run ( ctx )
listenAddr := fmt . Sprintf ( "%s:%s" , setting . HttpAddr , setting . HttpPort )
2019-10-22 06:07:43 -05:00
listener , err := net . Listen ( "tcp" , listenAddr )
if err != nil {
2019-11-18 02:49:08 -06:00
return errutil . Wrapf ( err , "failed to open listener on address %s" , listenAddr )
2019-10-22 06:07:43 -05:00
}
hs . log . Info ( "HTTP Server Listen" , "address" , listener . Addr ( ) . String ( ) , "protocol" , setting . Protocol , "subUrl" , setting . AppSubUrl , "socket" , setting . SocketPath )
2016-12-21 07:36:32 -06:00
2017-02-06 02:40:07 -06:00
hs . httpSrv = & http . Server { Addr : listenAddr , Handler : hs . macaron }
2018-05-01 07:13:38 -05:00
// handle http shutdown on server context done
go func ( ) {
<- ctx . Done ( )
2018-05-02 11:10:21 -05:00
// Hacky fix for race condition between ListenAndServe and Shutdown
time . Sleep ( time . Millisecond * 100 )
2018-05-01 07:13:38 -05:00
if err := hs . httpSrv . Shutdown ( context . Background ( ) ) ; err != nil {
hs . log . Error ( "Failed to shutdown server" , "error" , err )
}
} ( )
2016-12-21 07:36:32 -06:00
switch setting . Protocol {
case setting . HTTP :
2019-10-22 06:07:43 -05:00
err = hs . httpSrv . Serve ( listener )
2017-02-06 02:40:07 -06:00
if err == http . ErrServerClosed {
hs . log . Debug ( "server was shutdown gracefully" )
return nil
}
2019-08-16 10:06:54 -05:00
case setting . HTTP2 :
2019-10-22 06:07:43 -05:00
err = hs . listenAndServeH2TLS ( listener , setting . CertFile , setting . KeyFile )
2019-08-16 10:06:54 -05:00
if err == http . ErrServerClosed {
hs . log . Debug ( "server was shutdown gracefully" )
return nil
}
2016-12-21 07:36:32 -06:00
case setting . HTTPS :
2019-10-22 06:07:43 -05:00
err = hs . listenAndServeTLS ( listener , setting . CertFile , setting . KeyFile )
2017-02-06 02:40:07 -06:00
if err == http . ErrServerClosed {
hs . log . Debug ( "server was shutdown gracefully" )
return nil
}
2017-04-27 01:54:21 -05:00
case setting . SOCKET :
2018-03-22 08:14:44 -05:00
ln , err := net . ListenUnix ( "unix" , & net . UnixAddr { Name : setting . SocketPath , Net : "unix" } )
2017-04-27 01:54:21 -05:00
if err != nil {
2019-10-08 11:57:53 -05:00
hs . log . Debug ( "server was shutdown gracefully" , "err" , err )
2017-04-27 01:54:21 -05:00
return nil
}
2018-03-22 08:14:44 -05:00
// Make socket writable by group
2019-10-08 11:57:53 -05:00
if err := os . Chmod ( setting . SocketPath , 0660 ) ; err != nil {
hs . log . Debug ( "server was shutdown gracefully" , "err" , err )
return nil
}
2018-03-22 08:14:44 -05:00
2017-04-27 01:54:21 -05:00
err = hs . httpSrv . Serve ( ln )
if err != nil {
2019-10-08 11:57:53 -05:00
hs . log . Debug ( "server was shutdown gracefully" , "err" , err )
2017-04-27 01:54:21 -05:00
return nil
}
2016-12-21 07:36:32 -06:00
default :
hs . log . Error ( "Invalid protocol" , "protocol" , setting . Protocol )
err = errors . New ( "Invalid Protocol" )
}
return err
}
2019-10-22 06:07:43 -05:00
func ( hs * HTTPServer ) listenAndServeTLS ( listener net . Listener , certfile , keyfile string ) error {
2016-12-21 07:36:32 -06: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 07:29:20 -06: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-17 16:10:12 -05:00
hs . httpSrv . TLSConfig = tlsCfg
2018-04-16 13:22:12 -05:00
hs . httpSrv . TLSNextProto = make ( map [ string ] func ( * http . Server , * tls . Conn , http . Handler ) )
2017-06-17 16:10:12 -05:00
2019-10-22 06:07:43 -05:00
return hs . httpSrv . ServeTLS ( listener , setting . CertFile , setting . KeyFile )
2016-12-21 07:36:32 -06:00
}
2019-10-22 06:07:43 -05:00
func ( hs * HTTPServer ) listenAndServeH2TLS ( listener net . Listener , certfile , keyfile string ) error {
2019-08-16 10:06:54 -05: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 06:07:43 -05:00
return hs . httpSrv . ServeTLS ( listener , setting . CertFile , setting . KeyFile )
2019-08-16 10:06:54 -05:00
}
2018-03-22 16:13:46 -05:00
func ( hs * HTTPServer ) newMacaron ( ) * macaron . Macaron {
2016-12-21 07:36:32 -06:00
macaron . Env = setting . Env
m := macaron . New ( )
2018-07-02 01:35:50 -05: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 04:26:42 -05:00
hs . macaron . NotFound ( hs . NotFoundHandler )
2018-07-02 01:35:50 -05:00
}
func ( hs * HTTPServer ) addMiddlewaresAndStaticRoutes ( ) {
m := hs . macaron
2016-12-21 07:36:32 -06:00
m . Use ( middleware . Logger ( ) )
if setting . EnableGzip {
m . Use ( middleware . Gziper ( ) )
}
2017-11-15 06:51:15 -06:00
m . Use ( middleware . Recovery ( ) )
2016-12-21 07:36:32 -06:00
for _ , route := range plugins . StaticRoutes {
pluginRoute := path . Join ( "/public/plugins/" , route . PluginId )
2017-10-12 08:29:01 -05:00
hs . log . Debug ( "Plugins: Adding route" , "route" , pluginRoute , "dir" , route . Directory )
2018-07-02 01:35:50 -05:00
hs . mapStatic ( hs . macaron , route . Directory , "" , pluginRoute )
2016-12-21 07:36:32 -06:00
}
2018-05-08 06:54:00 -05:00
hs . mapStatic ( m , setting . StaticRootPath , "build" , "public/build" )
2016-12-21 07:36:32 -06:00
hs . mapStatic ( m , setting . StaticRootPath , "" , "public" )
hs . mapStatic ( m , setting . StaticRootPath , "robots.txt" , "robots.txt" )
2017-12-30 16:01:51 -06:00
if setting . ImageUploadProvider == "local" {
2018-05-24 08:26:27 -05:00
hs . mapStatic ( m , hs . Cfg . ImagesDir , "" , "/public/img/attachments" )
2017-12-30 16:01:51 -06:00
}
2019-05-06 02:22:59 -05:00
m . Use ( middleware . AddDefaultResponseHeaders ( ) )
2019-05-27 10:47:29 -05:00
if setting . ServeFromSubPath && setting . AppSubUrl != "" {
m . SetURLPrefix ( setting . AppSubUrl )
}
2016-12-21 07:36:32 -06: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 10:17:45 -05:00
m . Use ( hs . healthHandler )
2017-09-06 15:24:10 -05:00
m . Use ( hs . metricsEndpoint )
2019-04-08 06:31:46 -05:00
m . Use ( middleware . GetContextHandler (
hs . AuthTokenService ,
hs . RemoteCacheService ,
) )
2017-02-17 08:02:14 -06:00
m . Use ( middleware . OrgRedirect ( ) )
2016-12-21 07:36:32 -06:00
// needs to be after context handler
if setting . EnforceDomain {
m . Use ( middleware . ValidateHostHeader ( setting . Domain ) )
}
2018-10-26 03:40:33 -05:00
m . Use ( middleware . HandleNoCacheHeader ( ) )
2016-12-21 07:36:32 -06:00
}
2018-03-22 16:13:46 -05:00
func ( hs * HTTPServer ) metricsEndpoint ( ctx * macaron . Context ) {
2018-09-13 07:36:16 -05:00
if ! hs . Cfg . MetricsEndpointEnabled {
return
}
2019-06-12 00:27:47 -05:00
if ctx . Req . Method != http . MethodGet || ctx . Req . URL . Path != "/metrics" {
2017-09-06 15:24:10 -05:00
return
}
2018-11-19 12:15:18 -06:00
if hs . metricsEndpointBasicAuthEnabled ( ) && ! BasicAuthenticatedRequest ( ctx . Req , hs . Cfg . MetricsEndpointBasicAuthUsername , hs . Cfg . MetricsEndpointBasicAuthPassword ) {
2018-11-14 16:37:32 -06:00
ctx . Resp . WriteHeader ( http . StatusUnauthorized )
return
2018-11-14 14:42:47 -06:00
}
2019-07-15 00:33:48 -05:00
promhttp .
HandlerFor ( prometheus . DefaultGatherer , promhttp . HandlerOpts { } ) .
2017-10-23 02:35:46 -05:00
ServeHTTP ( ctx . Resp , ctx . Req . Request )
2017-09-06 15:24:10 -05:00
}
2018-03-22 16:13:46 -05:00
func ( hs * HTTPServer ) healthHandler ( ctx * macaron . Context ) {
2017-11-21 08:01:59 -06:00
notHeadOrGet := ctx . Req . Method != http . MethodGet && ctx . Req . Method != http . MethodHead
if notHeadOrGet || ctx . Req . URL . Path != "/api/health" {
2017-04-25 10:17:45 -05:00
return
}
data := simplejson . New ( )
2017-04-25 10:23:38 -05:00
data . Set ( "database" , "ok" )
2017-04-25 10:17:45 -05:00
data . Set ( "version" , setting . BuildVersion )
data . Set ( "commit" , setting . BuildCommit )
if err := bus . Dispatch ( & models . GetDBHealthQuery { } ) ; err != nil {
2017-04-25 10:24:36 -05:00
data . Set ( "database" , "failing" )
2017-05-10 08:23:59 -05: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 10:17:45 -05:00
}
dataBytes , _ := data . EncodePretty ( )
2019-10-08 11:57:53 -05:00
if _ , err := ctx . Resp . Write ( dataBytes ) ; err != nil {
hs . log . Error ( "Failed to write to response" , "err" , err )
}
2017-04-25 10:17:45 -05:00
}
2018-03-22 16:13:46 -05:00
func ( hs * HTTPServer ) mapStatic ( m * macaron . Macaron , rootDir string , dir string , prefix string ) {
2016-12-21 07:36:32 -06:00
headers := func ( c * macaron . Context ) {
c . Resp . Header ( ) . Set ( "Cache-Control" , "public, max-age=3600" )
}
2018-05-08 06:54:00 -05:00
if prefix == "public/build" {
headers = func ( c * macaron . Context ) {
c . Resp . Header ( ) . Set ( "Cache-Control" , "public, max-age=31536000" )
}
}
2016-12-21 07:36:32 -06: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-19 12:15:18 -06:00
func ( hs * HTTPServer ) metricsEndpointBasicAuthEnabled ( ) bool {
return hs . Cfg . MetricsEndpointBasicAuthUsername != "" && hs . Cfg . MetricsEndpointBasicAuthPassword != ""
}