Golang: How to handle and serve subdomains? - go

The problem is serving the domain as well as subdomains x,y,z (or in this example blog, admin, and design). When running the following and requesting blog.localhost:8080/ firefox cant find the server www.blog.localhost:8080.
package main
import (
"html/template"
"log"
"net/http"
)
var tpl *template.Template
const (
domain = "localhost"
blogDomain = "blog." + domain
adminDomain = "admin." + domain
designDomain = "design." + domain
)
func init() {
tpl = template.Must(template.ParseGlob("templates/*.gohtml"))
}
func main() {
// Default Handlers
http.HandleFunc("/", index)
// Blog Handlers
http.HandleFunc(blogDomain+"/", blogIndex)
// Admin Handlers
http.HandleFunc(adminDomain+"/", adminIndex)
// Design Handlers
http.HandleFunc(designDomain+"/", designIndex)
http.Handle("/static/", http.StripPrefix("/static", http.FileServer(http.Dir("static"))))
http.ListenAndServe(":8080", nil)
}
func index(res http.ResponseWriter, req *http.Request) {
err := tpl.ExecuteTemplate(res, "index.gohtml", nil)
if err != nil {
log.Fatalln("template didn't execute: ", err)
}
}
// Blog Handlers
func blogIndex(res http.ResponseWriter, req *http.Request) {
err := tpl.ExecuteTemplate(res, "index.gohtml", nil)
if err != nil {
log.Fatalln("template didn't execute: ", err)
}
}
// Admin Handlers
func adminIndex(res http.ResponseWriter, req *http.Request) {
err := tpl.ExecuteTemplate(res, "index.gohtml", nil)
if err != nil {
log.Fatalln("template didn't execute: ", err)
}
}
// Design Handlers
func designIndex(res http.ResponseWriter, req *http.Request) {
err := tpl.ExecuteTemplate(res, "index.gohtml", nil)
if err != nil {
log.Fatalln("template didn't execute: ", err)
}
}
Is it possible to serve subdomains using the standard library? If so how?
EDIT: Requesting localhost:8080/ works fine
EDIT2: I edited /etc/hosts to include the subdomains:
127.0.0.1 blog.localhost.com
127.0.0.1 admin.localhost.com
127.0.0.1 design.localhost.com
Pinging them works correctly but firefox cannot reach them.

Given the hosts file in your second edit, you can point firefox to, for example, blog.localhost.com:8080 but then you also have to handle that domain pattern, i.e. http.HandleFunc(blogDomain+":8080/", blogIndex).
If that's not what you want you can instead listen on port 80 i.e. http.ListenAndServe(":80", nil), you might need to run your app in sudo so that it has permission to use that port, then your blog.localhost.com should work.

Related

Why is my GO server not displaying my HTML files in the browser?

I am doing a GO course, and whenever I run my server code, I don't get any errors but when I try to type in "localhost:8080" in the browser, I get a message saying "localhost didn’t send any data. ERR_EMPTY_RESPONS". I have the same exact code as the course, except I am using HTML and not TMPL. Why isn't my HTML displaying in the browser?
package main
import (
"fmt"
"html/template"
"net/http"
)
const portNumber = ":8080"
func Home(w http.ResponseWriter, r *http.Request) {
renderTemplate(w, "home.html")
}
func About(w http.ResponseWriter, r *http.Request) {
}
func renderTemplate(w http.ResponseWriter, html string) {
parsedTemplate, _ := template.ParseFiles("./templates" + html)
err := parsedTemplate.Execute(w, nil)
if err != nil {
fmt.Println("error parsing template:", err)
return
}
}
func main() {
http.HandleFunc("/", Home)
http.HandleFunc("/about", About)
fmt.Println(fmt.Sprintf("Starting App on port %s", portNumber))
_ = http.ListenAndServe(portNumber, nil)
}
The ParseFiles method takes a path. You're missing the / after "./templates". So it should be:
parsedTemplate, _ := template.ParseFiles("./templates/" + html)

Web address routing works fine with http.ListenAndServe, but fails with cgi.Serve()

I'm working on a website using Go. The server constraints require that I use CGI. When I test the following code locally using http.ListenAndServe() (commented out below), the various handlers are called correctly depending on the address requested. However, if I use cgi.Serve() instead, the default router is executed for all addresses (i.e., the handler for "/" is always executed). I'd appreciate any clues as to how to fix the issue.
EDIT: Here is the simplest test case I can think of to show the problem:
//=============SIMPLIFIED CODE================//
package main
import (
"fmt"
"net/http"
"net/http/cgi"
)
func defaultHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Default")
}
func otherHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Other")
}
func main() {
http.HandleFunc("/other", otherHandler)
http.HandleFunc("/", defaultHandler)
/*
//Works fine
err := http.ListenAndServe(":8090", nil)
if err != nil {
panic(err)
}
*/
//Always fires defaultHandler no matter the address requested
err := cgi.Serve(nil)
if err != nil {
panic(err)
}
}
//=============CODE FROM ORIGINAL POST===================//
package main
import (
"fmt"
"net/http"
"net/http/cgi"
"net/url"
"os"
"github.com/go-cas/cas"
)
func logoutHandler(w http.ResponseWriter, r *http.Request) {
cas.RedirectToLogout(w, r)
}
func calendarHandler(w http.ResponseWriter, r *http.Request) {
if !cas.IsAuthenticated(r) {
cas.RedirectToLogin(w, r)
}
fmt.Fprintf(w, "Calendar for %s", cas.Username(r))
}
func defaultHandler(w http.ResponseWriter, r *http.Request) {
if !cas.IsAuthenticated(r) {
cas.RedirectToLogin(w, r)
}
fmt.Fprintf(w, "Hi there %s!", cas.Username(r))
}
func main() {
u, _ := url.Parse("https://www.examplecasserver.com")
client := cas.NewClient(&cas.Options{
URL: u,
})
http.Handle("/logout", client.HandleFunc(logoutHandler))
http.Handle("/calendar", client.HandleFunc(calendarHandler))
http.Handle("/", client.HandleFunc(defaultHandler))
/*
err := http.ListenAndServe(":8080", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
*/
err := cgi.Serve(nil)
if err != nil {
panic(err)
}
}
The CGI program expects some variables to be set in order to build the request.
Probably there is some issue with the configuration of your web server in which the variables are either not set correctly or not named correctly.
To verify this:
1) Add this before calling cgi.Serve and you'll see how the right handler is called (otherHandler)
os.Setenv("REQUEST_METHOD", "get")
os.Setenv("SERVER_PROTOCOL", "HTTP/1.1")
os.Setenv("SCRIPT_NAME", "/other")
2) Add this at the beginning of the main to check how the variables are being set by the web server:
fmt.Println(os.Environ())
In that output, look for the CGI meta variables defined in the spec:
http://www.ietf.org/rfc/rfc3875
Look for the section "Request Meta-Variables" in that page, you are probably looking for the SCRIPT_NAME or PATH_INFO variables.
EDIT
From the variable values you pasted below, it seems the issue is that the REQUEST_URI contains an additional path component:
REQUEST_URI=/main.cgi/other
So the easiest fix would be for you to map the routes accordingly:
http.HandleFunc("/main.cgi/other", otherHandler)
http.HandleFunc("/", defaultHandler) // or maybe /main.cgi

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.

How do I rewrite / redirect from http to https in Go?

I have put up TLS and it works. I know how to rewrite from http to https in nginx, but I do not use nginx anymore. I don't know how to do this in Go properly.
func main() {
certificate := "/srv/ssl/ssl-bundle.crt"
privateKey := "/srv/ssl/mykey.key"
http.HandleFunc("/", rootHander)
// log.Fatal(http.ListenAndServe(":80", nil))
log.Fatal(http.ListenAndServeTLS(":443", certificate, privateKey, nil))
}
func rootHander(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("To the moon!"))
}
How would I do this in a good way?
Create a handler which handles redirection to https like:
func redirectToTls(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "https://IPAddr:443"+r.RequestURI, http.StatusMovedPermanently)
}
Then redirect http traffic:
go func() {
if err := http.ListenAndServe(":80", http.HandlerFunc(redirectToTls)); err != nil {
log.Fatalf("ListenAndServe error: %v", err)
}
}()
The solutions posted above are a little inflexible, especially if the external hostname is different from the local host.
Here is the code I use for HTTP->HTTPS redirects:
package main
import (
"net"
"log"
"net/http"
)
var httpAddr ":8080"
var httpsAddr ":8443"
func main() {
srv := http.Server{
Addr: httpsAddr,
}
_, tlsPort, err := net.SplitHostPort(httpsAddr)
if err != nil {
return err
}
go redirectToHTTPS(tlsPort)
srv.ListenAndServeTLS("cert.pem", "key.pem")
}
func redirectToHTTPS(tlsPort string) {
httpSrv := http.Server{
Addr: httpAddr,
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request){
host, _, _ := net.SplitHostPort(r.Host)
u := r.URL
u.Host = net.JoinHostPort(host, tlsPort)
u.Scheme="https"
log.Println(u.String())
http.Redirect(w,r,u.String(), http.StatusMovedPermanently)
}),
}
log.Println(httpSrv.ListenAndServe())
}
If you are using standard ports (80,443) the splitting of joining of the adresses is not rquired and just setting the scheme on the URL is sufficient.
package main
import (
"fmt"
"net/http"
)
func redirectToHttps(w http.ResponseWriter, r *http.Request) {
// Redirect the incoming HTTP request. Note that "127.0.0.1:443" will only work if you are accessing the server from your local machine.
http.Redirect(w, r, "https://127.0.0.1:443"+r.RequestURI, http.StatusMovedPermanently)
}
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hi there!")
fmt.Println(r.RequestURI)
}
func main() {
http.HandleFunc("/", handler)
// Start the HTTPS server in a goroutine
go http.ListenAndServeTLS(":443", "cert.pem", "key.pem", nil)
// Start the HTTP server and redirect all incoming connections to HTTPS
http.ListenAndServe(":8080", http.HandlerFunc(redirectToHttps))
}
There is another great example and discussion here if you are using your own mux:
https://gist.github.com/d-schmidt/587ceec34ce1334a5e60

How can I inject a specific IP address in the test server ? Golang

I'm trying to test an application which provides information based on ip address. However I can't find how to set the Ip address manually . Any idea ?
func TestClientData(t *testing.T) {
URL := "http://home.com/hotel/lmx=100"
req, err := http.NewRequest("GET", URL, nil)
if err != nil {
t.Fatal(err)
}
req.RemoveAddr := "0.0.0.0" ??
w := httptest.NewRecorder()
handler(w, req)
b := w.Body.String()
t.Log(b)
}
The correct line would be:
req.RemoteAddr = "0.0.0.0"
You don't need the :=. It won't work because you don't create a new variable.
Like this (on playground http://play.golang.org/p/_6Z8wTrJsE):
package main
import (
"io"
"log"
"net/http"
"net/http/httptest"
)
func handler(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "Got request from ")
io.WriteString(w, r.RemoteAddr)
}
func main() {
url := "http://home.com/hotel/lmx=100"
req, err := http.NewRequest("GET", url, nil)
if err != nil {
log.Fatal(err)
}
// can't use := here, because RemoteAddr is a field on a struct
// and not a variable
req.RemoteAddr = "127.0.0.1"
w := httptest.NewRecorder()
handler(w, req)
log.Print(w.Body.String())
}

Resources