2014-09-28 46 views
9

爲什麼我無法從同一個golang程序運行HTTP和HTTPS?在同一個程序中運行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) 
    } 
} 

回答

19

ListenAndServeListenAndServeTLS打開監聽套接字,然後永遠循環服務客戶端連接。這些函數僅在返回錯誤時返回。

由於主協程正忙於等待ListenAndServe中的HTTP連接,所以主協程永遠不會啓動TLS服務器。

要解決這個問題,啓動HTTP服務器在新的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) 
} 
3

ListenAndServe(和ListenAndServeTLS)函數不會返回給它們的調用方(除非遇到錯誤)。您可以通過嘗試在兩次調用之間打印某些內容來測試這一點。

4

正如前文所說,無論ListenAndServeListenAndServeTLS被封鎖。話雖如此,但我同意上面的例子實際上解決了你的問題,因爲重點在於例程,但同樣的例子並不完全遵循成語。

您應該在此處使用錯誤通道,因爲您希望捕獲發送給您的所有錯誤,而不是隻返回一個錯誤。這裏是完整的工作示例,它將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) 
    } 

} 

希望這有助於! :)

+0

除幾乎總是用ListenAndServer錯誤完成的* only *事件記錄並退出(如果需要,允許外部機制重新啓動整個服務器/進程)。例如。到底是什麼問題和答案用'log.Fatal()'搶救遇到的第一個遇到的錯誤。如果這是所期望的行爲,那麼除了他們正在做的事情之外的任何事情都會分散注意力。 – 2015-04-06 15:35:02

+0

這是真的戴夫。我通常過於複雜的事情。我總是在代碼級重啓邏輯,而不是像supervisord這樣的東西。如果整個過程都停止,那就是我使用的東西。如果一個人死亡,第二個人死亡,整個腳本將被竊聽。必須承認,我沒有經歷過那麼多的http,但經歷了很多AMQP,其中一個保持不變,第二個失敗並且使整個邏輯被竊聽,因此我需要在內部循環下重新啓動。比我採取的方法,並在我有超過1聽衆大聲笑的地方使用它 – 2015-04-07 14:47:54

+0

@NevioVesić的例子具有一致性的優點。我唯一要添加的是一個關閉沒有錯誤的去程序的通道,特別是如果有人希望在處理錯誤後再次啓動它。 – 2016-05-13 23:18:18

相關問題