独立应用的本地子域名

huangapple go评论77阅读模式
英文:

Local subdomains for a standalone application

问题

我有一个网站,由三个较小的“独立”子站点组成:

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

其中,doc 是使用 Hugo :: A fast and modern static website engine 创建的站点,editormxgraph Graphditor example;其余文件构成了一个手工制作的首页。

除了部署到任何 Web 服务器上,我想将该网站作为一个“独立应用程序”进行分发。为此,我使用 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)})))
}

结果是,我可以运行 simpleserver -d <path-to-mysite> 并通过 localhostlocalhost/doclocalhost/editor 浏览站点。

然后,我想使用自定义的(子)域名,例如 mylocal.appdoc.mylocal.appeditor.mylocal.app。因此,我在 /etc/hosts 文件中添加了以下行:127.0.0.1 mylocal.app。因此,我可以浏览 mylocal.appmylocal.app/editormylocal.app/doc。此外,我还可以使用不同的端口将其更改为 mylocal.appmylocal.app:<editor-port>mylocal.app:<doc-port>

然而,当我尝试使用子域名时,它无法正确解析,因此任何反向代理策略都无法工作。由于不支持通配符,我可以在 /etc/hosts 文件中添加其他条目,但我更愿意避免这样做。

虽然可以运行 dnsmasq 这样的解决方案,但我希望保持应用程序的独立性。我找到了一些等效的 Go 语言包。然而,我觉得其中有很多我实际上并不需要的功能。

此外,由于我实际上不需要解析任何 IP,只需要提供 localhost 的别名,所以我认为一个代理就足够了。这样做也更容易配置,因为用户只需配置浏览器,而无需进行系统范围的修改。

然而,所有用户的流量都将由我的应用程序“过滤”。这样说对吗?如果是这样,你能否指导我以最简洁的方式实现它?我知道这很主观,但我的意思是一个相对简短(比如 10 行代码)的片段,以便用户可以轻松地查看发生了什么。

编辑

我想使用类似以下的代码:

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

或者

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)
}

这只是片段。一个完整的示例可以在 这里 找到,这是由 @Steve101 在评论中提到的。

然而,目前我不知道 systemDefaultHandler 是什么。那里没有解决这个问题。

此外,@faraz 建议使用 goproxy。我认为 HTTP/HTTPS transparent proxy 是我正在寻找的默认处理程序。但是,仅使用一个包来做到这一点似乎有些过度。我能否使用内置资源实现相同的功能?

英文:

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 &quot;github.com/ogier/pflag&quot;
&quot;fmt&quot;
&quot;net/http&quot;
&quot;net/http/httputil&quot;
&quot;os&quot;
&quot;path/filepath&quot;
)
var port = flag.IntP(&quot;port&quot;, &quot;p&quot;, 80, &quot;port to serve at&quot;)
var dir = flag.StringP(&quot;dir&quot;, &quot;d&quot;, &quot;./&quot;, &quot;dir to serve from&quot;)
var verb = flag.BoolP(&quot;verbose&quot;, &quot;v&quot;, false, &quot;&quot;)
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(&quot;Serving at port %d from dir %s\n\n&quot;,*port,str)
http.ListenAndServe(fmt.Sprintf(&quot;:%d&quot;,*port), loggingHandler(http.FileServer(justFilesFilesystem{http.Dir(*dir)})))
}

As a result, I can run simpleserver -d &lt;path-to-mysite&gt; 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:&lt;editor-port&gt; and mylocal.app:&lt;doc-port&gt; 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(&quot;*.mylocal.app&quot;, myHandler)
mymux.HandleFunc(&quot;*&quot;, &lt;systemDefaultHandler&gt;)
http.ListenAndServe(&quot;:8080&quot;, mymux)
}

or

func main() {
mymux := http.NewServeMux()
mymux.HandleFunc(&quot;editor.mylocal.app&quot;, editorHandler)
mymux.HandleFunc(&quot;doc.mylocal.app&quot;, docHandler)
mymux.HandleFunc(&quot;*.mylocal.app&quot;, rootHandler)
mymux.HandleFunc(&quot;*&quot;, &lt;systemDefaultHandler&gt;)
http.ListenAndServe(&quot;:8080&quot;, 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?

答案1

得分: 4

很遗憾,通过Go语言没有一种简单的方法来实现这一点。您需要像dnsmasq一样拦截系统的DNS请求,这必然需要对系统DNS配置进行一些修改(在Linux中是/etc/resolv.conf,在Mac上是/etc/resolver或防火墙规则),以将您的DNS请求路由到您的应用程序。使用DNS的缺点是您需要在应用程序内部构建一个类似于pow.cx的DNS服务器,这似乎过于复杂。

由于不可避免地需要修改系统配置,我建议在启动时或添加/删除目录时(通过fsnotify)对hosts文件进行更改。在关闭时,您也可以清除添加的条目。

如果您希望将这些更改限制在特定的浏览器中而不是进行系统范围的更改,您可以通过代理服务器(如goproxy)运行您的应用程序,并告诉浏览器使用该代理服务器进行请求。例如,您可以通过Chrome的首选项或设置--proxy-server标志来实现:

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

有关详细信息,请参阅Chrome的网络设置文档

此外,如果您愿意修改浏览器配置,您可以使用扩展程序来处理所需的请求。

英文:

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=&lt;scheme&gt;=&lt;uri&gt;[:&lt;port&gt;][;...] | &lt;uri&gt;[:&lt;port&gt;] | &quot;direct://&quot;

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.

答案2

得分: 0

只有一种不需要代理就能正常工作的解决方案,那就是为此注册一个域名,并在官方DNS条目中使用通配符将其解析为127.0.0.1,例如*.myApp.suche.org。这样,任何解析该IP的客户端都会得到127.0.0.1,并在本地工作。这种方式不需要管理员权限来修改/etc/hosts或等效文件。
如果要在代理后面工作而又不修改解析器(如/etc/hosts等),你可以提供一个wpad.dat文件(用于代理检测的JavaScript),该文件指定对于你的域名,所有流量都发送到你的服务器,其余流量发送到真实代理。如果该文件由你的服务器提供,脚本可以自动包含真实代理作为默认设置。

英文:

there is only solution that would work without proxy is that you register an domain for this and make an offical dns entry with wildcard to 127.0.0.1 like *.myApp.suche.org. So any client that resolve the ip get 127.0.0.1 and work local. this way you do not need administrator rights to modify the /etc/hosts or equivalent.
If it should work behind proxy without modify the resolver (etc/hosts etc) you can provide an wpad.dat (Javascript for Proxy detection) that say for your domain all traffic goes you server and the rest to the real proxy. If this is served in you server the script can automaticaly contain the real proxy as default.

huangapple
  • 本文由 发表于 2017年3月11日 10:18:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/42730590.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定