Send few response through one http.ResponseWriter - http

after I click on post button on frontend I receive data to my handler, create email and send it, but I want to inform user about status of email(sended or not) and redirect it back to main page. Problem is that I dont know how to pass alert and redirecting to main page at once.
Here is a handler code:
func contactHandler(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodPost {
r.ParseForm()
/* creating email */
err := smtp.SendMail("smtp.gmail.com:587", smtp.PlainAuth("", *emailFromLogin, *emailFromPassword, "smtp.gmail.com"), *emailFrom, []string{*emailTo}, []byte(msg))
http.Redirect(w, r, "/", http.StatusMovedPermanently)
}
}
That what I want send to user before redirecting.
if err != nil {
fmt.Fprint(w, "<script>Email didn't send<script>")
} else {
fmt.Fprint(w, "<script>Email sent successfully<script>")
}

You can't send data back with a redirect request, so here are 2 proposed alternatives.
Client side navigation
One option would be to send back a normal response with the message you want to show to the user, optionally with the URL to move to. After this, the client may do the navigation.
This may be an AJAX request, and client side JavaScript can process the result, and act upon it: display the message, and navigate. Here are some ways for client side JavaScript navigation:
window.location = "http://new-website.com";
window.location.href = "http://new-website.com";
window.location.assign("http://new-website.com");
window.location.replace("http://new-website.com");
Encode message in URL as parameter
Another common way is to send a redirect, and encode the message in the new URL as a query parameter, such as:
newPath := "/?msg=Hello"
This is a simple example, but if the message is more complex, you have to escape it to get a valid path. For that, you may use url.QueryEscape() like this:
msg := "Email didn't send"
path := "/?msg=" + url.QueryEscape(msg)
This would result in a path /?msg=Email+didn%27t+send (try it on the Go Playground). Another option would be to use url.URL and url.Values to assemble the URL, for example:
values := url.Values{}
values.Add("msg", "Email didn't send")
url := &url.URL{
Path: "/",
RawQuery: values.Encode(),
}
fmt.Println(url)
The above prints (try it on the Go Playground):
/?msg=Email+didn%27t+send
Of course, the new page must handle that query parameter, e.g. use JavaScript to detect if in this case the msg query param exists, and if so, display it somehow to the user.
Also note that you shouldn't use the StatusMovedPermanently code for this redirect, as that may be cached by browsers and proxies. Instead use StatusFound. This status code indicates to clients that the redirection is temporary and may change in the future, so if the client needs the original URL again, it will query the original again (and not the new one automatically).

Related

Is it possible to run http.ListenAndServe() AND ReadFromUDP() concurrently?

I am trying to write a simple web app that will listen for UDP packets.
But I can either only listen for UDP packets, or run the web app...
I am not familiar with GoLang, but here's the code I'm using to...
listen for UDP:
ServerConn, _ := net.ListenUDP("udp", &net.UDPAddr{IP:[]byte{#,#,#,#},Port:####,Zone:""})
defer ServerConn.Close()
buf := make([]byte, 1024)
for {
n, addr, _ := ServerConn.ReadFromUDP(buf)
fmt.Println("Received ", string(buf[0:n]), " from ", addr)
}
Server logic:
package main
We import 4 important libraries
1. “net/http” to access the core go http functionality
2. “fmt” for formatting our text
3. “html/template” a library that allows us to interact with our html file.
4. "time" - a library for working with date and time.
import (
"net/http"
"fmt"
"time"
"html/template"
)
//Create a struct that holds information to be displayed in our HTML file
type Welcome struct {
Name string
Time string
}
//Go application entrypoint
func main() {
//Instantiate a Welcome struct object and pass in some random information.
//We shall get the name of the user as a query parameter from the URL
welcome := Welcome{"Anonymous", time.Now().Format(time.Stamp)}
//We tell Go exactly where we can find our html file. We ask Go to parse the html file (Notice
// the relative path). We wrap it in a call to template.Must() which handles any errors and halts if there are fatal errors
templates := template.Must(template.ParseFiles("templates/welcome-template.html"))
//Our HTML comes with CSS that go needs to provide when we run the app. Here we tell go to create
// a handle that looks in the static directory, go then uses the "/static/" as a url that our
//html can refer to when looking for our css and other files.
http.Handle("/static/", //final url can be anything
http.StripPrefix("/static/",
http.FileServer(http.Dir("static")))) //Go looks in the relative "static" directory first using http.FileServer(), then matches it to a
//url of our choice as shown in http.Handle("/static/"). This url is what we need when referencing our css files
//once the server begins. Our html code would therefore be <link rel="stylesheet" href="/static/stylesheet/...">
//It is important to note the url in http.Handle can be whatever we like, so long as we are consistent.
//This method takes in the URL path "/" and a function that takes in a response writer, and a http request.
http.HandleFunc("/" , func(w http.ResponseWriter, r *http.Request) {
//Takes the name from the URL query e.g ?name=Martin, will set welcome.Name = Martin.
if name := r.FormValue("name"); name != "" {
welcome.Name = name;
}
//If errors show an internal server error message
//I also pass the welcome struct to the welcome-template.html file.
if err := templates.ExecuteTemplate(w, "welcome-template.html", welcome); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
})
//Start the web server, set the port to listen to 8080. Without a path it assumes localhost
//Print any errors from starting the webserver using fmt
fmt.Println("Listening");
fmt.Println(http.ListenAndServe(":8080", nil));
}
taken from(https://medium.com/google-cloud/building-a-go-web-app-from-scratch-to-deploying-on-google-cloud-part-1-building-a-simple-go-aee452a2e654)
I tried putting both of these extracts in 1 file, as well as running 2 files at the same time using
go run *.go
Any help would be appreciated!
You're going to need to start looking into goroutines - since you're asking to do two things concurrently. I suggest doing some reading into channels, goroutines, and concurrency in general :)

Go multiple response.WriteHeader calls

So I'm writing a basic webapp and I'm having trouble redirecting the user after a sucessfull login. The code is:
t, err := template.ParseFiles("home.html")
if err != nil {
log.Fatal("template.ParseFiles: ", err)
}
err = t.Execute(w, nil)
if err != nil {
log.Fatal("t.Execute: ", err)
}
if r.Method == "POST" {
r.ParseForm()
user := r.FormValue("username")
pass := r.FormValue("password")
if checkLogin(user, pass) {
loggedIn = true
http.Redirect(w, r, "/home", 302)
}
}
The error message is: "http: multiple response.WriteHeader calls".
My problem is that I don't see a way to serve the html file containing the login-form without calling t.Execute which sets the header.
How can I display the login page and still be able to redirect to a different page?
You are writing (using w) and then later trying to redirect (also using w) using 302 header redirection.
You can only send headers once, and if you start writing to w it assumes a 200 header (OK)
Also, Its best if you check the http.Method before writing to the ResponseWriter (w)
And, Remember to return after a redirection or handing over the ResponseWriter and Request pair to another function!
Hope this helps.
How can I display the login page and still be able to redirect to a different page?
Have a different route for authentication. Make the login form submit to the authentication route. Have a separate handler for authentication as well.
For example, your login form:
<form method="post" action="/auth">
your Go main:
http.HandleFunc("/", homeHandler)
http.HandleFunc("/auth", authHandler)
When authentication processing is complete you can redirect the user to the appropriate page. You could pass a parameter in the query string that contains the destination path for the redirect.

Golang - http.Redirect fails to redirect in a handler than handles post data

I was thinking of doing the following for the authentication.
(1) Client visits home,say http://localhost:3000
The client is shown a login form with username, password, gamecode.
(2) When the client submits this form, I use javascript to post the data to
http://localhost:3000/login that accepts POST requests. In my golang file the relevant route is defined as:
rtr := mux.NewRouter()
rtr.HandleFunc("/login", loginHandler).Methods("POST")
(3) Now I want to check if the three, username, password and the gamecode are valid. I start with the gamecode, and I have no difficulty reading the value submitted and checking in against the list of valid gamecodes in the database. Now, if the gamecode is invalid, I want to redirect the user to another route. I am failing to do so. These are the relevant lines:
// I check if gamecode is valid. If it is invalid, gameCodeExists = 0
if gameCodeExists == 0 {
// this message prints on the server as expected for an invalid code
fmt.Println("gamecode invalid...")
// this redirect never occurs
http.Redirect(w, req, "/loginfail", 302)
return
} else {
fmt.Println("Valid code")
}
Can someone please help me with what I am doing wrong, and how I can fix it?
Thank you.

How to pass new header to sendRedirect

I feel like this should be easy. I have an app where all I am trying to do is have a form page (index.jsp) that calls a servlet (CheckInfo.java) which sets a new header (myHeader) and redirects the user to another page (redirect.jsp). All of these files are on the same server. The index.jsp is sending the request just fine and CheckInfo is processing and redirecting, but myHeader is not showing up on redirect.jsp. I've read several posts talking about response.sendRedirect sends a 302 which doesn't pass headers and that I should use RequestDispatcher, but nothing seems to work. Is there no way to send headers from a servlet to a jsp?
Here is the servlet code:
response.setHeader("myHeader", "hey there");
response.sendRedirect("redirect.jsp");
I have also tried this:
response.setHeader("myHeader", "hey there");
RequestDispatcher view = request.getRequestDispatcher("redirect.jsp");
view.forward(request, response);
And I have this in redirect.jsp:
System.out.println(request.getHeader("myHeader"));
This does not print anything.
If the answer to my question is no... then I would settle for a way to set the header once I got back to the jsp. My reverse proxy is looking for a specific header to determine whether or not to perform an action. Obviously I tried response.addHeader() on redirect.jsp, but the page has already loaded at that point so that just made me feel dumb.
response.setHeader("myHeader", "hey there");
response.sendRedirect("redirect.jsp");
You are adding it as response header and it is 302 response. Browser on seeing a 302 response will just look for Location header and fire a new request to this location. Custom headers in the response are untouched whereas you are expecting these custom response headers to be included in the request (to new redirect location) which is not being sent.
Solution:-
1. you can use request dispatcher and forward the request instead of external redirect. And you need to use request attributes here.
2. you can call submit form using an ajax request may be jquery like and handle the response manually(for 302 response) but would not suggest you to use this approach as it is not a cleaner and intuitive approach. Just mentioning so that you know there are other ways to achieve this.
The problem is that the redirect() method of the response initiates a new request altogether, thereby loosing the attributes that were set before redirecting. Luckily there is a fluent way of solving the problem still. See below
response.setHeader("myHeader", "hey there");
request.getRequestDispatcher("redirect.jsp").forward(request, response);
Then in your destination you can do response.getHeaders("myHeader")
I have tested the code.
I hope it's clear that in case of asking the client to redirect to another URL - the browser shall not honor the cookies.
However, the 2nd method - where server forwards the request is feasible. The main mistake appears to be in mutating the response while we are supposed to change the request.
Then again, one cannot directly mutate a HttpServletRequest object. Here is one way to do so:
HttpServletRequestWrapper requestWrapper = new HttpServletRequestWrapper(request){
public String getHeader(String name) {
String value = super.getHeader(name);
if(Strings.isNullOrEmpty(value)) {
...
value = myNewHeader;
}
return value;
}
public Enumeration<String> getHeaders(String name) {
List<String> values = Collections.list(super.getHeaders(name));
if(values.size()==0) {
...
values.add(myNewHeader);
}
return Collections.enumeration(values);
}
public Enumeration<String> getHeaderNames() {
List<String> names = Collections.list(super.getHeaderNames());
names.add(myNewHeaderName);
...
return Collections.enumeration(names);
}
}
Followed by:
RequestDispatcher view = request.getRequestDispatcher("redirect.jsp");
// OR (If you can get servletContext)
RequestDispatcher view = servletContext.getRequestDispatcher("redirect.jsp");
view.forward(requestWrapper, response);
Reference:
https://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletRequestWrapper.html
For the headers case - getHeader(), getHeaders() and getHeaderNames() fn in the reqWrapper obj need Overriding.
Similarly you can override cookies and params.
See also: Modify request parameter with servlet filter
NOTE: It might not be possible to forward a req to an endpoint which expects a different MIME type.
A client side redirect creates a new HTTP request/response pair.
This link may help you more on debugging perspective -
Sending Custom headers

Golang Modify HTTP Request Parameters Such As URL Path Before Routing

It's common practice in some cases to pass plain URIs as suffix of the path instead of a query parameter. Here is an example from Internet Archive's Wayback Machine.
https://web.archive.org/web/20150825082012/http://example.com/
In this example, user is requesting a copy of http://example.com/ as captured at 2015-08-25 08:20:12. If we were to implement similar service in Go, we probably would have a router as follows:
http.HandleFunc("/web/", returnArchivedCopy)
Then in the returnArchivedCopy handler function, we will split r.URL.Path (where r is the Request object) to extract the date-time and the target URL. However there is a problem in this style of URL scheme; Go's net/http package calls cleanPath function on the path portion to sanitize it. This sanitization process does various cleanup tasks such as eeliminating . and .. from the path and replace multiple slashes with a single one. This later operation makes sense when because in Unix systems // in the file path are same as /. However this causes an issue in the above described use case as http://example becomes http:/example and the server internally returns a redirect response to the client with the sanitized path.
I am wondering, what are my options in this case? Is there a way to ask HTTP not to sanitize the request path while still utilizing all the default behavior that is shipped with the default (or slightly modified) server, multiplexer, and handler? Or is there a way to modify the request parameters (path in this case) before it hits the multiplexer's routing patterns. If the later is possible, we might try to perform something like URL encoding to avoid the redirect and later decode the URL back in the handler function before extracting desired bits.
I have experimented with some custom handlers and multiplexers, but I am new to Go, hence I was not quite sure how to delegate the routing back to the default handlers after making changes in the request.
You can implement a wrapper mux, that falls back to the default one, here's a very simple example:
func main() {
http.HandleFunc("/blah", func(w http.ResponseWriter, req *http.Request) {
w.Write([]byte("w00t"))
})
http.ListenAndServe(":9090", http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
p := strings.SplitN(req.URL.RequestURI()[1:] /*trim the first slash*/, "/", 3)
if len(p) != 3 || p[0] != "web" {
http.DefaultServeMux.ServeHTTP(w, req)
return
}
t, err := time.Parse("20060102150405", p[1])
if err != nil {
http.Error(w, "invalid time", 400)
return
}
url := p[2]
fmt.Fprintf(w, "requested url %v # %v", url, t)
}))
}

Resources