2020-11-13 15:51:07 +03:00
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package routes
import (
"errors"
"fmt"
"io"
"net/http"
"os"
"path"
"strings"
"time"
2021-01-26 18:36:53 +03:00
"code.gitea.io/gitea/modules/auth/sso"
2021-01-20 04:47:43 +03:00
"code.gitea.io/gitea/modules/context"
2020-11-18 01:44:52 +03:00
"code.gitea.io/gitea/modules/httpcache"
2020-11-13 15:51:07 +03:00
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage"
2021-01-26 18:36:53 +03:00
"code.gitea.io/gitea/modules/templates"
2021-01-30 11:55:53 +03:00
"code.gitea.io/gitea/modules/web/middleware"
2020-11-13 15:51:07 +03:00
2021-01-05 16:05:40 +03:00
"gitea.com/go-chi/session"
2020-11-13 15:51:07 +03:00
)
// LoggerHandler is a handler that will log the routing to the default gitea log
func LoggerHandler ( level log . Level ) func ( next http . Handler ) http . Handler {
return func ( next http . Handler ) http . Handler {
return http . HandlerFunc ( func ( w http . ResponseWriter , req * http . Request ) {
start := time . Now ( )
2021-01-13 23:30:46 +03:00
_ = log . GetLogger ( "router" ) . Log ( 0 , level , "Started %s %s for %s" , log . ColoredMethod ( req . Method ) , req . URL . RequestURI ( ) , req . RemoteAddr )
2020-11-13 15:51:07 +03:00
next . ServeHTTP ( w , req )
2021-01-20 04:47:43 +03:00
var status int
if v , ok := w . ( context . ResponseWriter ) ; ok {
status = v . Status ( )
}
2020-11-13 15:51:07 +03:00
2021-01-13 23:30:46 +03:00
_ = log . GetLogger ( "router" ) . Log ( 0 , level , "Completed %s %s %v %s in %v" , log . ColoredMethod ( req . Method ) , req . URL . RequestURI ( ) , log . ColoredStatus ( status ) , log . ColoredStatus ( status , http . StatusText ( status ) ) , log . ColoredTime ( time . Since ( start ) ) )
2020-11-13 15:51:07 +03:00
} )
}
}
func storageHandler ( storageSetting setting . Storage , prefix string , objStore storage . ObjectStorage ) func ( next http . Handler ) http . Handler {
return func ( next http . Handler ) http . Handler {
if storageSetting . ServeDirect {
return http . HandlerFunc ( func ( w http . ResponseWriter , req * http . Request ) {
if req . Method != "GET" && req . Method != "HEAD" {
next . ServeHTTP ( w , req )
return
}
2021-01-13 23:30:46 +03:00
if ! strings . HasPrefix ( req . URL . RequestURI ( ) , "/" + prefix ) {
2020-11-13 15:51:07 +03:00
next . ServeHTTP ( w , req )
return
}
2021-01-13 23:30:46 +03:00
rPath := strings . TrimPrefix ( req . URL . RequestURI ( ) , "/" + prefix )
2020-11-13 15:51:07 +03:00
u , err := objStore . URL ( rPath , path . Base ( rPath ) )
if err != nil {
if os . IsNotExist ( err ) || errors . Is ( err , os . ErrNotExist ) {
log . Warn ( "Unable to find %s %s" , prefix , rPath )
http . Error ( w , "file not found" , 404 )
return
}
log . Error ( "Error whilst getting URL for %s %s. Error: %v" , prefix , rPath , err )
http . Error ( w , fmt . Sprintf ( "Error whilst getting URL for %s %s" , prefix , rPath ) , 500 )
return
}
http . Redirect (
w ,
req ,
u . String ( ) ,
301 ,
)
} )
}
return http . HandlerFunc ( func ( w http . ResponseWriter , req * http . Request ) {
if req . Method != "GET" && req . Method != "HEAD" {
next . ServeHTTP ( w , req )
return
}
2021-01-13 23:30:46 +03:00
if ! strings . HasPrefix ( req . URL . RequestURI ( ) , "/" + prefix ) {
2020-11-13 15:51:07 +03:00
next . ServeHTTP ( w , req )
return
}
2021-01-13 23:30:46 +03:00
rPath := strings . TrimPrefix ( req . URL . RequestURI ( ) , "/" + prefix )
2020-11-13 15:51:07 +03:00
rPath = strings . TrimPrefix ( rPath , "/" )
2020-11-18 01:44:52 +03:00
fi , err := objStore . Stat ( rPath )
if err == nil && httpcache . HandleTimeCache ( req , w , fi ) {
return
}
2020-11-13 15:51:07 +03:00
//If we have matched and access to release or issue
fr , err := objStore . Open ( rPath )
if err != nil {
if os . IsNotExist ( err ) || errors . Is ( err , os . ErrNotExist ) {
log . Warn ( "Unable to find %s %s" , prefix , rPath )
http . Error ( w , "file not found" , 404 )
return
}
log . Error ( "Error whilst opening %s %s. Error: %v" , prefix , rPath , err )
http . Error ( w , fmt . Sprintf ( "Error whilst opening %s %s" , prefix , rPath ) , 500 )
return
}
defer fr . Close ( )
_ , err = io . Copy ( w , fr )
if err != nil {
log . Error ( "Error whilst rendering %s %s. Error: %v" , prefix , rPath , err )
http . Error ( w , fmt . Sprintf ( "Error whilst rendering %s %s" , prefix , rPath ) , 500 )
return
}
} )
}
}
2021-01-26 18:36:53 +03:00
type dataStore struct {
Data map [ string ] interface { }
2020-11-13 15:51:07 +03:00
}
2021-01-26 18:36:53 +03:00
func ( d * dataStore ) GetData ( ) map [ string ] interface { } {
return d . Data
2020-11-13 15:51:07 +03:00
}
2021-01-26 18:36:53 +03:00
// Recovery returns a middleware that recovers from any panics and writes a 500 and a log if so.
// This error will be created with the gitea 500 page.
func Recovery ( ) func ( next http . Handler ) http . Handler {
var rnd = templates . HTMLRenderer ( )
return func ( next http . Handler ) http . Handler {
return http . HandlerFunc ( func ( w http . ResponseWriter , req * http . Request ) {
defer func ( ) {
if err := recover ( ) ; err != nil {
combinedErr := fmt . Sprintf ( "PANIC: %v\n%s" , err , string ( log . Stack ( 2 ) ) )
log . Error ( "%v" , combinedErr )
sessionStore := session . GetSession ( req )
if sessionStore == nil {
if setting . IsProd ( ) {
http . Error ( w , http . StatusText ( 500 ) , 500 )
} else {
http . Error ( w , combinedErr , 500 )
}
return
}
2020-11-16 10:33:41 +03:00
2021-01-30 11:55:53 +03:00
var lc = middleware . Locale ( w , req )
2021-01-26 18:36:53 +03:00
var store = dataStore {
Data : templates . Vars {
"Language" : lc . Language ( ) ,
"CurrentURL" : setting . AppSubURL + req . URL . RequestURI ( ) ,
"i18n" : lc ,
} ,
}
2020-11-13 15:51:07 +03:00
2021-01-26 18:36:53 +03:00
// Get user from session if logged in.
user , _ := sso . SignedInUser ( req , w , & store , sessionStore )
if user != nil {
store . Data [ "IsSigned" ] = true
store . Data [ "SignedUser" ] = user
store . Data [ "SignedUserID" ] = user . ID
store . Data [ "SignedUserName" ] = user . Name
store . Data [ "IsAdmin" ] = user . IsAdmin
} else {
store . Data [ "SignedUserID" ] = int64 ( 0 )
store . Data [ "SignedUserName" ] = ""
}
2020-11-13 15:51:07 +03:00
2021-01-26 18:36:53 +03:00
w . Header ( ) . Set ( ` X-Frame-Options ` , ` SAMEORIGIN ` )
2020-11-17 23:50:06 +03:00
2021-01-26 18:36:53 +03:00
if ! setting . IsProd ( ) {
store . Data [ "ErrorMsg" ] = combinedErr
}
err = rnd . HTML ( w , 500 , "status/500" , templates . BaseVars ( ) . Merge ( store . Data ) )
if err != nil {
log . Error ( "%v" , err )
}
}
} ( )
2020-11-17 23:50:06 +03:00
2021-01-26 18:36:53 +03:00
next . ServeHTTP ( w , req )
} )
2020-11-17 23:50:06 +03:00
}
2020-11-13 15:51:07 +03:00
}