Skip to content

Commit

Permalink
Merge pull request linkedin#134 from exponea/multiple-http-listeners
Browse files Browse the repository at this point in the history
Add option to listen on multiple HTTP addresses
  • Loading branch information
toddpalino authored Nov 24, 2016
2 parents b6c9dad + c83a6d5 commit bc26a72
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 4 deletions.
13 changes: 11 additions & 2 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ type BurrowConfig struct {
Httpserver struct {
Enable bool `gcfg:"server"`
Port int `gcfg:"port"`
Listen []string `gcfg:"listen"`
}
Notify struct {
Interval int64 `gcfg:"interval"`
Expand Down Expand Up @@ -329,8 +330,16 @@ func ValidateConfig(app *ApplicationContext) error {

// HTTP Server
if app.Config.Httpserver.Enable {
if app.Config.Httpserver.Port == 0 {
errs = append(errs, "HTTP server port is not specified")
if len(app.Config.Httpserver.Listen) == 0 {
if app.Config.Httpserver.Port == 0 {
errs = append(errs, "HTTP server port is not specified")
}
listenPort := fmt.Sprintf(":%v", app.Config.Httpserver.Port)
app.Config.Httpserver.Listen = append(app.Config.Httpserver.Listen, listenPort)
} else {
if app.Config.Httpserver.Port != 0 {
errs = append(errs, "Either HTTP server port or listen can be specified, but not both")
}
}
}

Expand Down
3 changes: 3 additions & 0 deletions config/burrow.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ expire-group=604800
[httpserver]
server=on
port=8000
; Alternatively, use listen (cannot be specified when port is)
; listen=host:port
; listen=host2:port2

[smtp]
server=mailserver.example.com
Expand Down
45 changes: 43 additions & 2 deletions http_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ package main

import (
"encoding/json"
"fmt"
"github.com/linkedin/Burrow/protocol"
"io"
"net/http"
"os"
"strings"
"net"
"time"
log "github.com/cihub/seelog"
)

type HttpServer struct {
Expand All @@ -30,6 +32,25 @@ type appHandler struct {
handler func(*ApplicationContext, http.ResponseWriter, *http.Request) (int, string)
}


// tcpKeepAliveListener sets TCP keep-alive timeouts on accepted
// connections. It's used by ListenAndServe and ListenAndServeTLS so
// dead TCP connections (e.g. closing laptop mid-download) eventually
// go away.
type tcpKeepAliveListener struct {
*net.TCPListener
}

func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
tc, err := ln.AcceptTCP()
if err != nil {
return
}
tc.SetKeepAlive(true)
tc.SetKeepAlivePeriod(3 * time.Minute)
return tc, nil
}

func NewHttpServer(app *ApplicationContext) (*HttpServer, error) {
server := &HttpServer{
app: app,
Expand All @@ -48,7 +69,27 @@ func NewHttpServer(app *ApplicationContext) (*HttpServer, error) {
server.mux.Handle("/v2/zookeeper", appHandler{server.app, handleClusterList})
// server.mux.Handle("/v2/zookeeper/", appHandler{server.app, handleZookeeper})

go http.ListenAndServe(fmt.Sprintf(":%v", server.app.Config.Httpserver.Port), server.mux)
listeners := make([]net.Listener, 0, len(server.app.Config.Httpserver.Listen))

for _, listenAddress := range server.app.Config.Httpserver.Listen {
ln, err := net.Listen("tcp", listenAddress)
if err != nil {
for _, listenerToClose := range listeners {
closeErr := listenerToClose.Close()
if closeErr != nil {
log.Errorf("Could not close listener: %v", closeErr)
}
}
return nil, err
}
listeners = append(listeners, tcpKeepAliveListener{ln.(*net.TCPListener)})
}

httpServer := &http.Server{Handler: server.mux}
for _, listener := range listeners {
go httpServer.Serve(listener)
}

return server, nil
}

Expand Down

0 comments on commit bc26a72

Please sign in to comment.