GO HTTP server hanging - http

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".

Related

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
}

Accessing HackerNews API

I'm trying to access the HackerNews API to practice Go.
Anytime I go to my localhost to try see an output from Firebase database (where the data is stored) I am met with a Google Accounts authentication form.
Any help on this would be appreciated. In my terminal I used curl to check if I was getting a response from the server. I got a 200 OK response with content.
I thought I might be missing a Firebase client library but I'm not sure if that is the issue right now.
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
response, err := http.Get("https://hacker-news.firebaseio.com/v0/item/8863.json")
if err != nil {
fmt.Printf("The http request failed with the error %s\n", err)
} else {
data, _ := ioutil.ReadAll(response.Body)
fmt.Fprintf(w, string(data))
}
}
func main() {
fmt.Println("Starting the applicaiton")
http.HandleFunc("/", handler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
Aren't you suppose to add .json to URL (via https://github.com/HackerNews/API)?
e.g https://hacker-news.firebaseio.com/v0/item/8863.json

Basic Authentication for static resources

How can I add basic authentication to my static resources? With the code below, I'm able to view any files that are in the labels folder. I know in this question it was explained how to do it. But how would would I set the header when a http.ResponseWriter is not used?
package main
import (
"github.com/gorilla/mux"
"log"
"net/http"
"os"
)
func main() {
port := GetPort()
log.Println("[-] Listening on...", port)
r := mux.NewRouter()
r.PathPrefix("/labels/").Handler(http.StripPrefix("/labels/", http.FileServer(http.Dir("./labels/"))))
err := http.ListenAndServe(port, r)
log.Fatal(err)
}
// GetPort is for herkou deployment
func GetPort() string {
port := os.Getenv("PORT")
if port == "" {
port = "4747"
log.Println("[-] No PORT environment variable detected. Setting to ", port)
}
return ":" + port
}
Create a wrapper around each handler to pass the request from the authentication middleware which will forward the request further after authentication is done else return the response with error as
func authentication(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Println("Executing authentication")
next.ServeHTTP(w, r)
})
}
// open the dialog to download pdf files.
func dowloadPdf(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Disposition", "attachment; filename=YOUR_FILE")
w.Header().Set("Content-Type", r.Header.Get("Content-Type"))
w.Write([]byte("File downloaded"))
}
func main(){
pdfHandler := http.HandlerFunc(dowloadPdf)
http.Handle("/servepdf", authentication(pdfHandler))
http.ListenAndServe(":3000", nil)
}
But if I consider the fact there is no need to have authentication when serving static files like html, css, js etc. It would be better to create a handler to serve pdf files after authenticating users.
You can also use negorni middlewares with gorilla mux rather than creating custom middlewares.
package main
import (
"github.com/gorilla/mux"
"log"
"net/http"
"os"
)
func main() {
port := GetPort()
log.Println("[-] Listening on...", port)
r := mux.NewRouter()
r.PathPrefix("/labels/").Handler(http.StripPrefix("/labels/", ServeLabels(http.FileServer(http.Dir("./labels/")))))
err := http.ListenAndServe(port, r)
log.Fatal(err)
}
func ServeLabels(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("WWW-Authenticate", `Basic realm="mydomain"`)
h.ServeHTTP(w, r)
})
}
// GetPort is for herkou deployment
func GetPort() string {
port := os.Getenv("PORT")
if port == "" {
port = "4747"
log.Println("[-] No PORT environment variable detected. Setting to ", port)
}
return ":" + port
}
something like this, or you could just go ahead and use the gorilla mux middleware.

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