Go: Read method in http.Request - go

I'm reading Go Web Programming by Sau Sheong Chang. Here is a sample code for reading data from a request body:
import (
"fmt"
"net/http"
)
func bodyfunc(w http.ResponseWriter, r *http.Request) {
len := r.ContentLength
body := make([]byte, len)
r.Body.Read(body)
fmt.Fprintln(w, string(body))
}
func main() {
server := http.Server{
Addr: "127.0.0.1:8080",
}
http.HandleFunc("/body", bodyfunc)
server.ListenAndServe()
}
By definition, the Body field in the Request struct is actually an
io.ReadCloser interface. My question is: The Read method in this interface is just declared but not implemented. Meanwhile the code works well. The implementation of the Read method must have been done somewhere. Where is it?

The implementation is in https://golang.org/src/net/http/transfer.go#L637.
I used the delve debugger to find it.

Related

Additional arguments to http function Golang

I am trying to pass string to handler in given example.
package main
import (
"fmt"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:])
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
Here is what i tried but it throws an error as it expects regular number of arguments:
package main
import (
"fmt"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request, s *string) {
fmt.Fprintf(w, "Hi there, I love %s!", *s)
}
func main() {
files := "bar"
http.HandleFunc("/", handler(&files))
http.ListenAndServe(":8080", nil)
}
I'm a little unclear on what you're trying to do, but based off what you said, why not try to encapsulate the data you want to pass in like this:
package main
import (
"fmt"
"net/http"
)
type FilesHandler struct {
Files string
}
func (fh *FilesHandler) handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hi there, I love %s!", fh.Files)
}
func main() {
myFilesHandler := &FilesHandler{Files: "bar"}
http.HandleFunc("/", myFilesHandler.handler)
http.ListenAndServe(":8080", nil)
}
This provides a little more granular control of what you make available to your Handler.
There are lots of options here, you could:
Use a closure to set state for the enclosed handler
Use a method on a struct for the handler, and set global state there
Use the request context to store a value, then get it out
Use a package global to store the value
Write your own router with a new signature (not as complex as it sounds, but probably not a good idea)
Write a helper function to do things like extract params from the url
It depends what s is really - is it a constant, is it based on some state, does it belong in a separate package?
One of ways is to store data in global variable:
package main
import (
"fmt"
"net/http"
)
var replies map[string]string
func handler1(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
question := r.FormValue("question")
var answer string
var ok bool
if answer, ok = replies[question]; !ok {
answer = "I have no answer for this"
}
fmt.Fprintf(w, "Hi there, I love %s! My answer is: %s", question, answer)
}
func main() {
//files := "bar"
replies = map[string]string{
"UK": "London",
"FR": "Paris",
}
http.HandleFunc("/", handler1)
http.ListenAndServe(":8080", nil)
}
Here for brevity I've commented out files and put data as is into the map. You may read the file and put them there.
Usage with CURL:
$ curl -X POST 127.0.0.1:8080/ -d "question=FR"
Hi there, I love FR! My answer is: Paris
$ curl -X POST 127.0.0.1:8080/ -d "question=US"
Hi there, I love US! My answer is: I have no answer for this

Golang - User of package without selector

Please, I searched this a lot and after not been able to find, I am writing and not that I didn't try to search all over first. Couldn't get the right answer. I even tried to check Revel's function and couldn't get the answer from there as well.
When I run this program I get this error for line
./test.go:11: use of package http without selector
This error points at the line below where I have written
*http
inside the struct
Confusing part is that with test and dot I even get auto complete with VIM. So I don't know why is the error. Is it that it has to be somewhat like
*(net/http)
or something like that ?
package main
import (
"fmt"
"net/http"
)
type HandleHTTP struct {
*http
}
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Path is %s", r.URL.Path[1:])
}
func main() {
test := HandleHTTP{}
test.http.HandleFunc("/", handler)
test.http.ListenAndServe(":8080", nil)
}
If you want to have two or more instances serving from different ports you need to spin up two, or more, server. Would something like this, perhaps, work for you?
package main
import (
"fmt"
"net/http"
)
type HandleHTTP struct {
http *http.Server
}
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Path is %s", r.URL.Path[1:])
}
func main() {
mux1 := http.NewServeMux()
mux1.HandleFunc("/", handler)
test1 := HandleHTTP{http:&http.Server{Addr:":8081", Handler:mux1}}
mux2 := http.NewServeMux()
mux2.HandleFunc("/", handler)
test2 := HandleHTTP{http:&http.Server{Addr:":8082", Handler:mux2}}
// run the first one in a goroutine so that the second one is executed
go test1.http.ListenAndServe()
test2.http.ListenAndServe()
}

runtime error:Invalid memory Address or nil pointer dereference-golang

I am new to golang, am trying develop a login page with sesions. the code is building successfully but when I run in browser its saying 404 page not found.can any one help for me. Thanks in advance.
Here is my code
// main.go
package main
import (
_ "HarishSession/routers"
"github.com/astaxie/beego"
"fmt"
"net/http"
"html/template"
"strings"
"log"
"github.com/astaxie/beego/session"
"sync"
)
var globalSessions *session.Manager
var provides = make(map[string]Provider)
func sayhelloName(w http.ResponseWriter, r *http.Request) {
r.ParseForm() // parse arguments, you have to call this by yourself
fmt.Println("the information of form is",r.Form) // print form information in server side
fmt.Println("path", r.URL.Path)
fmt.Println("scheme", r.URL.Scheme)
fmt.Println(r.Form["url_long"])
for k, v := range r.Form {
fmt.Println("key:", k)
fmt.Println("val:", strings.Join(v, ""))
}
fmt.Fprintf(w, "Hello astaxie!") // send data to client side
}
type Manager struct {
cookieName string //private cookiename
lock sync.Mutex // protects session
provider Provider
maxlifetime int64
}
type Provider interface {
SessionInit(sid string) (Session, error)
SessionRead(sid string) (Session, error)
SessionDestroy(sid string) error
SessionGC(maxLifeTime int64)
}
type Session interface {
Set(key, value interface{}) error //set session value
Get(key interface{}) interface{} //get session value
Delete(key interface{}) error //delete session value
SessionID() string //back current sessionID
}
func NewManager(provideName, cookieName string, maxlifetime int64) (*Manager, error) {
provider, ok := provides[provideName]
if !ok {
return nil, fmt.Errorf("session: unknown provide %q (forgotten import?)", provideName)
}
return &Manager{provider: provider, cookieName: cookieName, maxlifetime: maxlifetime}, nil
}
func login(w http.ResponseWriter, r *http.Request) {
sess := globalSessions.SessionStart(w,r)
r.ParseForm()
fmt.Println("method:", r.Method)
if r.Method == "GET" {
t, _ := template.ParseFiles("login.tpl")
w.Header().Set("Content-Type", "text/html")
t.Execute(w,sess.Get("username"))
} else {
//logic part of log in
fmt.Println("username:",r.Form["username"])
fmt.Println("password:",r.Form["password"])
http.Redirect(w,r,"/",302)
}
}
func main() {
var globalSessions *session.Manager
http.HandleFunc("/", sayhelloName)
http.HandleFunc("/login", login)
err := http.ListenAndServe(":8080", nil) // set listen port
if err != nil {
log.Fatal("ListenAndServe the error is: ", err)
}
fmt.Println("hello")
beego.Run()
fmt.Println(globalSessions)
}
//router.go
package routers
import (
"HarishSession/controllers"
"github.com/astaxie/beego"
)
func init() {
beego.Router("/", &controllers.MainController{})
beego.Router("/login", &controllers.MainController{})
}
//default.go
package controllers
import (
"github.com/astaxie/beego"
)
type MainController struct {
beego.Controller
}
func (this *MainController) Get() {
this.Data["Website"] = "beego.me"
this.Data["Email"] = "astaxie#gmail.com"
this.TplNames = "index.tpl"
this.TplNames="login.tpl"
}
You have two variables at different scopes, each called globalSessions. One is in your definition in main.go, which is defined at global scope, and another is defined in the main function, and is defined as a local variable to main. These are separate variables. Your code is making this mistake of conflating them.
You can see this by paying closer attention to the stack trace entry:
github.com/astaxie/beego/session.(*Manager).SessionStart(0x0, 0x151e78, 0xc08212 0000, 0xc082021ad0, 0x0, 0x0)
as this points to globalSessions being uninitialized due to being nil. After that, troubleshooting is a direct matter of looking at the program to see what touches globalSessions.
Note that you should include the stack trace as part of your question. Don't just add it as a comment. It's critical to include this information: otherwise we would not have been able to easily trace the problem. Please improve the quality of your questions to make it easier for people to help you.
Also, you may want to take a serious look at go vet, which is a tool that helps to catch problems like this.
As this is the one line you used in code :
t, _ := template.ParseFiles("login.tpl")
So what you need to check is whether the file login.tpl is at the correct location, where it must be, or not. If not then correct the reference of it and also check same for the other references.
This helped me.

echo server distorting image response body

Trying to write a simple echo server for images, but it distorts the file. What's going wrong?
package main
import (
"fmt"
"io"
"net/http"
)
type FlushWriter struct {
w io.Writer
}
func (fw *FlushWriter) Write(bytes []byte) (int, error) {
count, e := fw.w.Write(bytes)
fw.w.(http.Flusher).Flush()
return count, e
}
func main() {
http.HandleFunc("/", index)
fmt.Println("listening on 8000")
http.ListenAndServe(":8000", nil)
}
func index(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "image/jpeg")
fw := &FlushWriter{ w: w }
io.Copy(fw, r.Body)
}
And to test it.
$ curl --data-binary #image.jpg -o test.jpg localhost:8000
You're ignoring errors in your code, and missing a ErrBodyReadAfterClose.
You can't read from the http.Request.Body once you start writing to the http.ResponseWriter
http://golang.org/pkg/net/http/#pkg-variables
ErrBodyReadAfterClose is returned when reading a Request or Response
Body after the body has been closed. This typically happens when the
body is read after an HTTP Handler calls WriteHeader or Write on its
ResponseWriter
You'll need to buffer the image on the server before writing it back.
Aside from the fact that Go won't let you do this, even if you were to make a handler that worked, most clients would cause this to deadlock with images larger than the sum of all the buffers involved. This requires a client that can simultaniously send and receive, which very few, if any would do.

Set http headers for multiple handlers in go

I'm trying to set an http header for multiple handlers. My first thought was to make a custom write function that would set the header before writing the response like the code sample at the bottom.
However, when I pass a pointer to the http.ResponseWriter and try to access it from my function it tells me that "type *http.ResponseWriter has no Header method".
What is the best way to set headers for multiple handlers, and also why isn't the pointer working the way I want it to?
func HelloServer(w http.ResponseWriter, req *http.Request) {
type Message struct {
Name string
Body string
Time int64
}
m := Message{"Alice", "Hello", 1294706395881547000}
b, _ := json.Marshal(m)
WriteJSON(&w, b)
}
func WriteJSON(wr *http.ResponseWriter, rawJSON []byte) {
*wr.Header().Set("Content-Type", "application/json")
io.WriteString(*wr, string(rawJSON))
}
func main() {
http.HandleFunc("/json", HelloServer)
err := http.ListenAndServe(":9000", nil)
if err != nil {
log.Fatal("ListenAndServer: ", err)
}
}
I'm not sure about the multiple handlers thing, but I do know why the code you wrote is failing. The key is that the line:
*wr.Header().Set("Content-Type", "application/json")
is being interpreted, because of operator precedence, as:
*(wr.Header().Set("Content-Type", "application/json"))
Since wr has the type *http.ResponseWriter, which is a pointer to and interface, rather than the interface itself, this won't work. I assume that you knew that, which is why you did *wr. I assume what you meant to imply to the compiler is:
(*wr).Header().Set("Content-Type", "application/json")
If I'm not mistaken, that should compile and behave properly.
You don't need to use *wr as it already references a pointer.
wr.Header().Set("Content-Type", "application/json") should be sufficient.
If you want to set "global" headers for every request, you can create a function that satisfies http.HandleFunc (go.auth has a good example) and then wrap your handlers like so:
http.HandleFunc("/hello", Defaults(helloHandler))
Also take a look at the net/http documentation, which has further examples.
I wrap my handlers with an error handler
which calls my AddSafeHeader function.
I based it on http://golang.org/doc/articles/error_handling.html
but it doesn't use ServeHTTP so it works with appstats:
http.Handle("/", appstats.NewHandler(util.ErrorHandler(rootHandler)))
Here:
package httputil
import (
"appengine"
"net/http"
"html/template"
)
func AddSafeHeaders(w http.ResponseWriter) {
w.Header().Set("X-Content-Type-Options", "nosniff")
w.Header().Set("X-XSS-Protection", "1; mode=block")
w.Header().Set("X-Frame-Options", "SAMEORIGIN")
w.Header().Set("Strict-Transport-Security", "max-age=2592000; includeSubDomains")
}
// Redirect to a fixed URL
type redirectHandler struct {
url string
code int
}
func (rh *redirectHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
Redirect(w, r, rh.url, rh.code)
}
func Redirect(w http.ResponseWriter, r *http.Request, urlStr string, code int) {
AddSafeHeaders(w)
http.Redirect(w, r, urlStr, code)
}
// RedirectHandler returns a request handler that redirects
// each request it receives to the given url using the given
// status code.
func RedirectHandler(url string, code int) http.Handler {
return &redirectHandler{url, code}
}
func ErrorHandler(fn func(appengine.Context, http.ResponseWriter, *http.Request)) func(appengine.Context, http.ResponseWriter, *http.Request) {
return func(c appengine.Context, w http.ResponseWriter, r *http.Request) {
defer func() {
if err, ok := recover().(error); ok {
c.Errorf("%v", err)
w.WriteHeader(http.StatusInternalServerError)
errorTemplate.Execute(w, err)
}
}()
AddSafeHeaders(w)
fn(c, w, r)
}
}
// Check aborts the current execution if err is non-nil.
func Check(err error) {
if err != nil {
panic(err)
}
}
var errorTemplate = template.Must(template.New("error").Parse(errorTemplateHTML))
const errorTemplateHTML = `
<html>
<head>
<title>XXX</title>
</head>
<body>
<h2>An error occurred:</h2>
<p>{{.}}</p>
</body>
</html>
`
http.ResponseWriter is an interface.
You should probably not be using a pointer to an interface. In net/http/server.go, the unexported response struct is the actual type that implements ResponseWriter when the server calls your handler, and importantly, when the server actually calls the handler's ServeHTTP, it passes a *response. It's already a pointer, but you don't see that because ResonseWriter is an interface. (the response pointer is created here, by (c *conn).readRequest. (The links will likely be for the wrong lines the future, but you should be able to locate them).
That's why the ServeHTTP function required to implement Handler is:
ServeHTTP(w ResponseWriter, r *Request)
i.e. not a pointer to ResponseWriter, as this declaration already permits a pointer to a struct that implements the ResponseWriter interface.
As I am new to Go, I created a minimal contrived example, based on elithrar's answer, which shows how to easily add headers to all your routes / responses. We do so, by creating a function that satisfies the http.HandlerFunc interface, then wraps the route handler functions:
package main
import (
"encoding/json"
"log"
"net/http"
"github.com/gorilla/mux"
)
// Hello world.
func Hello(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode("Hello World")
}
// HelloTwo world
func HelloTwo(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode("Hello Two World")
}
// JSONHeaders conforms to the http.HandlerFunc interface, and
// adds the Content-Type: application/json header to each response.
func JSONHeaders(handler http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
handler(w, r)
}
}
func main() {
router := mux.NewRouter()
// Now, instead of calling your handler function directly, pass it into the wrapper function.
router.HandleFunc("/", JSONHeaders(Hello)).Methods("GET")
router.HandleFunc("/hellotwo", JSONHeaders(HelloTwo)).Methods("GET")
log.Fatal(http.ListenAndServe(":3000", router))
}
Results:
$ go run test.go &
$ curl -i localhost:3000/
HTTP/1.1 200 OK
Content-Type: application/json
Date: Thu, 28 Feb 2019 22:27:04 GMT
Content-Length: 14
"Hello World"
What i end up doing:
// Accepts a user supplied http.HandlerFunc and then modifies it in various ways. In this case, it adds two new headers.
func CommonlHandler(h http.HandlerFunc) http.HandlerFunc {
return func (rs http.ResponseWriter, rq *http.Request) {
rs.Header().Add("Server", "Some server")
rs.Header().Add("Cache-Control", "no-store")
h(rs, rq)
}
// somewhere down the line, where you're setting up http request handlers
serveMux := http.NewServeMux()
serveMux.HandleFunc("/", CommonHandler(func(rs http.ResponseWriter, rq *http.Request) {
// Handle request as usual. Since it's wrapped in the CommonHandler and we've set some headers there, responses to requests to "/" will contain those headers.
// Be mindful what modifications you're doing here. If, for ex., you decide you want to apply different caching strategy than the Common one, since this will be passed to the CommonHandler, your changes will be overwritten and lost. So it may be a good idea to introduce checks in CommonHandler to determine whether the header exists, before you decide to create it.
}))
serveMux.HandleFunc("/contact", CommonHandler(func(rs http.ResponseWriter, rq *http.Request) {
// handle another request as usual
}))

Resources