123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290 |
- /*
- * Public Domain Software
- *
- * I (Matthias Ladkau) am the author of the source code in this file.
- * I have placed the source code in this file in the public domain.
- *
- * For further information see: http://creativecommons.org/publicdomain/zero/1.0/
- */
- package httputil
- import (
- "bytes"
- "crypto/tls"
- "crypto/x509"
- "flag"
- "fmt"
- "html"
- "io/ioutil"
- "net"
- "net/http"
- "os"
- "sync"
- "syscall"
- "testing"
- "time"
- "devt.de/krotik/common/cryptutil"
- "devt.de/krotik/common/fileutil"
- )
- const certdir = "certs"
- const testporthttp = ":9050"
- const testporthttps = ":9051"
- const invalidFileName = "**\x00"
- func TestMain(m *testing.M) {
- flag.Parse()
- // Setup
- if res, _ := fileutil.PathExists(certdir); res {
- os.RemoveAll(certdir)
- }
- err := os.Mkdir(certdir, 0770)
- if err != nil {
- fmt.Print("Could not create test directory:", err.Error())
- os.Exit(1)
- }
- // Run the tests
- res := m.Run()
- // Teardown
- err = os.RemoveAll(certdir)
- if err != nil {
- fmt.Print("Could not remove test directory:", err.Error())
- }
- os.Exit(res)
- }
- func TestHTTPSServer(t *testing.T) {
- // Generate a certificate and private key
- err := cryptutil.GenCert(certdir, "cert.pem", "key.pem", "localhost", "", 365*24*time.Hour, true, 2048, "")
- if err != nil {
- t.Error(err)
- return
- }
- // Add dummy handler
- http.HandleFunc("/httpsserver_test", func(w http.ResponseWriter, r *http.Request) {
- fmt.Fprintf(w, "Hello over HTTPS, %q", html.EscapeString(r.URL.Path))
- })
- hs := &HTTPServer{}
- var wg sync.WaitGroup
- wg.Add(1)
- go hs.RunHTTPSServer(certdir, "cert.pem", "key.pem", testporthttps, &wg)
- wg.Wait()
- // HTTPS Server has started
- if hs.LastError != nil {
- t.Error(hs.LastError)
- return
- }
- // Check we can't start two servers
- var wg2 sync.WaitGroup
- hs2 := &HTTPServer{}
- wg2.Add(1)
- err = hs2.RunHTTPSServer(certdir, "c.pem", "k.pem", testporthttps, &wg2)
- if hs2.LastError == nil ||
- (hs2.LastError.Error() != "open certs/c.pem: no such file or directory" &&
- hs2.LastError.Error() != "open certs/c.pem: The system cannot find the file specified.") ||
- err != hs2.LastError {
- t.Error("Unexpected error return:", hs2.LastError)
- return
- }
- // Add again to wait group so we can try again
- wg2.Add(1)
- err = hs2.RunHTTPSServer(certdir, "cert.pem", "key.pem", testporthttps, &wg2)
- if hs2.LastError == nil || (hs2.LastError.Error() != "listen tcp "+testporthttps+
- ": bind: address already in use" && hs2.LastError.Error() != "listen tcp "+testporthttps+
- ": bind: Only one usage of each socket address (protocol/network address/port) is normally permitted.") ||
- err != hs2.LastError {
- t.Error("Unexpected error return:", hs2.LastError)
- }
- // Add to the wait group so we can wait for the shutdown
- wg.Add(1)
- // Send something to the server
- if res := sendTestHTTPSRequest(certdir + "/cert.pem"); res != `Hello over HTTPS, "/httpsserver_test"` {
- t.Error("Unexpected request response:", res)
- return
- }
- // Server is shut down
- hs.Shutdown()
- if hs.Running == true {
- wg.Wait()
- } else {
- t.Error("Server was not running as expected")
- }
- }
- func TestSignalling(t *testing.T) {
- // Add dummy handler
- http.HandleFunc("/httpserver_test", func(w http.ResponseWriter, r *http.Request) {
- fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
- })
- hs := &HTTPServer{}
- var wg sync.WaitGroup
- wg.Add(1)
- go hs.RunHTTPServer(testporthttp, &wg)
- wg.Wait()
- // Server is started
- if hs.LastError != nil {
- t.Error(hs.LastError)
- return
- }
- // Check we can't start two servers
- var wg2 sync.WaitGroup
- wg2.Add(1)
- hs2 := &HTTPServer{}
- err := hs2.RunHTTPServer(testporthttp, &wg2)
- if hs2.LastError == nil || (hs2.LastError.Error() != "listen tcp "+testporthttp+
- ": bind: address already in use" && hs2.LastError.Error() != "listen tcp "+testporthttp+
- ": bind: Only one usage of each socket address (protocol/network address/port) is normally permitted.") ||
- err != hs2.LastError {
- t.Error("Unexpected error return:", hs2.LastError)
- }
- // Add to the wait group so we can wait for the shutdown
- wg.Add(1)
- // Send something to the server
- if res := sendTestRequest(); res != `Hello, "/httpserver_test"` {
- t.Error("Unexpected request response:", res)
- return
- }
- // Check we can send other signals
- hs.signalling <- syscall.SIGHUP
- time.Sleep(time.Duration(50) * time.Millisecond)
- if hs.Running != true {
- t.Error("Server should still be running after sending wrong shutdown signal")
- return
- }
- // Server is shut down
- hs.Shutdown()
- if hs.Running == true {
- wg.Wait()
- } else {
- t.Error("Server was not running as expected")
- }
- // Test listener panic
- originalListener, _ := net.Listen("tcp", testporthttp)
- sl := newSignalTCPListener(originalListener, originalListener.(*net.TCPListener), nil)
- go testUnknownSignalPanic(t, sl)
- sl.Signals <- -1
- }
- func testUnknownSignalPanic(t *testing.T, sl *signalTCPListener) {
- defer func() {
- if r := recover(); r == nil {
- t.Error("Sending an unknown signal did not cause a panic.")
- }
- }()
- sl.Accept()
- }
- func sendTestRequest() string {
- url := "http://localhost" + testporthttp + "/httpserver_test"
- var jsonStr = []byte(`{"msg":"Hello!"}`)
- req, _ := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
- req.Header.Set("X-Custom-Header", "myvalue")
- req.Header.Set("Content-Type", "application/json")
- client := &http.Client{}
- resp, err := client.Do(req)
- if err != nil {
- panic(err)
- }
- defer resp.Body.Close()
- body, _ := ioutil.ReadAll(resp.Body)
- return string(body)
- }
- func sendTestHTTPSRequest(caCert string) string {
- // Build ca cert pool
- caPool := x509.NewCertPool()
- serverCert, err := ioutil.ReadFile(caCert)
- if err != nil {
- panic(err)
- }
- caPool.AppendCertsFromPEM(serverCert)
- tr := &http.Transport{
- TLSClientConfig: &tls.Config{RootCAs: caPool},
- DisableCompression: true,
- }
- url := "https://localhost" + testporthttps + "/httpsserver_test"
- var jsonStr = []byte(`{"msg":"Hello!"}`)
- req, _ := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
- req.Header.Set("X-Custom-Header", "myvalue")
- req.Header.Set("Content-Type", "application/json")
- client := &http.Client{Transport: tr}
- resp, err := client.Do(req)
- if err != nil {
- panic(err)
- }
- defer resp.Body.Close()
- body, _ := ioutil.ReadAll(resp.Body)
- return string(body)
- }
|