Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
caddy
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
caddy
Commits
88c646c8
Commit
88c646c8
authored
Oct 30, 2015
by
Matthew Holt
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
core: Start() blocks until servers finish starting
Also improved/clarified some docs
parent
64cded82
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
86 additions
and
13 deletions
+86
-13
caddy/caddy.go
caddy/caddy.go
+28
-7
caddy/caddy_test.go
caddy/caddy_test.go
+32
-0
caddy/restart.go
caddy/restart.go
+1
-1
server/server.go
server/server.go
+25
-5
No files found.
caddy/caddy.go
View file @
88c646c8
...
...
@@ -11,6 +11,10 @@
//
// You should use caddy.Wait() to wait for all Caddy servers
// to quit before your process exits.
//
// Importing this package has the side-effect of trapping
// SIGINT on all platforms and SIGUSR1 on not-Windows systems.
// It has to do this in order to perform shutdowns or reloads.
package
caddy
import
(
...
...
@@ -83,11 +87,13 @@ const (
// Start starts Caddy with the given Caddyfile. If cdyfile
// is nil or the process is forked from a parent as part of
// a graceful restart, Caddy will check to see if Caddyfile
// was piped from stdin and use that.
// was piped from stdin and use that. It blocks until all the
// servers are listening.
//
// If this process is a fork and no Caddyfile was piped in,
// an error will be returned. If this process is NOT a fork
// and cdyfile is nil, a default configuration will be assumed.
// an error will be returned (the Restart() function does this
// for you automatically). If this process is NOT a fork and
// cdyfile is nil, a default configuration will be assumed.
// In any case, an error is returned if Caddy could not be
// started.
func
Start
(
cdyfile
Input
)
error
{
...
...
@@ -175,9 +181,12 @@ func Start(cdyfile Input) error {
// startServers starts all the servers in groupings,
// taking into account whether or not this process is
// a child from a graceful restart or not.
// a child from a graceful restart or not. It blocks
// until the servers are listening.
func
startServers
(
groupings
Group
)
error
{
for
i
,
group
:=
range
groupings
{
var
startupWg
sync
.
WaitGroup
for
_
,
group
:=
range
groupings
{
s
,
err
:=
server
.
New
(
group
.
BindAddr
.
String
(),
group
.
Configs
)
if
err
!=
nil
{
log
.
Fatal
(
err
)
...
...
@@ -206,8 +215,9 @@ func startServers(groupings Group) error {
}
wg
.
Add
(
1
)
go
func
(
s
*
server
.
Server
,
i
int
,
ln
server
.
ListenerFile
)
{
go
func
(
s
*
server
.
Server
,
ln
server
.
ListenerFile
)
{
defer
wg
.
Done
()
if
ln
!=
nil
{
err
=
s
.
Serve
(
ln
)
}
else
{
...
...
@@ -222,16 +232,27 @@ func startServers(groupings Group) error {
log
.
Println
(
err
)
}
}
}(
s
,
i
,
ln
)
}(
s
,
ln
)
startupWg
.
Add
(
1
)
go
func
(
s
*
server
.
Server
)
{
defer
startupWg
.
Done
()
s
.
WaitUntilStarted
()
}(
s
)
serversMu
.
Lock
()
servers
=
append
(
servers
,
s
)
serversMu
.
Unlock
()
}
startupWg
.
Wait
()
return
nil
}
// Stop stops all servers. It blocks until they are all stopped.
// It does NOT execute shutdown callbacks that may have been
// configured by middleware (they are executed on SIGINT).
func
Stop
()
error
{
letsencrypt
.
Deactivate
()
...
...
caddy/caddy_test.go
0 → 100644
View file @
88c646c8
package
caddy
import
(
"net/http"
"testing"
"time"
)
func
TestCaddyStartStop
(
t
*
testing
.
T
)
{
caddyfile
:=
"localhost:1984
\n
tls off"
for
i
:=
0
;
i
<
2
;
i
++
{
err
:=
Start
(
CaddyfileInput
{
Contents
:
[]
byte
(
caddyfile
)})
if
err
!=
nil
{
t
.
Fatalf
(
"Error starting, iteration %d: %v"
,
i
,
err
)
}
client
:=
http
.
Client
{
Timeout
:
time
.
Duration
(
2
*
time
.
Second
),
}
resp
,
err
:=
client
.
Get
(
"http://localhost:1984"
)
if
err
!=
nil
{
t
.
Fatalf
(
"Expected GET request to succeed (iteration %d), but it failed: %v"
,
i
,
err
)
}
resp
.
Body
.
Close
()
err
=
Stop
()
if
err
!=
nil
{
t
.
Fatalf
(
"Error stopping, iteration %d: %v"
,
i
,
err
)
}
}
}
caddy/restart.go
View file @
88c646c8
...
...
@@ -93,7 +93,7 @@ func Restart(newCaddyfile Input) error {
sigwpipe
.
Close
()
// close our copy of the write end of the pipe or we might be stuck
answer
,
err
:=
ioutil
.
ReadAll
(
sigrpipe
)
if
err
!=
nil
||
len
(
answer
)
==
0
{
log
.
Println
(
"restart: child failed to
answer
; changes not applied"
)
log
.
Println
(
"restart: child failed to
initialize
; changes not applied"
)
return
incompleteRestartErr
}
...
...
server/server.go
View file @
88c646c8
...
...
@@ -32,6 +32,7 @@ type Server struct {
listener
ListenerFile
// the listener which is bound to the socket
listenerMu
sync
.
Mutex
// protects listener
httpWg
sync
.
WaitGroup
// used to wait on outstanding connections
startChan
chan
struct
{}
// used to block until server is finished starting
}
type
ListenerFile
interface
{
...
...
@@ -42,6 +43,11 @@ type ListenerFile interface {
// New creates a new Server which will bind to addr and serve
// the sites/hosts configured in configs. This function does
// not start serving.
//
// Do not re-use a server (start, stop, then start again). We
// could probably add more locking to make this possible, but
// as it stands, you should dispose of a server after stopping it.
// The behavior of serving with a spent server is undefined.
func
New
(
addr
string
,
configs
[]
Config
)
(
*
Server
,
error
)
{
var
tls
bool
if
len
(
configs
)
>
0
{
...
...
@@ -58,6 +64,7 @@ func New(addr string, configs []Config) (*Server, error) {
},
tls
:
tls
,
vhosts
:
make
(
map
[
string
]
virtualHost
),
startChan
:
make
(
chan
struct
{}),
}
s
.
Handler
=
s
// this is weird, but whatever
...
...
@@ -94,6 +101,7 @@ func New(addr string, configs []Config) (*Server, error) {
func
(
s
*
Server
)
Serve
(
ln
ListenerFile
)
error
{
err
:=
s
.
setup
()
if
err
!=
nil
{
close
(
s
.
startChan
)
return
err
}
return
s
.
serve
(
ln
)
...
...
@@ -103,11 +111,13 @@ func (s *Server) Serve(ln ListenerFile) error {
func
(
s
*
Server
)
ListenAndServe
()
error
{
err
:=
s
.
setup
()
if
err
!=
nil
{
close
(
s
.
startChan
)
return
err
}
ln
,
err
:=
net
.
Listen
(
"tcp"
,
s
.
Addr
)
if
err
!=
nil
{
close
(
s
.
startChan
)
return
err
}
...
...
@@ -136,6 +146,7 @@ func (s *Server) serve(ln ListenerFile) error {
return
serveTLSWithSNI
(
s
,
s
.
listener
,
tlsConfigs
)
}
close
(
s
.
startChan
)
// unblock anyone waiting for this to start listening
return
s
.
Server
.
Serve
(
s
.
listener
)
}
...
...
@@ -182,6 +193,7 @@ func serveTLSWithSNI(s *Server, ln net.Listener, tlsConfigs []TLSConfig) error {
config
.
Certificates
[
i
],
err
=
tls
.
LoadX509KeyPair
(
tlsConfig
.
Certificate
,
tlsConfig
.
Key
)
config
.
Certificates
[
i
]
.
OCSPStaple
=
tlsConfig
.
OCSPStaple
if
err
!=
nil
{
close
(
s
.
startChan
)
return
err
}
}
...
...
@@ -196,6 +208,7 @@ func serveTLSWithSNI(s *Server, ln net.Listener, tlsConfigs []TLSConfig) error {
// TLS client authentication, if user enabled it
err
=
setupClientAuth
(
tlsConfigs
,
config
)
if
err
!=
nil
{
close
(
s
.
startChan
)
return
err
}
...
...
@@ -205,7 +218,7 @@ func serveTLSWithSNI(s *Server, ln net.Listener, tlsConfigs []TLSConfig) error {
// on POSIX systems.
ln
=
tls
.
NewListener
(
ln
,
config
)
// Begin serving; block until done
close
(
s
.
startChan
)
// unblock anyone waiting for this to start listening
return
s
.
Server
.
Serve
(
ln
)
}
...
...
@@ -215,7 +228,7 @@ func serveTLSWithSNI(s *Server, ln net.Listener, tlsConfigs []TLSConfig) error {
// seconds); on Windows it will close the listener
// immediately.
func
(
s
*
Server
)
Stop
()
error
{
s
.
Server
.
SetKeepAlivesEnabled
(
false
)
// TODO: Does this even do anything? :P
s
.
Server
.
SetKeepAlivesEnabled
(
false
)
if
runtime
.
GOOS
!=
"windows"
{
// force connections to close after timeout
...
...
@@ -229,7 +242,7 @@ func (s *Server) Stop() error {
// Wait for remaining connections to finish or
// force them all to close after timeout
select
{
case
<-
time
.
After
(
5
*
time
.
Second
)
:
// TODO: make configurable
?
case
<-
time
.
After
(
5
*
time
.
Second
)
:
// TODO: make configurable
case
<-
done
:
}
}
...
...
@@ -246,6 +259,13 @@ func (s *Server) Stop() error {
return
err
}
// WaitUntilStarted blocks until the server s is started, meaning
// that practically the next instruction is to start the server loop.
// It also unblocks if the server encounters an error during startup.
func
(
s
*
Server
)
WaitUntilStarted
()
{
<-
s
.
startChan
}
// ListenerFd gets the file descriptor of the listener.
func
(
s
*
Server
)
ListenerFd
()
uintptr
{
s
.
listenerMu
.
Lock
()
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment