使用Golang实现多应用的反向代理

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

Golang reverse proxy with multiple apps

问题

我想在虚拟机中运行两个或多个 Web 应用程序(在不同的端口和有时在同一端口的不同目录下),并从主机机器上提供服务。由于我需要用户在访问这些应用程序之前先登录,所以我不能使用像 Nginx 或 Apache 这样的静态代理。

所以这是我的情况:

192.168.1.1:主机 IP
192.168.1.2:虚拟机 IP

在虚拟机中,我有以下内容:

192.168.1.2/owncloud:owncloud 地址
192.168.1.2:8080:另一个应用程序
192.168.1.2:8888:第三个应用程序

我想要的是:

192.168.1.1/app1 --> 192.168.1.2/owncloud
192.168.1.1/app2 --> 192.168.1.2:8080
192.168.1.1/app2 --> 192.168.1.2:8888

我尝试使用 Golang 的 httputil.ReverseProxy 来实现这个路由,但并没有取得太大的成功:
我的代码基于这个示例:gist

package main

import (
	"log"
	"net/url"
	"net/http"
	"net/http/httputil"
)

func main() {
	remote, err := url.Parse("http://192.168.1.2:8080")
	if err != nil {
		panic(err)
	}

	proxy := httputil.NewSingleHostReverseProxy(remote)
	http.HandleFunc("/app2", handler(proxy))
	err = http.ListenAndServe(":80", nil)
	if err != nil {
		panic(err)
	}
}

func handler(p *httputil.ReverseProxy) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		log.Println(r.URL)
		r.URL.Path = "/"
		p.ServeHTTP(w, r)
	}
}

编辑:
我已更改虚拟机的 IP 地址为 192.168.1.2,而不是 192.168.1.1。

英文:

I want to serve two or more web applications running in a VM (different ports and some time in different directory under the same port) from the host machine and because I need the user to be logged before he can access those apps I can not use a static proxy like Nginx or Apache.

So here is my situation :

> 192.168.1.1 : is the host ip
> 192.168.1.2 : is the VM ip

Inside the VM I have this :

> 192.168.1.2/owncloud : owncloud address
> 192.168.1.2:8080 : an other app
> 192.168.1.2:8888 : 3rd app

I want to have this :

> 192.168.1.1/app1 --> 192.168.1.2/owncloud
> 192.168.1.1/app2 --> 192.168.1.2:8080
> 192.168.1.1/app2 --> 192.168.1.2:8888

I have tried to use golang httputil.ReverseProxy to achieve this routing but with no much success:
my code is based on this work : gist

package main

import(
	"log"
	"net/url"
	"net/http"
	"net/http/httputil"
)

func main() {
	remote, err := url.Parse("http://192.168.1.2:8080")
	if err != nil {
	        panic(err)
	}

	proxy := httputil.NewSingleHostReverseProxy(remote)
	http.HandleFunc("/app2", handler(proxy))
	err = http.ListenAndServe(":80", nil)
	if err != nil {
	        panic(err)
	}
}

func handler(p *httputil.ReverseProxy) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		log.Println(r.URL)
		r.URL.Path = "/"                
	        p.ServeHTTP(w, r)
	}
}

Edit :
I have changed the vm ip address: 192.168.1.2 not 192.168.1.1

答案1

得分: 6

创建一个如下所示的映射:

hostTarget = map[string]string{
	"app1.domain.com": "http://192.168.1.2/owncloud",
	"app2.domain.com": "http://192.168.1.2:8080",
	"app3.domain.com": "http://192.168.1.2:8888",
}

使用httputil.ReverseProxy构建你的处理程序:

type baseHandle struct{}

func (h *baseHandle) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	host := r.Host

	if target, ok := hostTarget[host]; ok {
		remoteUrl, err := url.Parse(target)
		if err != nil {
			log.Println("目标解析失败:", err)
			return
		}

		proxy := httputil.NewSingleHostReverseProxy(remoteUrl)
		proxy.ServeHTTP(w, r)
		return
	}
	w.Write([]byte("403:主机禁止访问 " + host))
}

监听并服务:

h := &baseHandle{}
http.Handle("/", h)

server := &http.Server{
	Addr:    ":8080",
	Handler: h,
}
log.Fatal(server.ListenAndServe())

你可以在全局映射中缓存httputil.ReverseProxy,以上所有内容都在此文件中。

这里有一个SSLDocker项目,看起来最适合你的需求。

英文:

Make a map like this

hostTarget = map[string]string{
	"app1.domain.com": "http://192.168.1.2/owncloud",
	"app2.domain.com": "http://192.168.1.2:8080",
	"app3.domain.com": "http://192.168.1.2:8888",
}

Use httputil.ReverseProxy build your handler

type baseHandle struct{}

func (h *baseHandle) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    host := r.Host

    if target, ok := hostTarget[host]; ok {
	    remoteUrl, err := url.Parse(target)
	    if err != nil {
		    log.Println("target parse fail:", err)
		    return
	    }

	    proxy := httputil.NewSingleHostReverseProxy(remoteUrl)
	    proxy.ServeHTTP(w, r)
	    return
    }
    w.Write([]byte("403: Host forbidden " + host))
}

ListenAndServe

h := &baseHandle{}
http.Handle("/", h)

server := &http.Server{
	Addr:    ":8080",
	Handler: h,
}
log.Fatal(server.ListenAndServe())

You can cached httputil.ReverseProxy in global map, all in file above.

Here is a SSLDocker project seen to match with you best.

答案2

得分: 4

你最好通过主机名而不是URL来完成这个任务。例如:

owncloud.domain.com -> IP 192.168.1.2,
app2.domain.com -> IP 192.168.1.3

如果你还不知道,主机名只是一个HTTP请求头(Host: domain.com),所以你可以在一个IP上拥有多个主机名(Apache将其称为“命名虚拟主机”)。

使用主机名而不是URL的好处是,另一端的Web应用程序不知道你正在添加的URL前缀,但需要遵守它们,所以你可能会遇到由Web应用程序编写的URL与反向代理所期望的URL不匹配的问题。而基于主机名的代理应该可以正常工作,因为大多数Web应用程序不会重写域名。(尽管如此,这是一个很大的概括,有些Web应用程序会允许你添加代理地址,但通常使用主机名会遇到更少的问题)

最大的问题是在你的域名服务器上设置子域名。我假设你的注册商/ DNS提供商允许你免费创建子域名(大多数应该可以),但如果你正在使用类似动态DNS的服务,并且这个服务是在你的家庭宽带连接上运行的,那么你可能会遇到问题,你将不得不购买自己的域名,并将子域名的CNAME指向你的动态DNS地址(或者使用动态DNS提供商的付费账户,如果他们提供子域名)。

最后,如果你正在考虑使用ownCloud,你可能还想看一下Pydio(以前叫做AjaxExplore)。它们都有不同的优点和缺点,但在我个人的观点中,Pydio是一个更好的产品。

英文:

You'd be better off doing this by hostname rather than URL. eg

owncloud.domain.com -> IP 192.168.1.2,
app2.domain.com     -> IP 192.168.1.3

If you weren't already aware, the hostname is just a HTTP request header (Host: domain.com), so you can have several hostnames per IP (Apache calls this "named virtual hosts").

The benefit of using hostnames rather than URLs is that the web app at the other end isn't aware of the URLs you're prefixing yet needs to honour them, so you can run into problems with the URLs being written by the web app not working against the URLs being expected by the reverse proxy. Where as hostname based proxies should work as most web apps wont rewrite the domain name. (this is a huge generalisation though, some web apps will allow you to add a proxy address - but generally you'll run into less issues with hostnames)

The biggest issue is having to set up sub-domains on your name server. I'm assuming your registra / DNS providers allow you to create subdomains free of charge (most should), but if you're using something like dynamic DNS with this running off your home broadband connection, then you will run into problems and will have to buy your own domain name with the subdomains CNAME'ed to your dynamic DNS address (or use a paid account with your dynamic DNS providers if they offer subdomains).

One last thing, if you're looking into owncloud then you might also want to take a look at Pydio (formally AjaxExplore) as well. They both have different strengths and weaknesses, but in my personal opinion Pydio is a better product.

答案3

得分: 1

你将错误的 IP 地址传递给了反向代理。应该使用你的虚拟机的 192.168.1.2

文档中,

NewSingleHostReverseProxy 返回一个新的 ReverseProxy,它将 URL 重写为目标中提供的 scheme、host 和基本路径。

英文:

Your passing the wrong IP to the reverse proxy. Should be your VM's 192.168.1.2.

In the docs

NewSingleHostReverseProxy returns a new ReverseProxy that rewrites URLs to the scheme, host, and base path provided in target.

huangapple
  • 本文由 发表于 2014年1月11日 05:58:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/21055182.html
匿名

发表评论

匿名网友

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

确定