Skip to content

Commit

Permalink
Merge branch 'devel' of https://github.com/tinode/chat into devel
Browse files Browse the repository at this point in the history
  • Loading branch information
or-else committed Jun 23, 2021
2 parents da7e315 + be96216 commit d3d0efe
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 0 deletions.
3 changes: 3 additions & 0 deletions docker/tinode/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,9 @@ ENV STORE_USE_ADAPTER=$TARGET_DB
# If 0, default value wll be used: # of nodes x 5.
ENV CLUSTER_NUM_PROXY_EVENT_GOROUTINES=0

# Url path for exposing the server's internal status. E.g. '/status'
ENV SERVER_STATUS_PATH=''

# Install root certificates, they are needed for email validator to work
# with the TLS SMTP servers like Gmail or Mailjet. Also add bash and grep.
RUN apk update && \
Expand Down
1 change: 1 addition & 0 deletions docker/tinode/config.template
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"max_subscriber_count": 32,
"max_tag_count": 16,
"expvar": "/stats/expvar/",
"server_status": "$SERVER_STATUS_PATH",

"media": {
"use_handler": "$MEDIA_HANDLER",
Expand Down
59 changes: 59 additions & 0 deletions server/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ import (
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"net"
"net/http"
"net/url"
"os"
"os/signal"
"sort"
"strconv"
"strings"
"syscall"
Expand Down Expand Up @@ -366,3 +368,60 @@ func authHttpRequest(req *http.Request) (types.Uid, []byte, error) {
}
return uid, nil, nil
}

func serveStatus(wrt http.ResponseWriter, req *http.Request) {
// Disable caching.
wrt.Header().Set("Cache-Control", "no-cache, no-store, private, max-age=0")
wrt.Header().Set("Expires", time.Unix(0, 0).Format(http.TimeFormat))
wrt.Header().Set("Pragma", "no-cache")
wrt.Header().Set("X-Accel-Expires", "0")

fmt.Fprintf(wrt, "<html><head>")
fmt.Fprintf(wrt, "<title>Server Status</title>")
fmt.Fprintf(wrt, "<style>tr:nth-child(even) { background: #f2f2f2 }</style>")
fmt.Fprintf(wrt, "</head>")

now := time.Now()
fmt.Fprintf(wrt, "<body>")
fmt.Fprintf(wrt, "<h1>%s</h1><div>Version: %s, build: %s. Present time: %s</div>", "Tinode Server", currentVersion, buildstamp, now.Format(time.RFC1123))
fmt.Fprintf(wrt, "<br><hr>")

fmt.Fprintf(wrt, "<div>Sessions: %d</div>", len(globals.sessionStore.sessCache))
fmt.Fprintf(wrt, "<table>")
fmt.Fprintf(wrt, "<tr><th>remoteAddr</th><th>UA</th><th>uid</th><th>sid</th><th>clnode</th><th>subs</th></tr>")
globals.sessionStore.Range(func(sid string, s *Session) bool {
keys := make([]string, 0, len(s.subs))
for tn := range s.subs {
keys = append(keys, tn)
}
sort.Strings(keys)
l := strings.Join(keys, ",")
fmt.Fprintf(wrt, "<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%+v</td><td>%s</td></tr>", s.remoteAddr, s.userAgent, s.uid.String(), sid, s.clnode, l)
return true
})
fmt.Fprintf(wrt, "</table>")
// If this server is a part of a cluster.
if globals.cluster != nil {
fmt.Fprintf(wrt, "<div>Cluster node: %s", globals.cluster.thisNodeName)
fmt.Fprintf(wrt, "</div>")
}

fmt.Fprint(wrt, "<br><hr>")
fmt.Fprintf(wrt, "<div>")
fmt.Fprintf(wrt, "<div>Topics: %d</div>", globals.hub.numTopics)
fmt.Fprintf(wrt, "<table>")

fmt.Fprintf(wrt, "<tr><th>topicId</th><th>xorig</th><th>isProxy</th><th>cat</th><th>perUser</th><th>perSubs</th><th>sessions</th></tr>")
globals.hub.topics.Range(func(_, t interface{}) bool {
topic := t.(*Topic)
var sd []string
for s, psd := range topic.sessions {
sd = append(sd, fmt.Sprintf("%s: %+v", s.sid, psd))
}
fmt.Fprintf(wrt, "<tr><td>%s</td><td>%s</td><td>%t</td><td>%d</td><td>%+v</td><td>%+v</td><td>%s</td></tr>", topic.name, topic.xoriginal, topic.isProxy, topic.cat, topic.perUser, topic.perSubs, strings.Join(sd, " "))
return true
})
fmt.Fprintf(wrt, "</div>")

fmt.Fprintf(wrt, "</body></html>")
}
5 changes: 5 additions & 0 deletions server/hub.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ type Hub struct {
// Topics must be indexed by name
topics *sync.Map

// Current number of loaded topics
numTopics int

// Channel for routing messages between topics, buffered at 4096
route chan *ServerComMessage

Expand Down Expand Up @@ -105,10 +108,12 @@ func (h *Hub) topicGet(name string) *Topic {
}

func (h *Hub) topicPut(name string, t *Topic) {
h.numTopics++
h.topics.Store(name, t)
}

func (h *Hub) topicDel(name string) {
h.numTopics--
h.topics.Delete(name)
}

Expand Down
12 changes: 12 additions & 0 deletions server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,8 @@ type configType struct {
MaxTagCount int `json:"max_tag_count"`
// URL path for exposing runtime stats. Disabled if the path is blank.
ExpvarPath string `json:"expvar"`
// URL path for internal server status. Disabled if the path is blank.
ServerStatusPath string `json:"server_status"`
// Take IP address of the client from HTTP header 'X-Forwarded-For'.
// Useful when tinode is behind a proxy. If missing, fallback to default RemoteAddr.
UseXForwardedFor bool `json:"use_x_forwarded_for"`
Expand Down Expand Up @@ -266,6 +268,7 @@ func main() {
tlsEnabled := flag.Bool("tls_enabled", false, "Override config value for enabling TLS.")
clusterSelf := flag.String("cluster_self", "", "Override the name of the current cluster node.")
expvarPath := flag.String("expvar", "", "Override the URL path where runtime stats are exposed. Use '-' to disable.")
serverStatusPath := flag.String("server_status", "", "Override the URL path where the server's internal status is displayed. Use '-' to disable.")
pprofFile := flag.String("pprof", "", "File name to save profiling info to. Disabled if not set.")
pprofUrl := flag.String("pprof_url", "", "Debugging only! URL path for exposing profiling info. Disabled if not set.")
flag.Parse()
Expand Down Expand Up @@ -617,6 +620,15 @@ func main() {
}
logs.Info.Printf("API served from root URL path '%s'", config.ApiPath)

sspath := *serverStatusPath
if sspath == "" || sspath == "-" {
sspath = config.ServerStatusPath
}
if sspath != "" && sspath != "-" {
logs.Info.Printf("Server status is available at '%s'", sspath)
mux.HandleFunc(sspath, serveStatus)
}

// Handle websocket clients.
mux.HandleFunc(config.ApiPath+"v0/channels", serveWebSocket)
// Handle long polling clients. Enable compression.
Expand Down
10 changes: 10 additions & 0 deletions server/sessionstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,16 @@ func (ss *SessionStore) Delete(s *Session) {
statsSet("LiveSessions", int64(len(ss.sessCache)))
}

func (ss *SessionStore) Range(f func(sid string, s *Session) bool) {
ss.lock.Lock()
for sid, s := range ss.sessCache {
if !f(sid, s) {
break
}
}
ss.lock.Unlock()
}

// Shutdown terminates sessionStore. No need to clean up.
// Don't send to clustered sessions, their servers are not being shut down.
func (ss *SessionStore) Shutdown() {
Expand Down
4 changes: 4 additions & 0 deletions server/tinode.conf
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@
// Could be overriden from the command line with --expvar.
"expvar": "/debug/vars",

// URL path for internal server status. Disabled if the path is blank or "-".
// Could be overriden from the command line with --server_status.
"server_status": "/status",

// Read IP address of the client from the HTTP header 'X-Forwarded-For'.
// Useful when Tinode is behind a proxy. If missing, fallback to default RemoteAddr.
"use_x_forwarded_for": true,
Expand Down

0 comments on commit d3d0efe

Please sign in to comment.