http.Server Serve method hangs when calling Shutdown immediately - http

I have an issue with Go's http.Server, which I'm embedding in a struct that is supposed to control the server startup and shutdown. The struct looks like this:
type HTTPListen struct {
Consumers []pipeline.Consumer
Cfg HTTPListenConfig
Srv *http.Server
Logger log.Logger
wg *sync.WaitGroup
mu sync.Mutex
state State
}
The issue is that in my test code, I call my struct's Start() method (which in turn runs the Serve() method on the http.Server), check a few vars, and then call Stop(), whitch Shutdown()s the server and then waits for the http.Server to exit (return err from the Serve() method).
Now, for some reason, the Serve() method seems to just hang on the WaitGroup.Wait(), when I try to shutdown the server immediately after starting. When I add a short pause (tried 100ms), or when running the tests with the race detector, It works just fine.
Not sure if it matters, but there are no incoming requests between calling Serve() and Shutdown().
EDIT: link to a playground minimal example. If you comment out the time.Sleep call, the program hangs.
Here is the relevant code for the two methods:
func (h *HTTPListen) Start() error {
h.Logger.Log("msg", "starting HTTPListen input")
addr := h.Cfg.ListenAddr
ln, err := net.Listen("tcp", addr)
if err != nil {
h.Logger.Log("msg", "failed to create listener on tcp/"+addr+": "+err.Error())
h.setState(StateFailed)
return err
}
h.wg.Add(1)
go func() {
defer h.wg.Done()
err := h.Srv.Serve(ln)
h.Logger.Log("msg", "HTTP server stopped: "+err.Error())
}()
h.setState(StateStarted)
h.Logger.Log("msg", "HTTPListen input started")
return nil
}
Stop method:
func (h *HTTPListen) Stop() error {
h.Logger.Log("msg", "stopping HTTPListen input")
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
defer cancel()
if err := h.Srv.Shutdown(ctx); err != nil {
h.Logger.Log("msg", "HTTP server shutdown deadline expired")
}
h.wg.Wait()
h.setState(StateStopped)
h.Logger.Log("msg", "HTTPListen input stopped")
return nil
}
Log output:
kwz#cyclone ~/s/stblogd> go test -v ./pkg/pipeline/input/ -run TestHTTPListen_StartStop
=== RUN TestHTTPListen_StartStop
msg="starting HTTPListen input"
msg="HTTPListen input started"
msg="stopping HTTPListen input"
... hangs indefinitely
Log output when running tests with the race detector:
kwz#cyclone ~/s/stblogd> go test -race -v ./pkg/pipeline/input/ -run TestHTTPListen_StartStop
=== RUN TestHTTPListen_StartStop
msg="starting HTTPListen input"
msg="HTTPListen input started"
msg="stopping HTTPListen input"
msg="HTTP server stopped: http: Server closed"
msg="HTTPListen input stopped"
--- PASS: TestHTTPListen_StartStop (0.00s)
PASS
ok stblogd/pkg/pipeline/input 1.007s
I'm tempted to just slap a short delay on the test and call it a day, but I would like to know why it behaves like this.

This is a known issue, see this thread:
https://github.com/golang/go/issues/20239
Hopefully they will fix it soon but for now it sounds like adding a short delay in your test is the simplest solution - it probably doesn't come up in real world use much because you won't trigger a shutdown so soon after starting.

Related

Downloading with POST function a file with golang gingonic

this is my function:
func downloadDoc(c *gin.Context) {
var fileToSearch service.ApDocumentsMedia
if err := c.BindJSON(&fileToSearch); err != nil {
c.AbortWithStatusJSON(http.StatusUnprocessableEntity, "is not binding!")
return
}
var file service.ApDocumentsMedia
if err := db.Where("uuid = ?", fileToSearch.Uuid).First(&file); err.RowsAffected <= 0 {
c.IndentedJSON(http.StatusNotFound, "Document not founded!")
return
}
c.Header("Content-Description", "File Transfer")
c.Header("Content-Transfer-Encoding", "binary")
c.Header("Content-Disposition", "attachment; filename="+strconv.Quote(file.Path))
c.Header("Content-Type", "application/octet-stream; charset=utf-8")
c.File(file.Path)
c.IndentedJSON(http.StatusOK, "File inviato")
}
but then when I go to call the function (using the frontend) it gives me this error:
http: panic serving 127.0.0.1:50138: http: wrote more than the declared Content-Length
goroutine 24 [running]:
net/http.(*conn).serve.func1()
/home/stage01/sdk/go1.18.3/src/net/http/server.go:1825 +0xbf
panic({0xac00e0, 0xc000020ab0})
/home/stage01/sdk/go1.18.3/src/runtime/panic.go:844 +0x258
github.com/gin-gonic/gin.(*Context).Render(0xc0000b2900, 0xc8, {0xc8d318, 0xc000245950})
/home/stage01/go/pkg/mod/github.com/gin-gonic/gin#v1.8.1/context.go:911 +0x112
github.com/gin-gonic/gin.(*Context).IndentedJSON(...)
/home/stage01/go/pkg/mod/github.com/gin-gonic/gin#v1.8.1/context.go:928
main.downloadDoc(0xc0000b2900)
/home/stage01/Scrivania/minia_git/docmanagement-alexperrucci/api/main.go:74 +0x36d
github.com/gin-gonic/gin.(*Context).Next(...)
/home/stage01/go/pkg/mod/github.com/gin-gonic/gin#v1.8.1/context.go:173
github.com/gin-gonic/gin.(*Engine).handleHTTPRequest(0xc000449520, 0xc0000b2900)
/home/stage01/go/pkg/mod/github.com/gin-gonic/gin#v1.8.1/gin.go:616 +0x671
github.com/gin-gonic/gin.(*Engine).ServeHTTP(0xc000449520, {0xc8f430?, 0xc0004ee620}, 0xc0000b2700)
/home/stage01/go/pkg/mod/github.com/gin-gonic/gin#v1.8.1/gin.go:572 +0x1dd
net/http.serverHandler.ServeHTTP({0xc8c968?}, {0xc8f430, 0xc0004ee620}, 0xc0000b2700)
/home/stage01/sdk/go1.18.3/src/net/http/server.go:2916 +0x43b
net/http.(*conn).serve(0xc00054b360, {0xc8ff38, 0xc00028f290})
/home/stage01/sdk/go1.18.3/src/net/http/server.go:1966 +0x5d7
created by net/http.(*Server).Serve
/home/stage01/sdk/go1.18.3/src/net/http/server.go:3071 +0x4db
This problem is given to me when once I click on the button that makes the post call (with the uuid of the file) it gives me this error and I don't understand why, would anyone know how to help me?

Why does printing the error from net.Interfaces crash my program?

I'm trying to get a list of system interfaces on my machine, I'm using the net package, which is defined here.
I have this tiny little snippet of code, which crashes when trying to print out the error, and I can't figure out why the error is sigsev'ing on me. It's supposed to return an error, or nil right?
I've ran this code with sudo, root and under a regular user account thinking it might be a permissions thing for the network interface, but it persists amongst all user levels.
package main
import (
"net"
"fmt"
)
func main() {
var err error
var interfaces []net.Interface
var ifString []string
interfaces, err = net.Interfaces()
fmt.Printf("%v",interfaces)
if err != nil {
for _, v := range interfaces {
ifString = append(ifString, v.Name)
}
} else {
fmt.Printf(err.Error())
ifString = append(ifString, "unable to get system interfaces")
}
}
Program output is as follows when running go build and executing it:
[{1 65536 lo up|loopback} {2 1500 eno1 b8:cc:3c:8e:d4:d3 up|broadcast|multicast} {9 1500 tun0 up|pointtopoint|multicast}]
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x18 pc=0x4a5269]
goroutine 1 [running]:
main.main()
/home/andrew/interface/borked.go:20 +0x269
You are receiving a nil pointer dereference because that's exactly what you're doing.
Given the following if statement in your code:
if err != nil {
for _, v := range interfaces {
ifString = append(ifString, v.Name)
}
} else {
fmt.Printf(err.Error())
...
}
The else is reached when err is nil. Yet you are attempting to access a field in it: err.Error(), dereferencing a nil pointer.
You need to invert your if statement, it's the wrong way around.

Implementing Long live tcp connection using ninenine ranch

I'm trying to implement a long live TCP connection with ninnenine ranch
erlang library .
But looking at the documentation i cannot see a way of doing that.
Also i have written my own ranch protocol as shown below
start_link(Ref, _Socket, Transport, Opts) ->
Pid = spawn_link(?MODULE, init, [Ref, Transport, Opts]),
{ok, Pid}.
init(Ref, Transport, _Opts = []) ->
{ok, Socket} = ranch:handshake(Ref),
loop(Socket, Transport).
loop(Socket, Transport) ->
case Transport:recv(Socket, 0, 5000) of
{ok, Data} when Data =/= <<4>> ->
%% Transport:send(Socket, Data),
io:format("~w Connction accpted~n", [Data]);
_ -> ok
%%, Transport:close(Socket)
end.
as you can see i have commented the Transport:close(Socket) and i'm not sending any response to the client since Transsport:send(socket,Data) is also commented thinking that is was going to solve the problem but still, my connections where closing immediately as opened . i have a golang client shown below
package main
import (
"fmt"
"log"
"net"
)
func main(){
conn, err := net.Dial("tcp", "localhost:5555")
if err != nil {
fmt.Println(err)
}
fmt.Println(conn /*, i*/)
conn.Write(XMLData)
buffer := make([]byte, 10024)
n, err := conn.Read(buffer)
fmt.Println(buffer[:n])
//conn.Close()
}
i though it was a time out in ranch causing that. I searched and i found that in the ranch, in the the file src/ranch_tcp.erl , we have the function listen implemented as below
listen(Opts) ->
Opts2 = ranch:set_option_default(Opts, backlog, 1024),
Opts3 = ranch:set_option_default(Opts2, nodelay, true),
Opts4 = ranch:set_option_default(Opts3, send_timeout, 30000),
Opts5 = ranch:set_option_default(Opts4, send_timeout_close, true),
%% We set the port to 0 because it is given in the Opts directly.
%% The port in the options takes precedence over the one in the
%% first argument.
gen_tcp:listen(0, ranch:filter_options(Opts5, disallowed_listen_options(),
[binary, {active, false}, {packet, raw}, {reuseaddr, true}])).
As you can see there is a timeout option specifically Opts5 Opts5 = ranch:set_option_default(Opts4, send_timeout_close, true) and Opts4 Opts4 = ranch:set_option_default(Opts3, send_timeout, 30000),.I disabled them but still not working . So what am i supposed to do to have a long live tcp connection using ranch.
Your protocol implementation has a flaw
loop(Socket, Transport) ->
case Transport:recv(Socket, 0, 5000) of
{ok, Data} when Data =/= <<4>> ->
%% Transport:send(Socket, Data),
io:format("~w Connction accpted~n", [Data]);
_ -> ok
%%, Transport:close(Socket)
end.
You do not call the loop/2 recursively in any of your case clause branch, thus the your protocol process dies when loop/2 returns bringing tcp connection down.

Go stdout stream from async command

I want to use GO to run an asynchronous command on windows 10. The command I am running is blocking, and if run directly from the terminal it provides a constant steam of status messages until ctrl-c
I want to run that command from GO via exec and catch the output from the exec command to the terminal in real time, i.e. not only when the GO application terminates.
I have tried numerous examples but with not success, I just get a blank terminal and even after exiting the GO application, I don't see the output from the command I executed.
You can use cmd.StdoutPipe to do that:
cmd := exec.Command(cmdName, cmdArgs...)
cmdReader, _ := cmd.StdoutPipe()
scanner := bufio.NewScanner(cmdReader)
done := make(chan bool)
go func() {
for scanner.Scan() {
fmt.Printf(scanner.Text())
}
done <- true
}()
cmd.Start()
<- done
err = cmd.Wait()
You can use io.MultiWriter to capture output and forward it to stdout and stderr.
var stdoutBuf, stderrBuf bytes.Buffer
cmd := exec.Command("/some-command")
cmd.Stdout = io.MultiWriter(os.Stdout, &stdoutBuf)
cmd.Stderr = io.MultiWriter(os.Stderr, &stderrBuf)
err := cmd.Start() // Starts command asynchronously
if err != nil {
fmt.Printf(err.Error())
}

Killing a Haskell binary

If I press Ctrl+C, this throws an exception (always in thread 0?). You can catch this if you want - or, more likely, run some cleanup and then rethrow it. But the usual result is to bring the program to a halt, one way or another.
Now suppose I use the Unix kill command. As I understand it, kill basically sends a (configurable) Unix signal to the specified process.
How does the Haskell RTS respond to this? Is it documented somewhere? I would imagine that sending SIGTERM would have the same effect as pressing Ctrl+C, but I don't know that for a fact...
(And, of course, you can use kill to send signals that have nothing to do with killing at all. Again, I would imagine that the RTS would ignore, say, SIGHUP or SIGPWR, but I don't know for sure.)
Googling "haskell catch sigterm" led me to System.Posix.Signals of the unix package, which has a rather nice looking system for catching and handling these signals. Just scroll down to the "Handling Signals" section.
EDIT: A trivial example:
import System.Posix.Signals
import Control.Concurrent (threadDelay)
import Control.Concurrent.MVar
termHandler :: MVar () -> Handler
termHandler v = CatchOnce $ do
putStrLn "Caught SIGTERM"
putMVar v ()
loop :: MVar () -> IO ()
loop v = do
putStrLn "Still running"
threadDelay 1000000
val <- tryTakeMVar v
case val of
Just _ -> putStrLn "Quitting" >> return ()
Nothing -> loop v
main = do
v <- newEmptyMVar
installHandler sigTERM (termHandler v) Nothing
loop v
Notice that I had to use an MVar to inform loop that it was time to quit. I tried using exitSuccess from System.Exit, but since termHandler executes in a thread that isn't the main one, it can't cause the program to exit. There might be an easier way to do it, but I've never used this module before so I don't know of one. I tested this on Ubuntu 12.10.
Searching for "signal" in the ghc source code on github revealed the installDefaultSignals function:
void
initDefaultHandlers(void)
{
struct sigaction action,oact;
// install the SIGINT handler
action.sa_handler = shutdown_handler;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
if (sigaction(SIGINT, &action, &oact) != 0) {
sysErrorBelch("warning: failed to install SIGINT handler");
}
#if defined(HAVE_SIGINTERRUPT)
siginterrupt(SIGINT, 1); // isn't this the default? --SDM
#endif
// install the SIGFPE handler
// In addition to handling SIGINT, also handle SIGFPE by ignoring it.
// Apparently IEEE requires floating-point exceptions to be ignored by
// default, but alpha-dec-osf3 doesn't seem to do so.
// Commented out by SDM 2/7/2002: this causes an infinite loop on
// some architectures when an integer division by zero occurs: we
// don't recover from the floating point exception, and the
// program just generates another one immediately.
#if 0
action.sa_handler = SIG_IGN;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
if (sigaction(SIGFPE, &action, &oact) != 0) {
sysErrorBelch("warning: failed to install SIGFPE handler");
}
#endif
#ifdef alpha_HOST_ARCH
ieee_set_fp_control(0);
#endif
// ignore SIGPIPE; see #1619
// actually, we use an empty signal handler rather than SIG_IGN,
// so that SIGPIPE gets reset to its default behaviour on exec.
action.sa_handler = empty_handler;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
if (sigaction(SIGPIPE, &action, &oact) != 0) {
sysErrorBelch("warning: failed to install SIGPIPE handler");
}
set_sigtstp_action(rtsTrue);
}
From that, you can see that GHC installs at least SIGINT and SIGPIPE handlers. I don't know if there are any other signal handlers hidden in the source code.

Resources