Pushing time to browser using channel - go

I'm trying to push the time to the browser using channels, so I wrote the below:
package main
import (
"net/http"
"time"
)
type DataPasser struct {
logs chan string
}
func main() {
passer := &DataPasser{logs: make(chan string)}
go func() {
for {
passer.logs <- time.Now().String()
}
}()
http.HandleFunc("/", passer.handleHello)
http.ListenAndServe(":9999", nil)
}
func (p *DataPasser) handleHello(w http.ResponseWriter, r *http.Request) {
for {
w.Write([]byte(<-p.logs))
}
/* for {
io.WriteString(w, <-p.logs)
}
*/
}
It worked by kept adding new lines with each new time, as below:
What I need is to get single line, that is cleared and replaced with the new time everytime the server sending time to it? any help?
UPDATE
I tried using SSE server sent event, as below but did not work:
package main
import (
"net/http"
"time"
)
type DataPasser struct {
logs chan string
}
func main() {
passer := &DataPasser{logs: make(chan string)}
t := time.NewTicker(time.Second)
defer t.Stop()
go func() {
for range t.C {
passer.logs <- time.Now().String()
}
}()
http.HandleFunc("/", passer.handleHello)
http.ListenAndServe(":9999", nil)
}
func (p *DataPasser) handleHello(w http.ResponseWriter, r *http.Request) {
setupCORS(&w, r)
w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive")
flusher, ok := w.(http.Flusher)
if !ok {
http.Error(w, "Internal error", 500)
return
}
for {
w.Write([]byte(<-p.logs))
// c := []byte(<-p.logs)
// fmt.Fprint(w, c)
flusher.Flush()
}
/* for {
io.WriteString(w, <-p.logs)
}
*/
// w.Write([]byte("Hi, from Service: " + ws.name))
}
func setupCORS(w *http.ResponseWriter, req *http.Request) {
(*w).Header().Set("Cache-Control", "no-cache")
(*w).Header().Set("Access-Control-Allow-Origin", "*")
(*w).Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
(*w).Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
}
And the html file as:
<html>
<head></head>
<body>
<div id="counter" width="500" height="600">
</body>
<script>
var source = new EventSource("http://localhost:9999/");
source.onmessage = function (event) {
console.log(event)
var counter = event.data; // JSON.parse(event.data);
document.getElementById("counter").innerHTML = counter;
}
</script>
</html>

The application must write the response in the text/event-stream format:
fmt.Fprintf(w, "data: %s\n\n", <-p.logs)
There are other problems. The handler should exit when the client disconnects or when there's an error writing to the response. The handler should flush the headers before waiting for the first event. Here's the updated code:
func (p *DataPasser) handleHello(w http.ResponseWriter, r *http.Request) {
setupCORS(w, r)
w.Header().Set("Content-Type", "text/event-stream")
flusher, ok := w.(http.Flusher)
if !ok {
http.Error(w, "Internal error", 500)
return
}
flusher.Flush()
done := r.Context().Done()
defer fmt.Println("EXIT")
for {
select {
case <-done:
// the client disconnected
return
case m := <-p.logs:
if _, err := fmt.Fprintf(w, "data: %s\n\n", m); err != nil {
// Write to connection failed. Subsequent writes will probably fail.
return
}
flusher.Flush()
}
}
}

Related

Reverse Proxy using Go to Cloud Run Instance

I feel like I'm close to having this working but so far I"m running into an issue building a small reverse proxy in Go to a GCP Cloud Run instance. The request 'goes through' but the response from the request is the default GCP Cloud Run 404. It appears when making the request back to Cloud Run the Host header is being ignored and therefore the request is not being routed correction.
What might I be missing here?
package main
import (
"log"
"net/http"
"net/http/httputil"
"net/url"
)
const apiUrl = "MY_CLOUD_RUN.a.run.app"
func main() {
http.HandleFunc("/", proxy)
log.Fatal(http.ListenAndServe(":8081", nil))
}
func proxy(res http.ResponseWriter, req *http.Request) {
// gets past CORS checks
if req.Method == http.MethodOptions {
headers := res.Header()
headers.Add("Access-Control-Allow-Origin", "*")
headers.Add("Vary", "Origin")
headers.Add("Vary", "Access-Control-Request-Method")
headers.Add("Vary", "Access-Control-Request-Headers")
headers.Add("Access-Control-Allow-Headers", "*")
headers.Add("Access-Control-Allow-Methods", "GET,HEAD,PUT,PATCH,POST,DELETE")
res.WriteHeader(http.StatusOK)
return
}
p := httputil.NewSingleHostReverseProxy(&url.URL{
Scheme: "http",
Host: apiUrl,
})
p.Director = func(req *http.Request) {
req.Header.Add("X-Forwarded-Host", req.Host)
req.Header.Add("X-Origin-Host", apiUrl)
req.Header.Add("Host", apiUrl)
req.Header.Add("Access-Control-Allow-Origin", "*")
req.URL.Scheme = "https"
req.URL.Host = apiUrl
}
p.ModifyResponse = func(res *http.Response) error {
res.Header.Set("Access-Control-Allow-Methods", "GET,HEAD,PUT,PATCH,POST,DELETE")
res.Header.Set("Access-Control-Allow-Credentials", "true")
res.Header.Set("Access-Control-Allow-Origin", "*")
res.Header.Set("Access-Control-Allow-Headers", "*")
return nil
}
p.ServeHTTP(res, req)
}
This is a bit more elaborate than the original initial write-up but what we wound up with was as follows.
package main
import (
"context"
"fmt"
"log"
"net/http"
"net/http/httputil"
"net/url"
"os"
"os/signal"
"time"
"golang.org/x/oauth2"
"google.golang.org/api/idtoken"
)
var port = ":8080"
var backend = "[CLOUD_RUN_INSTANCE_TO_PROXY].a.run.app"
func main() {
logger := log.New(os.Stdout, "proxy: ", log.LstdFlags)
logger.Println(fmt.Sprintf("Proxy server is starting for: %s on port: %s", backend, port))
router := http.NewServeMux()
router.Handle("/", proxyHandler())
server := &http.Server{
Addr: port,
Handler: logging(logger)(router),
ErrorLog: logger,
ReadTimeout: 30 * time.Second,
WriteTimeout: 30 * time.Second,
IdleTimeout: 15 * time.Second,
}
done := make(chan bool)
quit := make(chan os.Signal, 1)
signal.Notify(quit, os.Interrupt)
go func() {
<-quit
logger.Println("Proxy server is shutting down...")
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
server.SetKeepAlivesEnabled(false)
if err := server.Shutdown(ctx); err != nil {
logger.Fatalf("Could not gracefully shutdown the server: %v\n", err)
}
close(done)
}()
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
logger.Fatalf("Could not listen on %s: %v\n", port, err)
}
<-done
logger.Println("Server stopped")
}
func proxyHandler() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodOptions {
headers := w.Header()
headers.Add("Access-Control-Allow-Origin", "*")
headers.Add("Access-Control-Allow-Headers", "*")
headers.Add("Access-Control-Allow-Methods", "GET,HEAD,PUT,PATCH,POST,DELETE")
w.WriteHeader(http.StatusOK)
return
}
path := fmt.Sprintf("https://%s%s", backend, r.RequestURI)
at, _ := idTokenTokenSource(path)
p := httputil.NewSingleHostReverseProxy(&url.URL{
Scheme: "https",
Host: backend,
})
p.Director = func(r *http.Request) {
if at != nil {
at.SetAuthHeader(r)
}
}
p.ModifyResponse = func(res *http.Response) error {
res.Header.Set("Access-Control-Allow-Methods", "GET,HEAD,PUT,PATCH,POST,DELETE")
res.Header.Set("Access-Control-Allow-Credentials", "true")
res.Header.Set("Access-Control-Allow-Origin", "*")
res.Header.Set("Access-Control-Allow-Headers", "*")
return nil
}
r.URL.Scheme = "https"
r.URL.Host = backend
r.Header.Set("X-Forwarded-Host", r.Header.Get("Host"))
r.Host = backend
if at != nil {
at.SetAuthHeader(r)
}
p.ServeHTTP(w, r)
})
}
func logging(l *log.Logger) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
requestId := r.Header.Get("X-Request-Id")
if requestId == "" {
requestId = fmt.Sprintf("%d", time.Now().UnixNano())
}
w.Header().Set("X-Request-Id", requestId)
l.Println(requestId, r.Method, r.URL.Path, r.RemoteAddr, r.UserAgent())
}()
next.ServeHTTP(w, r)
})
}
}
func idTokenTokenSource(audience string) (*oauth2.Token, error) {
ts, err := idtoken.NewTokenSource(context.Background(), audience)
if err != nil {
return nil, err
}
t, err := ts.Token()
if err != nil {
return nil, err
}
return t, nil
}
A good chunk of some of the graceful shutdown, http setup, and logging came from: https://gist.github.com/enricofoltran/10b4a980cd07cb02836f70a4ab3e72d7

Is it possible to send a web socket message when a rest api is hit? [duplicate]

This question already has answers here:
global error variable remains nil after initialization
(2 answers)
How to use global var across files in a package?
(3 answers)
Closed 1 year ago.
I need to extend the code of this tutorial to run both a regular rest process and a WebSocket server as part of go routine.
Here is the Go part:
package main
import (
"fmt"
"log"
"net/http"
"github.com/gorilla/websocket"
)
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
}
var WS = websocket.Conn{}
func reader(conn *websocket.Conn) {
for {
// read in a message
messageType, p, err := conn.ReadMessage()
if err != nil {
log.Println(err)
return
}
// print out that message for clarity
fmt.Println(string(p))
if err := conn.WriteMessage(messageType, p); err != nil {
log.Println(err)
return
}
}
}
func homePage(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Home Page")
}
func restResponsePage(w http.ResponseWriter, r *http.Request) {
text := []byte("rest response")
if err := WS.WriteMessage(websocket.TextMessage, text); err != nil {
log.Println(err)
}
fmt.Fprintf(w, "rest response")
}
func wsEndpoint(w http.ResponseWriter, r *http.Request) {
upgrader.CheckOrigin = func(r *http.Request) bool { return true }
// upgrade this connection to a WebSocket
// connection
WS, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Println(err)
}
log.Println("Client Connected")
err = WS.WriteMessage(1, []byte("Hi from server!"))
if err != nil {
log.Println(err)
}
reader(WS)
}
func setupRoutes() {
http.HandleFunc("/", homePage)
http.HandleFunc("/restRequest", restResponsePage)
}
func setupWsRoute() {
http.HandleFunc("/ws", wsEndpoint)
}
func main() {
fmt.Println("Hello World")
go func() {
setupWsRoute()
log.Fatal(http.ListenAndServe(":8081", nil))
}()
setupRoutes()
log.Fatal(http.ListenAndServe(":8080", nil))
}
And the client index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Go WebSocket Tutorial</title>
</head>
<body>
<h2>Hello World</h2>
<script>
let socket = new WebSocket("ws://127.0.0.1:8081/ws");
console.log("Attempting Connection...");
socket.onopen = () => {
console.log("Successfully Connected");
socket.send("Hi From the Client!")
};
socket.onmessage = (event) => {
console.log("message from Server: ", event.data);
};
socket.onclose = event => {
console.log("Socket Closed Connection: ", event);
socket.send("Client Closed!")
};
socket.onerror = error => {
console.log("Socket Error: ", error);
};
</script>
</body>
</html>
When I hit the restRequest endpoint, before writing to the restResponsePage response writer w, I want to write a message to the websocket which I declared as a global var WS and initialized inside the reader method when the connection was upgraded.
This is somehow not working, instead, there is an exception and I don't have a clear idea on how to solve it.
Thanks to #ThinkGoodly for his help, changing the way the local WS was being declared and using the assignment operator instead of the short variable declaration did the trick.
Here is the whole code in case someone wants to use it. :)
package main
import (
"fmt"
"log"
"net/http"
"github.com/gorilla/websocket"
)
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
}
var WS *websocket.Conn
func reader(conn *websocket.Conn) {
for {
// read in a message
messageType, p, err := conn.ReadMessage()
if err != nil {
log.Println(err)
return
}
// print out that message for clarity
fmt.Println(string(p))
if err := conn.WriteMessage(messageType, p); err != nil {
log.Println(err)
return
}
}
}
func homePage(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Home Page")
}
func restResponsePage(w http.ResponseWriter, r *http.Request) {
text := []byte("rest response")
if err := WS.WriteMessage(websocket.TextMessage, text); err != nil {
log.Println(err)
}
fmt.Fprintf(w, "rest response")
}
func wsEndpoint(w http.ResponseWriter, r *http.Request) {
upgrader.CheckOrigin = func(r *http.Request) bool { return true }
// upgrade this connection to a WebSocket
// connection
WS, _ = upgrader.Upgrade(w, r, nil)
// if err != nil {
// log.Println(err)
// }
log.Println("Client Connected")
err1 := WS.WriteMessage(1, []byte("Hi from server!"))
if err1 != nil {
log.Println(err1)
}
reader(WS)
}
func setupRoutes() {
http.HandleFunc("/", homePage)
http.HandleFunc("/restRequest", restResponsePage)
}
func setupWsRoute() {
http.HandleFunc("/ws", wsEndpoint)
}
func main() {
fmt.Println("Hello World")
go func() {
setupWsRoute()
log.Fatal(http.ListenAndServe(":8081", nil))
}()
setupRoutes()
log.Fatal(http.ListenAndServe(":8080", nil))
}

Why Go sets different content-type based on where I declared the buffer

I experiment with steaming video and can't explain the following behaviour.
I get different response headers from Go's net/http server, from two different handlers, when the only difference is where I declare my buffer.
In the code below there are two handlers where the only difference is that one handler declares the buffer locally. in that version the streaming works correctly, Chrome stream the video, I see it gets response with Transfer-Encoding: chunked and content-type: video/mp4.
In the second version where the buffer is an input param, Chrome doesn't stream the video and the headers are different.
package main
import (
"bytes"
"io"
"log"
"net/http"
"os"
)
func main() {
file := "/Users/someUser/Documents/Zoom/Ronen/zoom_0.mp4"
f, err := os.Open(file)
defer f.Close()
if err != nil {
log.Fatalln(err)
}
http.ListenAndServe(":9008", GetVidHandler(f))
}
//This implementation works, buff is declared locally.
//I get header of content-type video/mp4
func GetVidHandler(f *os.File) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
offset := int64(0)
var buff [4096]byte
for {
n, _ := f.ReadAt(buff[:], offset)
offset += int64(n)
re := bytes.NewReader(buff[:])
io.Copy(w, re)
}
}
}
//This implementation doesn't stream, buff is input param
//I get header of content-type octet-stream
func GetVidHandler(f *os.File, buff [4096]byte) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
offset := int64(0)
for {
n, _ := f.ReadAt(buff[:], offset)
offset += int64(n)
re := bytes.NewReader(buff[:])
io.Copy(w, re)
}
}
}
Thanks for the comments, but fixing them still don't change the behaviour.
Foe example the following code gets
write tcp 127.0.0.1:9008->127.0.0.1:60443: write: broken pipe
package main
import (
"bytes"
"io"
"log"
"net/http"
"os"
)
type Viewer struct {
ch chan []byte
buff [3][4096]byte
}
func main() {
file := "/Users/someUSer/Documents/Zoom/Ronen/zoom_0.mp4"
f, err := os.Open(file)
defer f.Close()
if err != nil {
log.Fatalln(err)
}
m := make(map[int]*Viewer)
start := make(chan struct{})
go func() {
offset := int64(0)
<-start
log.Println("Starting...")
i := 0
for {
var b [4096]byte
n, err := f.ReadAt(b[:], offset)
if err != nil {
log.Println(err)
return
}
offset += int64(n)
for _, v := range m {
copy(v.buff[i%3][:], b[:n])
v.ch <- v.buff[i%3][:n]
}
i++
}
}()
http.ListenAndServe(":9008", GetVidHandler(m, start))
}
func GetVidHandler(m map[int]*Viewer, start chan struct{}) http.HandlerFunc {
i := 0
return func(w http.ResponseWriter, r *http.Request) {
current := i
v := &Viewer{ch: make(chan []byte)}
m[current] = v
i++
select {
case start <- struct{}{}:
default:
}
cn, ok := w.(http.CloseNotifier)
if !ok {
http.NotFound(w, r)
return
}
// flusher, ok := w.(http.Flusher)
// if !ok {
// http.NotFound(w, r)
// return
// }
// Send the initial headers saying we're gonna stream the response.
// w.Header().Set("Transfer-Encoding", "chunked")
// w.Header().Set("Content-Type", "video/mp4")
// w.WriteHeader(http.StatusOK)
// // flusher.Flush()
// b := <-v.ch
// log.Println(current, "-Writing ", len(b), " Bytes")
// log.Printf("%d - WRITE - First 100 bytes %v\n", current, b[:100])
// w.Write(b[:len(b)])
// flusher.Flush()
for {
select {
case <-cn.CloseNotify():
log.Println("Client stopped listening")
return
default:
b := <-v.ch
re := bytes.NewReader(b[:])
_, err := io.Copy(w, re)
if err != nil {
log.Println(err)
}
}
}
}
}
//Works
// func GetVidHandler(f *os.File) http.HandlerFunc {
// return func(w http.ResponseWriter, r *http.Request) {
// offset := int64(0)
// var buff [4096]byte
// for {
// n, _ := f.ReadAt(buff[:], offset)
// log.Printf("WRITE - First 100 bytes %v\n", buff[:100])
// offset += int64(n)
// re := bytes.NewReader(buff[:])
// io.Copy(w, re)
// }
// }
// }

Gorilla/mux middleware not being hit

I am on go version go1.10.4 linux/amd64
I am registering middleware, but they don't seem to be hit.
package main
import (
"encoding/json"
"fmt"
"github.com/dgrijalva/jwt-go"
"github.com/gorilla/context"
"github.com/mitchellh/mapstructure"
"huru/migrations"
"huru/models"
"huru/models/person"
"huru/routes"
"net/http"
"os"
"github.com/gorilla/mux"
_ "github.com/lib/pq"
log "github.com/sirupsen/logrus"
)
func loggingMiddleware(next http.Handler) http.Handler {
log.Println("logging middleware registered");
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Do stuff here
log.Println("Here is the request URI:",r.RequestURI)
// Call the next handler, which can be another middleware in the chain, or the final handler.
next.ServeHTTP(w, r)
})
}
type Exception struct {
Message string `json:"message"`
}
func authMiddleware(next http.Handler) http.Handler {
log.Println("auth middleware registered");
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
params := r.URL.Query()
fmt.Println("the params are:", params);
token, _ := jwt.Parse(params["token"][0], func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("there was an error")
}
return []byte("secret"), nil
})
claims, ok := token.Claims.(jwt.MapClaims)
if ! (ok && token.Valid) {
json.NewEncoder(w).Encode(Exception{Message: "Invalid authorization token"})
return;
}
var user person.Model
mapstructure.Decode(claims, &user)
context.Set(r, "logged_in_user", user)
next.ServeHTTP(w, r)
})
}
func errorMiddleware(next http.Handler) http.Handler {
log.Println("error handling middleware registered");
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
if err := recover(); err != nil {
log.Error("Caught error in defer/recover middleware: ", err)
originalError := err.(struct{ OriginalError error }).OriginalError
if originalError != nil {
log.Error("Original error in defer/recover middleware: ", originalError)
}
statusCode := err.(struct{ StatusCode int }).StatusCode
if statusCode != 0 {
w.WriteHeader(statusCode)
} else {
w.WriteHeader(http.StatusInternalServerError)
}
message := err.(struct{ Message string }).Message
if message == "" {
message = "Unknown error message."
}
json.NewEncoder(w).Encode(struct {
ID string
}{
message,
})
}
}()
next.ServeHTTP(w, r)
})
}
func main() {
routerParent := mux.NewRouter()
routerParent.Use(loggingMiddleware)
routerParent.Use(errorMiddleware)
routerParent.Use(authMiddleware)
router := routerParent.PathPrefix("/api/v1").Subrouter();
router.Use(loggingMiddleware)
router.Use(errorMiddleware)
router.Use(authMiddleware)
// register and login
{
handler := routes.LoginHandler{}
subRouter := router.PathPrefix("/").Subrouter()
handler.Mount(subRouter, struct{}{});
}
{
handler := routes.RegisterHandler{}
subRouter := router.PathPrefix("/").Subrouter()
handler.Mount(subRouter, struct{}{})
}
{
// people
handler := routes.PersonHandler{}
subRouter := router.PathPrefix("/").Subrouter()
subRouter.Use(authMiddleware)
handler.Mount(subRouter, routes.PersonInjection{People: models.PersonInit()})
}
// ...
}
none of these get logged:
log.Println("error handling middleware registered");
log.Println("auth middleware registered");
log.Println("logging middleware registered");
and at runtime none of middleware routes seem to get hit, nothing is logged there. Anyone know why that may be?
Note that I don't expect to need all these:
routerParent := mux.NewRouter()
routerParent.Use(loggingMiddleware)
routerParent.Use(errorMiddleware)
routerParent.Use(authMiddleware)
router := routerParent.PathPrefix("/api/v1").Subrouter();
router.Use(loggingMiddleware)
router.Use(errorMiddleware)
router.Use(authMiddleware)
in reality I probably just want:
routerParent := mux.NewRouter()
router := routerParent.PathPrefix("/api/v1").Subrouter();
router.Use(loggingMiddleware)
router.Use(errorMiddleware)
router.Use(authMiddleware)
but it's just there to prove that something is off. At the end of the main func, I have this to start the server:
host := os.Getenv("huru_api_host")
port := os.Getenv("huru_api_port")
if host == "" {
host = "localhost"
}
if port == "" {
port = "80"
}
log.Info(fmt.Sprintf("Huru API server listening on port %s", port))
path := fmt.Sprintf("%s:%s", host, port)
log.Fatal(http.ListenAndServe(path, routerParent))

get request url in martini.Context

I want to send email to myself with error occured on page http://localhost:3000/panic with containing error url - /panic in our case. But I can not figure out how to get url from c martini.Context inside RecoverWrap method.
package main
import (
"errors"
"github.com/go-martini/martini"
"net/http"
)
func main() {
m := martini.Classic()
m.Use(RecoverWrap)
m.Get("/panic", func() {
panic("some panic")
})
m.Get("/", func(req *http.Request, res http.ResponseWriter) {
res.Write([]byte("mainPage"))
})
m.Run()
}
func RecoverWrap(c martini.Context, w http.ResponseWriter) {
var err error
defer func(w http.ResponseWriter) {
r := recover()
if r != nil {
switch t := r.(type) {
case string:
err = errors.New(t)
case error:
err = t
default:
err = errors.New("Unknown error")
}
// how to get request url here
// I want to send email with error url
http.Error(w, "Something goes wrong", http.StatusInternalServerError)
}
}(w)
c.Next()
}
The answer is in adding req *http.Request parameter to func RecoverWrap(c martini.Context, req *http.Request, w http.ResponseWriter)
Full code:
package main
import (
"errors"
"fmt"
"github.com/go-martini/martini"
"net/http"
)
func main() {
m := martini.Classic()
m.Use(RecoverWrap)
m.Get("/panic", func() {
panic("some panic")
})
m.Get("/", func(req *http.Request, res http.ResponseWriter) {
res.Write([]byte("mainPage"))
})
m.Run()
}
func RecoverWrap(c martini.Context, req *http.Request, w http.ResponseWriter) {
var err error
defer func(w http.ResponseWriter) {
r := recover()
if r != nil {
switch t := r.(type) {
case string:
err = errors.New(t)
case error:
err = t
default:
err = errors.New("Unknown error")
}
fmt.Println("req.URL.Path")
fmt.Println(req.URL.Path)
http.Error(w, "Something goes wrong", http.StatusInternalServerError)
}
}(w)
c.Next()
}

Resources