golang http server does not accept post large data - http

At current time try use golang http server and compile it from this code:
package main
import (
"io"
"net/http"
"time"
)
func hello(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
io.WriteString(w, "Hello world!")
}
var mux map[string]func(http.ResponseWriter, *http.Request)
func main() {
server := http.Server{
Addr: ":8000",
MaxHeaderBytes: 30000000,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
Handler: &myHandler{},
}
mux = make(map[string]func(http.ResponseWriter, *http.Request))
mux["/"] = hello
server.ListenAndServe()
}
type myHandler struct{}
func (*myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if h, ok := mux[r.URL.String()]; ok {
h(w, r)
return
}
io.WriteString(w, "My server: "+r.URL.String())
}
Runs it and send test data via Apache Bench
ab.exe -c 30 -n 1000 -p ESServer.exe -T application/octet-stream http://localhost:8000/
It's working excelent with small files but ESServer.exe has size 8Mb and I'm receiving next error "apr_socket_recv: An existing connection was forcibly closed by the remote host. (730054)."
What problem may happens?

You're not reading the request body, so each request is going to block once all buffers are filled. You always need to read the request in full or forcibly disconnect the client to avoid the request hanging and consuming resources.
At a minimum, you can
io.Copy(ioutil.Discard, r.Body)

Related

GO HTTP server hanging

I'm trying to run this example of code but it just hangs and doesn't print anything out - any ideas?
package main
import (
"net/http"
"fmt"
)
func Hello(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello World"))
fmt.Println("Hi")
}
func main() {
http.HandleFunc("/", Hello)
if err := http.ListenAndServe(":8080", nil); err != nil {
panic(err)
}
}
It's simple.Open up your browser such as Chrome and go to localhost:8080 or 127.0.1:8080 and you will see the output
Your code listens on a local port 8080, accepts a request from the client, and responds to the client with the corresponding data.
Your responds to client is "Hello World".
w.Write([]byte("Hello World"))
The key is the http.HandleFunc function,http.HandleFunc binds to a route that executes the Hello function whenever you access 127.0.0.1:8080 from your browser .The browser will response you "Hello World" and Program will print "Hi".

Send async response and don't get zombie pids

I'm aiming for curl client sends http req to Go API and Go API does (1. run a background shell, 2. returns response to client instantly but 3. keeps on running the point 1 server-side command in the background). Problem is point 2 is not being returned to client instantly, client is only getting a response after point 3 finishes
I tried:
import (
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"os/exec"
"log"
"strings"
"flag"
"strconv"
"crypto/tls"
"crypto/x509"
"github.com/gorilla/handlers"
"github.com/gorilla/mux"
"github.com/go-ldap/ldap"
"regexp"
"errors"
"encoding/base64"
"time"
)
func insert(w http.ResponseWriter, r *http.Request) (error) {
fullcmd := fmt.Sprintf("/home/ec2-user/spark_home/bin/spark-submit %s", "dgs")
cmd := exec.Command("/bin/sh", "-c", fullcmd)
err4 := cmd.Start()
if err4 != nil {
e1 := fmt.Sprintf("Error")
l.Printf(e1)
http.Error(w, e1, http.StatusInternalServerError)
return err4
} else {
l.Printf("The data is being ingested asynchronously in the background \n")
fmt.Fprintf(w, "request received. The data is being ingested asynchronously in the background \n")
w.Header().Set("Content-Type", "text/plain")
w.Write([]byte(fmt.Sprintf("request received. The data is being ingested asynchronously in the background \n")))
}
//wait for the spark command to finish, need to Wait() otherwise zombie/orphan pid is created
cmd.Wait()
//do bunch of other commands here that take 30+ seconds
l.Printf("success")
return nil
}
r := mux.NewRouter()
r.HandleFunc("/test", insert).Methods(http.MethodPost)
http.Handle("/", r)
server := &http.Server{
Addr: ":" + strconv.Itoa(*port),
Handler: handlers.LoggingHandler(os.Stdout, http.DefaultServeMux),
TLSConfig: tlsConfig,
}
server.ListenAndServeTLS(TLS_SERVER_CERTFILE, TLS_SERVER_KEYFILE)
The response will be completed when the HTTP handler returns, so if you want to start a job that will continue, you have to do that in a separate goroutine. You can start the goroutine as soon as the shell process starts, using something like this:
func insert(w http.ResponseWriter, r *http.Request) (error) {
...
err4 := cmd.Start()
if err4 != nil {
...
}
w.Header().Set("Content-Type", "text/plain")
fmt.Fprintf(w, "request received. The data is being ingested asynchronously in the background \n")
go func() {
cmd.Wait()
// Do other stuff
}()
return nil
}

[go]Custom Server to handle request with specific path

I want to make a api which handles the request which have the path such as
http:\\localhost:8080\todo\something but I need to do using custom server.
Here is the piece of code I have written.
package main
import (
"net/http"
"fmt"
"io"
"time"
)
func myHandler(w http.ResponseWriter, req *http.Request){
io.WriteString(w, "hello, world!\n")
}
func main() {
//Custom http server
s := &http.Server{
Addr: ":8080",
Handler: http.HandlerFunc(myHandler),
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20,
}
err := s.ListenAndServe()
if err != nil {
fmt.Printf("Server failed: ", err.Error())
}
}
inspired by this post
My handler accepts all the request such http:localhost:8080\abc, http:localhost:8080\abc etc
How to give path in custom server so that it handles request only that matches the path.
If you want to use different URL paths, you have to create some mux, you can create one, use the default mux provided by go or use a third party mux like gorilla.
The following code is made using the standart http library provided.
func myHandler(w http.ResponseWriter, req *http.Request){
io.WriteString(w, "hello, world!\n")
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/todo/something", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Response"))
})
s := &http.Server{
Addr: ":8080",
Handler: mux,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20,
}
s.ListenAndServe()
}
Just to add on, though not recommended but a good starting point is to experiment with the DefaultServeMux that also comes with the std lib. The DefaultServeMux is used when you don't supply a mux to the http server.
func myHandler(w http.ResponseWriter, r *http.Request){
io.WriteString(w, "hello, world!\n")
}
func main(){
// you can register multiple handlers
// as long as it implements the http.Handler interface
http.HandleFunc("/todo/something", myHandler)
http.HandleFunc("/todo/thingTwo", func(w http.ResponseWriter, r *http.Request){
something := strings.TrimPrefix(r.URL.Path, "/todo/")
fmt.Fprintf(w, "We received %s", something)
})
http.ListenAndServe(":5000", nil) //Will use the default ServerMux in place of nil
}
The idea here is that you can use the default server Mux for testing, create your own Mux like #Motakjuq showed you using the http.NewServeMux() and assign to your server or you can use a thirdparty router lib like go-chi, httprouter or simply take your pick from a nice collection here or simply review the stdlib

Log when server is started

Is there any way to print something when the http server starts? For instance "Server is started at port 8080"
In Node (using Express), it would be like:
app.listen(8080, function() { console.log('Server started at port 8080') });
This is my code:
func main() {
http.HandleFunc("/", MyHandler)
http.ListenAndServe(":8080", nil)
}
Thanks.
Use Go's log package:
package main
import (
"net/http"
"log"
)
func main() {
addr := ":8080"
http.HandleFunc("/", MyHandler)
log.Println("listen on", addr)
log.Fatal( http.ListenAndServe(addr, nil) )
}
http.ListenAndServe opens the server port, and blocks forever waiting for clients. If it fails to open the port, the log.Fatal call will report the problem and exit the program.
You can't print a log message after ListenAndServe since it blocks and never returns, so basically you have two main options:
Print "Starting server on port...." and that's it - BUT if ListenAndServe could not start it returns an error, so unless there's some error or panic printed because of that, you can assume the server started.
Call ListenAndServe in a separate goroutine, and make sure there was no error returned and print "Server started..." etc.
I personally prefer the first approach.
To run ListenAndServe in a goroutine as mentioned by Not_a_Golfer, you can use an unbuffered, blocking channel to run it in a goroutine and also keep the server alive.
The following example creates a channel called done where <-done will keep the server alive as it waits for the goroutine to finish, which it won't in this case. Typically, the goroutine will tell the main function it is finished by executing done <- true.
package main
import (
"log"
"net/http"
)
func MyHandler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello World"))
}
func main() {
port := "8080"
http.HandleFunc("/", MyHandler)
done := make(chan bool)
go http.ListenAndServe(":"+port, nil)
log.Printf("Server started at port %v", port)
<-done
}
Here's a larger example that has the server verify it is operational, using Listen and Serve separately. The nice thing about doing it this way is you can capture an incorrect port easily.
package main
import (
"log"
"net"
"net/http"
"os"
)
func MyHandler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello World"))
}
func main() {
port := "8080"
http.HandleFunc("/", MyHandler)
listener, err := net.Listen("tcp", ":"+port)
if err != nil {
log.Fatal(err)
}
done := make(chan bool)
go http.Serve(listener, nil)
// Log server started
log.Printf("Server started at port %v", port)
// Attempt to connect
log.Printf("Fetching...")
res, err := http.Get("http://" + listener.Addr().String())
log.Printf("Received: %v, %v", res, err)
if err != nil {
log.Fatal(err)
}
res.Write(os.Stdout)
<-done
}

Want to stop http server remotely

I want to stop the http server remotely (which I can do), but I also want to receive a message indicating that it has been stopped after it has been stopped. That is causing me some problems. The only solution that I could find is as shown below, which I do not consider ideal.
Can anyone provide a better solution. The problem is that the final message sent to the client is not getting through unless I use the goroutine as shown at the end "go func() {".
Code is as follows:
//*************
func stopServer(ohtWriter http.ResponseWriter, phtRequest *http.Request) {// Stop The Server
//*************
var iBytesSent int
var oOsError os.Error
var sErmes string
println("Stopping Server")
iBytesSent,oOsError = ohtWriter.Write([]byte("Message from server - server now stopped."))
if oOsError != nil {
sErmes = ". Error = " +oOsError.String()
} else {
sErmes = ". No error on write"
}
println("stopServer: Bytes sent = " +strconv.Itoa(iBytesSent) +sErmes)
ohtFlusher, tCanFlush := ohtWriter.(http.Flusher)
if tCanFlush {
ohtFlusher.Flush()
}
go func() {
time.Sleep(3e9)
os.Exit(0)
}()
}
Yeah I think without support from the http package, a graceful shutdown is not really possible. This is maybe a little bit less cringe-worthy, but will still slam closed any other concurrent requests in flight at the time of this request. Maybe try filing a feature request on the Go issue tracker. Better yet, open up the http package, and add a graceful shutdown method, and submit it.
Edit: I guess if you control all of the http.Handlers in your app, you could keep a count of in-flight requests (using appropriate thread synchronization), and modify the code below to a) refuse new connections once "shutdown" is called and b) wait for all in-flight requests to complete before shutting down...
package main
import (
"http"
"os"
"io"
"log"
"strconv"
)
func main() {
http.HandleFunc("/", ServeHTTP)
http.ListenAndServe(":8081", nil)
}
const responseString = "Shutting down\n"
func ServeHTTP(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.Header().Set("Content-Length", strconv.Itoa(len(responseString)))
io.WriteString(w, responseString)
f, canFlush := w.(http.Flusher)
if canFlush {
f.Flush()
}
conn, _, err := w.(http.Hijacker).Hijack()
if err != nil {
log.Fatalf("error while shutting down: %v", err)
}
conn.Close()
log.Println("Shutting down")
os.Exit(0)
}
Haven't tried it yet, but using http.ServerConn directly might work.
Here's a simple way that's good enough for local development.
http://www.sergiotapia.me/how-to-stop-your-go-http-server/
package main
import (
"net/http"
"os"
"github.com/bmizerany/pat"
)
var mux = pat.New()
func main() {
mux.Get("/kill", http.HandlerFunc(kill))
http.Handle("/", mux)
http.ListenAndServe(":8080", nil)
}
func kill(w http.ResponseWriter, r *http.Request) {
os.Exit(0)
}

Resources