Files
nats-server/test/ports_test.go
Ivan Kozlovic 9715848a8e [ADDED] Websocket support
Websocket support can be enabled with a new websocket
configuration block:

```
websocket {
    # Specify a host and port to listen for websocket connections
    # listen: "host:port"

    # It can also be configured with individual parameters,
    # namely host and port.
    # host: "hostname"
    # port: 4443

    # This will optionally specify what host:port for websocket
    # connections to be advertised in the cluster
    # advertise: "host:port"

    # TLS configuration is required
    tls {
      cert_file: "/path/to/cert.pem"
      key_file: "/path/to/key.pem"
    }

    # If same_origin is true, then the Origin header of the
    # client request must match the request's Host.
    # same_origin: true

    # This list specifies the only accepted values for
    # the client's request Origin header. The scheme,
    # host and port must match. By convention, the
    # absence of port for an http:// scheme will be 80,
    # and for https:// will be 443.
    # allowed_origins [
    #    "http://www.example.com"
    #    "https://www.other-example.com"
    # ]

    # This enables support for compressed websocket frames
    # in the server. For compression to be used, both server
    # and client have to support it.
    # compression: true

    # This is the total time allowed for the server to
    # read the client request and write the response back
    # to the client. This include the time needed for the
    # TLS handshake.
    # handshake_timeout: "2s"
}
```

Signed-off-by: Ivan Kozlovic <ivan@synadia.com>
2020-05-20 11:14:39 -06:00

214 lines
5.8 KiB
Go

// Copyright 2018-2019 The NATS Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package test
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"
"time"
"github.com/nats-io/nats-server/v2/server"
)
// waits until a calculated list of listeners is resolved or a timeout
func waitForFile(path string, dur time.Duration) ([]byte, error) {
end := time.Now().Add(dur)
for time.Now().Before(end) {
if _, err := os.Stat(path); os.IsNotExist(err) {
time.Sleep(25 * time.Millisecond)
continue
} else {
return ioutil.ReadFile(path)
}
}
return nil, errors.New("Timeout")
}
func portFile(dirname string) string {
return filepath.Join(dirname, fmt.Sprintf("%s_%d.ports", filepath.Base(os.Args[0]), os.Getpid()))
}
func TestPortsFile(t *testing.T) {
portFileDir := os.TempDir()
opts := DefaultTestOptions
opts.PortsFileDir = portFileDir
opts.Port = -1
opts.HTTPPort = -1
opts.ProfPort = -1
opts.Cluster.Port = -1
opts.Websocket.Port = -1
tc := &server.TLSConfigOpts{
CertFile: "./configs/certs/server-cert.pem",
KeyFile: "./configs/certs/server-key.pem",
}
opts.Websocket.TLSConfig, _ = server.GenTLSConfig(tc)
s := RunServer(&opts)
// this for test cleanup in case we fail - will be ignored if server already shutdown
defer s.Shutdown()
ports := s.PortsInfo(5 * time.Second)
if ports == nil {
t.Fatal("services failed to start in 5 seconds")
}
// the pid file should be
portsFile := portFile(portFileDir)
if portsFile == "" {
t.Fatal("Expected a ports file")
}
// try to read a file here - the file should be a json
buf, err := waitForFile(portsFile, 5*time.Second)
if err != nil {
t.Fatalf("Could not read ports file: %v", err)
}
if len(buf) <= 0 {
t.Fatal("Expected a non-zero length ports file")
}
readPorts := server.Ports{}
json.Unmarshal(buf, &readPorts)
if len(readPorts.Nats) == 0 || !strings.HasPrefix(readPorts.Nats[0], "nats://") {
t.Fatal("Expected at least one nats url")
}
if len(readPorts.Monitoring) == 0 || !strings.HasPrefix(readPorts.Monitoring[0], "http://") {
t.Fatal("Expected at least one monitoring url")
}
if len(readPorts.Cluster) == 0 || !strings.HasPrefix(readPorts.Cluster[0], "nats://") {
t.Fatal("Expected at least one cluster listen url")
}
if len(readPorts.Profile) == 0 || !strings.HasPrefix(readPorts.Profile[0], "http://") {
t.Fatal("Expected at least one profile listen url")
}
if len(readPorts.WebSocket) == 0 || !strings.HasPrefix(readPorts.WebSocket[0], "wss://") {
t.Fatal("Expected at least one ws listen url")
}
// testing cleanup
s.Shutdown()
// if we called shutdown, the cleanup code should have kicked
if _, err := os.Stat(portsFile); os.IsNotExist(err) {
// good
} else {
t.Fatalf("the port file %s was not deleted", portsFile)
}
}
// makes a temp directory with two directories 'A' and 'B'
// the location of the ports file is changed from dir A to dir B.
func TestPortsFileReload(t *testing.T) {
// make a temp dir
tempDir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatalf("Error creating temp director (%s): %v", tempDir, err)
}
defer os.RemoveAll(tempDir)
// make child temp dir A
dirA := filepath.Join(tempDir, "A")
os.MkdirAll(dirA, 0777)
// write the config file with a reference to A
config := fmt.Sprintf(`
ports_file_dir: "%s"
port: -1
`, dirA)
config = strings.Replace(config, "\\", "/", -1)
confPath := filepath.Join(tempDir, fmt.Sprintf("%d.conf", os.Getpid()))
if err := ioutil.WriteFile(confPath, []byte(config), 0666); err != nil {
t.Fatalf("Error writing ports file (%s): %v", confPath, err)
}
opts, err := server.ProcessConfigFile(confPath)
if err != nil {
t.Fatalf("Error processing the configuration: %v", err)
}
s := RunServer(opts)
defer s.Shutdown()
ports := s.PortsInfo(5 * time.Second)
if ports == nil {
t.Fatal("services failed to start in 5 seconds")
}
// get the ports file path name
portsFileInA := portFile(dirA)
// the file should be in dirA
if !strings.HasPrefix(portsFileInA, dirA) {
t.Fatalf("expected ports file to be in [%s] but was in [%s]", dirA, portsFileInA)
}
// wait for it
buf, err := waitForFile(portsFileInA, 5*time.Second)
if err != nil {
t.Fatalf("Could not read ports file: %v", err)
}
if len(buf) <= 0 {
t.Fatal("Expected a non-zero length ports file")
}
// change the configuration for the ports file to dirB
dirB := filepath.Join(tempDir, "B")
os.MkdirAll(dirB, 0777)
config = fmt.Sprintf(`
ports_file_dir: "%s"
port: -1
`, dirB)
config = strings.Replace(config, "\\", "/", -1)
if err := ioutil.WriteFile(confPath, []byte(config), 0666); err != nil {
t.Fatalf("Error writing ports file (%s): %v", confPath, err)
}
// reload the server
if err := s.Reload(); err != nil {
t.Fatalf("error reloading server: %v", err)
}
// wait for the new file to show up
portsFileInB := portFile(dirB)
buf, err = waitForFile(portsFileInB, 5*time.Second)
if !strings.HasPrefix(portsFileInB, dirB) {
t.Fatalf("expected ports file to be in [%s] but was in [%s]", dirB, portsFileInB)
}
if err != nil {
t.Fatalf("Could not read ports file: %v", err)
}
if len(buf) <= 0 {
t.Fatal("Expected a non-zero length ports file")
}
// the file in dirA should have deleted
if _, err := os.Stat(portsFileInA); os.IsNotExist(err) {
// good
} else {
t.Fatalf("the port file %s was not deleted", portsFileInA)
}
}