-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Firts refactor commit * Update .gitignore * Switch over to using the conf package * More refactoring * Finish refactoring * Add missing server package
- Loading branch information
1 parent
f6ac4e7
commit b28df50
Showing
3 changed files
with
242 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
package server | ||
|
||
import ( | ||
"net/http" | ||
"sync" | ||
) | ||
|
||
// AddRoute adds a new single fire route to the server. | ||
func (s *Server) AddRoute(route *Route) { | ||
if s.wg == nil { | ||
s.wg = &sync.WaitGroup{} | ||
s.wg.Add(1) | ||
go func() { | ||
s.wg.Wait() | ||
s.Done <- s.finishedRoutes | ||
close(s.Done) | ||
}() | ||
} | ||
|
||
okMetric := true | ||
if route.MaxRequests != 0 { | ||
okMetric = false | ||
} else if route.MaxOK == 0 { | ||
route.MaxOK = 1 | ||
} | ||
|
||
rr := s.router.HandleFunc(route.Pattern, func(w http.ResponseWriter, r *http.Request) { | ||
var rc int64 | ||
var err error | ||
route.Lock() | ||
route.reqCount++ | ||
|
||
if okMetric { | ||
switch { | ||
case route.okCount >= route.MaxOK: | ||
route.DoneHandlerFunc(w, r) | ||
case route.okCount < route.MaxOK: | ||
err = route.HandlerFunc(w, r) | ||
|
||
if err == nil || err == OKDoneErr { | ||
route.okCount++ | ||
err = OKDoneErr | ||
} else if err != OKNotDoneErr { | ||
s.internalError(err.Error()) | ||
} | ||
|
||
if route.okCount == route.MaxOK { | ||
s.Lock() | ||
s.finishedRoutes[route] = err | ||
s.Unlock() | ||
s.wg.Done() | ||
} | ||
} | ||
route.Unlock() | ||
return | ||
} | ||
|
||
rc = route.reqCount | ||
route.Unlock() | ||
switch { | ||
case rc > route.MaxRequests: | ||
route.DoneHandlerFunc(w, r) | ||
case rc <= route.MaxRequests: | ||
err = route.HandlerFunc(w, r) | ||
if err == nil || err == OKDoneErr { | ||
route.Lock() | ||
route.okCount++ | ||
route.Unlock() | ||
err = OKDoneErr | ||
} else if err != OKNotDoneErr { | ||
s.internalError(err.Error()) | ||
} | ||
|
||
if rc == route.MaxRequests { | ||
s.Lock() | ||
s.finishedRoutes[route] = err | ||
s.Unlock() | ||
s.wg.Done() | ||
} | ||
} | ||
}) | ||
|
||
if len(route.Methods) > 0 { | ||
rr.Methods(route.Methods...) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package server | ||
|
||
import ( | ||
"net/http" | ||
"sync" | ||
"sync/atomic" | ||
) | ||
|
||
type Route struct { | ||
Pattern string | ||
Methods []string | ||
HandlerFunc func(w http.ResponseWriter, r *http.Request) error | ||
DoneHandlerFunc http.HandlerFunc | ||
MaxOK int64 | ||
MaxRequests int64 | ||
|
||
reqCount int64 | ||
okCount int64 | ||
|
||
sync.Mutex | ||
} | ||
|
||
func (r *Route) RequestCount() int64 { | ||
return atomic.LoadInt64(&r.reqCount) | ||
} | ||
|
||
func (r *Route) OkCount() int64 { | ||
return atomic.LoadInt64(&r.okCount) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
package server | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"log" | ||
"net/http" | ||
"sync" | ||
|
||
"github.com/gorilla/mux" | ||
) | ||
|
||
var OKDoneErr = errors.New("route done") | ||
var OKNotDoneErr = errors.New("not done") | ||
|
||
type Server struct { | ||
Port string | ||
|
||
// Certfile is the public certificate that should be used for TLS | ||
CertFile string | ||
// Keyfile is the private key that should be used for TLS | ||
KeyFile string | ||
|
||
// Done signals when the server has shutdown regardless of value. | ||
// Each route that finished will have an error message in the map. | ||
// Routes that finish successfully will have an OKDoneErr error. | ||
Done chan map[*Route]error | ||
|
||
ErrorLog *log.Logger | ||
InfoLog *log.Logger | ||
|
||
HostAddresses []string | ||
|
||
router *mux.Router | ||
server *http.Server | ||
|
||
serving bool // Set to true after Serve() is called, false after Stop() or Close() | ||
|
||
wg *sync.WaitGroup | ||
sync.Mutex | ||
|
||
finishedRoutes map[*Route]error | ||
} | ||
|
||
func NewServer() *Server { | ||
s := &Server{ | ||
router: mux.NewRouter(), | ||
finishedRoutes: make(map[*Route]error), | ||
} | ||
s.server = &http.Server{Handler: s} | ||
return s | ||
} | ||
|
||
func (s *Server) Serve() error { | ||
s.server.Addr = ":" + s.Port | ||
|
||
scheme := "http://" | ||
listenAndServe := func() error { | ||
return s.server.ListenAndServe() | ||
} | ||
|
||
// Are we using HTTPS? | ||
if s.CertFile != "" || s.KeyFile != "" { | ||
// Error out if only cert or key is given | ||
var err error | ||
switch { | ||
case s.CertFile == "": | ||
err = errors.New("given cert file for HTTPS but no key file. exit") | ||
case s.KeyFile == "": | ||
err = errors.New("given key file for HTTPS but no cert file. exit") | ||
} | ||
if err != nil { | ||
s.internalError(err.Error()) | ||
return err | ||
} | ||
|
||
scheme = "https://" | ||
listenAndServe = func() error { | ||
return s.server.ListenAndServeTLS(s.CertFile, s.KeyFile) | ||
} | ||
} | ||
|
||
var addresses string | ||
for _, ip := range s.HostAddresses { | ||
addresses += "\t - " + scheme + ip + "\n" | ||
} | ||
|
||
s.infoLog("listening:\n" + addresses) | ||
s.serving = true | ||
return listenAndServe() | ||
} | ||
|
||
func (s *Server) Shutdown(ctx context.Context) error { | ||
if !s.serving { | ||
return nil | ||
} | ||
|
||
return s.server.Shutdown(ctx) | ||
} | ||
|
||
func (s *Server) Close() error { | ||
if !s.serving { | ||
return nil | ||
} | ||
|
||
err := s.server.Close() | ||
return err | ||
} | ||
|
||
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { | ||
if !s.serving { | ||
s.serving = true | ||
} | ||
s.router.ServeHTTP(w, r) | ||
} | ||
|
||
func (s *Server) internalError(format string, v ...interface{}) { | ||
if s.ErrorLog != nil { | ||
s.ErrorLog.Printf(format, v...) | ||
} | ||
} | ||
|
||
func (s *Server) infoLog(format string, v ...interface{}) { | ||
if s.InfoLog != nil { | ||
s.InfoLog.Printf(format, v...) | ||
} | ||
} |