在同一个程序中同时运行HTTP和HTTPS。

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

Run both HTTP and HTTPS in same program

问题

为什么我无法从同一个golang程序中同时运行HTTP和HTTPS?

以下是初始化两个服务器的代码。先初始化的服务器会运行,第二个则不会运行。如果交换它们的位置,另一个会运行,而另一个则不会运行。

运行程序时没有返回错误,但是请求http://www.localhosthttps://secure.localhost会超时。

// 启动HTTP
err_http := http.ListenAndServe(fmt.Sprintf(":%d", port), http_r)
if err_http != nil {
    log.Fatal("Web server (HTTP): ", err_http)
}

// 启动HTTPS
err_https := http.ListenAndServeTLS(fmt.Sprintf(":%d", ssl_port), "D:/Go/src/www/ssl/public.crt", "D:/Go/src/www/ssl/private.key", https_r)
if err_https != nil {
    log.Fatal("Web server (HTTPS): ", err_https)
}

以下是完整的代码:

package main

import (
    "net/http"
    "fmt"
    "log"
    "os"
    "io"
    "runtime"

    // go get github.com/gorilla/mux
    "github.com/gorilla/mux"
)

const (
    HOST = "localhost"
)

func Handler_404(w http.ResponseWriter, r *http.Request){
    fmt.Fprint(w, "Oops, something went wrong!")
}

func Handler_www(w http.ResponseWriter, r *http.Request){
    fmt.Fprint(w, "Hello world :)")
}

func Handler_api(w http.ResponseWriter, r *http.Request){
    fmt.Fprint(w, "This is the API")
}

func Handler_secure(w http.ResponseWriter, r *http.Request){
    fmt.Fprint(w, "This is Secure")
}

func redirect(r *mux.Router, from string, to string){
    r.Host(from).Subrouter().HandleFunc("/", func (w http.ResponseWriter, r *http.Request){
        http.Redirect(w, r, to, 301)
    })
}

func main(){
    port := 9000
    ssl_port := 443

    runtime.GOMAXPROCS(runtime.NumCPU())

    http_r := mux.NewRouter()
    https_r := mux.NewRouter()

    // HTTP 404
    http_r.NotFoundHandler = http.HandlerFunc(Handler_404)

    // Redirect "http://HOST" => "http://www.HOST"
    redirect(http_r, HOST, fmt.Sprintf("http://www.%s:%d", HOST, port))

    // Redirect "http://secure.HOST" => "https://secure.HOST"
    redirect(http_r, "secure."+HOST, fmt.Sprintf("https://secure.%s", HOST))

    www := http_r.Host("www."+HOST).Subrouter()
    www.HandleFunc("/", Handler_www)

    api := http_r.Host("api."+HOST).Subrouter()
    api.HandleFunc("/", Handler_api)

    secure := https_r.Host("secure."+HOST).Subrouter()
    secure.HandleFunc("/", Handler_secure)

    // Start HTTP
    err_http := http.ListenAndServe(fmt.Sprintf(":%d", port), http_r)
    if err_http != nil {
        log.Fatal("Web server (HTTP): ", err_http)
    }

    // Start HTTPS
    err_https := http.ListenAndServeTLS(fmt.Sprintf(":%d", ssl_port), "D:/Go/src/www/ssl/public.crt", "D:/Go/src/www/ssl/private.key", https_r)
    if err_https != nil {
        log.Fatal("Web server (HTTPS): ", err_https)
    }
}

希望对你有帮助!

英文:

Why can't I run both HTTP and HTTPS from the same golang program?

Here is the code where the two servers are initiated.. The server which is initiated first will run - the second won't.. If they are switched arround the other will run and the other won't..

No errors are returned when running the program, but the requests http://www.localhost or https://secure.localhost times out

//	Start HTTP
err_http := http.ListenAndServe(fmt.Sprintf(":%d", port), http_r)
if err_http != nil {
log.Fatal("Web server (HTTP): ", err_http)
}
//	Start HTTPS
err_https := http.ListenAndServeTLS(fmt.Sprintf(":%d", ssl_port), "D:/Go/src/www/ssl/public.crt", "D:/Go/src/www/ssl/private.key", https_r)
if err_https != nil {
log.Fatal("Web server (HTTPS): ", err_https)
}

Here is the complete code

package main
import (
"net/http"
"fmt"
"log"
"os"
"io"
"runtime"
// go get github.com/gorilla/mux
"github.com/gorilla/mux"
)
const (
HOST = "localhost"
)
func Handler_404(w http.ResponseWriter, r *http.Request){
fmt.Fprint(w, "Oops, something went wrong!")
}
func Handler_www(w http.ResponseWriter, r *http.Request){
fmt.Fprint(w, "Hello world :)")
}
func Handler_api(w http.ResponseWriter, r *http.Request){
fmt.Fprint(w, "This is the API")
}
func Handler_secure(w http.ResponseWriter, r *http.Request){
fmt.Fprint(w, "This is Secure")
}
func redirect(r *mux.Router, from string, to string){
r.Host(from).Subrouter().HandleFunc("/", func (w http.ResponseWriter, r *http.Request){
http.Redirect(w, r, to, 301)
})
}
func main(){
port := 9000
ssl_port := 443
runtime.GOMAXPROCS(runtime.NumCPU())
http_r := mux.NewRouter()
https_r := mux.NewRouter()
//	HTTP 404
http_r.NotFoundHandler = http.HandlerFunc(Handler_404)
//	Redirect "http://HOST" => "http://www.HOST"
redirect(http_r, HOST, fmt.Sprintf("http://www.%s:%d", HOST, port))
//	Redirect "http://secure.HOST" => "https://secure.HOST"
redirect(http_r, "secure."+HOST, fmt.Sprintf("https://secure.%s", HOST))
www := http_r.Host("www."+HOST).Subrouter()
www.HandleFunc("/", Handler_www)
api := http_r.Host("api."+HOST).Subrouter()
api.HandleFunc("/", Handler_api)
secure := https_r.Host("secure."+HOST).Subrouter()
secure.HandleFunc("/", Handler_secure)
//	Start HTTP
err_http := http.ListenAndServe(fmt.Sprintf(":%d", port), http_r)
if err_http != nil {
log.Fatal("Web server (HTTP): ", err_http)
}
//	Start HTTPS
err_https := http.ListenAndServeTLS(fmt.Sprintf(":%d", ssl_port), "D:/Go/src/www/ssl/public.crt", "D:/Go/src/www/ssl/private.key", https_r)
if err_https != nil {
log.Fatal("Web server (HTTPS): ", err_https)
}
}

答案1

得分: 26

ListenAndServeListenAndServeTLS 打开监听套接字,然后循环永远为客户端连接提供服务。这些函数只在出现错误时返回。

主 goroutine 永远无法启动 TLS 服务器,因为主 goroutine 忙于等待 ListenAndServe 的 HTTP 连接。

为了解决这个问题,可以在一个新的 goroutine 中启动 HTTP 服务器:

// 启动 HTTP
go func() {
err_http := http.ListenAndServe(fmt.Sprintf(":%d", port), http_r)
if err_http != nil {
log.Fatal("Web server (HTTP): ", err_http)
}
}()
// 启动 HTTPS
err_https := http.ListenAndServeTLS(fmt.Sprintf(":%d", ssl_port), "D:/Go/src/www/ssl/public.crt", "D:/Go/src/www/ssl/private.key", https_r)
if err_https != nil {
log.Fatal("Web server (HTTPS): ", err_https)
}
英文:

ListenAndServe and ListenAndServeTLS open the listening socket and then loop forever serving client connections. These functions only return on an error.

The main goroutine never gets to the starting the TLS server because the main goroutine is busy waiting for HTTP connections in ListenAndServe.

To fix the problem, start the HTTP server in a new goroutine:

//  Start HTTP
go func() {
err_http := http.ListenAndServe(fmt.Sprintf(":%d", port), http_r)
if err_http != nil {
log.Fatal("Web server (HTTP): ", err_http)
}
}()
//  Start HTTPS
err_https := http.ListenAndServeTLS(fmt.Sprintf(":%d", ssl_port),     "D:/Go/src/www/ssl/public.crt", "D:/Go/src/www/ssl/private.key", https_r)
if err_https != nil {
log.Fatal("Web server (HTTPS): ", err_https)
}

答案2

得分: 12

如前所述,ListenAndServeListenAndServeTLS都是阻塞的。也就是说,我同意上面的示例实际上解决了你的问题,因为关键是要在goroutine中运行,但是同样的示例并没有完全遵循Go语言的惯例。

在这里,你应该使用错误通道,因为你想要捕获发送给你的所有错误,而不仅仅是返回一个错误。下面是一个完全工作的示例,它启动HTTP和HTTPS服务器,并将错误作为通道返回,稍后用于显示错误。

package main

import (
	"log"
	"net/http"
)

func Run(addr string, sslAddr string, ssl map[string]string) chan error {

	errs := make(chan error)

	// 启动HTTP服务器
	go func() {
		log.Printf("在 %s 上启动HTTP服务...", addr)

		if err := http.ListenAndServe(addr, nil); err != nil {
			errs <- err
		}

	}()

	// 启动HTTPS服务器
	go func() {
		log.Printf("在 %s 上启动HTTPS服务...", addr)
		if err := http.ListenAndServeTLS(sslAddr, ssl["cert"], ssl["key"], nil); err != nil {
			errs <- err
		}
	}()

	return errs
}

func sampleHandler(w http.ResponseWriter, req *http.Request) {
	w.Header().Set("Content-Type", "text/plain")
	w.Write([]byte("这是一个示例服务器。\n"))
}

func main() {
	http.HandleFunc("/", sampleHandler)

	errs := Run(":8080", ":10443", map[string]string{
		"cert": "/path/to/cert.pem",
		"key":  "/path/to/key.pem",
	})

	// 这将一直运行,直到通道接收到错误
	select {
	case err := <-errs:
		log.Printf("由于错误(错误信息:%s),无法启动服务。", err)
	}

}

希望对你有所帮助! 在同一个程序中同时运行HTTP和HTTPS。

英文:

As previously said, both ListenAndServe and ListenAndServeTLS are blocking. That being said, I would agree that examples above are in fact resolving your issue as the point is to be in goroutine BUT same examples are not quite following go idioms.

You should be using error channels here as you want to capture ALL errors that are sent to you instead of having just one error returned back. Here's fully working sample that starts HTTP as HTTPS servers and return errors as channel that's later on used just to display errors.

package main
import (
&quot;log&quot;
&quot;net/http&quot;
)
func Run(addr string, sslAddr string, ssl map[string]string) chan error {
errs := make(chan error)
// Starting HTTP server
go func() {
log.Printf(&quot;Staring HTTP service on %s ...&quot;, addr)
if err := http.ListenAndServe(addr, nil); err != nil {
errs &lt;- err
}
}()
// Starting HTTPS server
go func() {
log.Printf(&quot;Staring HTTPS service on %s ...&quot;, addr)
if err := http.ListenAndServeTLS(sslAddr, ssl[&quot;cert&quot;], ssl[&quot;key&quot;], nil); err != nil {
errs &lt;- err
}
}()
return errs
}
func sampleHandler(w http.ResponseWriter, req *http.Request) {
w.Header().Set(&quot;Content-Type&quot;, &quot;text/plain&quot;)
w.Write([]byte(&quot;This is an example server.\n&quot;))
}
func main() {
http.HandleFunc(&quot;/&quot;, sampleHandler)
errs := Run(&quot;:8080&quot;, &quot;:10443&quot;, map[string]string{
&quot;cert&quot;: &quot;/path/to/cert.pem&quot;,
&quot;key&quot;:  &quot;/path/to/key.pem&quot;,
})
// This will run forever until channel receives error
select {
case err := &lt;-errs:
log.Printf(&quot;Could not start serving service due to (error: %s)&quot;, err)
}
}

Hope this helps! 在同一个程序中同时运行HTTP和HTTPS。

答案3

得分: 5

func serveHTTP(mux *http.ServeMux, errs chan<- error) {
  errs <- http.ListenAndServe(":80", mux)
}

func serveHTTPS(mux *http.ServeMux, errs chan<- error) {
  errs <- http.ListenAndServeTLS(":443", "fullchain.pem", "privkey.pem", mux)
}


func main() {
  mux := http.NewServeMux()
  // 设置路由
  errs := make(chan error, 1) // 用于接收错误的通道
  go serveHTTP(mux, errs)     // 在一个线程中启动HTTP服务器
  go serveHTTPS(mux, errs)    // 在一个线程中启动HTTPS服务器
  log.Fatal(<-errs)           // 阻塞直到其中一个服务器写入错误
}

以上是给定的代码段的翻译结果。

英文:
func serveHTTP(mux *http.ServeMux, errs chan&lt;- error) {
errs &lt;- http.ListenAndServe(&quot;:80&quot;, mux)
}
func serveHTTPS(mux *http.ServeMux, errs chan&lt;- error) {
errs &lt;- http.ListenAndServeTLS(&quot;:443&quot;, &quot;fullchain.pem&quot;, &quot;privkey.pem&quot;, mux)
}
func main() {
mux := http.NewServeMux()
// setup routes for mux     // define your endpoints
errs := make(chan error, 1) // a channel for errors
go serveHTTP(mux, errs)     // start the http server in a thread
go serveHTTPS(mux, errs)    // start the https server in a thread
log.Fatal(&lt;-errs)           // block until one of the servers writes an error
}

答案4

得分: 4

ListenAndServe(以及ListenAndServeTLS)函数在没有遇到错误的情况下不会返回给调用者。你可以通过尝试在这两个调用之间打印一些内容来进行测试。

英文:

The ListenAndServe (and ListenAndServeTLS) functions do not return to their caller (unless an error is encountered). You can test this by trying to print something in between the two calls.

huangapple
  • 本文由 发表于 2014年9月29日 06:29:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/26090301.html
匿名

发表评论

匿名网友

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

确定