Why does http.NotFound() take the request as an argument? - go

The http.NotFound() method has the following signature:
func NotFound(w ResponseWriter, r *Request)
What is/was the purpose of the *Request argument?
Currently the value seems to be unused, and I find it hard to imagine what it might have been used for in the past.

This signature is the standard http.Handler signature.
NotFound obviously does not use the request:
// NotFound replies to the request with an HTTP 404 not found error.
func NotFound(w ResponseWriter, r *Request) { Error(w, "404 page not found", StatusNotFound) }
However, by sticking to the standard interface it interoperates with the rest of the http package:
http.HandleFunc("/favicon.ico", http.NotFound)

Related

How to log the HTTP return code with the chi router?

I use chi as my router and wrote a simple middleware that logs the request being made:
func logCalls(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Info().Msgf("%v → %v", r.URL, r.RemoteAddr)
next.ServeHTTP(w, r)
})
}
It works great but I am missing the HTTP return code.
The HTTP return code will obviously be available once all the routes are exhausted but at the same time I have to use my middleware before the routes.
Is there a way to hook it after the routing is done, and therefore have the return code I can log?
I think you need render from go-chi library.
https://github.com/go-chi/render/tree/v1.0.1
Example of usage is here:
https://github.com/go-chi/chi/blob/master/_examples/rest/main.go

Golang removes double slashes and fires GET instead POST requests , how can I skip this in Cloud Functions?

I have a Cloud function where the same endpoint accepts 2 methods: POST and GET.
My problem is when the client tries to upload a multipart/form-data file through a POST request and by mistake the url contains double slashes, Golang redirects to GET method.
I have looked some replies where they talk about the Clean method https://golang.org/src/path/path.go?s=2443:2895#L74. And how the Mux under the hod is redirecting to GET.
Is there any way where I can check if that request has been redirected? so I can decide if the client has typed double slashes send a 400 Response for example instead the response from the logic in the GET method. I can't find that info in the headers. fmt.Printf("%+v", r)
Is there any way to skip the Clean method and accept the double slashes?.
Endpoint: https://google.com/hello/folder/folder//image.jpg
package test
func Hello(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "GET":
//some logic here
return
case "POST":
//some logic here
return
default:
fmt.Fprintf(w, "Sorry, only GET and POST methods are supported.")
return
}
}
Thanks.

Why isn't the Response field of this HTTP Request being populated?

The comment for the field Response in the type http.Request is as follows.
// Response is the redirect response which caused this request
// to be created. This field is only populated during client
// redirects.
Response *Response
However, it seems to me that this field is not being populated during requests, as it is implied that it is. Consider the following example:
package main
import (
"net/http"
"log"
"fmt"
)
func handleA(writer http.ResponseWriter, request *http.Request) {
http.Redirect(writer, request, "/b", http.StatusSeeOther)
}
func handleB(writer http.ResponseWriter, request *http.Request) {
fmt.Println(request.Response)
}
func main() {
http.HandleFunc("/a", handleA)
http.HandleFunc("/b", handleB)
log.Fatal(http.ListenAndServe(":8080", nil))
}
If I compile and run this code and navigate to localhost:8080/a, then I get redirected to localhost:8080/b and the server prints <nil> to the console. But shouldn't it be printing out a non-nil value, since the request is coming as the result of a redirect?
In your sample, the redirect is happening in the browser; the server doesn't get to know what response generated the redirect. That field is populated when making HTTP requests from a Go app; for example, if you use http.Client to request a URL, and the response is a redirect, it generates a new Request for the redirect URL, and in that Request, the Response field will be populated with the response that triggered that redirect.
This is evidenced in the source for http.Client: https://golang.org/src/net/http/client.go#L669

How to set HTTP status code on http.ResponseWriter

How do I set the HTTP status code on an http.ResponseWriter (e.g. to 500 or 403)?
I can see that requests normally have a status code of 200 attached to them.
Use http.ResponseWriter.WriteHeader. From the documentation:
WriteHeader sends an HTTP response header with status code. If WriteHeader is not called explicitly, the first call to Write will trigger an implicit WriteHeader(http.StatusOK). Thus explicit calls to WriteHeader are mainly used to send error codes.
Example:
func ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("500 - Something bad happened!"))
}
Apart from WriteHeader(int) you can use the helper method http.Error, for example:
func yourFuncHandler(w http.ResponseWriter, r *http.Request) {
http.Error(w, "my own error message", http.StatusForbidden)
// or using the default message error
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
}
http.Error() and http.StatusText() methods are your friends
w.WriteHeader(http.StatusInternalServerError)
w.WriteHeader(http.StatusForbidden)
full list here

How to pass along an http Request in golang?

I have a Request object in golang, and I would like to feed the contents of this object through a net.Conn as part of the task of a proxy.
I want to call something like
req, err := http.ReadRequest(bufio.NewReader(conn_to_client))
conn_to_remote_server.Write(... ? ... )
but I have no idea what I would be passing in as the arguments. Any advice would be appreciated.
Check out Negroni middleware. It let's you pass your HTTP request through different middleware and custom HandlerFuncs.
Something like this:
n := negroni.New(
negroni.NewRecovery(),
negroni.HandlerFunc(myMiddleware),
negroni.NewLogger(),
negroni.NewStatic(http.Dir("public")),
)
...
...
func myMiddleware(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
log.Println("Logging on the way there...")
if r.URL.Query().Get("password") == "secret123" {
next(rw, r) //**<--------passing the request to next middleware/func**
} else {
http.Error(rw, "Not Authorized", 401)
}
log.Println("Logging on the way back...")
}
Notice how next(rw,r) is used to pass along the HTTP request
If you don't want to use Negroni, you can always look at it's implementation on how it passes the HTTP request to another middleware.
It uses custom handler which looks something like:
handlerFunc func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)
Ref: https://gobridge.gitbooks.io/building-web-apps-with-go/content/en/middleware/index.html

Resources