Serve html file with custom status code - go

I need to have a custom not found html page. Here is what I've tried:
package main
import (
"net/http"
"github.com/julienschmidt/httprouter"
)
func main() {
r := httprouter.New()
r.NotFound = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(404)
http.ServeFile(w, r, "files/not-found.html")
})
http.ListenAndServe(":8000", r)
}
I have the line w.WriteHeader(404) to make sure the status code is 404, but the code above gives the error:
http: multiple response.WriteHeader calls
Without the line w.WriteHeader(404) there are no errors and the page is shown correctly, but the status code is 200. I want it to be 404.

You can simply just write the contents yourself.
Something like:
r.NotFound = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
contents, err := ioutil.ReadFile("files/not-found.html")
if err != nil {
panic(err) // or do something useful
}
w.WriteHeader(404)
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.Write(contents)
})

David's answer worked, and this is another way.
// other header stuff
w.WriteHeader(http.StatusNotFound)
file, err := os.Open("files/not-found.html")
if err != nil {
log.Println(err)
return
}
_, err = io.Copy(w, file)
if err != nil {
log.Println(err)
}
file.Close() // consider defer ^

Related

how to use the output of a bash script in a Golang function

This might not even be possible but I've been researching for the past hour and come up blank. I have a bash script that gives me a certain output and I want to add that output to Golang in order to redirect a website based on the output of the bash script. Sorry if this makes no sense, im new to Go
Here is what I have to run the bash script and output the value
func main() {
out, err := exec.Command("/bin/sh", "script.sh").Output()
if err != nil {
log.Fatal(err)
}
fmt.Printf(string(out))
}
I then want to use the value that was output there in another function and to redirect a URL heres how I would redirect to a url and I wanted the $variable to be added. This is just an example I copied off the internet but its what I want to replicate.
func redirect(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "**$variable**", 301)
}
func main() {
http.HandleFunc("/", redirect)
err := http.ListenAndServe(":8080", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
Assuming your script must be only run once at startup, then this should do the trick:
var redirectTo string
func redirect(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, redirectTo, 301)
}
func main() {
out, err := exec.Command("/bin/sh", "script.sh").Output()
if err != nil {
log.Fatal(err)
}
redirectTo = string(out)
http.HandleFunc("/", redirect)
err = http.ListenAndServe(":8080", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
Or if you don't want to have a global variable you can generate the redirect function at runtime:
func main() {
out, err := exec.Command("/bin/sh", "script.sh").Output()
if err != nil {
log.Fatal(err)
}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, string(out), 301)
})
err = http.ListenAndServe(":8080", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
if you want to handle when out is empty, you can do:
func redirect(w http.ResponseWriter, r *http.Request) {
if len(redirectTo) == 0 {
http.Error(w, "No URL parsed!", 500)
return
}
http.Redirect(w, r, redirectTo, 301)
}
This will return an HTTP error 500 in that case.
You can also simply exit the program instead of serving:
out, err := exec.Command("/bin/sh", "script.sh").Output()
if err != nil {
log.Fatal(err)
}
if len(out) == 0 {
log.Fatal("no output was returned from script")
}
You can also add more verifications to out here if you wish, like if it is a correct URL, using net/url package for instance.

syntax error: unexpected newline, expecting comma or )

I get error syntax error: unexpected newline, expecting comma or ). im new at golang. im try with new code but same error. can someone help me?.
my code
package main
import "fmt"
import "net/http"
import "html/template"
import "path"
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
var filepath = path.Join("index.html")
var tmpl, err = template.ParseFiles(filepath)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
http.HandleFunc("/membuat-web-dengan-golang", func(w http.ResponseWriter, r *http.Request) {
var filepath = path.Join("membuat-web-dengan-golang.html")
var tmpl, err = template.ParseFiles(filepath)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("assets"))))
fmt.Println("server started at localhost:9000")
http.ListenAndServe(":9000", nil)
}
Hi your HandleFunc functions are missing the closing brackets. Check the fmt.Println(tmpl) line which I added to your code. The first closing curly bracket } is for the anonymous function and the second round bracket ) is for the HandleFunc.
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
var filepath = path.Join("index.html")
var tmpl, err = template.ParseFiles(filepath)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
fmt.Println(tmpl)})
http.HandleFunc("/membuat-web-dengan-golang", func(w http.ResponseWriter, r *http.Request) {
var filepath = path.Join("membuat-web-dengan-golang.html")
var tmpl, err = template.ParseFiles(filepath)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
fmt.Println(tmpl)})
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("assets"))))
fmt.Println("server started at localhost:9000")
http.ListenAndServe(":9000", nil)
}

Unable to redirect in a golang web app. It sticks to one page

This is code snippet from a file called upload.go.
I tried a lot of ways to redirect to another pages. I want to redirect to another page when the statements in POST are completed running.
package main
import (
"fmt"
"io"
"net/http"
"os"
"text/template"
)
func upload(w http.ResponseWriter, r *http.Request) {
if r.Method == "GET" {
// GET
t, _ := template.ParseFiles("upload.gtpl")
t.Execute(w, nil)
} else if r.Method == "POST" {
// Post
file, handler, err := r.FormFile("uploadfile")
if err != nil {
fmt.Println(err)
return
}
defer file.Close()
fmt.Fprintf(w, "%v", handler.Header)
f, err := os.OpenFile("./test/"+handler.Filename, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
fmt.Println(err)
return
}
defer f.Close()
io.Copy(f, file)
img, err := imgio.Open("./test/" + handler.Filename)
if err != nil {
panic(err)
}
inverted := effect.Invert(img)
if err := imgio.Save("filename.png", inverted, imgio.PNGEncoder()); err != nil {
panic(err)
}
fmt.Fprintf(w, "%v", handler.Header)
http.Redirect(w, r, "www.google.com", http.StatusMovedPermanently)
} else {
fmt.Println("Unknown HTTP " + r.Method + " Method")
}
}
func main() {
http.HandleFunc("/upload", upload)
http.HandleFunc("/hi", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hi")
http.Redirect(w, r, "www.google.com", http.StatusMovedPermanently)
})
http.ListenAndServe(":9090", nil) // setting listening port
}
It stays on the upload page what ever I do. Can anyone help me debug this?
Your code is writing to the ResponseWriter before trying to send a redirect.
Upon the first write to the ResponseWriter, the status code (200 OK) and headers are sent, if they haven't already been sent, and then the data you passed to the writer.
If you intend to send an HTTP redirect, you can't write any response body to the ResponseWriter. From reading your code, it doesn't make much sense why you are writing to it in the first place. They look like debugging print statements, which you probably ought to send to os.Stderr or a logger instead of the web page response body.
If you need to redirect after posting a form, you need to set the status to http.StatusSeeOther (303)
For example:
http.Redirect(w, r, "/index", http.StatusSeeOther)

golang httputil.NewSingleHostReverseProxy how to read response and modify the response?

I've a reverse proxy like this:
Iam using RoundTrip but this proxy server don't work correctly.
How to correctly read and modify response?
and somebody create proxy server via NewSingleHostReverseProxy.
Please Help.
package main
import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
"net/http/httputil"
"net/url"
)
type transport struct {
http.RoundTripper
}
func (t *transport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
resp, err = t.RoundTripper.RoundTrip(req)
if err != nil {
return nil, err
}
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
err = resp.Body.Close()
if err != nil {
return nil, err
}
b = bytes.Replace(b, []byte("Google"), []byte("GOOGLE"), -1)
body := ioutil.NopCloser(bytes.NewReader(b))
resp.Body = body
return resp, nil
}
func sameHost(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
r.Host = r.URL.Host
handler.ServeHTTP(w, r)
})
}
func main() {
u, _ := url.Parse("http://habrahabr.ru")
reverseProxy := httputil.NewSingleHostReverseProxy(u)
reverseProxy.Transport = &transport{http.DefaultTransport}
// wrap that proxy with our sameHost function
singleHosted := sameHost(reverseProxy)
http.ListenAndServe(":3000", singleHosted)
}
When you are going to http:// for most good sites (for example your habrahabr.ru) there is a redirect to https://, so request to http will return something like 301 Moved Permanently and you will not find content that you seek for. Also, after correct to https, make sure that site does not use javascript to load content, you can easily check this by curl:
curl localhost:3000
Also use some logging to determine what's wrong.

Gorilla session.AddFlash Does Not Add Flash Message

I have a registration page with two handlers, one for displaying the form, one for processing a form submission.
I am trying to use a session.AddFlash method to save an error, then do 302 redirect back to the registration form and display the error.
I set up a session store:
package web
import (
"github.com/gorilla/sessions"
)
var sessionStore = sessions.NewCookieStore([]byte(sessionSecret))
Then my handlers look like this:
package web
import (
"html/template"
"log"
"net/http"
)
func registerForm(w http.ResponseWriter, r *http.Request) {
session, err := sessionStore.Get(r, "mysession")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
data := map[string]interface{}{}
log.Print("Flashes: ")
log.Print(session.Flashes())
if flashes := session.Flashes(); len(flashes) > 0 {
data["error"] = flashes[0]
}
tmpl, _ := template.ParseFiles("web/templates/register.html.tmpl")
tmpl.Execute(w, data)
}
func register(w http.ResponseWriter, r *http.Request) {
session, err := sessionStore.Get(r, "mysession")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
r.ParseForm()
username := r.Form["username"][0]
password := r.Form["password"][0]
if UserExists(username) {
log.Print("Username already taken")
session.AddFlash("Username already taken")
http.Redirect(w, r, "/web/register", http.StatusFound)
return
}
_, err = CreateUser(username, password)
log.Print(err)
if err != nil {
session.AddFlash(err.Error())
http.Redirect(w, r, "/web/register", http.StatusFound)
return
}
http.Redirect(w, r, "/web/login", http.StatusFound)
}
By adding logs I can see that UserExists returns true therefor a flash message should be added however after redirection to the form handler there is no flash message saved in the session.
I think you have to save the session before you redirect.
session.Save(r, w)
http://www.gorillatoolkit.org/pkg/sessions#Session.Save

Resources