Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Local subdomains for a standalone application

I have a website, which is composed by three smaller 'independent' subsites:

  • mysite
    • index.html
    • favicons
    • images
    • doc
      • index.html
      • css
      • img
      • js
      • ...
    • editor
      • index.html
      • images
      • js
      • src
      • ...

Where doc is a site created with Hugo :: A fast and modern static website engine, editor is the mxgraph Graphditor example; and the remaining files make a hand-made landing page.

Besides deploying to any web server, I'd like to distribute the site as an 'standalone application'. To allow so, I wrote this really simple server in go:

package main

import (
  flag "github.com/ogier/pflag"
  "fmt"
  "net/http"
  "net/http/httputil"
  "os"
  "path/filepath"
)

var port = flag.IntP("port", "p", 80, "port to serve at")
var dir = flag.StringP("dir", "d", "./", "dir to serve from")
var verb = flag.BoolP("verbose", "v", false, "")

func init() {
  flag.Parse();
}

type justFilesFilesystem struct {
  fs http.FileSystem;
}
type neuteredReaddirFile struct {
  http.File
}

func (fs justFilesFilesystem) Open(name string) (http.File, error) {
  f, err := fs.fs.Open(name)
  if err != nil { return nil, err; }
  return neuteredReaddirFile{f}, nil
}

func (f neuteredReaddirFile) Readdir(count int) ([]os.FileInfo, error) {
  return nil, nil;
}

func loggingHandler(h http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        requestDump, err := httputil.DumpRequest(r, true)
        if err != nil { fmt.Println(err); }
        fmt.Println(string(requestDump))
        h.ServeHTTP(w, r)
    })
}

func main() {
  str, err := filepath.Abs(*dir)
  if err != nil { os.Exit(1); }
  fmt.Printf("Serving at port %d from dir %s\n\n",*port,str)

  http.ListenAndServe(fmt.Sprintf(":%d",*port), loggingHandler(http.FileServer(justFilesFilesystem{http.Dir(*dir)})))
}

As a result, I can run simpleserver -d <path-to-mysite> and browse the sites through localhost, localhost/doc and localhost/editor.

Then, I'd like to use custom (sub)domain(s) such as mylocal.app, doc.mylocal.app and editor.mylocal.app. So, I added the following line to my the /etc/hosts file: 127.0.0.1 mylocal.app. Therefore, I can browse mylocal.app, mylocal.app/editor and mylocal.app/doc. Moreover, I was able to change it to mylocal.app, mylocal.app:<editor-port> and mylocal.app:<doc-port> with different packages.

However, when I try to use a subdomain, it is not properly resolved, so any reverse-proxy strategy won't work. Since wildcards are not supported, I can add additional entries in the /etc/hosts file, but I'd prefer to avoid it.

Although, an alternative solution is to run dnsmasq, I'd like to keep the application standalone. I found some equivalent golang packages. However, I feel that many features are supported which I don't really need.

Furthermore, since I don't really have to resolve any IP, but to provide an alias of localhost, I think that a proxy could suffice. This would also be easier to configure, since the user could configure the browser only, and no system-wide modification would be required.

Yet, all the traffic from the user would be 'filtered' by my app. Is this correct? If so, can you point me to any reference to implement it in the most clean way. I know this is quite subjective, but I mean a relatively short (say 10 lines of code) snippet so that users can easily check what is going on.

EDIT

I'd like to use something like:

func main() {
    mymux := http.NewServeMux()
    mymux.HandleFunc("*.mylocal.app", myHandler)
    mymux.HandleFunc("*", <systemDefaultHandler>)
    http.ListenAndServe(":8080", mymux)
}

or

func main() {
    mymux := http.NewServeMux()
    mymux.HandleFunc("editor.mylocal.app", editorHandler)
    mymux.HandleFunc("doc.mylocal.app", docHandler)
    mymux.HandleFunc("*.mylocal.app", rootHandler)
    mymux.HandleFunc("*", <systemDefaultHandler>)
    http.ListenAndServe(":8080", mymux)
}

These are only snippets. A complete example is this, which was referenced in the comments by @Steve101.

However, at now, I don't know what systemDefaultHandler is. And that is not solved there.

Apart from that, @faraz suggested using goproxy. I think that the HTTP/HTTPS transparent proxy is the default handler I am looking for. But, using a package only to do that seems excessive to me. Can I achieve the same functionality with built-in resources?

like image 418
U.Martinez-Corral Avatar asked Mar 11 '17 02:03

U.Martinez-Corral


People also ask

Can localhost have subdomains?

localhost is not supposed to have any subdomains. To do so violates the approved RFC standards. localhost has an A record and in IPv6 environments, an AAAA record. All other DNS record types, including SOA are forbidden.

Can I have a subdomain on a different server?

Yes, It's possible. For Example. You would either Contact your Host to set this, or if you have the ability to point DNS your self then do so. Just make sure that the site you want to serve on that subdomain exists on the server that it's pointing to.

What is DNS subdomain?

A subdomain is a domain that is a part of a larger domain under the Domain Name System (DNS) hierarchy. It is used as an easy way to create a more memorable Web address for specific or unique content with a website.


1 Answers

Unfortunately, there's no dead simple way to do this through Go. You'll need to intercept your system's DNS requests just like dnsmasq, and that's inevitably going to require some modification the system DNS config (/etc/resolv.conf in Linux, /etc/resolver on a Mac, or firewall rule) to route your DNS requests to your app. Going the DNS has the downside that you'd need to build a DNS server inside your app similar to pow.cx, which seems unnecessarily complicated.

Since mucking with system config is inevitable, I'd vote for making changes to the hosts file on boot or when a directory is added/removed (via fsnotify.) On shutdown, you can clear the added entries too.

If you're looking to isolate these changes to a specific browser instead of make a system-wide change, you could always run your application through a proxy server like goproxy and tell your browser to use that proxy for requests. For example, you can do this in Chrome through its preferences or by setting the --proxy-server flag:

--proxy-server=<scheme>=<uri>[:<port>][;...] | <uri>[:<port>] | "direct://"

See Chrome's network settings docs for more details.

Also, if you're willing to much with browser configs, you could just use an extension to handle the requests as needed.

like image 105
fny Avatar answered Oct 31 '22 01:10

fny