Websockets; working
This commit is contained in:
		
							parent
							
								
									cdc9c0d62c
								
							
						
					
					
						commit
						846ee0fb2d
					
				
					 6 changed files with 118 additions and 4 deletions
				
			
		| 
						 | 
					@ -549,7 +549,7 @@ variable before running the `ntfy` command (e.g. `export NTFY_LISTEN_HTTP=:80`).
 | 
				
			||||||
| `smtp-server-listen`                       | `NTFY_SMTP_SERVER_LISTEN`                       | `[ip]:port`      | -       | Defines the IP address and port the SMTP server will listen on, e.g. `:25` or `1.2.3.4:25`                                                                                                                                      |
 | 
					| `smtp-server-listen`                       | `NTFY_SMTP_SERVER_LISTEN`                       | `[ip]:port`      | -       | Defines the IP address and port the SMTP server will listen on, e.g. `:25` or `1.2.3.4:25`                                                                                                                                      |
 | 
				
			||||||
| `smtp-server-domain`                       | `NTFY_SMTP_SERVER_DOMAIN`                       | *domain name*    | -       | SMTP server e-mail domain, e.g. `ntfy.sh`                                                                                                                                                                                       |
 | 
					| `smtp-server-domain`                       | `NTFY_SMTP_SERVER_DOMAIN`                       | *domain name*    | -       | SMTP server e-mail domain, e.g. `ntfy.sh`                                                                                                                                                                                       |
 | 
				
			||||||
| `smtp-server-addr-prefix`                  | `NTFY_SMTP_SERVER_ADDR_PREFIX`                  | `[ip]:port`      | -       | Optional prefix for the e-mail addresses to prevent spam, e.g. `ntfy-`                                                                                                                                                          |
 | 
					| `smtp-server-addr-prefix`                  | `NTFY_SMTP_SERVER_ADDR_PREFIX`                  | `[ip]:port`      | -       | Optional prefix for the e-mail addresses to prevent spam, e.g. `ntfy-`                                                                                                                                                          |
 | 
				
			||||||
| `keepalive-interval`                       | `NTFY_KEEPALIVE_INTERVAL`                       | *duration*       | 55s     | Interval in which keepalive messages are sent to the client. This is to prevent intermediaries closing the connection for inactivity. Note that the Android app has a hardcoded timeout at 77s, so it should be less than that. |
 | 
					| `keepalive-interval`                       | `NTFY_KEEPALIVE_INTERVAL`                       | *duration*       | 45s     | Interval in which keepalive messages are sent to the client. This is to prevent intermediaries closing the connection for inactivity. Note that the Android app has a hardcoded timeout at 77s, so it should be less than that. |
 | 
				
			||||||
| `manager-interval`                         | `$NTFY_MANAGER_INTERVAL`                        | *duration*       | 1m      | Interval in which the manager prunes old messages, deletes topics and prints the stats.                                                                                                                                         |
 | 
					| `manager-interval`                         | `$NTFY_MANAGER_INTERVAL`                        | *duration*       | 1m      | Interval in which the manager prunes old messages, deletes topics and prints the stats.                                                                                                                                         |
 | 
				
			||||||
| `global-topic-limit`                       | `NTFY_GLOBAL_TOPIC_LIMIT`                       | *number*         | 15,000  | Rate limiting: Total number of topics before the server rejects new topics.                                                                                                                                                     |
 | 
					| `global-topic-limit`                       | `NTFY_GLOBAL_TOPIC_LIMIT`                       | *number*         | 15,000  | Rate limiting: Total number of topics before the server rejects new topics.                                                                                                                                                     |
 | 
				
			||||||
| `visitor-subscription-limit`               | `NTFY_VISITOR_SUBSCRIPTION_LIMIT`               | *number*         | 30      | Rate limiting: Number of subscriptions per visitor (IP address)                                                                                                                                                                 |
 | 
					| `visitor-subscription-limit`               | `NTFY_VISITOR_SUBSCRIPTION_LIMIT`               | *number*         | 30      | Rate limiting: Number of subscriptions per visitor (IP address)                                                                                                                                                                 |
 | 
				
			||||||
| 
						 | 
					@ -597,7 +597,7 @@ OPTIONS:
 | 
				
			||||||
   --attachment-total-size-limit value, -A value     limit of the on-disk attachment cache (default: 5G) [$NTFY_ATTACHMENT_TOTAL_SIZE_LIMIT]
 | 
					   --attachment-total-size-limit value, -A value     limit of the on-disk attachment cache (default: 5G) [$NTFY_ATTACHMENT_TOTAL_SIZE_LIMIT]
 | 
				
			||||||
   --attachment-file-size-limit value, -Y value      per-file attachment size limit (e.g. 300k, 2M, 100M) (default: 15M) [$NTFY_ATTACHMENT_FILE_SIZE_LIMIT]
 | 
					   --attachment-file-size-limit value, -Y value      per-file attachment size limit (e.g. 300k, 2M, 100M) (default: 15M) [$NTFY_ATTACHMENT_FILE_SIZE_LIMIT]
 | 
				
			||||||
   --attachment-expiry-duration value, -X value      duration after which uploaded attachments will be deleted (e.g. 3h, 20h) (default: 3h) [$NTFY_ATTACHMENT_EXPIRY_DURATION]
 | 
					   --attachment-expiry-duration value, -X value      duration after which uploaded attachments will be deleted (e.g. 3h, 20h) (default: 3h) [$NTFY_ATTACHMENT_EXPIRY_DURATION]
 | 
				
			||||||
   --keepalive-interval value, -k value              interval of keepalive messages (default: 55s) [$NTFY_KEEPALIVE_INTERVAL]
 | 
					   --keepalive-interval value, -k value              interval of keepalive messages (default: 45s) [$NTFY_KEEPALIVE_INTERVAL]
 | 
				
			||||||
   --manager-interval value, -m value                interval of for message pruning and stats printing (default: 1m0s) [$NTFY_MANAGER_INTERVAL]
 | 
					   --manager-interval value, -m value                interval of for message pruning and stats printing (default: 1m0s) [$NTFY_MANAGER_INTERVAL]
 | 
				
			||||||
   --smtp-sender-addr value                          SMTP server address (host:port) for outgoing emails [$NTFY_SMTP_SENDER_ADDR]
 | 
					   --smtp-sender-addr value                          SMTP server address (host:port) for outgoing emails [$NTFY_SMTP_SENDER_ADDR]
 | 
				
			||||||
   --smtp-sender-user value                          SMTP user (if e-mail sending is enabled) [$NTFY_SMTP_SENDER_USER]
 | 
					   --smtp-sender-user value                          SMTP user (if e-mail sending is enabled) [$NTFY_SMTP_SENDER_USER]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										2
									
								
								go.mod
									
										
									
									
									
								
							
							
						
						
									
										2
									
								
								go.mod
									
										
									
									
									
								
							| 
						 | 
					@ -35,11 +35,13 @@ require (
 | 
				
			||||||
	github.com/golang/protobuf v1.5.2 // indirect
 | 
						github.com/golang/protobuf v1.5.2 // indirect
 | 
				
			||||||
	github.com/google/go-cmp v0.5.6 // indirect
 | 
						github.com/google/go-cmp v0.5.6 // indirect
 | 
				
			||||||
	github.com/googleapis/gax-go/v2 v2.1.1 // indirect
 | 
						github.com/googleapis/gax-go/v2 v2.1.1 // indirect
 | 
				
			||||||
 | 
						github.com/gorilla/websocket v1.4.2 // indirect
 | 
				
			||||||
	github.com/pkg/errors v0.9.1 // indirect
 | 
						github.com/pkg/errors v0.9.1 // indirect
 | 
				
			||||||
	github.com/pmezard/go-difflib v1.0.0 // indirect
 | 
						github.com/pmezard/go-difflib v1.0.0 // indirect
 | 
				
			||||||
	github.com/russross/blackfriday/v2 v2.1.0 // indirect
 | 
						github.com/russross/blackfriday/v2 v2.1.0 // indirect
 | 
				
			||||||
	go.opencensus.io v0.23.0 // indirect
 | 
						go.opencensus.io v0.23.0 // indirect
 | 
				
			||||||
	golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d // indirect
 | 
						golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d // indirect
 | 
				
			||||||
 | 
						golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
 | 
				
			||||||
	golang.org/x/sys v0.0.0-20211210111614-af8b64212486 // indirect
 | 
						golang.org/x/sys v0.0.0-20211210111614-af8b64212486 // indirect
 | 
				
			||||||
	golang.org/x/text v0.3.7 // indirect
 | 
						golang.org/x/text v0.3.7 // indirect
 | 
				
			||||||
	golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
 | 
						golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										3
									
								
								go.sum
									
										
									
									
									
								
							
							
						
						
									
										3
									
								
								go.sum
									
										
									
									
									
								
							| 
						 | 
					@ -189,6 +189,8 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m
 | 
				
			||||||
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
 | 
					github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
 | 
				
			||||||
github.com/googleapis/gax-go/v2 v2.1.1 h1:dp3bWCh+PPO1zjRRiCSczJav13sBvG4UhNyVTa1KqdU=
 | 
					github.com/googleapis/gax-go/v2 v2.1.1 h1:dp3bWCh+PPO1zjRRiCSczJav13sBvG4UhNyVTa1KqdU=
 | 
				
			||||||
github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
 | 
					github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
 | 
				
			||||||
 | 
					github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
 | 
				
			||||||
 | 
					github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
 | 
				
			||||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
 | 
					github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
 | 
				
			||||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 | 
					github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 | 
				
			||||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 | 
					github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 | 
				
			||||||
| 
						 | 
					@ -356,6 +358,7 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ
 | 
				
			||||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
				
			||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
				
			||||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
				
			||||||
 | 
					golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
 | 
				
			||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
					golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
					golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,7 +8,7 @@ import (
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	DefaultListenHTTP                = ":80"
 | 
						DefaultListenHTTP                = ":80"
 | 
				
			||||||
	DefaultCacheDuration             = 12 * time.Hour
 | 
						DefaultCacheDuration             = 12 * time.Hour
 | 
				
			||||||
	DefaultKeepaliveInterval         = 55 * time.Second // Not too frequently to save battery (Android read timeout is 77s!)
 | 
						DefaultKeepaliveInterval         = 45 * time.Second // Not too frequently to save battery (Android read timeout used to be 77s!)
 | 
				
			||||||
	DefaultManagerInterval           = time.Minute
 | 
						DefaultManagerInterval           = time.Minute
 | 
				
			||||||
	DefaultAtSenderInterval          = 10 * time.Second
 | 
						DefaultAtSenderInterval          = 10 * time.Second
 | 
				
			||||||
	DefaultMinDelay                  = 10 * time.Second
 | 
						DefaultMinDelay                  = 10 * time.Second
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										109
									
								
								server/server.go
									
										
									
									
									
								
							
							
						
						
									
										109
									
								
								server/server.go
									
										
									
									
									
								
							| 
						 | 
					@ -10,6 +10,8 @@ import (
 | 
				
			||||||
	"firebase.google.com/go/messaging"
 | 
						"firebase.google.com/go/messaging"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"github.com/emersion/go-smtp"
 | 
						"github.com/emersion/go-smtp"
 | 
				
			||||||
 | 
						"github.com/gorilla/websocket"
 | 
				
			||||||
 | 
						"golang.org/x/sync/errgroup"
 | 
				
			||||||
	"google.golang.org/api/option"
 | 
						"google.golang.org/api/option"
 | 
				
			||||||
	"heckel.io/ntfy/util"
 | 
						"heckel.io/ntfy/util"
 | 
				
			||||||
	"html/template"
 | 
						"html/template"
 | 
				
			||||||
| 
						 | 
					@ -99,6 +101,7 @@ var (
 | 
				
			||||||
	jsonPathRegex    = regexp.MustCompile(`^/[-_A-Za-z0-9]{1,64}(,[-_A-Za-z0-9]{1,64})*/json$`)
 | 
						jsonPathRegex    = regexp.MustCompile(`^/[-_A-Za-z0-9]{1,64}(,[-_A-Za-z0-9]{1,64})*/json$`)
 | 
				
			||||||
	ssePathRegex     = regexp.MustCompile(`^/[-_A-Za-z0-9]{1,64}(,[-_A-Za-z0-9]{1,64})*/sse$`)
 | 
						ssePathRegex     = regexp.MustCompile(`^/[-_A-Za-z0-9]{1,64}(,[-_A-Za-z0-9]{1,64})*/sse$`)
 | 
				
			||||||
	rawPathRegex     = regexp.MustCompile(`^/[-_A-Za-z0-9]{1,64}(,[-_A-Za-z0-9]{1,64})*/raw$`)
 | 
						rawPathRegex     = regexp.MustCompile(`^/[-_A-Za-z0-9]{1,64}(,[-_A-Za-z0-9]{1,64})*/raw$`)
 | 
				
			||||||
 | 
						wsPathRegex      = regexp.MustCompile(`^/[-_A-Za-z0-9]{1,64}(,[-_A-Za-z0-9]{1,64})*/ws$`)
 | 
				
			||||||
	publishPathRegex = regexp.MustCompile(`^/[-_A-Za-z0-9]{1,64}(,[-_A-Za-z0-9]{1,64})*/(publish|send|trigger)$`)
 | 
						publishPathRegex = regexp.MustCompile(`^/[-_A-Za-z0-9]{1,64}(,[-_A-Za-z0-9]{1,64})*/(publish|send|trigger)$`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	staticRegex      = regexp.MustCompile(`^/static/.+`)
 | 
						staticRegex      = regexp.MustCompile(`^/static/.+`)
 | 
				
			||||||
| 
						 | 
					@ -156,6 +159,10 @@ const (
 | 
				
			||||||
	emptyMessageBody         = "triggered"               // Used if message body is empty
 | 
						emptyMessageBody         = "triggered"               // Used if message body is empty
 | 
				
			||||||
	defaultAttachmentMessage = "You received a file: %s" // Used if message body is empty, and there is an attachment
 | 
						defaultAttachmentMessage = "You received a file: %s" // Used if message body is empty, and there is an attachment
 | 
				
			||||||
	fcmMessageLimit          = 4000                      // see maybeTruncateFCMMessage for details
 | 
						fcmMessageLimit          = 4000                      // see maybeTruncateFCMMessage for details
 | 
				
			||||||
 | 
						wsWriteWait              = 2 * time.Second
 | 
				
			||||||
 | 
						wsBufferSize             = 1024
 | 
				
			||||||
 | 
						wsReadLimit              = 64 // We only ever receive PINGs
 | 
				
			||||||
 | 
						wsPongWait               = 15 * time.Second
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// New instantiates a new Server. It creates the cache and adds a Firebase
 | 
					// New instantiates a new Server. It creates the cache and adds a Firebase
 | 
				
			||||||
| 
						 | 
					@ -404,6 +411,8 @@ func (s *Server) handleInternal(w http.ResponseWriter, r *http.Request) error {
 | 
				
			||||||
		return s.withRateLimit(w, r, s.handleSubscribeSSE)
 | 
							return s.withRateLimit(w, r, s.handleSubscribeSSE)
 | 
				
			||||||
	} else if r.Method == http.MethodGet && rawPathRegex.MatchString(r.URL.Path) {
 | 
						} else if r.Method == http.MethodGet && rawPathRegex.MatchString(r.URL.Path) {
 | 
				
			||||||
		return s.withRateLimit(w, r, s.handleSubscribeRaw)
 | 
							return s.withRateLimit(w, r, s.handleSubscribeRaw)
 | 
				
			||||||
 | 
						} else if r.Method == http.MethodGet && wsPathRegex.MatchString(r.URL.Path) {
 | 
				
			||||||
 | 
							return s.withRateLimit(w, r, s.handleSubscribeWS)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return errHTTPNotFound
 | 
						return errHTTPNotFound
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -805,6 +814,106 @@ func (s *Server) handleSubscribe(w http.ResponseWriter, r *http.Request, v *visi
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *Server) handleSubscribeWS(w http.ResponseWriter, r *http.Request, v *visitor) error {
 | 
				
			||||||
 | 
						if err := v.SubscriptionAllowed(); err != nil {
 | 
				
			||||||
 | 
							return errHTTPTooManyRequestsLimitSubscriptions
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer v.RemoveSubscription()
 | 
				
			||||||
 | 
						topicsStr := strings.TrimSuffix(r.URL.Path[1:], "/ws") // Hack
 | 
				
			||||||
 | 
						topicIDs := util.SplitNoEmpty(topicsStr, ",")
 | 
				
			||||||
 | 
						topics, err := s.topicsFromIDs(topicIDs...)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						poll := readParam(r, "x-poll", "poll", "po") == "1"
 | 
				
			||||||
 | 
						scheduled := readParam(r, "x-scheduled", "scheduled", "sched") == "1"
 | 
				
			||||||
 | 
						since, err := parseSince(r, poll)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						messageFilter, titleFilter, priorityFilter, tagsFilter, err := parseQueryFilters(r)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						upgrader := &websocket.Upgrader{
 | 
				
			||||||
 | 
							ReadBufferSize:  wsBufferSize,
 | 
				
			||||||
 | 
							WriteBufferSize: wsBufferSize,
 | 
				
			||||||
 | 
							CheckOrigin: func(r *http.Request) bool {
 | 
				
			||||||
 | 
								return true // We're open for business!
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						conn, err := upgrader.Upgrade(w, r, nil)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer conn.Close()
 | 
				
			||||||
 | 
						g, ctx := errgroup.WithContext(context.Background())
 | 
				
			||||||
 | 
						g.Go(func() error {
 | 
				
			||||||
 | 
							pongWait := s.config.KeepaliveInterval + wsPongWait
 | 
				
			||||||
 | 
							conn.SetReadLimit(wsReadLimit)
 | 
				
			||||||
 | 
							if err := conn.SetReadDeadline(time.Now().Add(pongWait)); err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							conn.SetPongHandler(func(appData string) error {
 | 
				
			||||||
 | 
								return conn.SetReadDeadline(time.Now().Add(pongWait))
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							for {
 | 
				
			||||||
 | 
								_, _, err := conn.NextReader()
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						g.Go(func() error {
 | 
				
			||||||
 | 
							ping := func() error {
 | 
				
			||||||
 | 
								if err := conn.SetWriteDeadline(time.Now().Add(wsWriteWait)); err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return conn.WriteMessage(websocket.PingMessage, nil)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							for {
 | 
				
			||||||
 | 
								select {
 | 
				
			||||||
 | 
								case <-ctx.Done():
 | 
				
			||||||
 | 
									return nil
 | 
				
			||||||
 | 
								case <-time.After(s.config.KeepaliveInterval):
 | 
				
			||||||
 | 
									v.Keepalive()
 | 
				
			||||||
 | 
									if err := ping(); err != nil {
 | 
				
			||||||
 | 
										return err
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						sub := func(msg *message) error {
 | 
				
			||||||
 | 
							if !passesQueryFilter(msg, messageFilter, titleFilter, priorityFilter, tagsFilter) {
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if err := conn.SetWriteDeadline(time.Now().Add(wsWriteWait)); err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return conn.WriteJSON(msg)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						w.Header().Set("Access-Control-Allow-Origin", "*") // CORS, allow cross-origin requests
 | 
				
			||||||
 | 
						if poll {
 | 
				
			||||||
 | 
							return s.sendOldMessages(topics, since, scheduled, sub)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						subscriberIDs := make([]int, 0)
 | 
				
			||||||
 | 
						for _, t := range topics {
 | 
				
			||||||
 | 
							subscriberIDs = append(subscriberIDs, t.Subscribe(sub))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer func() {
 | 
				
			||||||
 | 
							for i, subscriberID := range subscriberIDs {
 | 
				
			||||||
 | 
								topics[i].Unsubscribe(subscriberID) // Order!
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
						if err := sub(newOpenMessage(topicsStr)); err != nil { // Send out open message
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := s.sendOldMessages(topics, since, scheduled, sub); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return g.Wait()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func parseQueryFilters(r *http.Request) (messageFilter string, titleFilter string, priorityFilter []int, tagsFilter []string, err error) {
 | 
					func parseQueryFilters(r *http.Request) (messageFilter string, titleFilter string, priorityFilter []int, tagsFilter []string, err error) {
 | 
				
			||||||
	messageFilter = readParam(r, "x-message", "message", "m")
 | 
						messageFilter = readParam(r, "x-message", "message", "m")
 | 
				
			||||||
	titleFilter = readParam(r, "x-title", "title", "t")
 | 
						titleFilter = readParam(r, "x-title", "title", "t")
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -98,7 +98,7 @@
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Note that the Android app has a hardcoded timeout at 77s, so it should be less than that.
 | 
					# Note that the Android app has a hardcoded timeout at 77s, so it should be less than that.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# keepalive-interval: "30s"
 | 
					# keepalive-interval: "45s"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Interval in which the manager prunes old messages, deletes topics
 | 
					# Interval in which the manager prunes old messages, deletes topics
 | 
				
			||||||
# and prints the stats.
 | 
					# and prints the stats.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue