common/listener.go (111 lines of code) (raw):

// Copyright 2017 Microsoft. All rights reserved. // MIT License package common import ( "crypto/tls" "encoding/json" "net" "net/http" "net/url" "os" "github.com/Azure/azure-container-networking/log" "github.com/pkg/errors" ) // Listener represents an HTTP listener. type Listener struct { URL *url.URL protocol string localAddress string endpoints []string active bool listener net.Listener tlsListener net.Listener mux *http.ServeMux } // NewListener creates a new Listener. func NewListener(u *url.URL) (*Listener, error) { listener := Listener{ URL: u, protocol: u.Scheme, localAddress: u.Host + u.Path, } listener.mux = http.NewServeMux() return &listener, nil } // StartTLS creates the listener socket and starts the HTTPS server. func (l *Listener) StartTLS(errChan chan<- error, tlsConfig *tls.Config, address string) error { server := http.Server{ TLSConfig: tlsConfig, Handler: l.mux, } // listen on a separate endpoint for secure tls connections list, err := net.Listen(l.protocol, address) if err != nil { log.Printf("[Listener] Failed to listen on TlsEndpoint: %+v", err) return err } l.tlsListener = list log.Printf("[Listener] Started listening on tls endpoint %s.", address) // Launch goroutine for servicing https requests go func() { errChan <- server.ServeTLS(l.tlsListener, "", "") }() l.active = true return nil } // Start creates the listener socket and starts the HTTP server. func (l *Listener) Start(errChan chan<- error) error { list, err := net.Listen(l.protocol, l.localAddress) if err != nil { log.Printf("[Listener] Failed to listen: %+v", err) return err } l.listener = list log.Printf("[Listener] Started listening on %s.", l.localAddress) // Launch goroutine for servicing requests. go func() { errChan <- http.Serve(l.listener, l.mux) }() l.active = true return nil } // Stop stops listening for requests. func (l *Listener) Stop() { // Ignore if not active. if !l.active { return } l.active = false // Stop servicing requests. _ = l.listener.Close() if l.tlsListener != nil { // Stop servicing requests on secure listener _ = l.tlsListener.Close() log.Printf("[Listener] Stopped listening on tls endpoint %s", l.tlsListener.Addr()) } // Delete the unix socket. if l.protocol == "unix" { _ = os.Remove(l.localAddress) } log.Printf("[Listener] Stopped listening on %s", l.listener.Addr()) } // GetMux returns the HTTP mux for the listener. func (l *Listener) GetMux() *http.ServeMux { return l.mux } // GetEndpoints returns the list of registered protocol endpoints. func (l *Listener) GetEndpoints() []string { return l.endpoints } // AddEndpoint registers a protocol endpoint. func (l *Listener) AddEndpoint(endpoint string) { l.endpoints = append(l.endpoints, endpoint) } // AddHandler registers a protocol handler. func (l *Listener) AddHandler(path string, handler http.HandlerFunc) { l.mux.HandleFunc(path, handler) } // todo: Decode and Encode below should not be methods, just functions. They make no use of Listener fields. // Decode receives and decodes JSON payload to a request. func Decode(w http.ResponseWriter, r *http.Request, request interface{}) error { var err error if r.Body == nil { err = errors.New("request body is empty") } else { err = json.NewDecoder(r.Body).Decode(request) } if err != nil { http.Error(w, "Failed to decode request: "+err.Error(), http.StatusBadRequest) log.Printf("[Listener] Failed to decode request: %v\n", err.Error()) } return err } // Encode encodes and sends a response as JSON payload. func Encode(w http.ResponseWriter, response interface{}) error { // Set the content type as application json w.Header().Set("Content-Type", "application/json; charset=UTF-8") err := json.NewEncoder(w).Encode(response) if err != nil { http.Error(w, "Failed to encode response: "+err.Error(), http.StatusInternalServerError) log.Printf("[Listener] Failed to encode response: %v\n", err.Error()) } return err }