Merge pull request #103 from cmfatih/master

Add JSONP support for monitoring routes
This commit is contained in:
Derek Collison
2015-08-21 23:06:42 -07:00
3 changed files with 133 additions and 18 deletions

View File

@@ -236,6 +236,19 @@ You can also report detailed subscription information on a per connection basis
}
```
Monitoring endpoints support JSONP for CORS so you can easily create single page
web applications for monitoring. Simply pass `callback` query parameter to any
endpoint. For example; `http://localhost:8222/connz?callback=cb`
```javascript
// JQuery example
$.getJSON('http://localhost:8222/connz?callback=?', function(data) {
console.log(data);
});
```
## Building
This code currently requires at _least_ version 1.1 of Go, but we encourage

View File

@@ -140,8 +140,9 @@ func (s *Server) HandleConnz(w http.ResponseWriter, r *http.Request) {
if err != nil {
Errorf("Error marshalling response to /connz request: %v", err)
}
w.Header().Set("Content-Type", "application/json")
w.Write(b)
// Handle response
ResponseHandler(w, r, b)
}
func castToSliceString(input []interface{}) []string {
@@ -220,8 +221,9 @@ func (s *Server) HandleRoutez(w http.ResponseWriter, r *http.Request) {
if err != nil {
Errorf("Error marshalling response to /routez request: %v", err)
}
w.Header().Set("Content-Type", "application/json")
w.Write(b)
// Handle response
ResponseHandler(w, r, b)
}
// HandleStats process HTTP requests for subjects stats.
@@ -232,8 +234,9 @@ func (s *Server) HandleSubsz(w http.ResponseWriter, r *http.Request) {
if err != nil {
Errorf("Error marshalling response to /subscriptionsz request: %v", err)
}
w.Header().Set("Content-Type", "application/json")
w.Write(b)
// Handle response
ResponseHandler(w, r, b)
}
// Varz will output server information on the monitoring port at /varz.
@@ -287,16 +290,16 @@ func myUptime(d time.Duration) string {
// HandleRoot will show basic info and links to others handlers.
func (s *Server) HandleRoot(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "<html lang=\"en\">gnatsd monitoring<br/><br/>")
vlink := fmt.Sprintf("http://%s/varz", r.Host)
fmt.Fprintf(w, "<a href=%s>%s</a><br/>", vlink, vlink)
clink := fmt.Sprintf("http://%s/connz", r.Host)
fmt.Fprintf(w, "<a href=%s>%s</a><br/>", clink, clink)
rlink := fmt.Sprintf("http://%s/routez", r.Host)
fmt.Fprintf(w, "<a href=%s>%s</a><br/>", rlink, rlink)
slink := fmt.Sprintf("http://%s/subscriptionsz", r.Host)
fmt.Fprintf(w, "<a href=%s>%s</a><br/>", slink, slink)
fmt.Fprint(w, "</html>")
fmt.Fprintf(w, `<html lang="en">
<body>
gnatsd monitoring
<br/><br/>
<a href=http://%s/varz>http://%s/varz</a><br/>
<a href=http://%s/connz>http://%s/connz</a><br/>
<a href=http://%s/routez>http://%s/routez</a><br/>
<a href=http://%s/subscriptionsz>http://%s/subscriptionsz</a><br/>
</body>
</html>`, r.Host, r.Host, r.Host, r.Host, r.Host, r.Host, r.Host, r.Host)
}
// HandleVarz will process HTTP requests for server information.
@@ -322,8 +325,9 @@ func (s *Server) HandleVarz(w http.ResponseWriter, r *http.Request) {
if err != nil {
Errorf("Error marshalling response to /varz request: %v", err)
}
w.Header().Set("Content-Type", "application/json")
w.Write(b)
// Handle response
ResponseHandler(w, r, b)
}
// Grab RSS and PCPU
@@ -337,3 +341,19 @@ func updateUsage(v *Varz) {
v.CPU = pcpu
v.Cores = numCores
}
// ResponseHandler handles responses for monitoring routes
func ResponseHandler(w http.ResponseWriter, r *http.Request, data []byte) {
// Get callback from request
callback := r.URL.Query().Get("callback")
// If callback is not empty then
if callback != "" {
// Response for JSONP
w.Header().Set("Content-Type", "application/javascript")
fmt.Fprintf(w, "%s(%s)", callback, data)
} else {
// Otherwise JSON
w.Header().Set("Content-Type", "application/json")
w.Write(data)
}
}

View File

@@ -8,6 +8,7 @@ import (
"io/ioutil"
"net/http"
"net/url"
"strings"
"testing"
"time"
@@ -99,6 +100,10 @@ func TestVarz(t *testing.T) {
if resp.StatusCode != 200 {
t.Fatalf("Expected a 200 response, got %d\n", resp.StatusCode)
}
ct := resp.Header.Get("Content-Type")
if ct != "application/json" {
t.Fatalf("Expected application/json content-type, got %s\n", ct)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
@@ -151,6 +156,17 @@ func TestVarz(t *testing.T) {
if v.OutBytes != 5 {
t.Fatalf("Expected OutBytes of 5, got %v\n", v.OutBytes)
}
// Test JSONP
respj, errj := http.Get(fmt.Sprintf("http://localhost:%d/", DEFAULT_HTTP_PORT) + "varz?callback=callback")
if errj != nil {
t.Fatalf("Expected no error: Got %v\n", err)
}
ct = respj.Header.Get("Content-Type")
if ct != "application/javascript" {
t.Fatalf("Expected application/javascript content-type, got %s\n", ct)
}
defer respj.Body.Close()
}
func TestConnz(t *testing.T) {
@@ -165,6 +181,10 @@ func TestConnz(t *testing.T) {
if resp.StatusCode != 200 {
t.Fatalf("Expected a 200 response, got %d\n", resp.StatusCode)
}
ct := resp.Header.Get("Content-Type")
if ct != "application/json" {
t.Fatalf("Expected application/json content-type, got %s\n", ct)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
@@ -249,6 +269,17 @@ func TestConnz(t *testing.T) {
if ci.OutBytes != 5 {
t.Fatalf("Expected OutBytes of 1, got %v\n", ci.OutBytes)
}
// Test JSONP
respj, errj := http.Get(fmt.Sprintf("http://localhost:%d/", DEFAULT_HTTP_PORT) + "connz?callback=callback")
if errj != nil {
t.Fatalf("Expected no error: Got %v\n", err)
}
ct = respj.Header.Get("Content-Type")
if ct != "application/javascript" {
t.Fatalf("Expected application/javascript content-type, got %s\n", ct)
}
defer respj.Body.Close()
}
func TestConnzWithSubs(t *testing.T) {
@@ -629,6 +660,10 @@ func TestConnzWithRoutes(t *testing.T) {
if resp.StatusCode != 200 {
t.Fatalf("Expected a 200 response, got %d\n", resp.StatusCode)
}
ct := resp.Header.Get("Content-Type")
if ct != "application/json" {
t.Fatalf("Expected application/json content-type, got %s\n", ct)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
@@ -682,6 +717,17 @@ func TestConnzWithRoutes(t *testing.T) {
if route.DidSolicit != false {
t.Fatalf("Expected unsolicited route, got %v\n", route.DidSolicit)
}
// Test JSONP
respj, errj := http.Get(fmt.Sprintf("http://localhost:%d/", DEFAULT_HTTP_PORT) + "routez?callback=callback")
if errj != nil {
t.Fatalf("Expected no error: Got %v\n", err)
}
ct = respj.Header.Get("Content-Type")
if ct != "application/javascript" {
t.Fatalf("Expected application/javascript content-type, got %s\n", ct)
}
defer respj.Body.Close()
}
func TestSubsz(t *testing.T) {
@@ -699,6 +745,10 @@ func TestSubsz(t *testing.T) {
if resp.StatusCode != 200 {
t.Fatalf("Expected a 200 response, got %d\n", resp.StatusCode)
}
ct := resp.Header.Get("Content-Type")
if ct != "application/json" {
t.Fatalf("Expected application/json content-type, got %s\n", ct)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
@@ -719,6 +769,38 @@ func TestSubsz(t *testing.T) {
t.Fatalf("Expected NumMatches of 1, got %d\n", sl.NumMatches)
}
// Test JSONP
respj, errj := http.Get(fmt.Sprintf("http://localhost:%d/", DEFAULT_HTTP_PORT) + "subscriptionsz?callback=callback")
ct = respj.Header.Get("Content-Type")
if errj != nil {
t.Fatalf("Expected no error: Got %v\n", err)
}
if ct != "application/javascript" {
t.Fatalf("Expected application/javascript content-type, got %s\n", ct)
}
defer respj.Body.Close()
}
// Tests handle root
func TestHandleRoot(t *testing.T) {
s := runMonitorServer(DEFAULT_HTTP_PORT)
defer s.Shutdown()
nc := createClientConnSubscribeAndPublish(t)
defer nc.Close()
resp, err := http.Get(fmt.Sprintf("http://localhost:%d/", DEFAULT_HTTP_PORT))
if err != nil {
t.Fatalf("Expected no error: Got %v\n", err)
}
if resp.StatusCode != 200 {
t.Fatalf("Expected a 200 response, got %d\n", resp.StatusCode)
}
ct := resp.Header.Get("Content-Type")
if !strings.Contains(ct, "text/html") {
t.Fatalf("Expected text/html response, got %s\n", ct)
}
defer resp.Body.Close()
}
// Create a connection to test ConnInfo