Golang getting a 404 whilst server is running - http

I am trying to run a basic web app, following a tutorial, using Golang and the routing package Gorilla/mux. The server is running fine but it refuses to find the index.html file regardless of what I put in the browser, always returning a 404.
Here is the code:
main.go
package main
import (
"database/sql"
"fmt"
"net/http"
"github.com/gorilla/mux"
_ "github.com/lib/pq"
)
const (
host = "localhost"
port = 5432
user = "postgres"
password = "0102"
dbname = "bird_encyclopaedia"
)
func newRouter() *mux.Router {
r := mux.NewRouter()
r.HandleFunc("/hello", handler).Methods("GET")
staticFileDirectory := http.Dir("./assets/")
staticFileHandler := http.StripPrefix("/assets/", http.FileServer(staticFileDirectory))
r.PathPrefix("/assets/").Handler(staticFileHandler).Methods("GET")
r.HandleFunc("/bird", getBirdHandler).Methods("GET")
r.HandleFunc("/bird", createBirdHandler).Methods("POST")
return r
}
func main() {
fmt.Println("Starting server dickface...")
connString := fmt.Sprintf("host=%s port=%d user=%s "+
"password=%s dbname=%s sslmode=disable",
host, port, user, password, dbname)
db, err := sql.Open("postgres", connString)
if err != nil {
panic(err)
}
err = db.Ping()
if err != nil {
panic(err)
}
InitStore(&dbStore{db: db})
r := newRouter()
fmt.Println("Serving on port 8080")
http.ListenAndServe(":8080", r)
}
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello World!")
}
The html file is just in the assets/index.html directory, I can supply that if need be but I can't see the problem being in the actual html?
I have trawled through the code many times and cannot see why the server would not be able to find the directory. I have tried localhost/8080/assets, localhost/8080/assets/index.html, localhost/8080, and all other variants.
If I append it with /hello mind it returns the Hello world as seen in main.go
And if I append it with /bird it returns "null" instead of 404.

You don't need http.StripPrefix() since you are not using the assets in the URL.
Just change these two lines:
staticFileHandler := http.StripPrefix("/assets/", http.FileServer(staticFileDirectory))
r.PathPrefix("/assets/").Handler(staticFileHandler).Methods("GET")
to
staticFileHandler := http.FileServer(staticFileDirectory)
r.PathPrefix("/").Handler(staticFileHandler).Methods("GET")

Related

Pull info from private site that requires login (Golang)

I am trying to pull my classes from my online timetable, however, it seems as if I cannot get past the login stage. My code is:
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"net/url"
"strings"
"github.com/PuerkitoBio/goquery"
)
type App struct {
Client *http.Client
}
type Timetable struct {
Name string
}
const (
baseURL string = "https://myclasswebsite.com"
)
func (app *App) login() {
//login := loginInfo()
client := app.Client
loginURL := baseURL + "/portal2/#!/login"
data := url.Values{
"inputEmail": {"my_actual_username"},
"password": {"my_actual_password"},
}
response, err := client.PostForm(loginURL, data)
if err != nil {
log.Fatalln(err)
}
defer response.Body.Close()
_, err = ioutil.ReadAll(response.Body)
if err != nil {
log.Fatalln(err)
}
}
func (app *App) getTimetable() []Timetable {
timetableURL := baseURL + "/portal/dashboard"
client := app.Client
response, err := client.Get(timetableURL)
if err != nil {
log.Fatalln("Error fetching response. ", err)
}
defer response.Body.Close()
document, err := goquery.NewDocumentFromReader(response.Body)
fmt.Println(document.Html())
if err != nil {
log.Fatal("Error loading HTTP response body. ", err)
}
var classes []Timetable
document.Find(".timetable table").Each(func(i int, s *goquery.Selection) {
className := strings.TrimSpace(s.Text())
class := Timetable{
Name: className,
}
classes = append(classes, class)
})
return classes
}
I changed the base URL and login info just for privacy reasons, however, the rest of the code is as-is.
My main. go file is:
package main
import (
"fmt"
"net/http"
"net/http/cookiejar"
)
func main() {
jar, _ := cookiejar.New(nil)
app := App{
Client: &http.Client{Jar: jar},
}
app.login()
classes := app.getTimetable()
fmt.Println("class array is", classes)
for index, class := range classes {
fmt.Printf("%d: %s\n", index+1, class.Name)
}
}
The final print returns an empty slice, and when I print the response.Html() to the console, I receive the login-page Html rather than the dashboard-page HTML.
I'm in no way expecting anyone to fix this for me but a second pair of eyes and maybe a clue in which direction I should go would be helpful. Thank you so much!
Since I'm unfamiliar with your class website, some ideas for progressing:
You're not checking the status code from your login call. You may be getting a non-200 status code.
After you've confirmed the status code, check the cookie jar to ensure that a cookie has been saved. This is probably on the unlikelier side, but it's worth checking.
Lastly, attempt the same sequence with curl with -v (if you haven't already). That will give you more insight as to what is happening with your call plan.
I ended up ignoring some cookies accidentally while testing. Whoops...

Accessing HackerNews API

I'm trying to access the HackerNews API to practice Go.
Anytime I go to my localhost to try see an output from Firebase database (where the data is stored) I am met with a Google Accounts authentication form.
Any help on this would be appreciated. In my terminal I used curl to check if I was getting a response from the server. I got a 200 OK response with content.
I thought I might be missing a Firebase client library but I'm not sure if that is the issue right now.
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
response, err := http.Get("https://hacker-news.firebaseio.com/v0/item/8863.json")
if err != nil {
fmt.Printf("The http request failed with the error %s\n", err)
} else {
data, _ := ioutil.ReadAll(response.Body)
fmt.Fprintf(w, string(data))
}
}
func main() {
fmt.Println("Starting the applicaiton")
http.HandleFunc("/", handler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
Aren't you suppose to add .json to URL (via https://github.com/HackerNews/API)?
e.g https://hacker-news.firebaseio.com/v0/item/8863.json

Basic Authentication for static resources

How can I add basic authentication to my static resources? With the code below, I'm able to view any files that are in the labels folder. I know in this question it was explained how to do it. But how would would I set the header when a http.ResponseWriter is not used?
package main
import (
"github.com/gorilla/mux"
"log"
"net/http"
"os"
)
func main() {
port := GetPort()
log.Println("[-] Listening on...", port)
r := mux.NewRouter()
r.PathPrefix("/labels/").Handler(http.StripPrefix("/labels/", http.FileServer(http.Dir("./labels/"))))
err := http.ListenAndServe(port, r)
log.Fatal(err)
}
// GetPort is for herkou deployment
func GetPort() string {
port := os.Getenv("PORT")
if port == "" {
port = "4747"
log.Println("[-] No PORT environment variable detected. Setting to ", port)
}
return ":" + port
}
Create a wrapper around each handler to pass the request from the authentication middleware which will forward the request further after authentication is done else return the response with error as
func authentication(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Println("Executing authentication")
next.ServeHTTP(w, r)
})
}
// open the dialog to download pdf files.
func dowloadPdf(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Disposition", "attachment; filename=YOUR_FILE")
w.Header().Set("Content-Type", r.Header.Get("Content-Type"))
w.Write([]byte("File downloaded"))
}
func main(){
pdfHandler := http.HandlerFunc(dowloadPdf)
http.Handle("/servepdf", authentication(pdfHandler))
http.ListenAndServe(":3000", nil)
}
But if I consider the fact there is no need to have authentication when serving static files like html, css, js etc. It would be better to create a handler to serve pdf files after authenticating users.
You can also use negorni middlewares with gorilla mux rather than creating custom middlewares.
package main
import (
"github.com/gorilla/mux"
"log"
"net/http"
"os"
)
func main() {
port := GetPort()
log.Println("[-] Listening on...", port)
r := mux.NewRouter()
r.PathPrefix("/labels/").Handler(http.StripPrefix("/labels/", ServeLabels(http.FileServer(http.Dir("./labels/")))))
err := http.ListenAndServe(port, r)
log.Fatal(err)
}
func ServeLabels(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("WWW-Authenticate", `Basic realm="mydomain"`)
h.ServeHTTP(w, r)
})
}
// GetPort is for herkou deployment
func GetPort() string {
port := os.Getenv("PORT")
if port == "" {
port = "4747"
log.Println("[-] No PORT environment variable detected. Setting to ", port)
}
return ":" + port
}
something like this, or you could just go ahead and use the gorilla mux middleware.

HTTP2 not enabled by default on localhost

I might be just clueless on this but my basic localhost server doesn't have HTTP2 Enabled for some odd reason, I normally proxy behind Caddy, but as I don't want to use my domain for this side project, I created a basic server in Go, and ran it, it works okay, but the headers show HTTP/1.1 instead of 2.0, what's wrong?
package main
import (
"fmt"
"net/http"
"html/template"
"os"
)
func IfError(err error, Quit bool) {
if err != nil {
fmt.Println(err.Error())
if(Quit) {
os.Exit(1);
}
}
}
func ServeHome(w http.ResponseWriter, r *http.Request) {
t, err := template.ParseFiles("html/home")
IfError(err, false)
err = t.Execute(w, nil)
IfError(err, false)
}
func RedirectRoot(fs http.Handler, home http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/" {
home.ServeHTTP(w, r)
} else {
fs.ServeHTTP(w, r)
}
})
}
func main() {
proto := ":8081"
ServeFiles := http.FileServer(http.Dir("static/"))
http.Handle("/", RedirectRoot(ServeFiles, http.HandlerFunc(ServeHome)))
fmt.Printf("Listening on ... %s", proto)
IfError(http.ListenAndServe(proto, nil), true)
}
Very basic stuff, but doesn't work even thought the documentation says it works by default. Also, my go version is 1.8.3
Yes, its enabled by default when you use with SSL certs.
Doc Reference: Starting with Go 1.6, the http package has transparent
support for the HTTP/2 protocol when using HTTPS.
err := http.ListenAndServeTLS(":8081", "server.crt", "server.key", handler)
if err != nil && err != http.ErrServerClosed {
log.Fatal("ListenAndServe: ", err)
}
Then, access it via
https://localhost:8081/

How Can I Make the Go HTTP Client NOT Follow Redirects Automatically?

I'm currently writing some software in Go that interacts with a REST API. The REST API endpoint I'm trying to query returns an HTTP 302 redirect along with an HTTP Location header, pointing to a resource URI.
I'm trying to use my Go script to grab the HTTP Location header for later processing.
Here's what I'm currently doing to achieve this functionality:
package main
import (
"errors"
"fmt"
"io/ioutil"
"net/http"
)
var BASE_URL = "https://api.example.com/v1"
var STORMPATH_API_KEY_ID = "xxx"
var STORMPATH_API_KEY_SECRET = "xxx"
func noRedirect(req *http.Request, via []*http.Request) error {
return errors.New("Don't redirect!")
}
func main() {
client := &http.Client{
CheckRedirect: noRedirect
}
req, err := http.NewRequest("GET", BASE_URL+"/tenants/current", nil)
req.SetBasicAuth(EXAMPLE_API_KEY_ID, EXAMPLE_API_KEY_SECRET)
resp, err := client.Do(req)
// If we get here, it means one of two things: either this http request
// actually failed, or we got an http redirect response, and should process it.
if err != nil {
if resp.StatusCode == 302 {
fmt.Println("got redirect")
} else {
panic("HTTP request failed.")
}
}
defer resp.Body.Close()
}
This feels like a bit of a hack to me. By overriding the http.Client's CheckRedirect function, I'm essentially forced to treat HTTP redirects like errors (which they aren't).
I've seen several other places suggesting to use an HTTP transport instead of an HTTP client -- but I'm not sure how to make this work since I need the HTTP Client as I need to use HTTP Basic Auth to communicate with this REST API.
Can any of you tell me a way to make HTTP requests with Basic Authentication -- while not following redirects -- that doesn't involve throwing errors and error handling?
There's a much simpler solution right now:
client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
This way, the http package automatically knows: "Ah, I shouldn't follow any redirects", but does not throw any error. From the comment in the source code:
As a special case, if CheckRedirect returns ErrUseLastResponse,
then the most recent response is returned with its body
unclosed, along with a nil error.
Another option, using the client itself, without the RoundTrip:
// create a custom error to know if a redirect happened
var RedirectAttemptedError = errors.New("redirect")
client := &http.Client{}
// return the error, so client won't attempt redirects
client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
return RedirectAttemptedError
}
// Work with the client...
resp, err := client.Head(urlToAccess)
// test if we got the custom error
if urlError, ok := err.(*url.Error); ok && urlError.Err == RedirectAttemptedError{
err = nil
}
UPDATE: this solution is for go < 1.7
It is possible, but the solution inverts the problem a little. Here's a sample written up as a golang test.
package redirects
import (
"github.com/codegangsta/martini-contrib/auth"
"github.com/go-martini/martini"
"net/http"
"net/http/httptest"
"testing"
)
func TestBasicAuthRedirect(t *testing.T) {
// Start a test server
server := setupBasicAuthServer()
defer server.Close()
// Set up the HTTP request
req, err := http.NewRequest("GET", server.URL+"/redirect", nil)
req.SetBasicAuth("username", "password")
if err != nil {
t.Fatal(err)
}
transport := http.Transport{}
resp, err := transport.RoundTrip(req)
if err != nil {
t.Fatal(err)
}
// Check if you received the status codes you expect. There may
// status codes other than 200 which are acceptable.
if resp.StatusCode != 200 && resp.StatusCode != 302 {
t.Fatal("Failed with status", resp.Status)
}
t.Log(resp.Header.Get("Location"))
}
// Create an HTTP server that protects a URL using Basic Auth
func setupBasicAuthServer() *httptest.Server {
m := martini.Classic()
m.Use(auth.Basic("username", "password"))
m.Get("/ping", func() string { return "pong" })
m.Get("/redirect", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/ping", 302)
})
server := httptest.NewServer(m)
return server
}
You should be able to put the above code into it's own package called "redirects" and run it after fetching the required dependencies using
mkdir redirects
cd redirects
# Add the above code to a file with an _test.go suffix
go get github.com/codegangsta/martini-contrib/auth
go get github.com/go-martini/martini
go test -v
Hope this helps!
To make request with Basic Auth that does not follow redirect use RoundTrip function that accepts *Request
This code
package main
import (
"fmt"
"io/ioutil"
"net/http"
"os"
)
func main() {
var DefaultTransport http.RoundTripper = &http.Transport{}
req, _ := http.NewRequest("GET", "http://httpbin.org/headers", nil)
req.SetBasicAuth("user", "password")
resp, _ := DefaultTransport.RoundTrip(req)
defer resp.Body.Close()
contents, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Printf("%s", err)
os.Exit(1)
}
fmt.Printf("%s\n", string(contents))
}
outputs
{
"headers": {
"Accept-Encoding": "gzip",
"Authorization": "Basic dXNlcjpwYXNzd29yZA==",
"Connection": "close",
"Host": "httpbin.org",
"User-Agent": "Go 1.1 package http",
"X-Request-Id": "45b512f1-22e9-4e49-8acb-2f017e0a4e35"
}
}
As an addition of top rated answer,
You can control the particle size
func myCheckRedirect(req *http.Request, via []*http.Request, times int) error {
err := fmt.Errorf("redirect policy: stopped after %d times", times)
if len(via) >= times {
return err
}
return nil
}
...
client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return myCheckRedirect(req, via, 1)
},
}
ref: https://golangbyexample.com/http-no-redirect-client-golang/

Resources