Reputation: 5830
I'm trying to start an HTTP server in Go, and when the server is started, a message should be printed, in case of an error, an error message should be printed.
Given the following code:
const (
HTTPServerPort = ":4000"
)
func main() {
var httpServerError = make(chan error)
var waitGroup sync.WaitGroup
setupHTTPHandlers()
waitGroup.Add(1)
go func() {
defer waitGroup.Done()
httpServerError <- http.ListenAndServe(HTTPServerPort, nil)
}()
if <-httpServerError != nil {
fmt.Println("The Logging API service could not be started.", <-httpServerError)
} else {
fmt.Println("Logging API Service running @ http://localhost" + HTTPServerPort)
}
waitGroup.Wait()
}
When I do start the application, I don't see anything printed to the console, where I would like to see:
Logging API Service running @ http://localhost:4000
When I change the port to an invalid one, the following output is printed to the console:
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan receive]:
main.main()
...app.go:45 +0x107
exit status 2
Could anyone point me in the right direction so that I know what I'm doing wrong with this implementation?
Upvotes: 8
Views: 7333
Reputation: 195
This is quite an old post, but reflection helped me out. We are waiting for the internal listeners to be populated and by doing that we know when the actual listeners were started even before the code is doing "accept" on the socket.
func waitForHttpStartByReflection(httpServer *http.Server) {
val := reflect.ValueOf(httpServer).Elem()
listenersVal := val.FieldByName("listeners")
listenersVal = reflect.NewAt(listenersVal.Type(), unsafe.Pointer(listenersVal.UnsafeAddr())).Elem()
for {
listenersValue := listenersVal.Interface().(map[*net.Listener]struct{})
if len(listenersValue) > 0 {
break
}
}
}
Upvotes: 2
Reputation: 1124
You can't do this unless you change the logic in your code or use Listen
and Serve
separately. Because ListenAndServe
is a blocking function. If there something unexpected happens, it will return you an error. Provided it is not, it will keep blocking running the server. There is neither an event that is triggered whenever a server is started.
Let's run Listen
and Serve
separately then.
l, err := net.Listen("tcp", ":8080")
if err != nil {
// handle error
}
// Signal that server is open for business.
if err := http.Serve(l, rootHandler); err != nil {
// handle error
}
See https://stackoverflow.com/a/44598343/4792552.
P.S. net.Listen
doesn't block because it runs in background. In other means, it simply spawns a socket connection in OS level and returns you with the details/ID of it. Thus, you use that ID to proxy orders to that socket.
Upvotes: 12
Reputation: 10527
The issue is that your if statement will always read from the httpServerError
channel. However the only time something writes to that is if the server fails.
Try a select
statement:
select{
case err := <-httpServerError
fmt.Println("The Logging API service could not be started.", err)
default:
fmt.Println("Logging API Service running @ http://localhost" + HTTPServerPort
}
The default
case will be ran if the channel does not have anything on it.
Notice this does not read from the channel twice like your example. Once you read a value from a channel, its gone. Think of it as a queue.
Upvotes: 0