commit
a2ad8f8ac5
|
@ -133,4 +133,3 @@ Example programs can be found in the `github.com/miekg/exdns` repository.
|
|||
* CAA parsing is broken;
|
||||
* Replies with TC bit are not parsed to the end.
|
||||
* SIG(0)
|
||||
* Make tests run faster by removing the time.Sleep()s
|
||||
|
|
|
@ -9,27 +9,25 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
func newTestServer(t *testing.T) {
|
||||
// Defined in server_test.go
|
||||
func TestClientSync(t *testing.T) {
|
||||
HandleFunc("miek.nl.", HelloServer)
|
||||
HandleFunc("example.com.", AnotherHelloServer)
|
||||
go func() {
|
||||
err := ListenAndServe(":8063", "udp", nil)
|
||||
if err != nil {
|
||||
t.Log("ListenAndServe: ", err.Error())
|
||||
t.Fatal()
|
||||
}
|
||||
}()
|
||||
time.Sleep(4e8)
|
||||
}
|
||||
|
||||
func TestClientSync(t *testing.T) {
|
||||
s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to run test server: %s", err)
|
||||
}
|
||||
defer s.Shutdown()
|
||||
|
||||
m := new(Msg)
|
||||
m.SetQuestion("miek.nl.", TypeSOA)
|
||||
|
||||
c := new(Client)
|
||||
r, _, _ := c.Exchange(m, "127.0.0.1:6053")
|
||||
|
||||
r, _, e := c.Exchange(m, addrstr)
|
||||
if e != nil {
|
||||
t.Logf("failed to exchange: %s", e.Error())
|
||||
t.Fail()
|
||||
}
|
||||
if r != nil && r.Rcode != RcodeSuccess {
|
||||
t.Log("failed to get an valid answer")
|
||||
t.Fail()
|
||||
|
@ -38,13 +36,26 @@ func TestClientSync(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClientEDNS0(t *testing.T) {
|
||||
HandleFunc("miek.nl.", HelloServer)
|
||||
HandleFunc("example.com.", AnotherHelloServer)
|
||||
|
||||
s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to run test server: %s", err)
|
||||
}
|
||||
defer s.Shutdown()
|
||||
|
||||
m := new(Msg)
|
||||
m.SetQuestion("miek.nl.", TypeDNSKEY)
|
||||
|
||||
m.SetEdns0(2048, true)
|
||||
|
||||
c := new(Client)
|
||||
r, _, _ := c.Exchange(m, "127.0.0.1:6053")
|
||||
r, _, e := c.Exchange(m, addrstr)
|
||||
if e != nil {
|
||||
t.Logf("failed to exchange: %s", e.Error())
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
if r != nil && r.Rcode != RcodeSuccess {
|
||||
t.Log("failed to get an valid answer")
|
||||
|
@ -54,6 +65,15 @@ func TestClientEDNS0(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestSingleSingleInflight(t *testing.T) {
|
||||
HandleFunc("miek.nl.", HelloServer)
|
||||
HandleFunc("example.com.", AnotherHelloServer)
|
||||
|
||||
s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to run test server: %s", err)
|
||||
}
|
||||
defer s.Shutdown()
|
||||
|
||||
m := new(Msg)
|
||||
m.SetQuestion("miek.nl.", TypeDNSKEY)
|
||||
|
||||
|
@ -63,7 +83,7 @@ func TestSingleSingleInflight(t *testing.T) {
|
|||
ch := make(chan time.Duration)
|
||||
for i := 0; i < nr; i++ {
|
||||
go func() {
|
||||
_, rtt, _ := c.Exchange(m, "127.0.0.1:6053")
|
||||
_, rtt, _ := c.Exchange(m, addrstr)
|
||||
ch <- rtt
|
||||
}()
|
||||
}
|
||||
|
|
84
server.go
84
server.go
|
@ -302,20 +302,54 @@ func (srv *Server) ActivateAndServe() error {
|
|||
|
||||
// Shutdown gracefully shuts down a server. After a call to Shutdown, ListenAndServe and
|
||||
// ActivateAndServe will return. All in progress queries are completed before the server
|
||||
// is taken down. If the Shutdown was not succesful an error is returned.
|
||||
// is taken down. If the Shutdown was not succesful an error is taking longer than reading
|
||||
// timeout.
|
||||
func (srv *Server) Shutdown() error {
|
||||
// TODO(miek): does this work with socket activation? Double check if we set the
|
||||
// address there. And... is it needed?
|
||||
c := new(Client)
|
||||
c.Net = srv.Net
|
||||
switch srv.Net {
|
||||
case "tcp", "tcp4", "tcp6":
|
||||
go func() { srv.stopTCP <- true }()
|
||||
case "udp", "udp4", "udp6":
|
||||
go func() { srv.stopUDP <- true }()
|
||||
net, addr := srv.Net, srv.Addr
|
||||
switch {
|
||||
case srv.Listener != nil:
|
||||
a := srv.Listener.Addr()
|
||||
net, addr = a.Network(), a.String()
|
||||
case srv.PacketConn != nil:
|
||||
a := srv.PacketConn.LocalAddr()
|
||||
net, addr = a.Network(), a.String()
|
||||
}
|
||||
c.Exchange(new(Msg), srv.Addr)
|
||||
return nil
|
||||
|
||||
fin := make(chan bool)
|
||||
switch net {
|
||||
case "tcp", "tcp4", "tcp6":
|
||||
go func() {
|
||||
srv.stopTCP <- true
|
||||
srv.wgTCP.Wait()
|
||||
fin <- true
|
||||
}()
|
||||
|
||||
case "udp", "udp4", "udp6":
|
||||
go func() {
|
||||
srv.stopUDP <- true
|
||||
srv.wgUDP.Wait()
|
||||
fin <- true
|
||||
}()
|
||||
}
|
||||
|
||||
c := &Client{Net: net}
|
||||
go c.Exchange(new(Msg), addr) // extra query to help ReadXXX loop to pass
|
||||
|
||||
select {
|
||||
case <-time.After(srv.getReadTimeout()):
|
||||
return &Error{err: "server shutdown is pending"}
|
||||
case <-fin:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// getReadTimeout is a helper func to use system timeout if server did not intend to change it.
|
||||
func (srv *Server) getReadTimeout() time.Duration {
|
||||
rtimeout := dnsTimeout
|
||||
if srv.ReadTimeout != 0 {
|
||||
rtimeout = srv.ReadTimeout
|
||||
}
|
||||
return rtimeout
|
||||
}
|
||||
|
||||
// serveTCP starts a TCP listener for the server.
|
||||
|
@ -326,23 +360,19 @@ func (srv *Server) serveTCP(l *net.TCPListener) error {
|
|||
if handler == nil {
|
||||
handler = DefaultServeMux
|
||||
}
|
||||
rtimeout := dnsTimeout
|
||||
if srv.ReadTimeout != 0 {
|
||||
rtimeout = srv.ReadTimeout
|
||||
}
|
||||
rtimeout := srv.getReadTimeout()
|
||||
// deadline is not used here
|
||||
for {
|
||||
rw, e := l.AcceptTCP()
|
||||
select {
|
||||
case <-srv.stopTCP:
|
||||
// Asked to shutdown
|
||||
srv.wgTCP.Wait()
|
||||
return nil
|
||||
default:
|
||||
}
|
||||
if e != nil {
|
||||
continue
|
||||
}
|
||||
m, e := srv.readTCP(rw, rtimeout)
|
||||
select {
|
||||
case <-srv.stopTCP:
|
||||
return nil
|
||||
default:
|
||||
}
|
||||
if e != nil {
|
||||
continue
|
||||
}
|
||||
|
@ -356,21 +386,17 @@ func (srv *Server) serveTCP(l *net.TCPListener) error {
|
|||
// Each request is handled in a seperate goroutine.
|
||||
func (srv *Server) serveUDP(l *net.UDPConn) error {
|
||||
defer l.Close()
|
||||
|
||||
handler := srv.Handler
|
||||
if handler == nil {
|
||||
handler = DefaultServeMux
|
||||
}
|
||||
rtimeout := dnsTimeout
|
||||
if srv.ReadTimeout != 0 {
|
||||
rtimeout = srv.ReadTimeout
|
||||
}
|
||||
rtimeout := srv.getReadTimeout()
|
||||
// deadline is not used here
|
||||
for {
|
||||
m, s, e := srv.readUDP(l, rtimeout)
|
||||
select {
|
||||
case <-srv.stopUDP:
|
||||
// Asked to shutdown
|
||||
srv.wgUDP.Wait()
|
||||
return nil
|
||||
default:
|
||||
}
|
||||
|
|
153
server_test.go
153
server_test.go
|
@ -10,7 +10,6 @@ import (
|
|||
"runtime"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func HelloServer(w ResponseWriter, req *Msg) {
|
||||
|
@ -31,21 +30,54 @@ func AnotherHelloServer(w ResponseWriter, req *Msg) {
|
|||
w.WriteMsg(m)
|
||||
}
|
||||
|
||||
func RunLocalUDPServer(laddr string) (*Server, string, error) {
|
||||
pc, err := net.ListenPacket("udp", laddr)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
server := &Server{PacketConn: pc}
|
||||
go func() {
|
||||
server.ActivateAndServe()
|
||||
pc.Close()
|
||||
}()
|
||||
// in order to let all Server internals to finish before test will touch
|
||||
// server's internal fields, we need to cycle thru other goroutinges for
|
||||
// one more time
|
||||
runtime.Gosched()
|
||||
return server, pc.LocalAddr().String(), nil
|
||||
}
|
||||
|
||||
func RunLocalTCPServer(laddr string) (*Server, string, error) {
|
||||
l, err := net.Listen("tcp", laddr)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
server := &Server{Listener: l}
|
||||
go func() {
|
||||
server.ActivateAndServe()
|
||||
l.Close()
|
||||
}()
|
||||
// in order to let all Server internals to finish before test will touch
|
||||
// server's internal fields, we need to cycle thru other goroutinges for
|
||||
// one more time
|
||||
runtime.Gosched()
|
||||
return server, l.Addr().String(), nil
|
||||
}
|
||||
|
||||
func TestServing(t *testing.T) {
|
||||
HandleFunc("miek.nl.", HelloServer)
|
||||
HandleFunc("example.com.", AnotherHelloServer)
|
||||
go func() {
|
||||
err := ListenAndServe(":8053", "udp", nil)
|
||||
if err != nil {
|
||||
t.Log("ListenAndServe: ", err.Error())
|
||||
t.Fatal()
|
||||
}
|
||||
}()
|
||||
time.Sleep(4e8)
|
||||
|
||||
s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to run test server: %s", err)
|
||||
}
|
||||
defer s.Shutdown()
|
||||
|
||||
c := new(Client)
|
||||
m := new(Msg)
|
||||
m.SetQuestion("miek.nl.", TypeTXT)
|
||||
r, _, err := c.Exchange(m, "127.0.0.1:8053")
|
||||
r, _, err := c.Exchange(m, addrstr)
|
||||
if err != nil {
|
||||
t.Log("failed to exchange miek.nl", err)
|
||||
t.Fatal()
|
||||
|
@ -57,7 +89,7 @@ func TestServing(t *testing.T) {
|
|||
}
|
||||
|
||||
m.SetQuestion("example.com.", TypeTXT)
|
||||
r, _, err = c.Exchange(m, "127.0.0.1:8053")
|
||||
r, _, err = c.Exchange(m, addrstr)
|
||||
if err != nil {
|
||||
t.Log("failed to exchange example.com", err)
|
||||
t.Fatal()
|
||||
|
@ -70,7 +102,7 @@ func TestServing(t *testing.T) {
|
|||
|
||||
// Test Mixes cased as noticed by Ask.
|
||||
m.SetQuestion("eXaMplE.cOm.", TypeTXT)
|
||||
r, _, err = c.Exchange(m, "127.0.0.1:8053")
|
||||
r, _, err = c.Exchange(m, addrstr)
|
||||
if err != nil {
|
||||
t.Log("failed to exchange eXaMplE.cOm", err)
|
||||
t.Fail()
|
||||
|
@ -86,16 +118,20 @@ func BenchmarkServe(b *testing.B) {
|
|||
b.StopTimer()
|
||||
HandleFunc("miek.nl.", HelloServer)
|
||||
a := runtime.GOMAXPROCS(4)
|
||||
go func() {
|
||||
ListenAndServe("127.0.0.1:8053", "udp", nil)
|
||||
}()
|
||||
|
||||
s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
|
||||
if err != nil {
|
||||
b.Fatalf("Unable to run test server: %s", err)
|
||||
}
|
||||
defer s.Shutdown()
|
||||
|
||||
c := new(Client)
|
||||
m := new(Msg)
|
||||
m.SetQuestion("miek.nl", TypeSOA)
|
||||
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
c.Exchange(m, "127.0.0.1:8053")
|
||||
c.Exchange(m, addrstr)
|
||||
}
|
||||
runtime.GOMAXPROCS(a)
|
||||
}
|
||||
|
@ -104,16 +140,19 @@ func benchmarkServe6(b *testing.B) {
|
|||
b.StopTimer()
|
||||
HandleFunc("miek.nl.", HelloServer)
|
||||
a := runtime.GOMAXPROCS(4)
|
||||
go func() {
|
||||
ListenAndServe("[::1]:8053", "udp", nil)
|
||||
}()
|
||||
s, addrstr, err := RunLocalUDPServer("[::1]:0")
|
||||
if err != nil {
|
||||
b.Fatalf("Unable to run test server: %s", err)
|
||||
}
|
||||
defer s.Shutdown()
|
||||
|
||||
c := new(Client)
|
||||
m := new(Msg)
|
||||
m.SetQuestion("miek.nl", TypeSOA)
|
||||
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
c.Exchange(m, "[::1]:8053")
|
||||
c.Exchange(m, addrstr)
|
||||
}
|
||||
runtime.GOMAXPROCS(a)
|
||||
}
|
||||
|
@ -131,16 +170,18 @@ func BenchmarkServeCompress(b *testing.B) {
|
|||
b.StopTimer()
|
||||
HandleFunc("miek.nl.", HelloServerCompress)
|
||||
a := runtime.GOMAXPROCS(4)
|
||||
go func() {
|
||||
ListenAndServe("127.0.0.1:8053", "udp", nil)
|
||||
}()
|
||||
s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
|
||||
if err != nil {
|
||||
b.Fatalf("Unable to run test server: %s", err)
|
||||
}
|
||||
defer s.Shutdown()
|
||||
|
||||
c := new(Client)
|
||||
m := new(Msg)
|
||||
m.SetQuestion("miek.nl", TypeSOA)
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
c.Exchange(m, "127.0.0.1:8053")
|
||||
c.Exchange(m, addrstr)
|
||||
}
|
||||
runtime.GOMAXPROCS(a)
|
||||
}
|
||||
|
@ -227,19 +268,13 @@ func HelloServerLargeResponse(resp ResponseWriter, req *Msg) {
|
|||
}
|
||||
|
||||
func TestServingLargeResponses(t *testing.T) {
|
||||
mux := NewServeMux()
|
||||
mux.HandleFunc("example.", HelloServerLargeResponse)
|
||||
HandleFunc("example.", HelloServerLargeResponse)
|
||||
|
||||
server := &Server{
|
||||
Addr: "127.0.0.1:10000",
|
||||
Net: "udp",
|
||||
Handler: mux,
|
||||
s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to run test server: %s", err)
|
||||
}
|
||||
|
||||
go func() {
|
||||
server.ListenAndServe()
|
||||
}()
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
defer s.Shutdown()
|
||||
|
||||
// Create request
|
||||
m := new(Msg)
|
||||
|
@ -250,7 +285,7 @@ func TestServingLargeResponses(t *testing.T) {
|
|||
M.Lock()
|
||||
M.max = 2
|
||||
M.Unlock()
|
||||
_, _, err := c.Exchange(m, "127.0.0.1:10000")
|
||||
_, _, err = c.Exchange(m, addrstr)
|
||||
if err != nil {
|
||||
t.Logf("failed to exchange: %s", err.Error())
|
||||
t.Fail()
|
||||
|
@ -259,14 +294,14 @@ func TestServingLargeResponses(t *testing.T) {
|
|||
M.Lock()
|
||||
M.max = 20
|
||||
M.Unlock()
|
||||
_, _, err = c.Exchange(m, "127.0.0.1:10000")
|
||||
_, _, err = c.Exchange(m, addrstr)
|
||||
if err == nil {
|
||||
t.Logf("failed to fail exchange, this should generate packet error")
|
||||
t.Fail()
|
||||
}
|
||||
// But this must work again
|
||||
c.UDPSize = 7000
|
||||
_, _, err = c.Exchange(m, "127.0.0.1:10000")
|
||||
_, _, err = c.Exchange(m, addrstr)
|
||||
if err != nil {
|
||||
t.Logf("failed to exchange: %s", err.Error())
|
||||
t.Fail()
|
||||
|
@ -275,32 +310,26 @@ func TestServingLargeResponses(t *testing.T) {
|
|||
|
||||
// TODO(miek): These tests should actually fail when the server does
|
||||
// not shut down.
|
||||
// (asergeyev) I put err check logic which is not yet in use but IMO
|
||||
// this test will not change as shutdown internals improve.
|
||||
func TestShutdownTCP(t *testing.T) {
|
||||
server := Server{Addr: ":8055", Net: "tcp"}
|
||||
go func() {
|
||||
err := server.ListenAndServe()
|
||||
if err != nil {
|
||||
t.Logf("failed to setup the tcp server: %s\n", err.Error())
|
||||
t.Fail()
|
||||
}
|
||||
t.Logf("successfully stopped the tcp server")
|
||||
}()
|
||||
time.Sleep(4e8)
|
||||
server.Shutdown()
|
||||
time.Sleep(1e9)
|
||||
s, _, err := RunLocalTCPServer("127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to run test server: %s", err)
|
||||
}
|
||||
err = s.Shutdown()
|
||||
if err != nil {
|
||||
t.Errorf("Could not shutdown test TCP server, %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestShutdownUDP(t *testing.T) {
|
||||
server := Server{Addr: ":8054", Net: "udp"}
|
||||
go func() {
|
||||
err := server.ListenAndServe()
|
||||
if err != nil {
|
||||
t.Logf("failed to setup the udp server: %s\n", err.Error())
|
||||
t.Fail()
|
||||
}
|
||||
t.Logf("successfully stopped the udp server")
|
||||
}()
|
||||
time.Sleep(4e8)
|
||||
server.Shutdown()
|
||||
time.Sleep(1e9)
|
||||
s, _, err := RunLocalUDPServer("127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to run test server: %s", err)
|
||||
}
|
||||
err = s.Shutdown()
|
||||
if err != nil {
|
||||
t.Errorf("Could not shutdown test UDP server, %s", err)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue