在同一程序中运行HTTP和HTTPS

时间:2014-09-28 22:29:22

标签: http https go

为什么我不能从同一个golang程序运行HTTP和HTTPS?

这是启动两个服务器的代码..首先启动的服务器将运行 - 第二个不会运行..如果它们被切换到另一个将运行而另一个将不运行..

运行程序时未返回任何错误,但请求http://www.localhosthttps://secure.localhost超时

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

这是完整的代码

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

4 个答案:

答案 0 :(得分:20)

ListenAndServeListenAndServeTLS打开侦听套接字,然后永远循环服务客户端连接。这些函数仅在错误时返回。

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

要解决此问题,请在新的goroutine中启动HTTP服务器:

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

答案 1 :(得分:7)

如前所述,ListenAndServeListenAndServeTLS都是封锁的。话虽这么说,我同意上面的例子实际上是解决你的问题,因为重点是在goroutine,但相同的例子并不完全遵循成语。

您应该在此处使用错误通道,因为您希望捕获发送给您的所有错误,而不是仅返回一个错误。这是一个完整的工作示例,它将HTTP作为HTTPS服务器启动,并将错误作为以后用于显示错误的频道返回。

package main

import (
    "log"
    "net/http"
)

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

    errs := make(chan error)

    // Starting HTTP server
    go func() {
        log.Printf("Staring HTTP service on %s ...", addr)

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

    }()

    // Starting HTTPS server
    go func() {
        log.Printf("Staring HTTPS service on %s ...", 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("This is an example server.\n"))
}

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

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

    // This will run forever until channel receives error
    select {
    case err := <-errs:
        log.Printf("Could not start serving service due to (error: %s)", err)
    }

}

希望这有帮助! :)

答案 2 :(得分:4)

ListenAndServe(和ListenAndServeTLS)函数不会返回其调用者(除非遇到错误)。您可以尝试在两次调用之间打印一些东西来测试它。

答案 3 :(得分:2)

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()
  // 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(<-errs)           // block until one of the servers writes an error
}