123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212 |
- /*
- * DudelDu
- *
- * Copyright 2016 Matthias Ladkau. All rights reserved.
- *
- * This Source Code Form is subject to the terms of the MIT
- * License, If a copy of the MIT License was not distributed with this
- * file, You can obtain one at https://opensource.org/licenses/MIT.
- */
- package dudeldu
- import (
- "log"
- "net"
- "os"
- "os/signal"
- "sync"
- "syscall"
- "time"
- )
- /*
- ProductVersion is the current version of DudelDu
- */
- const ProductVersion = "1.3.1"
- /*
- ConnectionHandler is a function to handle new connections
- */
- type ConnectionHandler func(net.Conn, net.Error)
- /*
- DebugLogger is the debug logging interface of the Server
- */
- type DebugLogger interface {
- /*
- IsDebugOutputEnabled returns true if debug output is enabled.
- */
- IsDebugOutputEnabled() bool
- /*
- PrintDebug will print debug output if `DebugOutput` is enabled.
- */
- PrintDebug(v ...interface{})
- }
- /*
- Server data structure
- */
- type Server struct {
- Running bool // Flag indicating if the server is running
- Handler ConnectionHandler // Handler function for new connections
- DebugOutput bool // Enable additional debugging output
- LogPrint func(v ...interface{}) // Print logger method.
- signalling chan os.Signal // Channel for receiving signals
- tcpListener *net.TCPListener // TCP listener which accepts connections
- serving bool // Internal flag indicating if the socket should be served
- wgStatus *sync.WaitGroup // Optional wait group which should be notified once the server has started
- }
- /*
- NewServer creates a new DudelDu server.
- */
- func NewServer(handler ConnectionHandler) *Server {
- return &Server{
- Running: false,
- Handler: handler,
- DebugOutput: false,
- LogPrint: log.Print,
- }
- }
- /*
- IsDebugOutputEnabled returns true if debug output is enabled.
- */
- func (ds *Server) IsDebugOutputEnabled() bool {
- return ds.DebugOutput
- }
- /*
- PrintDebug will print debug output if `DebugOutput` is enabled.
- */
- func (ds *Server) PrintDebug(v ...interface{}) {
- if ds.DebugOutput {
- ds.LogPrint(v...)
- }
- }
- /*
- Run starts the DudelDu Server which can be stopped via ^C (Control-C).
- laddr should be the local address which should be given to net.Listen.
- wgStatus is an optional wait group which will be notified once the server is listening
- and once the server has shutdown.
- This function will not return unless the server is shutdown.
- */
- func (ds *Server) Run(laddr string, wgStatus *sync.WaitGroup) error {
- // Create listener
- listener, err := net.Listen("tcp", laddr)
- if err != nil {
- if wgStatus != nil {
- wgStatus.Done()
- }
- return err
- }
- ds.tcpListener = listener.(*net.TCPListener)
- ds.wgStatus = wgStatus
- // Attach SIGINT handler - on unix and windows this is send
- // when the user presses ^C (Control-C).
- ds.signalling = make(chan os.Signal)
- signal.Notify(ds.signalling, syscall.SIGINT)
- // Put the serve call into a wait group so we can wait until shutdown
- // completed
- var wg sync.WaitGroup
- wg.Add(1)
- // Kick off the serve thread
- go func() {
- defer wg.Done()
- ds.Running = true
- ds.serv()
- }()
- for {
- // Listen for shutdown signal
- if ds.IsDebugOutputEnabled() {
- ds.PrintDebug("Listen for shutdown signal")
- }
- signal := <-ds.signalling
- if signal == syscall.SIGINT {
- // Shutdown the server
- ds.serving = false
- // Wait until the server has shut down
- wg.Wait()
- ds.Running = false
- break
- }
- }
- if wgStatus != nil {
- wgStatus.Done()
- }
- return nil
- }
- /*
- Shutdown sends a shutdown signal.
- */
- func (ds *Server) Shutdown() {
- if ds.serving {
- ds.signalling <- syscall.SIGINT
- }
- }
- /*
- serv waits for new connections and assigns a handler to them.
- */
- func (ds *Server) serv() {
- ds.serving = true
- for ds.serving {
- // Wait up to a second for a new connection
- ds.tcpListener.SetDeadline(time.Now().Add(time.Second))
- newConn, err := ds.tcpListener.Accept()
- // Notify wgStatus if it was specified
- if ds.wgStatus != nil {
- ds.wgStatus.Done()
- ds.wgStatus = nil
- }
- netErr, ok := err.(net.Error)
- // Check if got an error and notify an error handler
- if newConn != nil || (ok && !(netErr.Timeout() || netErr.Temporary())) {
- go ds.Handler(newConn, netErr)
- }
- }
- ds.tcpListener.Close()
- }
|