2014-02-19 13:50:53 +04:00
// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
2014-05-02 05:21:46 +04:00
package cmd
2014-02-19 13:50:53 +04:00
import (
2019-12-15 12:51:28 +03:00
"context"
2014-02-19 13:50:53 +04:00
"fmt"
2020-07-26 23:31:28 +03:00
"net"
2014-02-19 13:50:53 +04:00
"net/http"
2017-02-05 16:06:25 +03:00
_ "net/http/pprof" // Used for debugging if enabled and a web server is running
2014-04-16 04:01:20 +04:00
"os"
2014-09-29 13:38:46 +04:00
"strings"
2014-02-19 13:50:53 +04:00
2019-10-23 18:32:19 +03:00
"code.gitea.io/gitea/modules/graceful"
2016-11-10 19:24:48 +03:00
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/routers"
2017-04-25 10:24:51 +03:00
"code.gitea.io/gitea/routers/routes"
2016-12-26 04:16:37 +03:00
2017-02-27 04:49:05 +03:00
context2 "github.com/gorilla/context"
2019-08-23 19:40:30 +03:00
"github.com/unknwon/com"
2016-11-05 19:56:35 +03:00
"github.com/urfave/cli"
2018-08-21 16:56:50 +03:00
"golang.org/x/crypto/acme/autocert"
2017-12-13 11:57:28 +03:00
ini "gopkg.in/ini.v1"
2014-02-19 13:50:53 +04:00
)
2016-11-04 14:42:18 +03:00
// CmdWeb represents the available web sub-command.
2014-02-19 13:50:53 +04:00
var CmdWeb = cli . Command {
Name : "web" ,
2016-12-21 15:13:17 +03:00
Usage : "Start Gitea web server" ,
Description : ` Gitea web server is the only thing you need to run ,
2014-03-24 15:36:38 +04:00
and it takes care of all the other things for you ` ,
2014-02-19 13:50:53 +04:00
Action : runWeb ,
2015-02-01 20:41:03 +03:00
Flags : [ ] cli . Flag {
2016-11-10 01:18:22 +03:00
cli . StringFlag {
Name : "port, p" ,
Value : "3000" ,
Usage : "Temporary port number to prevent conflict" ,
} ,
2017-01-09 14:54:57 +03:00
cli . StringFlag {
Name : "pid, P" ,
2020-08-15 23:15:27 +03:00
Value : setting . PIDFile ,
2017-01-09 14:54:57 +03:00
Usage : "Custom pid file path" ,
} ,
2015-02-01 20:41:03 +03:00
} ,
2014-02-19 13:50:53 +04:00
}
2017-12-26 01:23:43 +03:00
func runHTTPRedirector ( ) {
source := fmt . Sprintf ( "%s:%s" , setting . HTTPAddr , setting . PortToRedirect )
dest := strings . TrimSuffix ( setting . AppURL , "/" )
log . Info ( "Redirecting: %s to %s" , source , dest )
handler := http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
target := dest + r . URL . Path
if len ( r . URL . RawQuery ) > 0 {
target += "?" + r . URL . RawQuery
}
http . Redirect ( w , r , target , http . StatusTemporaryRedirect )
} )
2019-11-24 05:11:24 +03:00
var err = runHTTP ( "tcp" , source , context2 . ClearHandler ( handler ) )
2017-12-26 01:23:43 +03:00
if err != nil {
2019-04-02 10:48:31 +03:00
log . Fatal ( "Failed to start port redirection: %v" , err )
2017-12-26 01:23:43 +03:00
}
}
2018-08-21 16:56:50 +03:00
func runLetsEncrypt ( listenAddr , domain , directory , email string , m http . Handler ) error {
certManager := autocert . Manager {
Prompt : autocert . AcceptTOS ,
HostPolicy : autocert . HostWhitelist ( domain ) ,
Cache : autocert . DirCache ( directory ) ,
Email : email ,
}
2018-12-11 18:46:12 +03:00
go func ( ) {
log . Info ( "Running Let's Encrypt handler on %s" , setting . HTTPAddr + ":" + setting . PortToRedirect )
2019-10-15 16:39:51 +03:00
// all traffic coming into HTTP will be redirect to HTTPS automatically (LE HTTP-01 validation happens here)
2019-11-24 05:11:24 +03:00
var err = runHTTP ( "tcp" , setting . HTTPAddr + ":" + setting . PortToRedirect , certManager . HTTPHandler ( http . HandlerFunc ( runLetsEncryptFallbackHandler ) ) )
2018-12-11 18:46:12 +03:00
if err != nil {
2019-04-02 10:48:31 +03:00
log . Fatal ( "Failed to start the Let's Encrypt handler on port %s: %v" , setting . PortToRedirect , err )
2018-12-11 18:46:12 +03:00
}
} ( )
2019-11-24 05:11:24 +03:00
return runHTTPSWithTLSConfig ( "tcp" , listenAddr , certManager . TLSConfig ( ) , context2 . ClearHandler ( m ) )
2018-08-21 16:56:50 +03:00
}
func runLetsEncryptFallbackHandler ( w http . ResponseWriter , r * http . Request ) {
if r . Method != "GET" && r . Method != "HEAD" {
http . Error ( w , "Use HTTPS" , http . StatusBadRequest )
return
}
2018-12-13 00:00:24 +03:00
// Remove the trailing slash at the end of setting.AppURL, the request
// URI always contains a leading slash, which would result in a double
// slash
target := strings . TrimRight ( setting . AppURL , "/" ) + r . URL . RequestURI ( )
2018-08-21 16:56:50 +03:00
http . Redirect ( w , r , target , http . StatusFound )
}
2016-05-12 21:32:28 +03:00
func runWeb ( ctx * cli . Context ) error {
2019-12-15 12:51:28 +03:00
managerCtx , cancel := context . WithCancel ( context . Background ( ) )
graceful . InitManager ( managerCtx )
defer cancel ( )
2019-10-15 16:39:51 +03:00
if os . Getppid ( ) > 1 && len ( os . Getenv ( "LISTEN_FDS" ) ) > 0 {
log . Info ( "Restarting Gitea on PID: %d from parent PID: %d" , os . Getpid ( ) , os . Getppid ( ) )
} else {
log . Info ( "Starting Gitea on PID: %d" , os . Getpid ( ) )
}
// Set pid file setting
2017-01-09 14:54:57 +03:00
if ctx . IsSet ( "pid" ) {
2020-08-15 23:15:27 +03:00
setting . PIDFile = ctx . String ( "pid" )
setting . WritePIDFile = true
2017-01-09 14:54:57 +03:00
}
2019-10-15 16:39:51 +03:00
// Perform global initialization
2019-12-15 12:51:28 +03:00
routers . GlobalInit ( graceful . GetManager ( ) . HammerContext ( ) )
2014-02-19 13:50:53 +04:00
2019-10-15 16:39:51 +03:00
// Set up Macaron
2017-04-25 10:24:51 +03:00
m := routes . NewMacaron ( )
routes . RegisterRoutes ( m )
2014-03-23 09:48:01 +04:00
2015-02-01 20:41:03 +03:00
// Flag for port number in case first time run conflict.
if ctx . IsSet ( "port" ) {
2016-11-27 13:14:25 +03:00
setting . AppURL = strings . Replace ( setting . AppURL , setting . HTTPPort , ctx . String ( "port" ) , 1 )
2016-08-12 00:55:10 +03:00
setting . HTTPPort = ctx . String ( "port" )
2017-12-13 11:57:28 +03:00
switch setting . Protocol {
case setting . UnixSocket :
case setting . FCGI :
2019-12-10 15:23:26 +03:00
case setting . FCGIUnix :
2017-12-13 11:57:28 +03:00
default :
// Save LOCAL_ROOT_URL if port changed
cfg := ini . Empty ( )
if com . IsFile ( setting . CustomConf ) {
// Keeps custom settings if there is already something.
if err := cfg . Append ( setting . CustomConf ) ; err != nil {
return fmt . Errorf ( "Failed to load custom conf '%s': %v" , setting . CustomConf , err )
}
}
defaultLocalURL := string ( setting . Protocol ) + "://"
if setting . HTTPAddr == "0.0.0.0" {
defaultLocalURL += "localhost"
} else {
defaultLocalURL += setting . HTTPAddr
}
defaultLocalURL += ":" + setting . HTTPPort + "/"
cfg . Section ( "server" ) . Key ( "LOCAL_ROOT_URL" ) . SetValue ( defaultLocalURL )
if err := cfg . SaveTo ( setting . CustomConf ) ; err != nil {
return fmt . Errorf ( "Error saving generated JWT Secret to custom config: %v" , err )
}
}
2015-02-01 20:41:03 +03:00
}
2018-01-13 01:16:49 +03:00
listenAddr := setting . HTTPAddr
2019-12-10 15:23:26 +03:00
if setting . Protocol != setting . UnixSocket && setting . Protocol != setting . FCGIUnix {
2020-07-26 23:31:28 +03:00
listenAddr = net . JoinHostPort ( listenAddr , setting . HTTPPort )
2016-08-12 00:46:33 +03:00
}
2016-11-27 13:14:25 +03:00
log . Info ( "Listen: %v://%s%s" , setting . Protocol , listenAddr , setting . AppSubURL )
2016-08-12 00:55:10 +03:00
2016-12-26 04:16:37 +03:00
if setting . LFS . StartServer {
log . Info ( "LFS server enabled" )
}
2017-02-05 16:06:25 +03:00
if setting . EnablePprof {
go func ( ) {
2018-01-13 01:16:49 +03:00
log . Info ( "Starting pprof server on localhost:6060" )
2017-02-05 16:06:25 +03:00
log . Info ( "%v" , http . ListenAndServe ( "localhost:6060" , nil ) )
} ( )
}
2016-08-12 00:55:10 +03:00
var err error
2014-05-26 04:11:25 +04:00
switch setting . Protocol {
case setting . HTTP :
2019-10-15 16:39:51 +03:00
NoHTTPRedirector ( )
2019-11-24 05:11:24 +03:00
err = runHTTP ( "tcp" , listenAddr , context2 . ClearHandler ( m ) )
2014-05-26 04:11:25 +04:00
case setting . HTTPS :
2018-08-21 16:56:50 +03:00
if setting . EnableLetsEncrypt {
err = runLetsEncrypt ( listenAddr , setting . Domain , setting . LetsEncryptDirectory , setting . LetsEncryptEmail , context2 . ClearHandler ( m ) )
break
}
2017-12-26 01:23:43 +03:00
if setting . RedirectOtherPort {
go runHTTPRedirector ( )
2019-10-15 16:39:51 +03:00
} else {
NoHTTPRedirector ( )
2017-12-26 01:23:43 +03:00
}
2019-11-24 05:11:24 +03:00
err = runHTTPS ( "tcp" , listenAddr , setting . CertFile , setting . KeyFile , context2 . ClearHandler ( m ) )
2014-11-04 04:46:53 +03:00
case setting . FCGI :
2019-10-15 16:39:51 +03:00
NoHTTPRedirector ( )
2019-12-10 15:23:26 +03:00
err = runFCGI ( "tcp" , listenAddr , context2 . ClearHandler ( m ) )
2016-11-27 13:14:25 +03:00
case setting . UnixSocket :
2019-10-15 16:39:51 +03:00
NoHTTPRedirector ( )
2019-11-24 05:11:24 +03:00
err = runHTTP ( "unix" , listenAddr , context2 . ClearHandler ( m ) )
2019-12-10 15:23:26 +03:00
case setting . FCGIUnix :
NoHTTPRedirector ( )
err = runFCGI ( "unix" , listenAddr , context2 . ClearHandler ( m ) )
2014-05-26 04:11:25 +04:00
default :
2019-04-02 10:48:31 +03:00
log . Fatal ( "Invalid protocol: %s" , setting . Protocol )
2014-05-26 04:11:25 +04:00
}
if err != nil {
2019-10-15 16:39:51 +03:00
log . Critical ( "Failed to start server: %v" , err )
2014-03-18 17:58:58 +04:00
}
2019-10-15 16:39:51 +03:00
log . Info ( "HTTP Listener: %s Closed" , listenAddr )
2019-12-15 12:51:28 +03:00
<- graceful . GetManager ( ) . Done ( )
2019-11-30 17:40:22 +03:00
log . Info ( "PID: %d Gitea Web Finished" , os . Getpid ( ) )
2019-10-15 16:39:51 +03:00
log . Close ( )
2016-05-12 21:32:28 +03:00
return nil
2014-02-19 13:50:53 +04:00
}