Showing preview only (645K chars total). Download the full file or copy to clipboard to get everything.
Repository: smancke/guble
Branch: master
Commit: 83e654d595ec
Files: 190
Total size: 597.1 KB
Directory structure:
gitextract_j4dqg380/
├── .gitignore
├── .travis.yml
├── Dockerfile
├── LICENSE
├── README.md
├── api/
│ └── swagger.yaml
├── client/
│ ├── client.go
│ ├── client_test.go
│ └── mocks_client_gen_test.go
├── guble-cli/
│ ├── README.md
│ ├── main.go
│ └── main_test.go
├── logformatter/
│ ├── logstash_formatter.go
│ └── logstash_formatter_test.go
├── main.go
├── protocol/
│ ├── cmd.go
│ ├── cmd_test.go
│ ├── log.go
│ ├── log_test.go
│ ├── message.go
│ ├── message_test.go
│ └── path.go
├── restclient/
│ ├── guble_sender.go
│ ├── guble_sender_test.go
│ ├── logger.go
│ └── sender.go
├── scripts/
│ ├── Dockerfile-cluster
│ ├── compose.cluster.test.yml
│ ├── compose.postgres.test.yml
│ ├── cov.sh
│ ├── dependencies_graph.sh
│ ├── file-hex.sh
│ ├── generate_coverage.sh
│ └── generate_mocks.sh
├── server/
│ ├── apns/
│ │ ├── apns.go
│ │ ├── apns_metrics.go
│ │ ├── apns_pusher.go
│ │ ├── apns_sender.go
│ │ ├── apns_sender_test.go
│ │ ├── apns_test.go
│ │ ├── logger.go
│ │ ├── mocks_connector_gen_test.go
│ │ ├── mocks_kvstore_gen_test.go
│ │ ├── mocks_pusher_gen_test.go
│ │ └── mocks_router_gen_test.go
│ ├── auth/
│ │ ├── accessmanager.go
│ │ ├── accessmanager_test.go
│ │ ├── allow_all_accessmanager.go
│ │ ├── logger.go
│ │ ├── mocks_auth_gen_test.go
│ │ └── rest_accessmanager.go
│ ├── benchmarking_apns_test.go
│ ├── benchmarking_common_test.go
│ ├── benchmarking_fcm_test.go
│ ├── benchmarking_fetch_test.go
│ ├── benchmarking_test.go
│ ├── cluster/
│ │ ├── cluster.go
│ │ ├── cluster_benchmarking_test.go
│ │ ├── cluster_conflict.go
│ │ ├── cluster_delegate.go
│ │ ├── cluster_event_delegate.go
│ │ ├── cluster_test.go
│ │ ├── codec.go
│ │ ├── codec_test.go
│ │ ├── logger.go
│ │ └── synchronizer.go
│ ├── cluster_integration_test.go
│ ├── config.go
│ ├── config_test.go
│ ├── connector/
│ │ ├── connector.go
│ │ ├── connector_test.go
│ │ ├── logger.go
│ │ ├── manager.go
│ │ ├── manager_test.go
│ │ ├── mocks_connector_gen_test.go
│ │ ├── mocks_kvstore_gen_test.go
│ │ ├── mocks_router_gen_test.go
│ │ ├── queue.go
│ │ ├── request.go
│ │ ├── subscriber.go
│ │ └── substitution.go
│ ├── fcm/
│ │ ├── fcm.go
│ │ ├── fcm_metrics.go
│ │ ├── fcm_sender.go
│ │ ├── fcm_test.go
│ │ ├── json_error.go
│ │ ├── logger.go
│ │ ├── mocks_gcm_gen_test.go
│ │ ├── mocks_kvstore_gen_test.go
│ │ ├── mocks_router_gen_test.go
│ │ ├── mocks_store_gen_test.go
│ │ └── testutil.go
│ ├── fcm_integration_test.go
│ ├── gubled.go
│ ├── gubled_test.go
│ ├── integration_test.go
│ ├── kvstore/
│ │ ├── common_test.go
│ │ ├── gorm.go
│ │ ├── kvstore.go
│ │ ├── memory.go
│ │ ├── memory_test.go
│ │ ├── postgres.go
│ │ ├── postgres_config.go
│ │ ├── postgres_config_test.go
│ │ ├── postgres_test.go
│ │ ├── sqlite.go
│ │ └── sqlite_test.go
│ ├── logger.go
│ ├── metrics/
│ │ ├── average.go
│ │ ├── average_test.go
│ │ ├── disabled.go
│ │ ├── enabled.go
│ │ ├── enabled_test.go
│ │ ├── int.go
│ │ ├── map.go
│ │ ├── metrics.go
│ │ ├── metrics_test.go
│ │ ├── ns.go
│ │ ├── rate.go
│ │ ├── rate_test.go
│ │ ├── time.go
│ │ └── zero.go
│ ├── mocks_apns_pusher_gen_test.go
│ ├── mocks_auth_gen_test.go
│ ├── mocks_router_gen_test.go
│ ├── mocks_store_gen_test.go
│ ├── redundancy_test.go
│ ├── rest/
│ │ ├── mocks_router_gen_test.go
│ │ ├── rest_message_api.go
│ │ └── rest_message_api_test.go
│ ├── router/
│ │ ├── errors.go
│ │ ├── logger.go
│ │ ├── message_queue.go
│ │ ├── mocks_auth_gen_test.go
│ │ ├── mocks_checker_gen_test.go
│ │ ├── mocks_kvstore_gen_test.go
│ │ ├── mocks_router_gen_test.go
│ │ ├── mocks_store_gen_test.go
│ │ ├── route.go
│ │ ├── route_config.go
│ │ ├── route_config_test.go
│ │ ├── route_params.go
│ │ ├── route_test.go
│ │ ├── router.go
│ │ ├── router_metrics.go
│ │ └── router_test.go
│ ├── service/
│ │ ├── logger.go
│ │ ├── mocks_checker_gen_test.go
│ │ ├── mocks_router_gen_test.go
│ │ ├── module.go
│ │ ├── service.go
│ │ └── service_test.go
│ ├── sms/
│ │ ├── logger.go
│ │ ├── mocks_router_gen_test.go
│ │ ├── mocks_sender_gen_test.go
│ │ ├── mocks_store_gen_test.go
│ │ ├── nexmo_sms.go
│ │ ├── nexmo_sms_sender.go
│ │ ├── nexmo_sms_sender_test.go
│ │ ├── sms_gateway.go
│ │ ├── sms_gateway_test.go
│ │ └── sms_metrics.go
│ ├── store/
│ │ ├── dummystore/
│ │ │ ├── dummy_message_store.go
│ │ │ └── dummy_message_store_test.go
│ │ ├── fetch_request.go
│ │ ├── filestore/
│ │ │ ├── cache.go
│ │ │ ├── index_list.go
│ │ │ ├── index_list_test.go
│ │ │ ├── logger.go
│ │ │ ├── message_partition.go
│ │ │ ├── message_partition_robustness_test.go
│ │ │ ├── message_partition_test.go
│ │ │ ├── message_store.go
│ │ │ └── message_store_test.go
│ │ └── store.go
│ ├── utils_test.go
│ ├── webserver/
│ │ ├── logger.go
│ │ ├── web_server.go
│ │ └── web_server_test.go
│ └── websocket/
│ ├── logger.go
│ ├── mocks_auth_gen_test.go
│ ├── mocks_router_gen_test.go
│ ├── mocks_store_gen_test.go
│ ├── mocks_websocket_gen_test.go
│ ├── receiver.go
│ ├── receiver_test.go
│ ├── websocket_connector.go
│ └── websocket_connector_test.go
├── test.sh
└── testutil/
└── testutil.go
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
cover.out
.goxc.json
/.idea
*.iml
guble
guble-cli/guble-cli
================================================
FILE: .travis.yml
================================================
language: go
go:
- tip
sudo: required
services:
- docker
- postgresql
before_script:
- psql -c 'create database guble;' -U postgres
before_install:
- go get github.com/wadey/gocovmerge
- go get github.com/mattn/goveralls
- go get golang.org/x/tools/cmd/cover
script:
- GO_TEST_DISABLED=true go test -v ./...
after_success:
- scripts/generate_coverage.sh
- goveralls -coverprofile=full_cov.out -service=travis-ci
- if [ "$TRAVIS_BRANCH" == "master" ]; then
GOOS=linux go build -a --ldflags '-linkmode external -extldflags "-static"' . ;
GOOS=linux go build -a --ldflags '-linkmode external -extldflags "-static"' -o ./guble-cli/guble-cli ./guble-cli ;
docker build -t smancke/guble . ;
docker login -e="$DOCKER_EMAIL" -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD" ;
docker push smancke/guble ;
fi
env:
global:
- secure: V9+UswYO6l0EuekA5YBviUdz0OcWfT3QsY1Bgoml8lmWP3/Rdq0fpxGh1hHUWt1pyAl3Aymw5Sc9DU/STmb5k6YjimS649Hu3jZ2AJfjLxh8ZA+vTgFiQc4mN4FqDAFhnPVB/aOSQhGyRlWalxikNy3nhcJrN+uWOpzRqzg0icNOdfTKpSH1cRJO0Ja34f4AEmLuNvGUAyZVpLuZJFL5mE9sJ1G1baqgFf/kTQ67jF+Ezg+1AY+NYaYwd5PUGFgIKf/qVT5Wqtrff26Yxzr/hECEBypAvNmCdLSoV/qyzZvzUTgYZTPmUnDks0uUEup9YzEQZ9XxwIQyHSXZ9D6h2vZxyr0TlZvBtdzWNiLHjBSISF8ZzOthI7NIi/e4YRYlqCF3apZuRo6o2fneHqzonza0OpJQdCKACXgycFe0ZTXk1o7SdT1d1JgeFckmL0kS8H2N4E/DaIAPq8zaC4bOlaYaUYt6vXNwEKK99q0X97gLJFdrBBY7lzKs9bbVa7b2Dhkh67PUt6WhoHUjLSN+9jTn+oda8VEKtXxyaWM6AsCRHgBiy0VaxuHbU2k1mpSCLdBfJGbrDITA4+nyPopv/oky4xHX1FGSMGFw73Ejafu9Xo0cpvIpVcNjeagUugQ5ThPQMSua9hxSZJx6alIUhptDUesiYHJAWUVPQi4N/3A=
- secure: SHIH8wBBTWslUnXeIPa3XpPugTX2IgKu3CB0OAbEE9e1nkAop1bbbas5chJgSA276xseBriH7aBSPe25XB4q9JM0YDelC7pK7dmSiLQMiAYvBb8SiGpfTAArBen4hiJiYaJo9hAE51Q4tjZ03vlIvTCYFjJ0rsBoTnbk9W6iVNEQfKzo9KfVBshYcS4BswwBgPSGtck/V3I3oASTmWpPdCGhhDipuOA9UG4hbnyWyeDqi1Mf0Dukggya4Qg+Z2o3WFI5qKGN/L6Qulgse9Rszrlikas5g2iDP11e9eO/tn/2nipIGZd/0xgCcG4tfcoqVn0PzOIOLE33vgqDrUvaaIsmVL/h0nQvC+EhVjgtrNcV/c3gDFH/3GaFX/J2wtT7396CpNCJbje/5fo9pFKS/QXjyqeIRrjq5Rux59RkZNoZIYyXbgM2UW3F8ebHFgaLd4+3Ec67zelxvixJWP1s2iDkZ2C4M7eHSBSvwpM0leebXPDOXeInCPspD5AWkhmo29m7X6J6fT7lwkfbSTyvCAQCKMzuRIsxMaAdxMCco5eVMam8CaAqZoAL/8RbnC+G9BiknyNDxx/W3qLfbnTpXlljKIBapNRYiut0RglrcPjpGAhHwbefXNwjb8AHxzx/GnU3GIHzjkQXCGDGMLJ6cPm/Iik2tVZD/eqgRxGqWNA=
- secure: BNjkm6Hb8go2xem0JLsSkFhACUgwxhBhqgsEfcpzJG1+gIC0ZZtvK5ARBusOgEytmT1tsyDbT99FT1MJ3LsucNB4EixLU/8UEoY80r/QD67eK4dKzUIiQdsPmJTUMJnzfTqgyQF2byilu6tHSHWL+MwFVmaQh04R1T1Zo0LyZMFhWjWIGx2lNhHbsLQWjb7KFLLlYx9lg4POf4eTTnrhhdJHTpUOmoty57+jf+Sen+hPOanGGsSajo6GTqN2SMmsLOCykwytsSUA0ZZ/QuEsL+1htm0vpQXqsfUxQ3KAIbyDUVTrSQsAPvULM1ymlEyIeFEeABTCVNUQb8sMpc/5VbKTNd/jEhM6oidZfakLnx3RV6kZCtrHMbkHh6ta8KcxTpt7TcnyGjTnMD5jCVjgAM99j8x7QMfAd+boRr05intzHB8GFv0IDYq9tZ93/umQHyqX8ctN+kNpmy0kSshusd3QPZ+FeZrMgWhfKvYkrjEZ1Pd/wWaqb4Pv0DUfqvlwYKshvCtH7u7TCP63Nbnt2rY+CNgfXWBDfPkxIDoF8UrXIHEZXY2C5JOGfEtS27AjUin46vHFQKr/oaYYMiUXVu25mbTkNNR67Q/6yxD0f7VqVFmWAmT1pWdDd6Gc0uT4fsP8H8Le1PAciPjMGvUFIegQK7W9TnnvDA1w0IsADwE= # DOCKER_PASSWORD
================================================
FILE: Dockerfile
================================================
FROM alpine
COPY ./guble ./guble-cli/guble-cli /usr/local/bin/
RUN mkdir -p /var/lib/guble
VOLUME ["/var/lib/guble"]
ENTRYPOINT ["/usr/local/bin/guble"]
EXPOSE 8080
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2015 Sebastian Mancke
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
================================================
FILE: README.md
================================================
# Guble Messaging Server
Guble is a simple user-facing messaging and data replication server written in Go.
[](https://www.codacy.com/app/cosminrentea/guble?utm_source=github.com&utm_medium=referral&utm_content=smancke/guble&utm_campaign=badger)
[](https://github.com/smancke/guble/releases/latest)
[](https://hub.docker.com/r/smancke/guble/)
[](https://travis-ci.org/smancke/guble)
[](https://goreportcard.com/report/github.com/smancke/guble)
[](https://codebeat.co/projects/github-com-smancke-guble)
[](https://coveralls.io/github/smancke/guble?branch=master)
[](https://godoc.org/github.com/smancke/guble)
[](https://awesome-go.com)
# Overview
Guble is in an early state (release 0.4).
It is already working well and is very useful, but the protocol, API and storage formats
may still change (until reaching 0.7).
If you intend to use guble, please get in contact with us.
The goal of guble is to be a simple and fast message bus for user interaction and replication of data between multiple devices:
* Very easy consumption of messages with web and mobile clients
* Fast realtime messaging, as well as playback of messages from a persistent commit log
* Reliable and scalable over multiple nodes
* User-aware semantics to easily support messaging scenarios between people using multiple devices
* Batteries included: usable as front-facing server, without the need of a proxy layer
* Self-contained: no mandatory dependencies to other services
## Working Features (0.4)
* Publishing and subscription of messages to topics and subtopics
* Persistent message store with transparent live and offline fetching
* WebSocket and REST APIs for message publishing
* Commandline client and Go client library
* Firebase Cloud Messaging (FCM) adapter: delivery of messages as FCM push notifications
* Docker images for server and client
* Simple Authentication and Access-Management
* Clean shutdown
* Improved logging using [logrus](https://github.com/Sirupsen/logrus) and logstash formatter
* Health-Check with Endpoint
* Collection of Basic Metrics, with Endpoint
* Added Postgresql as KV Backend
* Load testing with 5000 messages per instance
* Support for Apple Push Notification services (a new connector alongside Firebase)
* Upgrade, cleanup, abstraction, documentation, and test coverage of the Firebase connector
* GET list of subscribers / list of topics per subscriber (userID , deviceID)
* Support for SMS-sending using Nexmo (a new connector alongside Firebase)
## Throughput
Measured on an old notebook with i5-2520M, dual core and SSD. Message payload was 'Hello Word'.
Load driver and server were set up on the same machine, so 50% of the cpu was allocated to the load driver.
* End-2-End: Delivery of ~35.000 persistent messages per second
* Fetching: Receive of ~70.000 persistent messages per second
During the tests, the memory consumption of the server was around ~25 MB.
## Table of Contents
- [Roadmap](#roadmap)
- [Roadmap Release 0.5](#roadmap-release-05)
- [Roadmap Release 0.6](#roadmap-release-06)
- [Roadmap Release 0.7](#roadmap-release-07)
- [Guble Docker Image](#guble-docker-image)
- [Start the Guble Server](#start-the-guble-server)
- [Connecting with the Guble Client](#connecting-with-the-guble-client)
- [Build and Run](#build-and-run)
- [Build and Start the Server](#build-and-start-the-server)
- [Configuration](#configuration)
- [Run All Tests](#run-all-tests)
- [Clients](#clients)
- [Protocol Reference](#protocol-reference)
- [REST API](#rest-api)
- [Headers](#headers)
- [WebSocket Protocol](#websocket-protocol)
- [Message Format](#message-format)
- [Client Commands](#client-commands)
- [Server Status Messages](#server-status-messages)
- [Topics](#topics)
- [Subtopics](#subtopics)
# Roadmap
This is the current (and fast changing) roadmap and todo list:
## Roadmap Release 0.5
* Replication across multiple servers (in a Guble cluster)
* Acknowledgement of message delivery for connectors
* Storing the sequence-Id of topics in KV store, if we turn off persistence
* Filtering of messages in guble server (e.g. sent by the REST client) according to URL parameters: UserID, DeviceID, Connector name
* Updating README to show subscribe/unsubscribe/get/posting, health/metrics
## Roadmap Release 0.6
* Make notification messages optional by client configuration
* Correct behaviour of receive command with `maxCount` on subtopics
* Cancel of fetch in the message store and multiple concurrent fetch commands for the same topic
* Configuration of different persistence strategies for topics
* Delivery semantics: user must read on one device / deliver only to one device / notify if not connected, etc.
* User-specific persistent subscriptions across all clients of the user
* Client: (re-)setup of subscriptions after client reconnect
* Message size limit configurable by the client with fetching by URL
## Roadmap Release 0.7
* HTTPS support in the service
* Minimal example: chat application
* Stable JavaScript client: https://github.com/smancke/guble-js
* (TBD) Improved authentication and access-management
* (TBD) Add Consul as KV Backend
* (TBD) Index-based search of messages using [GoLucene](https://github.com/balzaczyy/golucene)
# Guble Docker Image
We are providing Docker images of the server and client for your convenience.
## Start the Guble Server
There is an automated Docker build for the master at the Docker Hub.
To start the server with Docker simply type:
```
docker run -p 8080:8080 smancke/guble
```
To see available configuration options:
```
docker run smancke/guble --help
```
All options can be supplied on the commandline or by a corresponding environment variable with the prefix `GUBLE_`.
So to let guble be more verbose, you can either use:
```
docker run smancke/guble --log=info
```
or
```
docker run -e GUBLE_LOG=info smancke/guble
```
The Docker image has a volume mount point at `/var/lib/guble`, so if you want to bind-mount the persistent storage from your host you should use:
```
docker run -p 8080:8080 -v /host/storage/path:/var/lib/guble smancke/guble
```
## Connecting with the Guble Client
The Docker image includes the guble commandline client `guble-cli`.
You can execute it within a running guble container and connect to the server:
```
docker run -d --name guble smancke/guble
docker exec -it guble /usr/local/bin/guble-cli
```
Visit the [`guble-cli` documentation](https://github.com/smancke/guble/tree/master/guble-cli) for more details.
# Build and Run
Since Go makes it very easy to build from source, you can compile guble using a single command.
A prerequisite is having an installed Go environment and an empty directory:
```
sudo apt-get install golang
mkdir guble && cd guble
export GOPATH=`pwd`
```
## Build and Start the Server
Build and start guble with the following commands (assuming that directory `/var/lib/guble` is already created with read-write rights for the current user):
```
go get github.com/smancke/guble
bin/guble --log=info
```
### Configuration
|CLI Option|Env Variable|Values|Default|Description|
|--- |--- |--- |--- |--- |
|`--env`|GUBLE_ENV|development | integration | preproduction | production|development|Name of the environment on which the application is running. Used mainly for logging|
|`--health-endpoint`|GUBLE_HEALTH_ENDPOINT|resource/path/to/healthendpoint|/admin/healthcheck|The health endpoint to be used by the HTTP server.Can be disabled by setting the value to ""|
|`--http`|GUBLE_HTTP_LISTEN|format: [host]:port||The address to for the HTTP server to listen on|
|`--kvs`|GUBLE_KVS|memory | file | postgres|file|The storage backend for the key-value store to use|
|`--log`|GUBLE_LOG|panic | fatal | error | warn | info | debug|error|The log level in which the process logs|
|`--metrics-endpoint`|GUBLE_METRICS_ENDPOINT|resource/path/to/metricsendpoint|/admin/metrics|The metrics endpoint to be used by the HTTP server.Can be disabled by setting the value to ""|
|`--ms`|GUBLE_MS|memory | file|file|The message storage backend|
|`--profile`|GUBLE_PROFILE|cpu | mem | block||The profiler to be used|
|`--storage-path`|GUBLE_STORAGE_PATH|path/to/storage|/var/lib/guble|The path for storing messages and key-value data like subscriptions if defined.The path must exists!|
#### APNS
|CLI Option|Env Variable|Values|Default|Description|
|--- |--- |--- |--- |--- |
|`--apns`|GUBLE_APNS|true | false|false|Enable the APNS module in general as well as the connector to the development endpoint|
|`--apns-production`|GUBLE_APNS_PRODUCTION|true | false|false|Enables the connector to the apns production endpoint, requires the apns option to be set|
|`--apns-cert-file`|GUBLE_APNS_CERT_FILE|path/to/cert/file||The APNS certificate file name, use this as an alternative to the certificate bytes option|
|`--apns-cert-bytes`|GUBLE_APNS_CERT_BYTES|cert-bytes-as-hex-string||The APNS certificate bytes, use this as an alternative to the certificate file option|
|`--apns-cert-password`|GUBLE_APNS_CERT_PASSWORD|password||The APNS certificate password|
|`--apns-app-topic`|GUBLE_APNS_APP_TOPIC|topic||The APNS topic (as used by the mobile application)|
|`--apns-prefix`|GUBLE_APNS_PREFIX|prefix|/apns/|The APNS prefix / endpoint|
|`--apns-workers`|GUBLE_APNS_WORKERS|number of workers|Number of CPUs|The number of workers handling traffic with APNS (default: number of CPUs)|
#### SMS
|CLI Option|Env Variable|Values|Default |Description|
|--- |--- |--- |--- |--- |
|`sms`|GUBLE_SMS|true | false|false |Enable the SMS gateway|
|`sms_api_key`|GUBLE_SMS_API_KEY|api key||The Nexmo API Key for Sending sms|
|`sms_api_secret`|GUBLE_SMS_API_SECRET|api secret||The Nexmo API Secret for Sending sms|
|`sms_topic`|GUBLE_SMS_TOPIC|topic|/sms|The topic for sms route|
|`sms_workers`|GUBLE_SMS_WORKERS|number of workers|Number of CPUs|The number of workers handling traffic with Nexmo sms endpoint|
#### FCM
|CLI Option|Env Variable|Values|Default|Description|
|--- |--- |--- |--- |--- |
|`--fcm|GUBLE_FCM`|true | false|false|Enable the Google Firebase Cloud Messaging connector|
|`--fcm-api-key`|GUBLE_FCM_API_KEY|api key||The Google API Key for Google Firebase Cloud Messaging|
|`--fcm-workers`|GUBLE_FCM_WORKERS|number of workers|Number of CPUs|The number of workers handling traffic with Firebase Cloud Messaging|
|`--fcm-endpoint`|GUBLE_FCM_ENDPOINT|format: url-schema|https://fcm.googleapis.com/fcm/send|The Google Firebase Cloud Messaging endpoint|
|`--fcm-prefix`|GUBLE_FCM_PREFIX|prefix|/fcm/|The FCM prefix / endpoint|
#### Postgres
|CLI Option|Env Variable|Values|Default|Description|
|--- |--- |--- |--- |--- |
|`--pg-host`|GUBLE_PG_HOST|hostname|localhost|The PostgreSQL hostname|
|`--pg-port`|GUBLE_PG_PORT|port|5432|The PostgreSQL port|
|`--pg-user`|GUBLE_PG_USER|user|guble|The PostgreSQL user|
|`--pg-password`|GUBLE_PG_PASSWORD|password|guble|The PostgreSQL password|
|`--pg-dbname`|GUBLE_PG_DBNAME|database|guble|The PostgreSQL database name|
## Run All Tests
```
go get -t github.com/smancke/guble/...
go test github.com/smancke/guble/...
```
# Clients
The following clients are available:
* __Commandline Client__: https://github.com/smancke/guble/tree/master/guble-cli
* __Go client library__: https://github.com/smancke/guble/tree/master/client
* __JavaScript library__: (in early stage) https://github.com/smancke/guble-js
# Protocol Reference
## REST API
Currently there is a minimalistic REST API, just for publishing messages.
```
POST /api/message/<topic>
```
URL parameters:
* __userId__: The PublisherUserId
* __messageId__: The PublisherMessageId
### Headers
You can set fields in the header JSON of the message by providing the corresponding HTTP headers with the prefix `X-Guble-`.
Curl example with the resulting message:
```
curl -X POST -H "x-Guble-Key: Value" --data Hello 'http://127.0.0.1:8080/api/message/foo?userId=marvin&messageId=42'
```
Results in:
```
16,/foo,marvin,VoAdxGO3DBEn8vv8,42,1451236804
{"Key":"Value"}
Hello
```
## WebSocket Protocol
The communication with the guble server is done by ordinary WebSockets, using a binary encoding.
### Message Format
All payload messages sent from the server to the client are using the following format:
```
<path:string>,<sequenceId:int64>,<publisherUserId:string>,<publisherApplicationId:string>,<publisherMessageId:string>,<messagePublishingTime:unix-timestamp>\n
[<application headers json>]\n
<body>
example 1:
/foo/bar,42,user01,phone1,id123,1420110000
{"Content-Type": "text/plain", "Correlation-Id": "7sdks723ksgqn"}
Hello World
example 2:
/foo/bar,42,user01,54sdcj8sd7,id123,1420110000
anyByteData
```
* All text formats are assumed to be UTF-8 encoded.
* Message `sequenceId`s are `int64`, and distinct within a topic.
The message `sequenceId`s are strictly monotonically increasing depending on the message age, but there is no guarantee for the right order while transmitting.
### Client Commands
The client can send the following commands.
#### Send
Publish a message to a topic:
```
> <path> [<publisherMessageId>]\n
[<header>\n]..
\n
<body>
example:
> /foo
Hello World
```
#### Subscribe/Receive
Receive messages from a path (e.g. a topic or subtopic).
This command can be used to subscribe for incoming messages on a topic,
as well as for replaying the message history.
```
+ <path> [<startId>[,<maxCount>]]
```
* `path`: the topic to receive the messages from
* `startId`: the message id to start the replay
** If no `startId` is given, only future messages will be received (simple subscribe).
** If the `startId` is negative, it is interpreted as relative count of last messages in the history.
* `maxCount`: the maximum number of messages to replay
__Note__: Currently, the fetching of stored messages does not recognize subtopics.
Examples:
```
+ /foo # Subscribe to all future messages matching /foo
+ /foo/bar # Subscribe to all future messages matching /foo/bar
+ /foo 0 # Receive all message from the topic and subscribe for further incoming messages.
+ /foo 42 # Receive all message with message ids >= 42
# from the topic and subscribe for further incoming messages.
+ /foo 0 20 # Receive the first (oldest) 20 messages within the topic and stop.
# (If the topic has less messages, it will stop after receiving all existing ones.)
+ /foo -20 # Receive the last (newest) 20 messages from the topic and then
# subscribe for further incoming messages.
+ /foo -20 20 # Receive the last (newest) 20 messages within the topic and stop.
# (If the topic has less messages, it will stop after receiving all existing ones.)
```
#### Unsubscribe/Cancel
Cancel further receiving of messages from a path (e.g. a topic or subtopic).
```
- <path>
example:
- /foo
- /foo/bar
```
### Server Status Messages
The server sends status messages to the client. All positive status messages start with `>`.
Status messages reporting an error start with `!`. Status messages are in the following format.
```
'#'<msgType> <Explanation text>\n
<json data>
```
#### Connection Message
```
#ok-connected You are connected to the server.\n
{"ApplicationId": "the app id", "UserId": "the user id", "Time": "the server time as unix timestamp "}
```
Example:
```
#connected You are connected to the server.
{"ApplicationId": "phone1", "UserId": "user01", "Time": "1420110000"}
```
#### Send Success Notification
This notification confirms, that the messaging system has successfully received the message and now starts transmitting it to the subscribers:
```
#send <publisherMessageId>
{"sequenceId": "sequence id", "path": "/foo", "publisherMessageId": "publishers message id", "messagePublishingTime": "unix-timestamp"}
```
#### Receive Success Notification
Depending on the type of `+` (receive) command, up to three different notification messages will be sent back.
Be aware, that a server may send more receive notifications that you would have expected in first place, e.g. when:
* Additional messages are stored, while the first fetching is in progress
* The server decides to meanwhile stop the online subscription and change to fetching,
because your client is too slow to read all incoming messages.
1. When the fetch operation starts:
```
#fetch-start <path> <count>
```
* `path`: the topic path
* `count`: the number of messages that will be returned
2. When the fetch operation is done:
```
#fetch-done <path>
```
* `path`: the topic path
3. When the subscription to new messages was taken:
```
#subscribed-to <path>
```
* `path`: the topic path
#### Unsubscribe Success Notification
An unsubscribe/cancel operation is confirmed by the following notification:
```
#canceled <path>
```
#### Send Error Notification
This message indicates, that the message could not be delivered.
```
!error-send <publisherMessageId> <error text>
{"sequenceId": "sequence id", "path": "/foo", "publisherMessageId": "publishers message id", "messagePublishingTime": "unix-timestamp"}
```
#### Bad Request
This notification has the same meaning as the http 400 Bad Request.
```
!error-bad-request unknown command 'sdcsd'
```
#### Internal Server Error
This notification has the same meaning as the http 500 Internal Server Error.
```
!error-server-internal this computing node has problems
```
## Topics
Messages can be hierarchically routed by topics, so they are represented by a path, separated by `/`.
The server takes care, that a message only gets delivered once, even if it is matched by multiple
subscription paths.
### Subtopics
The path delimiter gives the semantic of subtopics.
With this, a subscription to a parent topic (e.g. `/foo`)
also results in receiving all messages of the subtopics (e.g. `/foo/bar`).
================================================
FILE: api/swagger.yaml
================================================
swagger: '2.0'
info:
version: "0.0.1"
title: Guble API
schemes:
- http
paths:
/api/subscribers/{topic}:
get:
produces:
- application/json
tags:
- REST
- APNS
- FCM
description: |
Get subscribers registered for a topic
parameters:
- name: topic
in: path
type: string
required: true
description: name of the subscribtion topic
responses:
200:
description: successful response
schema:
type: array
items:
$ref: '#/definitions/Subscriber'
500:
description: unknown error
/api/message/{topic}:
post:
consumes:
- application/json
tags:
- REST
- SMS
- APNS
- FCM
description: |
Send message to a connector
parameters:
- name: topic
in: path
type: string
required: true
description: |
Name of the subscribtion topic.
'sms' is a special topic to send a sms message and
must not be used neither as APNS nor as FCM topic.
- name: message
in: body
required: true
description: a json message in the format expected by the connector
schema:
type: object
- name: userId
in: header
required: false
type: string
- name: x-guble
in: header
required: false
type: string
description: x-guble- is a generic header prefix
- name: filterConnector
in: query
description: |
Specifies a connector which should handle message.
As the message is in the connector specific format,
the parameter should be treated as mandatory.
required: false
type: string
enum:
- apns
- fcm
- name: filterUserID
in: query
description: Specifies a subscribed user which should received the notification.
required: false
type: string
- in: query
name: filterDeviceToken
description: Specifies a device token which should received the notification.
required: false
type: string
responses:
200:
description: successful response
400:
description: malformed request
500:
description: unknown error
/apns/{device_token}/{user_id}/{topic}:
post:
tags:
- APNS
description: |
Create APN subscription
parameters:
- name: device_token
in: path
type: string
required: true
description: device token which mobile device received from APNS
- name: user_id
in: path
type: string
required: true
description: customer uuid or 'anonymous'
- name: topic
in: path
type: string
required: true
description: name of the subscribtion topic
responses:
200:
description: successful response
500:
description: unknown error
delete:
tags:
- APNS
description: |
Delete APN subscription
parameters:
- name: device_token
in: path
type: string
required: true
description: device token which mobile device received from APNS
- name: user_id
in: path
type: string
required: true
description: customer uuid or 'anonymous'
- name: topic
in: path
type: string
required: true
description: name of the subscribtion topic
responses:
200:
description: successful response
404:
description: subscription not found
500:
description: unknown error
/apns/:
get:
tags:
- APNS
description: |
Return the list of APNS subscriptions
parameters:
- in: query
name: device_token
description: device token which mobile device received from APNS
required: false
type: string
- in: query
name: user_id
description: device token
required: false
type: string
responses:
200:
description: list of topics
schema:
type: array
items: {
type: string
}
400:
description: missing filters
500:
description: unknown error
/apns/substitute/:
post:
tags:
- APNS
description: |
Substitutes field value of the APNS subscriber object.
Provided old value must match the current value stored in the object for operation
to be succcessful
parameters:
- name: body
in: body
required: true
schema:
$ref: '#/definitions/SubstitutionRequest'
responses:
200:
description: successful response
schema:
$ref: '#/definitions/SubstitutionResponse'
400:
description: invalid substitution request
schema:
$ref: '#/definitions/ErrorResponse'
500:
description: unknown error
schema:
$ref: '#/definitions/ErrorResponse'
/fcm/{device_token}/{user_id}/{topic}:
post:
tags:
- FCM
description: |
Create FCM subscription
parameters:
- name: device_token
in: path
type: string
required: true
description: device token which mobile device received from FCM
- name: user_id
in: path
type: string
required: true
description: customer uuid or 'anonymous'
- name: topic
in: path
type: string
required: true
description: name of the subscribtion topic
responses:
200:
description: successful response
500:
description: unknown error
delete:
tags:
- FCM
description: |
Delete FCM subscription
parameters:
- name: device_token
in: path
type: string
required: true
description: device token which mobile device received from FCM
- name: user_id
in: path
type: string
required: true
description: customer uuid or 'anonymous'
- name: topic
in: path
type: string
required: true
description: name of the subscribtion topic
responses:
200:
description: successful response
404:
description: subscription not found
500:
description: unknown error
/fcm/:
get:
tags:
- FCM
description: |
Return the list of subscriptions
parameters:
- in: query
name: device_token
description: device token which mobile device received from FCM
required: false
type: string
- in: query
name: user_id
description: device token
required: false
type: string
responses:
200:
description: list of topics
schema:
type: array
items: {
type: string
}
400:
description: missing filters
500:
description: unknown error
/fcm/substitute/:
post:
tags:
- FCM
description: |
Substitutes field value of the FCMC subscriber object.
Provided old value must match the current value stored in the object for operation
to be succcessful
parameters:
- name: body
in: body
required: true
schema:
$ref: '#/definitions/SubstitutionRequest'
responses:
200:
description: successful response
schema:
$ref: '#/definitions/SubstitutionResponse'
400:
description: invalid substitution request
schema:
$ref: '#/definitions/ErrorResponse'
500:
description: unknown error
schema:
$ref: '#/definitions/ErrorResponse'
/admin/healtcheck:
get:
produces:
- application/json
tags:
- ADMIN
description: Application health check
responses:
200:
description: successful response
500:
description: unknown error
/admin/metrics:
get:
produces:
- application/json
tags:
- ADMIN
description: Application metrics
responses:
200:
description: successful response
schema:
type: object
500:
description: unknown error
/stream/:
get:
tags:
- WEBSOCKET
description: Web socket interface
responses:
201:
description: Response code is 101 after protocol was switched
definitions:
ErrorResponse:
type: object
properties:
error:
description: error message
type: string
Subscriber:
type: object
required:
- connector
- device_token
- user_id
properties:
connector:
description: name of the connector
type: string
enum:
- apns
- fcm
device_token:
description: device token
type: string
user_id:
description: customer uuid or 'anonymous'
type: string
SubstitutionRequest:
type: object
required:
- field
- old_value
- new_value
properties:
field:
description: field name
type: string
enum:
- device_token
- user_id
old_value:
description: old value
type: string
new_value:
description: new value
type: string
SubstitutionResponse:
type: object
properties:
modified:
description: number of modified entries
type: integer
================================================
FILE: client/client.go
================================================
package client
import (
"github.com/smancke/guble/protocol"
log "github.com/Sirupsen/logrus"
"github.com/gorilla/websocket"
"net/http"
"sync"
"time"
)
var logger = log.WithFields(log.Fields{
"module": "client",
})
type WSConnection interface {
WriteMessage(messageType int, data []byte) error
ReadMessage() (messageType int, p []byte, err error)
Close() error
}
func DefaultConnectionFactory(url string, origin string) (WSConnection, error) {
logger.WithField("url", url).Info("Connecting to")
header := http.Header{"Origin": []string{origin}}
conn, _, err := websocket.DefaultDialer.Dial(url, header)
if err != nil {
return nil, err
}
logger.WithField("url", url).Info("Connected to")
return conn, nil
}
type WSConnectionFactory func(url string, origin string) (WSConnection, error)
type Client interface {
Start() error
Close()
Subscribe(path string) error
Unsubscribe(path string) error
Send(path string, body string, header string) error
SendBytes(path string, body []byte, header string) error
WriteRawMessage(message []byte) error
Messages() chan *protocol.Message
StatusMessages() chan *protocol.NotificationMessage
Errors() chan *protocol.NotificationMessage
SetWSConnectionFactory(WSConnectionFactory)
IsConnected() bool
}
type client struct {
mu sync.RWMutex
ws WSConnection
messages chan *protocol.Message
statusMessages chan *protocol.NotificationMessage
errors chan *protocol.NotificationMessage
url string
origin string
shouldStopChan chan bool
shouldStopFlag bool
autoReconnect bool
wSConnectionFactory func(url string, origin string) (WSConnection, error)
// flag, to indicate if the client is connected
connected bool
}
// Open is a shortcut for New() and Start()
func Open(url, origin string, channelSize int, autoReconnect bool) (Client, error) {
c := New(url, origin, channelSize, autoReconnect)
c.SetWSConnectionFactory(DefaultConnectionFactory)
return c, c.Start()
}
// New creates a new client, without starting the connection
func New(url, origin string, channelSize int, autoReconnect bool) Client {
return &client{
messages: make(chan *protocol.Message, channelSize),
statusMessages: make(chan *protocol.NotificationMessage, channelSize),
errors: make(chan *protocol.NotificationMessage, channelSize),
url: url,
origin: origin,
shouldStopChan: make(chan bool, 1),
autoReconnect: autoReconnect,
}
}
func (c *client) SetWSConnectionFactory(connection WSConnectionFactory) {
c.wSConnectionFactory = connection
}
func (c *client) IsConnected() bool {
c.mu.RLock()
defer c.mu.RUnlock()
return c.connected
}
func (c *client) setIsConnected(connected bool) {
c.mu.Lock()
defer c.mu.Unlock()
c.connected = connected
}
// Connect and start the read go routine.
// If an error occurs on first connect, it will be returned.
// Further connection errors will only be logged.
func (c *client) Start() error {
var err error
c.ws, err = c.wSConnectionFactory(c.url, c.origin)
c.setIsConnected(err == nil)
if c.IsConnected() {
go c.readLoop()
} else if c.autoReconnect {
go c.startWithReconnect()
}
return err
}
func (c *client) startWithReconnect() {
for {
if c.IsConnected() {
err := c.readLoop()
if err == nil {
return
}
}
if c.shouldStop() {
return
}
var err error
c.ws, err = c.wSConnectionFactory(c.url, c.origin)
if err != nil {
c.setIsConnected(false)
logger.WithError(err).Error("Error on connect, retry in 50 ms")
time.Sleep(time.Millisecond * 50)
} else {
c.setIsConnected(true)
logger.Warn("Reconnected again")
}
}
}
func (c *client) readLoop() error {
for {
_, msg, err := c.ws.ReadMessage()
if err != nil {
c.setIsConnected(false)
if c.shouldStop() {
return nil
}
logger.WithError(err).Error("Error when reading from websocket")
c.errors <- clientErrorMessage(err.Error())
return err
}
logger.WithField("msg", string(msg)).Debug("Raw >")
c.handleIncomingMessage(msg)
}
}
func (c *client) shouldStop() bool {
if c.shouldStopFlag {
return true
}
select {
case <-c.shouldStopChan:
c.shouldStopFlag = true
return true
default:
return false
}
}
func (c *client) handleIncomingMessage(msg []byte) {
parsed, err := protocol.Decode(msg)
if err != nil {
logger.WithError(err).Error("Error on parsing of incoming message")
c.errors <- clientErrorMessage(err.Error())
return
}
switch message := parsed.(type) {
case *protocol.Message:
c.messages <- message
case *protocol.NotificationMessage:
if message.IsError {
select {
case c.errors <- message:
default:
}
} else {
select {
case c.statusMessages <- message:
default:
}
}
}
}
func (c *client) Subscribe(path string) error {
cmd := &protocol.Cmd{
Name: protocol.CmdReceive,
Arg: path,
}
err := c.ws.WriteMessage(websocket.BinaryMessage, cmd.Bytes())
return err
}
func (c *client) Unsubscribe(path string) error {
cmd := &protocol.Cmd{
Name: protocol.CmdCancel,
Arg: path,
}
err := c.ws.WriteMessage(websocket.BinaryMessage, cmd.Bytes())
return err
}
func (c *client) Send(path string, body string, header string) error {
return c.SendBytes(path, []byte(body), header)
}
func (c *client) SendBytes(path string, body []byte, header string) error {
cmd := &protocol.Cmd{
Name: protocol.CmdSend,
Arg: path,
Body: body,
HeaderJSON: header,
}
return c.WriteRawMessage(cmd.Bytes())
}
func (c *client) WriteRawMessage(message []byte) error {
return c.ws.WriteMessage(websocket.BinaryMessage, message)
}
func (c *client) Messages() chan *protocol.Message {
return c.messages
}
func (c *client) StatusMessages() chan *protocol.NotificationMessage {
return c.statusMessages
}
func (c *client) Errors() chan *protocol.NotificationMessage {
return c.errors
}
func (c *client) Close() {
c.shouldStopChan <- true
c.ws.Close()
}
func clientErrorMessage(message string) *protocol.NotificationMessage {
return &protocol.NotificationMessage{
IsError: true,
Name: "clientError",
Arg: message,
}
}
================================================
FILE: client/client_test.go
================================================
package client
import (
"github.com/smancke/guble/testutil"
"fmt"
"strings"
"testing"
"time"
"github.com/gorilla/websocket"
"github.com/stretchr/testify/assert"
)
var aNormalMessage = `/foo/bar,42,user01,phone01,{},1420110000,0
Hello World`
var aSendNotification = "#send"
var anErrorNotification = "!error-send"
func MockConnectionFactory(connectionMock *MockWSConnection) func(string, string) (WSConnection, error) {
return func(url string, origin string) (WSConnection, error) {
return connectionMock, nil
}
}
func TestConnectErrorWithoutReconnection(t *testing.T) {
a := assert.New(t)
// given a client
c := New("url", "origin", 1, false)
// which raises an error on connect
callCounter := 0
c.SetWSConnectionFactory(func(url string, origin string) (WSConnection, error) {
a.Equal("url", url)
a.Equal("origin", origin)
callCounter++
return nil, fmt.Errorf("emulate connection error")
})
// when we start
err := c.Start()
// then
a.Error(err)
a.Equal(1, callCounter)
}
func TestConnectErrorWithoutReconnectionUsingOpen(t *testing.T) {
a := assert.New(t)
c, err := Open("url", "origin", 1, false)
// which raises an error on connect
callCounter := 0
c.SetWSConnectionFactory(func(url string, origin string) (WSConnection, error) {
a.Equal("url", url)
a.Equal("origin", origin)
callCounter++
return nil, fmt.Errorf("emulate connection error")
})
a.Error(err)
}
func TestConnectErrorWithReconnection(t *testing.T) {
ctrl, finish := testutil.NewMockCtrl(t)
defer finish()
a := assert.New(t)
// given a client
c := New("url", "origin", 1, true)
// which raises an error twice and then allows to connect
callCounter := 0
connMock := NewMockWSConnection(ctrl)
connMock.EXPECT().ReadMessage().Do(func() { time.Sleep(time.Second) })
c.SetWSConnectionFactory(func(url string, origin string) (WSConnection, error) {
a.Equal("url", url)
a.Equal("origin", origin)
if callCounter <= 2 {
callCounter++
return nil, fmt.Errorf("emulate connection error")
}
return connMock, nil
})
// when we start
err := c.Start()
// then we get an error, first
a.Error(err)
a.False(c.IsConnected())
// when we wait for two iterations and 10ms buffer time to connect
time.Sleep(time.Millisecond * 110)
// then we got connected
a.True(c.IsConnected())
a.Equal(3, callCounter)
}
func TestStopableClient(t *testing.T) {
ctrl, finish := testutil.NewMockCtrl(t)
defer finish()
a := assert.New(t)
// given a client
c := New("url", "origin", 1, true)
// with a closeable connection
connMock := NewMockWSConnection(ctrl)
close := make(chan bool, 1)
connMock.EXPECT().ReadMessage().
Do(func() { <-close }).
Return(0, []byte{}, fmt.Errorf("expected close error"))
connMock.EXPECT().Close().Do(func() {
close <- true
})
c.SetWSConnectionFactory(MockConnectionFactory(connMock))
// when we start
err := c.Start()
// than we are connected
a.NoError(err)
a.True(c.IsConnected())
// when we clode
c.Close()
time.Sleep(time.Millisecond * 1)
// than the client returns
a.False(c.IsConnected())
}
func TestReceiveAMessage(t *testing.T) {
ctrl, finish := testutil.NewMockCtrl(t)
defer finish()
a := assert.New(t)
// given a client
c := New("url", "origin", 10, false)
// with a closeable connection
connMock := NewMockWSConnection(ctrl)
close := make(chan bool, 1)
// normal message
call1 := connMock.EXPECT().ReadMessage().
Return(4, []byte(aNormalMessage), nil)
call2 := connMock.EXPECT().ReadMessage().
Return(4, []byte(aSendNotification), nil)
call3 := connMock.EXPECT().ReadMessage().
Return(4, []byte("---"), nil)
call4 := connMock.EXPECT().ReadMessage().
Return(4, []byte(anErrorNotification), nil)
call5 := connMock.EXPECT().ReadMessage().
Do(func() { <-close }).
Return(0, []byte{}, fmt.Errorf("expected close error")).
AnyTimes()
call5.After(call4)
call4.After(call3)
call3.After(call2)
call2.After(call1)
c.SetWSConnectionFactory(MockConnectionFactory(connMock))
connMock.EXPECT().Close().Do(func() {
close <- true
})
// when we start
err := c.Start()
a.NoError(err)
a.True(c.IsConnected())
// than we receive the expected message
select {
case m := <-c.Messages():
a.Equal(aNormalMessage, string(m.Bytes()))
case <-time.After(time.Millisecond * 10):
a.Fail("timeout while waiting for message")
}
// and we receive the notification
select {
case m := <-c.StatusMessages():
a.Equal(aSendNotification, string(m.Bytes()))
case <-time.After(time.Millisecond * 10):
a.Fail("timeout while waiting for message")
}
// parse error
select {
case m := <-c.Errors():
a.True(strings.HasPrefix(string(m.Bytes()), "!clientError "))
case <-time.After(time.Millisecond * 10):
a.Fail("timeout while waiting for message")
}
// and we receive the error notification
select {
case m := <-c.Errors():
a.Equal(anErrorNotification, string(m.Bytes()))
case <-time.After(time.Millisecond * 10):
a.Fail("timeout while waiting for message")
}
c.Close()
}
func TestSendAMessage(t *testing.T) {
ctrl, finish := testutil.NewMockCtrl(t)
defer finish()
// a := assert.New(t)
// given a client
c := New("url", "origin", 1, true)
// when expects a message
connMock := NewMockWSConnection(ctrl)
connMock.EXPECT().WriteMessage(websocket.BinaryMessage, []byte("> /foo\n{}\nTest"))
connMock.EXPECT().
ReadMessage().
Return(websocket.BinaryMessage, []byte(aNormalMessage), nil).
Do(func() {
time.Sleep(time.Millisecond * 50)
}).
AnyTimes()
c.SetWSConnectionFactory(MockConnectionFactory(connMock))
c.Start()
// then the expectation is meet by sending it
c.Send("/foo", "Test", "{}")
// stop client after 200ms
time.AfterFunc(time.Millisecond*200, func() { c.Close() })
}
func TestSendSubscribeMessage(t *testing.T) {
ctrl, finish := testutil.NewMockCtrl(t)
defer finish()
// given a client
c := New("url", "origin", 1, true)
// when expects a message
connMock := NewMockWSConnection(ctrl)
connMock.EXPECT().WriteMessage(websocket.BinaryMessage, []byte("+ /foo"))
connMock.EXPECT().
ReadMessage().
Return(websocket.BinaryMessage, []byte(aNormalMessage), nil).
Do(func() {
time.Sleep(time.Millisecond * 50)
}).
AnyTimes()
c.SetWSConnectionFactory(MockConnectionFactory(connMock))
c.Start()
c.Subscribe("/foo")
// stop client after 200ms
time.AfterFunc(time.Millisecond*200, func() { c.Close() })
}
func TestSendUnSubscribeMessage(t *testing.T) {
ctrl, finish := testutil.NewMockCtrl(t)
defer finish()
// given a client
c := New("url", "origin", 1, true)
// when expects a message
connMock := NewMockWSConnection(ctrl)
connMock.EXPECT().WriteMessage(websocket.BinaryMessage, []byte("- /foo"))
connMock.EXPECT().
ReadMessage().
Return(websocket.BinaryMessage, []byte(aNormalMessage), nil).
Do(func() {
time.Sleep(time.Millisecond * 50)
}).
AnyTimes()
c.SetWSConnectionFactory(MockConnectionFactory(connMock))
c.Start()
c.Unsubscribe("/foo")
// stop client after 200ms
time.AfterFunc(time.Millisecond*200, func() { c.Close() })
}
================================================
FILE: client/mocks_client_gen_test.go
================================================
// Automatically generated by MockGen. DO NOT EDIT!
// Source: github.com/smancke/guble/client (interfaces: WSConnection,Client)
package client
import (
"github.com/golang/mock/gomock"
"github.com/smancke/guble/protocol"
)
// Mock of WSConnection interface
type MockWSConnection struct {
ctrl *gomock.Controller
recorder *_MockWSConnectionRecorder
}
// Recorder for MockWSConnection (not exported)
type _MockWSConnectionRecorder struct {
mock *MockWSConnection
}
func NewMockWSConnection(ctrl *gomock.Controller) *MockWSConnection {
mock := &MockWSConnection{ctrl: ctrl}
mock.recorder = &_MockWSConnectionRecorder{mock}
return mock
}
func (_m *MockWSConnection) EXPECT() *_MockWSConnectionRecorder {
return _m.recorder
}
func (_m *MockWSConnection) Close() error {
ret := _m.ctrl.Call(_m, "Close")
ret0, _ := ret[0].(error)
return ret0
}
func (_mr *_MockWSConnectionRecorder) Close() *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "Close")
}
func (_m *MockWSConnection) ReadMessage() (int, []byte, error) {
ret := _m.ctrl.Call(_m, "ReadMessage")
ret0, _ := ret[0].(int)
ret1, _ := ret[1].([]byte)
ret2, _ := ret[2].(error)
return ret0, ret1, ret2
}
func (_mr *_MockWSConnectionRecorder) ReadMessage() *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadMessage")
}
func (_m *MockWSConnection) WriteMessage(_param0 int, _param1 []byte) error {
ret := _m.ctrl.Call(_m, "WriteMessage", _param0, _param1)
ret0, _ := ret[0].(error)
return ret0
}
func (_mr *_MockWSConnectionRecorder) WriteMessage(arg0, arg1 interface{}) *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteMessage", arg0, arg1)
}
// Mock of Client interface
type MockClient struct {
ctrl *gomock.Controller
recorder *_MockClientRecorder
}
// Recorder for MockClient (not exported)
type _MockClientRecorder struct {
mock *MockClient
}
func NewMockClient(ctrl *gomock.Controller) *MockClient {
mock := &MockClient{ctrl: ctrl}
mock.recorder = &_MockClientRecorder{mock}
return mock
}
func (_m *MockClient) EXPECT() *_MockClientRecorder {
return _m.recorder
}
func (_m *MockClient) Close() {
_m.ctrl.Call(_m, "Close")
}
func (_mr *_MockClientRecorder) Close() *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "Close")
}
func (_m *MockClient) Errors() chan *protocol.NotificationMessage {
ret := _m.ctrl.Call(_m, "Errors")
ret0, _ := ret[0].(chan *protocol.NotificationMessage)
return ret0
}
func (_mr *_MockClientRecorder) Errors() *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "Errors")
}
func (_m *MockClient) IsConnected() bool {
ret := _m.ctrl.Call(_m, "IsConnected")
ret0, _ := ret[0].(bool)
return ret0
}
func (_mr *_MockClientRecorder) IsConnected() *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "IsConnected")
}
func (_m *MockClient) Messages() chan *protocol.Message {
ret := _m.ctrl.Call(_m, "Messages")
ret0, _ := ret[0].(chan *protocol.Message)
return ret0
}
func (_mr *_MockClientRecorder) Messages() *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "Messages")
}
func (_m *MockClient) Send(_param0 string, _param1 string, _param2 string) error {
ret := _m.ctrl.Call(_m, "Send", _param0, _param1, _param2)
ret0, _ := ret[0].(error)
return ret0
}
func (_mr *_MockClientRecorder) Send(arg0, arg1, arg2 interface{}) *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "Send", arg0, arg1, arg2)
}
func (_m *MockClient) SendBytes(_param0 string, _param1 []byte, _param2 string) error {
ret := _m.ctrl.Call(_m, "SendBytes", _param0, _param1, _param2)
ret0, _ := ret[0].(error)
return ret0
}
func (_mr *_MockClientRecorder) SendBytes(arg0, arg1, arg2 interface{}) *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "SendBytes", arg0, arg1, arg2)
}
func (_m *MockClient) SetWSConnectionFactory(_param0 WSConnectionFactory) {
_m.ctrl.Call(_m, "SetWSConnectionFactory", _param0)
}
func (_mr *_MockClientRecorder) SetWSConnectionFactory(arg0 interface{}) *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "SetWSConnectionFactory", arg0)
}
func (_m *MockClient) Start() error {
ret := _m.ctrl.Call(_m, "Start")
ret0, _ := ret[0].(error)
return ret0
}
func (_mr *_MockClientRecorder) Start() *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "Start")
}
func (_m *MockClient) StatusMessages() chan *protocol.NotificationMessage {
ret := _m.ctrl.Call(_m, "StatusMessages")
ret0, _ := ret[0].(chan *protocol.NotificationMessage)
return ret0
}
func (_mr *_MockClientRecorder) StatusMessages() *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "StatusMessages")
}
func (_m *MockClient) Subscribe(_param0 string) error {
ret := _m.ctrl.Call(_m, "Subscribe", _param0)
ret0, _ := ret[0].(error)
return ret0
}
func (_mr *_MockClientRecorder) Subscribe(arg0 interface{}) *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "Subscribe", arg0)
}
func (_m *MockClient) Unsubscribe(_param0 string) error {
ret := _m.ctrl.Call(_m, "Unsubscribe", _param0)
ret0, _ := ret[0].(error)
return ret0
}
func (_mr *_MockClientRecorder) Unsubscribe(arg0 interface{}) *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "Unsubscribe", arg0)
}
func (_m *MockClient) WriteRawMessage(_param0 []byte) error {
ret := _m.ctrl.Call(_m, "WriteRawMessage", _param0)
ret0, _ := ret[0].(error)
return ret0
}
func (_mr *_MockClientRecorder) WriteRawMessage(arg0 interface{}) *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteRawMessage", arg0)
}
================================================
FILE: guble-cli/README.md
================================================
# The guble command line client
This is the command line client for the guble messaging server. It is intended
for demonstration and debugging use.
[](https://travis-ci.org/smancke/guble)
## Starting the client with docker
The guble docker image has the command line client included. You can execute it within a running golang container and
connect to the server.
```
docker run -d --name guble smancke/guble
docker exec -it guble /go/bin/guble-cli
```
## Building from source
```
go get github.com/smancke/guble/guble-cli
bin/guble-cli
```
## Start options
```
usage: guble-cli [--exit] [--verbose] [--url URL] [--user USER] [--log-info] [--log-debug] [COMMANDS [COMMANDS ...]]
positional arguments:
commands
options:
--exit, -x Exit after sending the commands
--verbose, -v Display verbose server communication
--url URL The websocket url to connect (ws://localhost:8080/stream/)
--user USER The user name to connect with (guble-cli)
--log-info Log on INFO level (false)
--log-debug Log on DEBUG level (false)
```
## Commands in the client
In the running client, you can use the commands from the websocket api, e.g:
```
? # prints some usage info
+ /foo/bar # subscribe to the topic /foo/bar
+ /foo 0 # read from message 0 and subscribe to the topic /foo
+ /foo 0 5 # read messages 0-5 from /foo
+ /foo -5 # read the last 5 messages and subscribe to the topic /foo
- /foo # cancel the subscription for /foo
> /foo # send a message to /foo
> /foo/bar 42 # send a message to /foo/bar with publisherid 42
```
================================================
FILE: guble-cli/main.go
================================================
package main
import (
"bufio"
"fmt"
"os"
"os/signal"
"strings"
"syscall"
log "github.com/Sirupsen/logrus"
"github.com/smancke/guble/client"
"github.com/smancke/guble/protocol"
"gopkg.in/alecthomas/kingpin.v2"
)
var (
exit = kingpin.Flag("exit", "Exit after sending the commands").Short('x').Bool()
commands = kingpin.Arg("commands", "The commands to send after startup").Strings()
verbose = kingpin.Flag("verbose", "Display verbose server communication").Short('v').Bool()
url = kingpin.Flag("url", "The websocket url to connect to").Default("ws://localhost:8080/stream/").String()
user = kingpin.Flag("user", "The user name to connect with (guble-cli)").Short('u').Default("guble-cli").String()
logLevel = kingpin.Flag("log", "Log level").
Short('l').
Default(log.ErrorLevel.String()).
Envar("GUBLE_LOG").
Enum(logLevels()...)
logger = log.WithField("app", "guble-cli")
)
func logLevels() (levels []string) {
for _, level := range log.AllLevels {
levels = append(levels, level.String())
}
return
}
// This is a minimal commandline client to connect through a websocket
func main() {
kingpin.Parse()
// set log level
level, err := log.ParseLevel(*logLevel)
if err != nil {
logger.WithField("error", err).Fatal("Invalid log level")
}
log.SetLevel(level)
origin := "http://localhost/"
url := fmt.Sprintf("%v/user/%v", removeTrailingSlash(*url), *user)
client, err := client.Open(url, origin, 100, true)
if err != nil {
log.Fatal(err)
}
go writeLoop(client)
go readLoop(client)
for _, cmd := range *commands {
client.WriteRawMessage([]byte(cmd))
}
if *exit {
return
}
waitForTermination(func() {})
}
func readLoop(client client.Client) {
for {
select {
case incomingMessage := <-client.Messages():
if *verbose {
fmt.Println(string(incomingMessage.Bytes()))
} else {
fmt.Printf("%v: %v\n", incomingMessage.UserID, incomingMessage.BodyAsString())
}
case e := <-client.Errors():
fmt.Println("ERROR: " + string(e.Bytes()))
case status := <-client.StatusMessages():
fmt.Println(string(status.Bytes()))
fmt.Println()
}
}
}
func writeLoop(client client.Client) {
shouldStop := false
for !shouldStop {
func() {
defer protocol.PanicLogger()
reader := bufio.NewReader(os.Stdin)
text, err := reader.ReadString('\n')
if err != nil {
return
}
if strings.TrimSpace(text) == "" {
return
}
if strings.TrimSpace(text) == "?" || strings.TrimSpace(text) == "help" {
printHelp()
return
}
if strings.HasPrefix(text, ">") {
fmt.Print("header: ")
header, err := reader.ReadString('\n')
if err != nil {
return
}
text += header
fmt.Print("body: ")
body, err := reader.ReadString('\n')
if err != nil {
return
}
text += strings.TrimSpace(body)
}
if *verbose {
log.Printf("Sending: %v\n", text)
}
if err := client.WriteRawMessage([]byte(text)); err != nil {
shouldStop = true
logger.WithError(err).Error("Error on Writing message")
}
}()
}
}
func waitForTermination(callback func()) {
sigc := make(chan os.Signal)
signal.Notify(sigc, syscall.SIGINT, syscall.SIGTERM)
log.Printf("%q", <-sigc)
callback()
os.Exit(0)
}
func printHelp() {
fmt.Println(`
## Commands
? # print this info
+ /foo/bar # subscribe to the topic /foo/bar
+ /foo 0 # read from message 0 and subscribe to the topic /foo
+ /foo 0 5 # read messages 0-5 from /foo
+ /foo -5 # read the last 5 messages and subscribe to the topic /foo
- /foo # cancel the subscription for /foo
> /foo # send a message to /foo
> /foo/bar 42 # send a message to /foo/bar with publisherid 42
`)
}
func removeTrailingSlash(path string) string {
if len(path) > 1 && path[len(path)-1] == '/' {
return path[:len(path)-1]
}
return path
}
================================================
FILE: guble-cli/main_test.go
================================================
package main
import (
"fmt"
"github.com/stretchr/testify/assert"
"io/ioutil"
"os"
"testing"
)
func Test_PrintHelp(t *testing.T) {
expectedHelpMessage := `
## Commands
? # print this info
+ /foo/bar # subscribe to the topic /foo/bar
+ /foo 0 # read from message 0 and subscribe to the topic /foo
+ /foo 0 5 # read messages 0-5 from /foo
+ /foo -5 # read the last 5 messages and subscribe to the topic /foo
- /foo # cancel the subscription for /foo
> /foo # send a message to /foo
> /foo/bar 42 # send a message to /foo/bar with publisherid 42
` + "\n"
rescueStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
printHelp()
w.Close()
out, _ := ioutil.ReadAll(r)
os.Stdout = rescueStdout
resultMessage := fmt.Sprintf("%s", out)
assert.Equal(t, expectedHelpMessage, resultMessage)
}
func Test_removeTrailingSlash(t *testing.T) {
cases := []struct {
expected, path string
}{
{"/foo/user/marvin", "/foo/user/marvin"},
{"/foo/user/marvin", "/foo/user/marvin/"},
{"/", "/"},
}
for i, c := range cases {
assert.Equal(t, c.expected, removeTrailingSlash(c.path), fmt.Sprintf("Failed at case no=%d", i))
}
}
================================================
FILE: logformatter/logstash_formatter.go
================================================
package logformatter
import (
"encoding/json"
"fmt"
"github.com/Sirupsen/logrus"
"os"
"time"
)
const (
defaultServiceName = "guble"
defaultLogType = "application"
defaultApplicationType = "service"
)
// LogstashFormatter generates json in logstash format.
// Logstash site: http://logstash.net/
type LogstashFormatter struct {
//Type of the fields
Type string // if not empty use for logstash type field.
//Env is the environment on which the application is running
Env string
//ServiceName will be by default guble
ServiceName string
//ApplicationType will be by default "service". Other values could be "service", "system", "appserver", "webserver"
ApplicationType string
//LogType will be by default application. Other possible values "access", "error", "application", "system"
LogType string
//TimestampFormat sets the format used for timestamps.
TimestampFormat string
}
// Format the logrus entry to a byte slice, or return an error.
func (f *LogstashFormatter) Format(entry *logrus.Entry) ([]byte, error) {
fields := make(logrus.Fields)
for k, v := range entry.Data {
switch v := v.(type) {
case error:
// Otherwise errors are ignored by `encoding/json`
// https://github.com/Sirupsen/logrus/issues/137
// https://github.com/sirupsen/logrus/issues/377
fields[k] = v.Error()
default:
fields[k] = v
}
}
if f.Env != "" {
fields["environment"] = f.Env
}
timeStampFormat := f.TimestampFormat
if timeStampFormat == "" {
timeStampFormat = time.RFC3339
}
fields["@timestamp"] = entry.Time.Format(timeStampFormat)
if f.ServiceName != "" {
fields["service"] = f.ServiceName
} else {
fields["service"] = defaultServiceName
}
if f.ApplicationType != "" {
fields["application_type"] = f.ServiceName
} else {
fields["application_type"] = defaultApplicationType
}
if f.LogType != "" {
fields["log_type"] = f.LogType
} else {
fields["log_type"] = defaultLogType
}
// set level field, prefixing fields clashes
if v, ok := entry.Data["loglevel"]; ok {
fields["fields.loglevel"] = v
}
fields["loglevel"] = entry.Level.String()
//set host field, prefixing fields clashes
if v, ok := entry.Data["host"]; ok {
fields["fields.host"] = v
}
if hostname, err := os.Hostname(); err == nil {
fields["host"] = hostname
}
// set type field
if f.Type != "" {
if v, ok := entry.Data["type"]; ok {
fields["fields.type"] = v
}
fields["type"] = f.Type
}
// set message field, prefixing fields clashes
if v, ok := entry.Data["msg"]; ok {
fields["fields.msg"] = v
}
fields["msg"] = entry.Message
serialized, err := json.Marshal(fields)
if err != nil {
return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
}
return append(serialized, '\n'), nil
}
================================================
FILE: logformatter/logstash_formatter_test.go
================================================
package logformatter
import (
"bytes"
"encoding/json"
"testing"
"github.com/Sirupsen/logrus"
"github.com/stretchr/testify/assert"
)
func TestLogstashFormatter_Format(t *testing.T) {
a := assert.New(t)
lf := LogstashFormatter{Type: "abc", ServiceName: "guble", Env: "prod"}
fields := logrus.Fields{
"msg": "def",
"level": "ijk",
"type": "lmn",
"one": 1,
"pi": 3.14,
"bool": true,
}
entry := logrus.WithFields(fields)
entry.Message = "msg"
entry.Level = logrus.InfoLevel
b, _ := lf.Format(entry)
var data map[string]interface{}
dec := json.NewDecoder(bytes.NewReader(b))
dec.UseNumber()
dec.Decode(&data)
// base fields
a.Equal("application", data["log_type"])
a.Equal("service", data["application_type"])
a.Equal("guble", data["service"])
a.Equal("prod", data["environment"])
a.NotEmpty(data["@timestamp"])
a.NotEmpty(data["host"])
a.Equal("abc", data["type"])
a.Equal("msg", data["msg"])
a.Equal("info", data["loglevel"])
// substituted fields
a.Equal("def", data["fields.msg"])
a.Equal("lmn", data["fields.type"])
// formats
a.Equal(json.Number("1"), data["one"])
a.Equal(json.Number("3.14"), data["pi"])
a.Equal(true, data["bool"])
}
================================================
FILE: main.go
================================================
package main
import (
"github.com/smancke/guble/server"
)
func main() {
server.Main()
}
================================================
FILE: protocol/cmd.go
================================================
package protocol
import (
"bytes"
"fmt"
"strings"
)
// Valid command names
const (
CmdSend = ">"
CmdReceive = "+"
CmdCancel = "-"
)
// Cmd is a representation of a command, which the client sends to the server
type Cmd struct {
// The name of the command
Name string
// The argument line, following the commandName
Arg string
// The header line, if the command has one
HeaderJSON string
// The command payload, if the command has such
Body []byte
}
// ParseCmd parses a slice of bytes and return a *Cmd
func ParseCmd(message []byte) (*Cmd, error) {
msg := &Cmd{}
if len(message) == 0 {
return nil, fmt.Errorf("empty command")
}
parts := strings.SplitN(string(message), "\n", 3)
firstLine := strings.SplitN(parts[0], " ", 2)
msg.Name = firstLine[0]
if len(firstLine) > 1 {
msg.Arg = firstLine[1]
}
if len(parts) > 1 {
msg.HeaderJSON = parts[1]
}
if len(parts) > 2 {
msg.Body = []byte(parts[2])
}
return msg, nil
}
// Bytes serializes the the command into a byte slice
func (cmd *Cmd) Bytes() []byte {
buff := &bytes.Buffer{}
buff.WriteString(cmd.Name)
buff.WriteString(" ")
buff.WriteString(cmd.Arg)
if len(cmd.HeaderJSON) > 0 || len(cmd.Body) > 0 {
buff.WriteString("\n")
}
if len(cmd.HeaderJSON) > 0 {
buff.WriteString(cmd.HeaderJSON)
}
if len(cmd.Body) > 0 {
buff.WriteString("\n")
buff.Write(cmd.Body)
}
return buff.Bytes()
}
================================================
FILE: protocol/cmd_test.go
================================================
package protocol
import (
assert "github.com/stretchr/testify/assert"
"testing"
)
var aSendCommand = `> /foo
{"meta": "data"}
Hello World`
var aSubscribeCommand = "+ /foo/bar"
func TestParsingASendCommand(t *testing.T) {
assert := assert.New(t)
cmd, err := ParseCmd([]byte(aSendCommand))
assert.NoError(err)
assert.Equal(CmdSend, cmd.Name)
assert.Equal("/foo", cmd.Arg)
assert.Equal(`{"meta": "data"}`, cmd.HeaderJSON)
assert.Equal("Hello World", string(cmd.Body))
}
func TestSerializeASendCommand(t *testing.T) {
cmd := &Cmd{
Name: CmdSend,
Arg: "/foo",
HeaderJSON: `{"meta": "data"}`,
Body: []byte("Hello World"),
}
assert.Equal(t, aSendCommand, string(cmd.Bytes()))
}
func Test_Cmd_EmptyCommand_Error(t *testing.T) {
assert := assert.New(t)
_, err := ParseCmd([]byte{})
assert.Error(err)
}
func TestParsingASubscribeCommand(t *testing.T) {
assert := assert.New(t)
cmd, err := ParseCmd([]byte(aSubscribeCommand))
assert.NoError(err)
assert.Equal(CmdReceive, cmd.Name)
assert.Equal("/foo/bar", cmd.Arg)
assert.Equal("", cmd.HeaderJSON)
assert.Nil(cmd.Body)
}
func TestSerializeASubscribeCommand(t *testing.T) {
cmd := &Cmd{
Name: CmdReceive,
Arg: "/foo/bar",
}
assert.Equal(t, aSubscribeCommand, string(cmd.Bytes()))
}
================================================
FILE: protocol/log.go
================================================
package protocol
import (
"bytes"
"fmt"
log "github.com/Sirupsen/logrus"
"runtime"
"strings"
)
func PanicLogger() {
if r := recover(); r != nil {
log.Printf("PANIC (%v): %v", identifyLogOrigin(), r)
log.Printf(getStackTraceMessage(fmt.Sprintf("%v", r)))
}
}
func identifyLogOrigin() string {
var name, file string
var line int
var pc [16]uintptr
n := runtime.Callers(3, pc[:])
for _, pc := range pc[:n] {
fn := runtime.FuncForPC(pc)
if fn == nil {
continue
}
file, line = fn.FileLine(pc)
name = fn.Name()
if !strings.HasPrefix(name, "runtime.") {
break
}
}
switch {
case name != "":
return fmt.Sprintf("%v:%v", name, line)
case file != "":
return fmt.Sprintf("%v:%v", file, line)
}
return fmt.Sprintf("pc:%x", pc)
}
func getStackTraceMessage(msg string) string {
var name, file string
var line int
var pc [16]uintptr
n := runtime.Callers(3, pc[:])
buff := &bytes.Buffer{}
buff.WriteString(msg)
buff.WriteString("\n")
for _, pc := range pc[:n] {
fn := runtime.FuncForPC(pc)
if fn == nil {
continue
}
file, line = fn.FileLine(pc)
name = fn.Name()
switch {
case name != "":
buff.WriteString(fmt.Sprintf("! %v:%v\n", name, line))
case file != "":
buff.WriteString(fmt.Sprintf("! %v:%v\n", file, line))
}
}
return string(buff.Bytes())
}
================================================
FILE: protocol/log_test.go
================================================
package protocol
import (
"bytes"
log "github.com/Sirupsen/logrus"
"github.com/stretchr/testify/assert"
"os"
"testing"
)
func Test_log_functions_panic_logger(t *testing.T) {
a := assert.New(t)
w := bytes.NewBuffer([]byte{})
log.SetOutput(w)
defer log.SetOutput(os.Stderr)
raisePanic()
a.Contains(w.String(), "PANIC")
a.Contains(w.String(), "raisePanic")
a.Contains(w.String(), "Don't panic!")
}
func raisePanic() {
defer PanicLogger()
panic("Don't panic!")
}
================================================
FILE: protocol/message.go
================================================
package protocol
import (
"bytes"
"encoding/json"
"fmt"
"strconv"
"strings"
log "github.com/Sirupsen/logrus"
)
// Message is a struct that represents a message in the guble protocol, as the server sends it to the client.
type Message struct {
// The sequenceId of the message, which is given by the
// server an is strictly monotonically increasing at least within a root topic.
ID uint64
// The topic path
Path Path
// The user id of the message sender
UserID string
// The id of the sending application
ApplicationID string
// Filters applied to this message. The message will be sent only to the
// routes that match the filters
Filters map[string]string
// The time of publishing, as Unix Timestamp date
Time int64
// The header line of the message (optional). If set, then it has to be a valid JSON object structure.
HeaderJSON string
// The message payload
Body []byte
// Used in cluster mode to identify a guble node
NodeID uint8
}
type MessageDeliveryCallback func(*Message)
// Metadata returns the first line of a serialized message, without the newline
func (msg *Message) Metadata() string {
buff := &bytes.Buffer{}
msg.writeMetadata(buff)
return string(buff.Bytes())
}
func (msg *Message) String() string {
return fmt.Sprintf("%d", msg.ID)
}
func (msg *Message) BodyAsString() string {
return string(msg.Body)
}
// Bytes serializes the message into a byte slice
func (msg *Message) Bytes() []byte {
buff := &bytes.Buffer{}
msg.writeMetadata(buff)
if len(msg.HeaderJSON) > 0 || len(msg.Body) > 0 {
buff.WriteString("\n")
}
if len(msg.HeaderJSON) > 0 {
buff.WriteString(msg.HeaderJSON)
}
if len(msg.Body) > 0 {
buff.WriteString("\n")
buff.Write(msg.Body)
}
return buff.Bytes()
}
func (msg *Message) writeMetadata(buff *bytes.Buffer) {
buff.WriteString(string(msg.Path))
buff.WriteString(",")
buff.WriteString(strconv.FormatUint(msg.ID, 10))
buff.WriteString(",")
buff.WriteString(msg.UserID)
buff.WriteString(",")
buff.WriteString(msg.ApplicationID)
buff.WriteString(",")
buff.Write(msg.encodeFilters())
buff.WriteString(",")
buff.WriteString(strconv.FormatInt(msg.Time, 10))
buff.WriteString(",")
buff.WriteString(strconv.FormatUint(uint64(msg.NodeID), 10))
}
func (msg *Message) encodeFilters() []byte {
if msg.Filters == nil {
return []byte{}
}
data, err := json.Marshal(msg.Filters)
if err != nil {
log.WithError(err).WithField("filters", msg.Filters).Error("Error encoding filters")
return []byte{}
}
return data
}
func (msg *Message) decodeFilters(data []byte) {
if len(data) == 0 {
return
}
msg.Filters = make(map[string]string)
err := json.Unmarshal(data, &msg.Filters)
if err != nil {
log.WithError(err).WithField("data", string(data)).Error("Error decoding filters")
}
}
func (msg *Message) SetFilter(key, value string) {
if msg.Filters == nil {
msg.Filters = make(map[string]string, 1)
}
msg.Filters[key] = value
}
// Valid constants for the NotificationMessage.Name
const (
SUCCESS_CONNECTED = "connected"
SUCCESS_SEND = "send"
SUCCESS_FETCH_START = "fetch-start"
SUCCESS_FETCH_END = "fetch-end"
SUCCESS_SUBSCRIBED_TO = "subscribed-to"
SUCCESS_CANCELED = "canceled"
ERROR_SUBSCRIBED_TO = "error-subscribed-to"
ERROR_BAD_REQUEST = "error-bad-request"
ERROR_INTERNAL_SERVER = "error-server-internal"
)
// NotificationMessage is a representation of a status messages or error message, sent from the server
type NotificationMessage struct {
// The name of the message
Name string
// The argument line, following the messageName
Arg string
// The optional json data supplied with the message
Json string
// Flag which indicates, if the notification is an error
IsError bool
}
// Bytes serializes the notification message into a byte slice
func (msg *NotificationMessage) Bytes() []byte {
buff := &bytes.Buffer{}
if msg.IsError {
buff.WriteString("!")
} else {
buff.WriteString("#")
}
buff.WriteString(msg.Name)
if len(msg.Arg) > 0 {
buff.WriteString(" ")
buff.WriteString(msg.Arg)
}
if len(msg.Json) > 0 {
buff.WriteString("\n")
buff.WriteString(msg.Json)
}
return buff.Bytes()
}
// Decode decodes a message, sent from the server to the client.
// The decoded messages can have one of the types: *Message or *NotificationMessage
func Decode(message []byte) (interface{}, error) {
if len(message) >= 1 && (message[0] == '#' || message[0] == '!') {
return parseNotificationMessage(message)
}
return ParseMessage(message)
}
func ParseMessage(message []byte) (*Message, error) {
parts := strings.SplitN(string(message), "\n", 3)
if len(message) == 0 {
return nil, fmt.Errorf("empty message")
}
meta := strings.Split(parts[0], ",")
if len(meta) != 7 {
return nil, fmt.Errorf("message metadata has to have 7 fields, but was %v", parts[0])
}
if len(meta[0]) == 0 || meta[0][0] != '/' {
return nil, fmt.Errorf("message has invalid topic, got %v", meta[0])
}
id, err := strconv.ParseUint(meta[1], 10, 0)
if err != nil {
return nil, fmt.Errorf("message metadata to have an integer (message-id) as second field, but was %v", meta[1])
}
publishingTime, err := strconv.ParseInt(meta[5], 10, 64)
if err != nil {
return nil, fmt.Errorf("message metadata to have an integer (publishing time) as sixth field, but was %v", meta[5])
}
nodeID, err := strconv.ParseUint(meta[6], 10, 8)
if err != nil {
return nil, fmt.Errorf("message metadata to have an integer (nodeID) as seventh field, but was %v", meta[6])
}
msg := &Message{
ID: id,
Path: Path(meta[0]),
UserID: meta[2],
ApplicationID: meta[3],
Time: publishingTime,
NodeID: uint8(nodeID),
}
msg.decodeFilters([]byte(meta[4]))
if len(parts) >= 2 {
msg.HeaderJSON = parts[1]
}
if len(parts) == 3 {
msg.Body = []byte(parts[2])
}
return msg, nil
}
func parseNotificationMessage(message []byte) (*NotificationMessage, error) {
msg := &NotificationMessage{}
if len(message) < 2 || (message[0] != '#' && message[0] != '!') {
return nil, fmt.Errorf("message has to start with '#' or '!' and a name, but got '%v'", message)
}
msg.IsError = message[0] == '!'
parts := strings.SplitN(string(message)[1:], "\n", 2)
firstLine := strings.SplitN(parts[0], " ", 2)
msg.Name = firstLine[0]
if len(firstLine) > 1 {
msg.Arg = firstLine[1]
}
if len(parts) > 1 {
msg.Json = parts[1]
}
return msg, nil
}
================================================
FILE: protocol/message_test.go
================================================
package protocol
import (
"strings"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
var aNormalMessage = `/foo/bar,42,user01,phone01,{"user":"user01"},1420110000,1
{"Content-Type": "text/plain", "Correlation-Id": "7sdks723ksgqn"}
Hello World`
var aMinimalMessage = "/,42,,,,1420110000,0"
var aConnectedNotification = `#connected You are connected to the server.
{"ApplicationId": "phone1", "UserId": "user01", "Time": "1420110000"}`
// 2015-01-01T12:00:00+01:00 is equal to 1420110000
var unixTime, _ = time.Parse(time.RFC3339, "2015-01-01T12:00:00+01:00")
func TestParsingANormalMessage(t *testing.T) {
assert := assert.New(t)
msgI, err := Decode([]byte(aNormalMessage))
assert.NoError(err)
assert.IsType(&Message{}, msgI)
msg := msgI.(*Message)
assert.Equal(uint64(42), msg.ID)
assert.Equal(Path("/foo/bar"), msg.Path)
assert.Equal("user01", msg.UserID)
assert.Equal("phone01", msg.ApplicationID)
assert.Equal(map[string]string{"user": "user01"}, msg.Filters)
assert.Equal(unixTime.Unix(), msg.Time)
assert.Equal(uint8(1), msg.NodeID)
assert.Equal(`{"Content-Type": "text/plain", "Correlation-Id": "7sdks723ksgqn"}`, msg.HeaderJSON)
assert.Equal("Hello World", string(msg.Body))
}
func TestSerializeANormalMessage(t *testing.T) {
// given: a message
msg := &Message{
ID: uint64(42),
Path: Path("/foo/bar"),
UserID: "user01",
ApplicationID: "phone01",
Filters: map[string]string{"user": "user01"},
Time: unixTime.Unix(),
NodeID: 1,
HeaderJSON: `{"Content-Type": "text/plain", "Correlation-Id": "7sdks723ksgqn"}`,
Body: []byte("Hello World"),
}
// then: the serialisation is as expected
assert.Equal(t, aNormalMessage, string(msg.Bytes()))
assert.Equal(t, "Hello World", msg.BodyAsString())
// and: the first line is as expected
assert.Equal(t, strings.SplitN(aNormalMessage, "\n", 2)[0], msg.Metadata())
}
func TestSerializeAMinimalMessage(t *testing.T) {
msg := &Message{
ID: uint64(42),
Path: Path("/"),
Time: unixTime.Unix(),
}
assert.Equal(t, aMinimalMessage, string(msg.Bytes()))
}
func TestSerializeAMinimalMessageWithBody(t *testing.T) {
msg := &Message{
ID: uint64(42),
Path: Path("/"),
Time: unixTime.Unix(),
Body: []byte("Hello World"),
}
assert.Equal(t, aMinimalMessage+"\n\nHello World", string(msg.Bytes()))
}
func TestParsingAMinimalMessage(t *testing.T) {
assert := assert.New(t)
msgI, err := Decode([]byte(aMinimalMessage))
assert.NoError(err)
assert.IsType(&Message{}, msgI)
msg := msgI.(*Message)
assert.Equal(uint64(42), msg.ID)
assert.Equal(Path("/"), msg.Path)
assert.Equal("", msg.UserID)
assert.Equal("", msg.ApplicationID)
assert.Nil(msg.Filters)
assert.Equal(unixTime.Unix(), msg.Time)
assert.Equal("", msg.HeaderJSON)
assert.Equal("", string(msg.Body))
}
func TestErrorsOnParsingMessages(t *testing.T) {
assert := assert.New(t)
var err error
_, err = Decode([]byte(""))
assert.Error(err)
// missing meta field
_, err = Decode([]byte("42,/foo/bar,user01,phone1,id123\n{}\nBla"))
assert.Error(err)
// id not an integer
_, err = Decode([]byte("xy42,/foo/bar,user01,phone1,id123,1420110000\n"))
assert.Error(err)
// path is empty
_, err = Decode([]byte("42,,user01,phone1,id123,1420110000\n"))
assert.Error(err)
// Error Message without Name
_, err = Decode([]byte("!"))
assert.Error(err)
}
func TestParsingNotificationMessage(t *testing.T) {
assert := assert.New(t)
msgI, err := Decode([]byte(aConnectedNotification))
assert.NoError(err)
assert.IsType(&NotificationMessage{}, msgI)
msg := msgI.(*NotificationMessage)
assert.Equal(SUCCESS_CONNECTED, msg.Name)
assert.Equal("You are connected to the server.", msg.Arg)
assert.Equal(`{"ApplicationId": "phone1", "UserId": "user01", "Time": "1420110000"}`, msg.Json)
assert.Equal(false, msg.IsError)
}
func TestSerializeANotificationMessage(t *testing.T) {
msg := &NotificationMessage{
Name: SUCCESS_CONNECTED,
Arg: "You are connected to the server.",
Json: `{"ApplicationId": "phone1", "UserId": "user01", "Time": "1420110000"}`,
IsError: false,
}
assert.Equal(t, aConnectedNotification, string(msg.Bytes()))
}
func TestSerializeAnErrorMessage(t *testing.T) {
msg := &NotificationMessage{
Name: ERROR_BAD_REQUEST,
Arg: "you are so bad.",
IsError: true,
}
assert.Equal(t, "!"+ERROR_BAD_REQUEST+" "+"you are so bad.", string(msg.Bytes()))
}
func TestSerializeANotificationMessageWithEmptyArg(t *testing.T) {
msg := &NotificationMessage{
Name: SUCCESS_SEND,
Arg: "",
IsError: false,
}
assert.Equal(t, "#"+SUCCESS_SEND, string(msg.Bytes()))
}
func TestParsingErrorNotificationMessage(t *testing.T) {
assert := assert.New(t)
raw := "!bad-request unknown command 'sdcsd'"
msgI, err := Decode([]byte(raw))
assert.NoError(err)
assert.IsType(&NotificationMessage{}, msgI)
msg := msgI.(*NotificationMessage)
assert.Equal("bad-request", msg.Name)
assert.Equal("unknown command 'sdcsd'", msg.Arg)
assert.Equal("", msg.Json)
assert.Equal(true, msg.IsError)
}
func Test_Message_getPartitionFromTopic(t *testing.T) {
a := assert.New(t)
a.Equal("foo", Path("/foo/bar/bazz").Partition())
a.Equal("foo", Path("/foo").Partition())
a.Equal("", Path("/").Partition())
a.Equal("", Path("").Partition())
}
func TestMessage_Filters(t *testing.T) {
a := assert.New(t)
msg := &Message{}
msg.SetFilter("user", "user01")
msg.SetFilter("device_id", "ID_DEVICE")
a.NotNil(msg.Filters)
a.Equal(msg.Filters["user"], "user01")
a.Equal(msg.Filters["device_id"], "ID_DEVICE")
a.JSONEq(`{"user": "user01","device_id":"ID_DEVICE"}`, string(msg.encodeFilters()))
}
func TestMessage_decodeFilters(t *testing.T) {
a := assert.New(t)
msg := &Message{}
filters := []byte(`{"user": "user01","device_id":"ID_DEVICE"}`)
msg.decodeFilters(filters)
a.NotNil(msg.Filters)
a.Contains(msg.Filters, "user")
a.Contains(msg.Filters, "device_id")
a.Equal(msg.Filters["user"], "user01")
a.Equal(msg.Filters["device_id"], "ID_DEVICE")
}
================================================
FILE: protocol/path.go
================================================
package protocol
import "strings"
// Path is the path of a topic
type Path string
// Partition returns the parsed partition from the path.
func (path Path) Partition() string {
if len(path) > 0 && path[0] == '/' {
path = path[1:]
}
return strings.SplitN(string(path), "/", 2)[0]
}
func (path Path) RemovePrefixSlash() string {
return strings.TrimPrefix(string(path), "/")
}
================================================
FILE: restclient/guble_sender.go
================================================
package restclient
import (
"bytes"
"fmt"
"net/http"
"strings"
"io/ioutil"
"net/url"
log "github.com/Sirupsen/logrus"
)
type gubleSender struct {
Endpoint string
httpClient *http.Client
}
// New returns a new Sender.
func New(endpoint string) Sender {
return &gubleSender{
Endpoint: endpoint,
httpClient: &http.Client{},
}
}
func (gs gubleSender) GetSubscribers(topic string) ([]byte, error) {
logger.WithField("topic", topic).Info("GetSubscribers called")
body := make([]byte, 0)
request, err := http.NewRequest(
http.MethodGet,
fmt.Sprintf("%s/subscribers/%s", gs.Endpoint, trimPrefixSlash(topic)),
bytes.NewReader(body),
)
logger.WithField("url", fmt.Sprintf("%s/subscribers/%s", gs.Endpoint, topic))
if err != nil {
return nil, err
}
response, err := gs.httpClient.Do(request)
if err != nil {
return nil, err
}
defer response.Body.Close()
if response.StatusCode != http.StatusOK {
logger.WithFields(log.Fields{
"header": response.Header,
"code": response.StatusCode,
"status": response.Status,
}).Error("Guble response error")
return nil, fmt.Errorf("Error code returned from guble: %d", response.StatusCode)
}
content, err := ioutil.ReadAll(response.Body)
if err != nil {
return nil, err
}
logger.WithFields(log.Fields{
"header": response.Header,
"code": response.StatusCode,
"body": string(content),
}).Debug("Guble response")
return content, nil
}
func (gs gubleSender) Check() bool {
request, err := http.NewRequest(http.MethodHead, gs.Endpoint, nil)
if err != nil {
logger.WithError(err).Error("error creating request url")
return false
}
response, err := gs.httpClient.Do(request)
if err != nil {
logger.WithError(err).Error("error reaching guble server endpoint")
return false
}
defer response.Body.Close()
return response.StatusCode == http.StatusOK
}
func (gs gubleSender) Send(topic string, body []byte, userID string, params map[string]string) error {
logger.WithFields(log.Fields{
"topic": topic,
"body": body,
"userID": userID,
"params": params,
}).Debug("Sending guble message")
request, err := http.NewRequest(http.MethodPost, getURL(gs.Endpoint, topic, userID, params), bytes.NewReader(body))
if err != nil {
return err
}
response, err := gs.httpClient.Do(request)
if err != nil {
return err
}
defer response.Body.Close()
if response.StatusCode != http.StatusOK {
logger.WithFields(log.Fields{
"header": response.Header,
"code": response.StatusCode,
"status": response.Status,
}).Error("Guble response error")
return fmt.Errorf("Error code returned from guble: %d", response.StatusCode)
}
return nil
}
func getURL(endpoint, topic, userID string, params map[string]string) string {
uv := url.Values{}
uv.Add("userId", userID)
if params != nil {
for k, v := range params {
if k != "" {
uv.Add(k, v)
}
}
}
return fmt.Sprintf("%s/%s?%s", endpoint, topic, uv.Encode())
}
func trimPrefixSlash(topic string) string {
if strings.HasPrefix(topic, "/") {
return strings.TrimPrefix(topic, "/")
}
return topic
}
================================================
FILE: restclient/guble_sender_test.go
================================================
package restclient
import (
"github.com/stretchr/testify/assert"
"net/url"
"testing"
)
func TestGetURL(t *testing.T) {
a := assert.New(t)
testcases := map[string]struct {
endpoint string
topic string
userID string
params map[string]string
// expected result
expected string
}{
"endpoint only, no topic, no user, no params": {
endpoint: "http://localhost:8080/api",
expected: "http://localhost:8080/api/?userId=",
},
"endpoint, valid topic, no user, no params": {
endpoint: "http://localhost:8080/api",
topic: "topic",
expected: "http://localhost:8080/api/topic?userId=",
},
"endpoint, valid topic, valid user, no params": {
endpoint: "http://localhost:8080/api",
topic: "topic",
userID: "user",
expected: "http://localhost:8080/api/topic?userId=user",
},
"endpoint, valid topic, valid user, empty params": {
endpoint: "http://localhost:8080/api",
topic: "topic",
userID: "user",
params: map[string]string{},
expected: "http://localhost:8080/api/topic?userId=user",
},
"endpoint, valid topic, valid user, one valid param": {
endpoint: "http://localhost:8080/api",
topic: "topic",
userID: "user",
params: map[string]string{"filterCriteria1": "value1"},
expected: "http://localhost:8080/api/topic?filterCriteria1=value1&userId=user",
},
"endpoint, valid topic, valid user, more valid params": {
endpoint: "http://localhost:8080/api",
topic: "topic",
userID: "user",
params: map[string]string{
"filterCriteria1": "value1",
"filterCriteria2": "value2",
},
expected: "http://localhost:8080/api/topic?filterCriteria1=value1&filterCriteria2=value2&userId=user",
},
"endpoint, valid topic, valid user, one param value invalid inside URL": {
endpoint: "http://localhost:8080/api",
topic: "topic",
userID: "user",
params: map[string]string{"filterCriteria1": "?"},
expected: "http://localhost:8080/api/topic?filterCriteria1=%3F&userId=user",
},
"endpoint, valid topic, valid user, one param key empty": {
endpoint: "http://localhost:8080/api",
topic: "topic",
userID: "user",
params: map[string]string{"": "value"},
expected: "http://localhost:8080/api/topic?userId=user",
},
}
var err error
for name, c := range testcases {
_, err = url.Parse(c.expected)
a.NoError(err)
a.Equal(c.expected,
getURL(c.endpoint, c.topic, c.userID, c.params),
"Failed check for case: "+name)
}
}
================================================
FILE: restclient/logger.go
================================================
package restclient
import (
log "github.com/Sirupsen/logrus"
)
var logger = log.WithFields(log.Fields{
"module": "restclient",
})
================================================
FILE: restclient/sender.go
================================================
package restclient
// Sender is an interface used to send a message to the guble server.
type Sender interface {
// Send a a message(body) to the guble Server, to the given topic, with the given userID.
Send(topic string, body []byte, userID string, params map[string]string) error
// Check returns `true` if the guble server endpoint is reachable, or `false` otherwise.
Check() bool
// GetSubscribers returns a binary encoded JSON of all subscribers of 'topic' or an error otherwise
GetSubscribers(topic string) ([]byte, error)
}
================================================
FILE: scripts/Dockerfile-cluster
================================================
# this Dockerfile requires u to build the app locally with the name `guble`
FROM phusion/baseimage
RUN mkdir -p /var/lib/guble
COPY guble /go/bin/app
EXPOSE 10000 8080
VOLUME /var/lib/guble
ENTRYPOINT ['/go/bin/app']
================================================
FILE: scripts/compose.cluster.test.yml
================================================
version: '2'
services:
cluster_1:
build:
context: ..
dockerfile: scripts/Dockerfile-cluster
entrypoint:
- /go/bin/app
environment:
- GUBLE_NODE_ID=1
- GUBLE_LOG=debug
- GUBLE_REMOTES=localhost:10000 localhost:10001
ports:
- "8080:8080"
- "10000:10000"
cluster_2:
build:
context: ..
dockerfile: scripts/Dockerfile-cluster
entrypoint:
- /go/bin/app
environment:
- GUBLE_NODE_ID=2
- GUBLE_LOG=debug
- GUBLE_REMOTES=localhost:10000 localhost:10001
ports:
- "8080:8080"
- "10001:10000"
================================================
FILE: scripts/compose.postgres.test.yml
================================================
# docker-compose file to run test(s) using dockerized Postgresql
# Start Postgres from root of project with following command:
# sudo docker-compose -f scripts/compose.postgres.test.yml up -d
# Stop Postgres from root of project with following command:
# sudo docker-compose -f scripts/compose.postgres.test.yml down
version: '2'
services:
postgres:
image: postgres:9
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=
- POSTGRES_DB=guble
volumes:
- /tmp/guble_test_postgres:/var/lib/postgresql/data
ports:
- "5432:5432"
================================================
FILE: scripts/cov.sh
================================================
#!/bin/bash -e
# Run from parent directory via:
# ./scripts/cov.sh
# Requires installation of:
# go get golang.org/x/tools/cmd/cover
# go get github.com/wadey/gocovmerge
source scripts/generate_coverage.sh
# If we have an arg, assume travis run and push to coveralls. Otherwise launch browser results
go tool cover -html=full_cov.out
rm -f full_cov.out
================================================
FILE: scripts/dependencies_graph.sh
================================================
#!/bin/bash -e
# Requires installation of package: graphviz
(echo "digraph G {"
go list -f '{{range .Imports}}{{printf "\t%q -> %q;\n" $.ImportPath .}}{{end}}' $(go list -f '{{join .Deps " "}}' github.com/smancke/guble ) github.com/smancke/guble
echo "}" ) | dot -Tsvg -o dependencies_graph.svg
================================================
FILE: scripts/file-hex.sh
================================================
#!/usr/bin/env bash
xxd -p $1 | tr -d '\n'
================================================
FILE: scripts/generate_coverage.sh
================================================
#!/bin/bash -e
# Requires local installation of: `github.com/wadey/gocovmerge`
cd $GOPATH/src/github.com/smancke/guble
rm -rf ./cov
mkdir cov
i=0
for dir in $(find . -maxdepth 10 -not -path './.git*' -not -path '*/_test.go' -type d);
do
if ls ${dir}/*.go &> /dev/null; then
GO_TEST_DISABLED=true go test -v -covermode=atomic -coverprofile=./cov/$i.out ./${dir}
i=$((i+1))
fi
done
gocovmerge ./cov/*.out > full_cov.out
rm -rf ./cov
================================================
FILE: scripts/generate_mocks.sh
================================================
#!/bin/bash -xe
# Prerequisites: mockgen should be installed
# go get github.com/golang/mock/mockgen
if [ -z "$GOPATH" ]; then
echo "Missing $GOPATH!";
exit 1
fi
# replace in file if last operation was successful
function replace {
FILE=$1; shift;
while [ -n "$1" ]; do
echo "Replacing: $1"
sed -i "s/$1//g" $FILE
shift
done
}
MOCKGEN=$GOPATH/bin/mockgen
# server/service mocks
$MOCKGEN -self_package service -package service \
-destination server/service/mocks_router_gen_test.go \
github.com/smancke/guble/server/router \
Router &
$MOCKGEN -self_package service -package service \
-destination server/service/mocks_checker_gen_test.go \
github.com/docker/distribution/health \
Checker &
# server/router mocks
$MOCKGEN -self_package router -package router \
-destination server/router/mocks_router_gen_test.go \
github.com/smancke/guble/server/router \
Router
replace "server/router/mocks_router_gen_test.go" "router \"github.com\/smancke\/guble\/server\/router\"" "router\."
$MOCKGEN -self_package router -package router \
-destination server/router/mocks_store_gen_test.go \
github.com/smancke/guble/server/store \
MessageStore &
$MOCKGEN -self_package router -package router \
-destination server/router/mocks_kvstore_gen_test.go \
github.com/smancke/guble/server/kvstore \
KVStore &
$MOCKGEN -self_package router -package router \
-destination server/router/mocks_auth_gen_test.go \
github.com/smancke/guble/server/auth \
AccessManager &
$MOCKGEN -self_package router -package router \
-destination server/router/mocks_checker_gen_test.go \
github.com/docker/distribution/health \
Checker &
# client mocks
$MOCKGEN -self_package client -package client \
-destination client/mocks_client_gen_test.go \
github.com/smancke/guble/client \
WSConnection,Client
replace "client/mocks_client_gen_test.go" "client \"github.com\/smancke\/guble\/client\"" "client\."
# server/apns mocks
$MOCKGEN -package apns \
-destination server/apns/mocks_router_gen_test.go \
github.com/smancke/guble/server/router \
Router &
$MOCKGEN -package apns \
-destination server/apns/mocks_kvstore_gen_test.go \
github.com/smancke/guble/server/kvstore \
KVStore &
$MOCKGEN -package apns \
-destination server/apns/mocks_connector_gen_test.go \
github.com/smancke/guble/server/connector \
Sender,Request,Subscriber &
$MOCKGEN -package apns \
-destination server/apns/mocks_pusher_gen_test.go \
github.com/smancke/guble/server/apns \
Pusher &
# server/fcm mocks
$MOCKGEN -package fcm \
-destination server/fcm/mocks_router_gen_test.go \
github.com/smancke/guble/server/router \
Router &
$MOCKGEN -self_package fcm -package fcm \
-destination server/fcm/mocks_kvstore_gen_test.go \
github.com/smancke/guble/server/kvstore \
KVStore &
$MOCKGEN -self_package fcm -package fcm \
-destination server/fcm/mocks_store_gen_test.go \
github.com/smancke/guble/server/store \
MessageStore &
$MOCKGEN -self_package fcm -package fcm \
-destination server/fcm/mocks_gcm_gen_test.go \
github.com/Bogh/gcm \
Sender &
# server mocks
$MOCKGEN -package server \
-destination server/mocks_router_gen_test.go \
github.com/smancke/guble/server/router \
Router &
$MOCKGEN -self_package server -package server \
-destination server/mocks_auth_gen_test.go \
github.com/smancke/guble/server/auth \
AccessManager &
$MOCKGEN -self_package server -package server \
-destination server/mocks_store_gen_test.go \
github.com/smancke/guble/server/store \
MessageStore &
$MOCKGEN -package server \
-destination server/mocks_apns_pusher_gen_test.go \
github.com/smancke/guble/server/apns \
Pusher &
# server/auth mocks
$MOCKGEN -self_package auth -package auth \
-destination server/auth/mocks_auth_gen_test.go \
github.com/smancke/guble/server/auth \
AccessManager
replace "server/auth/mocks_auth_gen_test.go" \
"auth \"github.com\/smancke\/guble\/server\/auth\"" \
"auth\."
# server/connector mocks
$MOCKGEN -self_package connector -package connector \
-destination server/connector/mocks_connector_gen_test.go \
github.com/smancke/guble/server/connector \
Connector,Sender,ResponseHandler,Manager,Queue,Request,Subscriber
replace "server/connector/mocks_connector_gen_test.go" \
"connector \"github.com\/smancke\/guble\/server\/connector\"" \
"connector\."
$MOCKGEN -self_package connector -package connector \
-destination server/connector/mocks_router_gen_test.go \
github.com/smancke/guble/server/router \
Router &
$MOCKGEN -self_package connector -package connector \
-destination server/connector/mocks_kvstore_gen_test.go \
github.com/smancke/guble/server/kvstore \
KVStore &
# server/websocket mocks
$MOCKGEN -self_package websocket -package websocket \
-destination server/websocket/mocks_websocket_gen_test.go \
github.com/smancke/guble/server/websocket \
WSConnection
replace "server/websocket/mocks_websocket_gen_test.go" \
"websocket \"github.com\/smancke\/server\/websocket\"" \
"websocket\."
$MOCKGEN -self_package websocket -package websocket \
-destination server/websocket/mocks_router_gen_test.go \
github.com/smancke/guble/server/router \
Router &
$MOCKGEN -self_package websocket -package websocket \
-destination server/websocket/mocks_store_gen_test.go \
github.com/smancke/guble/server/store \
MessageStore &
$MOCKGEN -self_package websocket -package websocket \
-destination server/websocket/mocks_auth_gen_test.go \
github.com/smancke/guble/server/auth \
AccessManager &
# server/rest Mocks
$MOCKGEN -package rest \
-destination server/rest/mocks_router_gen_test.go \
github.com/smancke/guble/server/router \
Router &
# server/sms Mocks
$MOCKGEN -package sms \
-destination server/sms/mocks_sender_gen_test.go \
github.com/smancke/guble/server/sms \
Sender &
$MOCKGEN -package sms \
-destination server/sms/mocks_router_gen_test.go \
github.com/smancke/guble/server/router \
Router &
$MOCKGEN -self_package router -package sms \
-destination server/sms/mocks_store_gen_test.go \
github.com/smancke/guble/server/store \
MessageStore &
wait
================================================
FILE: server/apns/apns.go
================================================
package apns
import (
"errors"
"fmt"
"github.com/sideshow/apns2"
"github.com/smancke/guble/server/connector"
"github.com/smancke/guble/server/metrics"
"github.com/smancke/guble/server/router"
"time"
)
const (
// schema is the default database schema for APNS
schema = "apns_registration"
)
var (
errSenderNotRecreated = errors.New("APNS Sender could not be recreated.")
)
// Config is used for configuring the APNS module.
type Config struct {
Enabled *bool
Production *bool
CertificateFileName *string
CertificateBytes *[]byte
CertificatePassword *string
AppTopic *string
Workers *int
Prefix *string
IntervalMetrics *bool
}
// apns is the private struct for handling the communication with APNS
type apns struct {
Config
connector.Connector
}
// New creates a new connector.ResponsiveConnector without starting it
func New(router router.Router, sender connector.Sender, config Config) (connector.ResponsiveConnector, error) {
baseConn, err := connector.NewConnector(
router,
sender,
connector.Config{
Name: "apns",
Schema: schema,
Prefix: *config.Prefix,
URLPattern: fmt.Sprintf("/{%s}/{%s}/{%s:.*}", deviceIDKey, userIDKey, connector.TopicParam),
Workers: *config.Workers,
},
)
if err != nil {
logger.WithError(err).Error("Base connector error")
return nil, err
}
a := &apns{
Config: config,
Connector: baseConn,
}
a.SetResponseHandler(a)
return a, nil
}
func (a *apns) Start() error {
err := a.Connector.Start()
if err == nil {
a.startMetrics()
}
return err
}
func (a *apns) startMetrics() {
mTotalSentMessages.Set(0)
mTotalSendErrors.Set(0)
mTotalResponseErrors.Set(0)
mTotalResponseInternalErrors.Set(0)
mTotalResponseRegistrationErrors.Set(0)
mTotalResponseOtherErrors.Set(0)
mTotalSendNetworkErrors.Set(0)
mTotalSendRetryCloseTLS.Set(0)
mTotalSendRetryUnrecoverable.Set(0)
if *a.IntervalMetrics {
a.startIntervalMetric(mMinute, time.Minute)
a.startIntervalMetric(mHour, time.Hour)
a.startIntervalMetric(mDay, time.Hour*24)
}
}
func (a *apns) startIntervalMetric(m metrics.Map, td time.Duration) {
metrics.RegisterInterval(a.Context(), m, td, resetIntervalMetrics, processAndResetIntervalMetrics)
}
func (a *apns) HandleResponse(request connector.Request, responseIface interface{}, metadata *connector.Metadata, errSend error) error {
logger.Info("Handle APNS response")
if errSend != nil {
logger.WithField("error", errSend.Error()).WithField("error_type", errSend).Error("error when trying to send APNS notification")
mTotalSendErrors.Add(1)
if *a.IntervalMetrics && metadata != nil {
addToLatenciesAndCountsMaps(currentTotalErrorsLatenciesKey, currentTotalErrorsKey, metadata.Latency)
}
return errSend
}
r, ok := responseIface.(*apns2.Response)
if !ok {
mTotalResponseErrors.Add(1)
return fmt.Errorf("Response could not be converted to an APNS Response")
}
messageID := request.Message().ID
subscriber := request.Subscriber()
subscriber.SetLastID(messageID)
if err := a.Manager().Update(subscriber); err != nil {
logger.WithField("error", err.Error()).Error("Manager could not update subscription")
mTotalResponseInternalErrors.Add(1)
return err
}
if r.Sent() {
logger.WithField("id", r.ApnsID).Info("APNS notification was successfully sent")
mTotalSentMessages.Add(1)
if *a.IntervalMetrics && metadata != nil {
addToLatenciesAndCountsMaps(currentTotalMessagesLatenciesKey, currentTotalMessagesKey, metadata.Latency)
}
return nil
}
logger.Error("APNS notification was not sent")
logger.WithField("id", r.ApnsID).WithField("reason", r.Reason).Info("APNS notification was not sent - details")
switch r.Reason {
case
apns2.ReasonMissingDeviceToken,
apns2.ReasonBadDeviceToken,
apns2.ReasonDeviceTokenNotForTopic,
apns2.ReasonUnregistered:
logger.WithField("id", r.ApnsID).Info("trying to remove subscriber because a relevant error was received from APNS")
mTotalResponseRegistrationErrors.Add(1)
err := a.Manager().Remove(subscriber)
if err != nil {
logger.WithField("id", r.ApnsID).Error("could not remove subscriber")
}
default:
logger.Error("handling other APNS errors")
mTotalResponseOtherErrors.Add(1)
}
return nil
}
================================================
FILE: server/apns/apns_metrics.go
================================================
package apns
import (
"github.com/smancke/guble/server/metrics"
"time"
)
var (
ns = metrics.NS("apns")
mTotalSentMessages = ns.NewInt("total_sent_messages")
mTotalSendErrors = ns.NewInt("total_sent_message_errors")
mTotalResponseErrors = ns.NewInt("total_response_errors")
mTotalResponseInternalErrors = ns.NewInt("total_response_internal_errors")
mTotalResponseRegistrationErrors = ns.NewInt("total_response_registration_errors")
mTotalResponseOtherErrors = ns.NewInt("total_response_other_errors")
mTotalSendNetworkErrors = ns.NewInt("total_send_network_errors")
mTotalSendRetryCloseTLS = ns.NewInt("total_send_retry_close_tls")
mTotalSendRetryUnrecoverable = ns.NewInt("total_send_retry_unrecoverable")
mMinute = ns.NewMap("minute")
mHour = ns.NewMap("hour")
mDay = ns.NewMap("day")
)
const (
currentTotalMessagesLatenciesKey = "current_messages_total_latencies_nanos"
currentTotalMessagesKey = "current_messages_count"
currentTotalErrorsLatenciesKey = "current_errors_total_latencies_nanos"
currentTotalErrorsKey = "current_errors_count"
)
func processAndResetIntervalMetrics(m metrics.Map, td time.Duration, t time.Time) {
msgLatenciesValue := m.Get(currentTotalMessagesLatenciesKey)
msgNumberValue := m.Get(currentTotalMessagesKey)
errLatenciesValue := m.Get(currentTotalErrorsLatenciesKey)
errNumberValue := m.Get(currentTotalErrorsKey)
m.Init()
resetIntervalMetrics(m, t)
metrics.SetRate(m, "last_messages_rate_sec", msgNumberValue, td, time.Second)
metrics.SetRate(m, "last_errors_rate_sec", errNumberValue, td, time.Second)
metrics.SetAverage(m, "last_messages_average_latency_msec",
msgLatenciesValue, msgNumberValue, metrics.MilliPerNano, metrics.DefaultAverageLatencyJSONValue)
metrics.SetAverage(m, "last_errors_average_latency_msec",
errLatenciesValue, errNumberValue, metrics.MilliPerNano, metrics.DefaultAverageLatencyJSONValue)
}
func resetIntervalMetrics(m metrics.Map, t time.Time) {
m.Set("current_interval_start", metrics.NewTime(t))
metrics.AddToMaps(currentTotalMessagesLatenciesKey, 0, m)
metrics.AddToMaps(currentTotalMessagesKey, 0, m)
metrics.AddToMaps(currentTotalErrorsLatenciesKey, 0, m)
metrics.AddToMaps(currentTotalErrorsKey, 0, m)
}
func addToLatenciesAndCountsMaps(latenciesKey string, countKey string, latency time.Duration) {
metrics.AddToMaps(latenciesKey, int64(latency), mMinute, mHour, mDay)
metrics.AddToMaps(countKey, 1, mMinute, mHour, mDay)
}
================================================
FILE: server/apns/apns_pusher.go
================================================
package apns
import (
"crypto/tls"
"github.com/sideshow/apns2"
"github.com/sideshow/apns2/certificate"
"golang.org/x/net/http2"
"net"
"net/http"
"sync"
"time"
)
const (
//see https://github.com/sideshow/apns2/issues/24 and https://github.com/sideshow/apns2/issues/20
tlsDialTimeout = 20 * time.Second
httpClientTimeout = 30 * time.Second
)
type Pusher interface {
Push(*apns2.Notification) (*apns2.Response, error)
}
type closable interface {
CloseTLS()
}
func newPusher(c Config) (Pusher, error) {
logger.Info("creating new apns pusher")
var (
cert tls.Certificate
errCert error
)
if c.CertificateFileName != nil && *c.CertificateFileName != "" {
cert, errCert = certificate.FromP12File(*c.CertificateFileName, *c.CertificatePassword)
} else {
cert, errCert = certificate.FromP12Bytes(*c.CertificateBytes, *c.CertificatePassword)
}
if errCert != nil {
return nil, errCert
}
var clientFactory func(certificate tls.Certificate) *apns2Client
if *c.Production {
clientFactory = newProductionClient
} else {
clientFactory = newDevelopmentClient
}
apns2.TLSDialTimeout = tlsDialTimeout
apns2.HTTPClientTimeout = httpClientTimeout
logger.Info("created new apns pusher")
return clientFactory(cert), nil
}
func newProductionClient(certificate tls.Certificate) *apns2Client {
logger.Info("APNS Pusher in Production mode")
c := newApns2Client(certificate)
c.Production()
logger.WithField("apns_url", c.Host).Info("APNS Pusher in Production mode url")
return c
}
func newDevelopmentClient(certificate tls.Certificate) *apns2Client {
logger.Info("APNS Pusher in Development mode")
c := newApns2Client(certificate)
c.Development()
logger.WithField("apns_url", c.Host).Info("APNS Pusher in Development mode url")
return c
}
type apns2Client struct {
*apns2.Client
tlsConn net.Conn
mu sync.Mutex
}
func newApns2Client(certificate tls.Certificate) *apns2Client {
logger.Info("creating new apns2client")
c := &apns2Client{}
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{certificate},
}
if len(certificate.Certificate) > 0 {
tlsConfig.BuildNameToCertificate()
}
transport := &http2.Transport{
TLSClientConfig: tlsConfig,
DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
conn, err := tls.DialWithDialer(&net.Dialer{Timeout: tlsDialTimeout, KeepAlive: 2 * time.Second}, network, addr, cfg)
c.mu.Lock()
defer c.mu.Unlock()
if err == nil {
c.tlsConn = conn
} else {
c.tlsConn = nil
}
return conn, err
},
}
client := &apns2.Client{
HTTPClient: &http.Client{
Transport: transport,
Timeout: httpClientTimeout,
},
Certificate: certificate,
Host: apns2.DefaultHost,
}
c.Client = client
logger.Info("created new apns2client")
return c
}
// interface closable used used by apns_sender
func (c *apns2Client) CloseTLS() {
c.mu.Lock()
defer c.mu.Unlock()
if c.tlsConn != nil {
logger.Info("Trying to close TLS connection")
c.tlsConn.Close()
logger.Info("Closed TLS connection")
c.tlsConn = nil
}
}
================================================
FILE: server/apns/apns_sender.go
================================================
package apns
import (
"errors"
"github.com/jpillora/backoff"
"github.com/sideshow/apns2"
"github.com/smancke/guble/server/connector"
"net"
"time"
)
const (
// deviceIDKey is the key name set on the route params to identify the application
deviceIDKey = "device_token"
userIDKey = "user_id"
)
var (
errPusherInvalidParams = errors.New("Invalid parameters of APNS Pusher")
ErrRetryFailed = errors.New("Retry failed")
)
type sender struct {
client Pusher
appTopic string
}
func NewSender(config Config) (connector.Sender, error) {
pusher, err := newPusher(config)
if err != nil {
logger.WithField("error", err.Error()).Error("APNS Pusher creation error")
return nil, err
}
return NewSenderUsingPusher(pusher, *config.AppTopic)
}
func NewSenderUsingPusher(pusher Pusher, appTopic string) (connector.Sender, error) {
if pusher == nil || appTopic == "" {
return nil, errPusherInvalidParams
}
return &sender{
client: pusher,
appTopic: appTopic,
}, nil
}
func (s sender) Send(request connector.Request) (interface{}, error) {
deviceToken := request.Subscriber().Route().Get(deviceIDKey)
logger.WithField("deviceToken", deviceToken).Info("Trying to push a message to APNS")
push := func() (interface{}, error) {
return s.client.Push(&apns2.Notification{
Priority: apns2.PriorityHigh,
Topic: s.appTopic,
DeviceToken: deviceToken,
Payload: request.Message().Body,
})
}
withRetry := &retryable{
Backoff: backoff.Backoff{
Min: 1 * time.Second,
Max: 10 * time.Second,
Factor: 2,
Jitter: true,
},
maxTries: 3,
}
result, err := withRetry.execute(push)
if err != nil && err == ErrRetryFailed {
if closable, ok := s.client.(closable); ok {
logger.Warn("Close TLS and retry again")
mTotalSendRetryCloseTLS.Add(1)
closable.CloseTLS()
return push()
} else {
mTotalSendRetryUnrecoverable.Add(1)
logger.Error("Cannot Close TLS. Unrecoverable state")
}
}
return result, err
}
type retryable struct {
backoff.Backoff
maxTries int
}
func (r *retryable) execute(op func() (interface{}, error)) (interface{}, error) {
tryCounter := 0
for {
tryCounter++
result, opError := op()
// retry on network errors
if _, ok := opError.(net.Error); ok {
mTotalSendNetworkErrors.Add(1)
if tryCounter >= r.maxTries {
return "", ErrRetryFailed
}
d := r.Duration()
logger.WithField("error", opError.Error()).Warn("Retry in ", d)
time.Sleep(d)
continue
} else {
return result, opError
}
}
}
================================================
FILE: server/apns/apns_sender_test.go
================================================
package apns
import (
"errors"
"github.com/golang/mock/gomock"
"github.com/smancke/guble/protocol"
"github.com/smancke/guble/server/router"
"github.com/smancke/guble/testutil"
"github.com/stretchr/testify/assert"
"testing"
)
func TestNewSender_ErrorBytes(t *testing.T) {
a := assert.New(t)
//given
emptyBytes := []byte("")
emptyPassword := ""
cfg := Config{
CertificateBytes: &emptyBytes,
CertificatePassword: &emptyPassword,
}
//when
pusher, err := NewSender(cfg)
// then
a.Error(err)
a.Nil(pusher)
}
func TestNewSender_ErrorFile(t *testing.T) {
a := assert.New(t)
//given
wrongFilename := "."
emptyPassword := ""
cfg := Config{
CertificateFileName: &wrongFilename,
CertificatePassword: &emptyPassword,
}
//when
pusher, err := NewSender(cfg)
// then
a.Error(err)
a.Nil(pusher)
}
func TestSender_Send(t *testing.T) {
_, finish := testutil.NewMockCtrl(t)
defer finish()
a := assert.New(t)
// given
routeParams := make(map[string]string)
routeParams["device_id"] = "1234"
routeConfig := router.RouteConfig{
Path: protocol.Path("path"),
RouteParams: routeParams,
}
route := router.NewRoute(routeConfig)
msg := &protocol.Message{
Body: []byte("{}"),
}
mSubscriber := NewMockSubscriber(testutil.MockCtrl)
mSubscriber.EXPECT().Route().Return(route).AnyTimes()
mRequest := NewMockRequest(testutil.MockCtrl)
mRequest.EXPECT().Subscriber().Return(mSubscriber).AnyTimes()
mRequest.EXPECT().Message().Return(msg).AnyTimes()
mPusher := NewMockPusher(testutil.MockCtrl)
mPusher.EXPECT().Push(gomock.Any()).Return(nil, nil)
// and
s, err := NewSenderUsingPusher(mPusher, "com.myapp")
a.NoError(err)
// when
rsp, err := s.Send(mRequest)
// then
a.NoError(err)
a.Nil(rsp)
}
func TestSender_Retry(t *testing.T) {
_, finish := testutil.NewMockCtrl(t)
defer finish()
a := assert.New(t)
// given
routeParams := make(map[string]string)
routeParams["device_id"] = "1234"
routeConfig := router.RouteConfig{
Path: protocol.Path("path"),
RouteParams: routeParams,
}
route := router.NewRoute(routeConfig)
msg := &protocol.Message{
Body: []byte("{}"),
}
mSubscriber := NewMockSubscriber(testutil.MockCtrl)
mSubscriber.EXPECT().Route().Return(route).AnyTimes()
mRequest := NewMockRequest(testutil.MockCtrl)
mRequest.EXPECT().Subscriber().Return(mSubscriber).AnyTimes()
mRequest.EXPECT().Message().Return(msg).AnyTimes()
mPusher := NewMockPusher(testutil.MockCtrl)
mPusher.EXPECT().Push(gomock.Any()).Return(nil, errMockTimeout)
mPusher.EXPECT().Push(gomock.Any()).Return(nil, nil)
// and
s, err := NewSenderUsingPusher(mPusher, "com.myapp")
a.NoError(err)
// when
rsp, err := s.Send(mRequest)
// then
a.NoError(err)
a.Nil(rsp)
}
type resultpair struct {
result interface{}
err error
}
func Test_Retriable(t *testing.T) {
a := assert.New(t)
testCases := []struct {
name string
maxTries int
results []resultpair
expectedResult string
expectedError error
expectedMethodCalls int
}{
{"No errors", 3, []resultpair{{result: "0"}}, "0", nil, 1},
{"Retry once", 3, []resultpair{{err: errMockTimeout}, {result: "1"}}, "1", nil, 2},
{"Retry twice", 3, []resultpair{{err: errMockTimeout}, {err: errMockTimeout}, {result: "2"}}, "2", nil, 3},
{"Retry only twice", 3, []resultpair{{err: errMockTimeout}, {err: errMockTimeout}, {err: errMockTimeout, result: ""}, {result: "3"}}, "", ErrRetryFailed, 3},
{"Do not retry", 3, []resultpair{{err: errMockOther, result: ""}, {result: "1"}}, "", errMockOther, 1},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
// given
var counter int
method := func() (interface{}, error) {
elem := tc.results[counter]
counter++
return elem.result, elem.err
}
withRetry := &retryable{
maxTries: tc.maxTries,
}
// when
result, err := withRetry.execute(method)
// then
a.EqualValues(tc.expectedResult, result)
a.EqualValues(tc.expectedError, err)
a.EqualValues(tc.expectedMethodCalls, counter)
})
}
}
// see - net.Error
type mockTimeout struct{}
func (e *mockTimeout) Error() string { return "mock i/o timeout" }
func (e *mockTimeout) Timeout() bool { return true }
func (e *mockTimeout) Temporary() bool { return true }
var errMockTimeout error = &mockTimeout{}
var errMockOther error = errors.New("mock not retriable")
================================================
FILE: server/apns/apns_test.go
================================================
package apns
import (
"errors"
"github.com/golang/mock/gomock"
"github.com/sideshow/apns2"
"github.com/smancke/guble/protocol"
"github.com/smancke/guble/server/connector"
"github.com/smancke/guble/testutil"
"github.com/stretchr/testify/assert"
"testing"
)
var ErrSendRandomError = errors.New("A Sender error")
func TestNew_WithoutKVStore(t *testing.T) {
_, finish := testutil.NewMockCtrl(t)
defer finish()
a := assert.New(t)
//given
mRouter := NewMockRouter(testutil.MockCtrl)
errKVS := errors.New("No KVS was set-up in Router")
mRouter.EXPECT().KVStore().Return(nil, errKVS).AnyTimes()
mSender := NewMockSender(testutil.MockCtrl)
prefix := "/apns/"
workers := 1
cfg := Config{
Prefix: &prefix,
Workers: &workers,
}
//when
c, err := New(mRouter, mSender, cfg)
//then
a.Error(err)
a.Nil(c)
}
func TestConn_HandleResponseOnSendError(t *testing.T) {
_, finish := testutil.NewMockCtrl(t)
defer finish()
a := assert.New(t)
//given
c, _ := newAPNSConnector(t)
mRequest := NewMockRequest(testutil.MockCtrl)
//when
err := c.HandleResponse(mRequest, nil, nil, ErrSendRandomError)
//then
a.Equal(ErrSendRandomError, err)
}
func TestConn_HandleResponse(t *testing.T) {
_, finish := testutil.NewMockCtrl(t)
defer finish()
a := assert.New(t)
//given
c, mKVS := newAPNSConnector(t)
mSubscriber := NewMockSubscriber(testutil.MockCtrl)
mSubscriber.EXPECT().SetLastID(gomock.Any())
mSubscriber.EXPECT().Key().Return("key").AnyTimes()
mSubscriber.EXPECT().Encode().Return([]byte("{}"), nil).AnyTimes()
mKVS.EXPECT().Put(schema, "key", []byte("{}")).Times(2)
c.Manager().Add(mSubscriber)
message := &protocol.Message{
ID: 42,
}
mRequest := NewMockRequest(testutil.MockCtrl)
mRequest.EXPECT().Message().Return(message).AnyTimes()
mRequest.EXPECT().Subscriber().Return(mSubscriber).AnyTimes()
response := &apns2.Response{
ApnsID: "id-life",
StatusCode: 200,
}
//when
err := c.HandleResponse(mRequest, response, nil, nil)
//then
a.NoError(err)
}
func TestNew_HandleResponseHandleSubscriber(t *testing.T) {
_, finish := testutil.NewMockCtrl(t)
defer finish()
a := assert.New(t)
//given
c, mKVS := newAPNSConnector(t)
removeForReasons := []string{
apns2.ReasonMissingDeviceToken,
apns2.ReasonBadDeviceToken,
apns2.ReasonDeviceTokenNotForTopic,
apns2.ReasonUnregistered,
}
for _, reason := range removeForReasons {
message := &protocol.Message{
ID: 42,
}
mSubscriber := NewMockSubscriber(testutil.MockCtrl)
mSubscriber.EXPECT().SetLastID(gomock.Any())
mSubscriber.EXPECT().Cancel()
mSubscriber.EXPECT().Key().Return("key").AnyTimes()
mSubscriber.EXPECT().Encode().Return([]byte("{}"), nil).AnyTimes()
mKVS.EXPECT().Put(schema, "key", []byte("{}")).Times(2)
mKVS.EXPECT().Delete(schema, "key")
c.Manager().Add(mSubscriber)
mRequest := NewMockRequest(testutil.MockCtrl)
mRequest.EXPECT().Message().Return(message).AnyTimes()
mRequest.EXPECT().Subscriber().Return(mSubscriber).AnyTimes()
response := &apns2.Response{
ApnsID: "id-life",
StatusCode: 400,
Reason: reason,
}
//when
err := c.HandleResponse(mRequest, response, nil, nil)
//then
a.NoError(err)
}
}
func TestNew_HandleResponseDoNotHandleSubscriber(t *testing.T) {
_, finish := testutil.NewMockCtrl(t)
defer finish()
a := assert.New(t)
//given
c, mKVS := newAPNSConnector(t)
noActionForReasons := []string{
apns2.ReasonPayloadEmpty,
apns2.ReasonPayloadTooLarge,
apns2.ReasonBadTopic,
apns2.ReasonTopicDisallowed,
apns2.ReasonBadMessageID,
apns2.ReasonBadExpirationDate,
apns2.ReasonBadPriority,
apns2.ReasonDuplicateHeaders,
apns2.ReasonBadCertificateEnvironment,
apns2.ReasonBadCertificate,
apns2.ReasonForbidden,
apns2.ReasonBadPath,
apns2.ReasonMethodNotAllowed,
apns2.ReasonTooManyRequests,
apns2.ReasonIdleTimeout,
apns2.ReasonShutdown,
apns2.ReasonInternalServerError,
apns2.ReasonServiceUnavailable,
apns2.ReasonMissingTopic,
}
for _, reason := range noActionForReasons {
message := &protocol.Message{
ID: 42,
}
mSubscriber := NewMockSubscriber(testutil.MockCtrl)
mSubscriber.EXPECT().SetLastID(gomock.Any())
mSubscriber.EXPECT().Key().Return("key").AnyTimes()
mSubscriber.EXPECT().Encode().Return([]byte("{}"), nil).AnyTimes()
mSubscriber.EXPECT().Cancel()
mKVS.EXPECT().Put(schema, "key", []byte("{}")).Times(2)
mKVS.EXPECT().Delete(schema, "key")
c.Manager().Add(mSubscriber)
mRequest := NewMockRequest(testutil.MockCtrl)
mRequest.EXPECT().Message().Return(message).AnyTimes()
mRequest.EXPECT().Subscriber().Return(mSubscriber).AnyTimes()
response := &apns2.Response{
ApnsID: "id-apns",
StatusCode: 400,
Reason: reason,
}
//when
err := c.HandleResponse(mRequest, response, nil, nil)
//then
a.NoError(err)
c.Manager().Remove(mSubscriber)
}
}
func newAPNSConnector(t *testing.T) (c connector.ResponsiveConnector, mKVS *MockKVStore) {
mKVS = NewMockKVStore(testutil.MockCtrl)
mRouter := NewMockRouter(testutil.MockCtrl)
mRouter.EXPECT().KVStore().Return(mKVS, nil).AnyTimes()
mSender := NewMockSender(testutil.MockCtrl)
prefix := "/apns/"
workers := 1
intervalMetrics := false
password := "test"
bytes := []byte("test")
cfg := Config{
Prefix: &prefix,
Workers: &workers,
IntervalMetrics: &intervalMetrics,
CertificatePassword: &password,
CertificateBytes: &bytes,
}
c, err := New(mRouter, mSender, cfg)
assert.NoError(t, err)
assert.NotNil(t, c)
return
}
================================================
FILE: server/apns/logger.go
================================================
package apns
import (
log "github.com/Sirupsen/logrus"
)
var logger = log.WithField("module", "apns")
================================================
FILE: server/apns/mocks_connector_gen_test.go
================================================
// Automatically generated by MockGen. DO NOT EDIT!
// Source: github.com/smancke/guble/server/connector (interfaces: Sender,Request,Subscriber)
package apns
import (
"context"
"github.com/golang/mock/gomock"
"github.com/smancke/guble/protocol"
"github.com/smancke/guble/server/connector"
"github.com/smancke/guble/server/router"
)
// Mock of Sender interface
type MockSender struct {
ctrl *gomock.Controller
recorder *_MockSenderRecorder
}
// Recorder for MockSender (not exported)
type _MockSenderRecorder struct {
mock *MockSender
}
func NewMockSender(ctrl *gomock.Controller) *MockSender {
mock := &MockSender{ctrl: ctrl}
mock.recorder = &_MockSenderRecorder{mock}
return mock
}
func (_m *MockSender) EXPECT() *_MockSenderRecorder {
return _m.recorder
}
func (_m *MockSender) Send(_param0 connector.Request) (interface{}, error) {
ret := _m.ctrl.Call(_m, "Send", _param0)
ret0, _ := ret[0].(interface{})
ret1, _ := ret[1].(error)
return ret0, ret1
}
func (_mr *_MockSenderRecorder) Send(arg0 interface{}) *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "Send", arg0)
}
// Mock of Request interface
type MockRequest struct {
ctrl *gomock.Controller
recorder *_MockRequestRecorder
}
// Recorder for MockRequest (not exported)
type _MockRequestRecorder struct {
mock *MockRequest
}
func NewMockRequest(ctrl *gomock.Controller) *MockRequest {
mock := &MockRequest{ctrl: ctrl}
mock.recorder = &_MockRequestRecorder{mock}
return mock
}
func (_m *MockRequest) EXPECT() *_MockRequestRecorder {
return _m.recorder
}
func (_m *MockRequest) Message() *protocol.Message {
ret := _m.ctrl.Call(_m, "Message")
ret0, _ := ret[0].(*protocol.Message)
return ret0
}
func (_mr *_MockRequestRecorder) Message() *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "Message")
}
func (_m *MockRequest) Subscriber() connector.Subscriber {
ret := _m.ctrl.Call(_m, "Subscriber")
ret0, _ := ret[0].(connector.Subscriber)
return ret0
}
func (_mr *_MockRequestRecorder) Subscriber() *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "Subscriber")
}
// Mock of Subscriber interface
type MockSubscriber struct {
ctrl *gomock.Controller
recorder *_MockSubscriberRecorder
}
// Recorder for MockSubscriber (not exported)
type _MockSubscriberRecorder struct {
mock *MockSubscriber
}
func NewMockSubscriber(ctrl *gomock.Controller) *MockSubscriber {
mock := &MockSubscriber{ctrl: ctrl}
mock.recorder = &_MockSubscriberRecorder{mock}
return mock
}
func (_m *MockSubscriber) EXPECT() *_MockSubscriberRecorder {
return _m.recorder
}
func (_m *MockSubscriber) Cancel() {
_m.ctrl.Call(_m, "Cancel")
}
func (_mr *_MockSubscriberRecorder) Cancel() *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "Cancel")
}
func (_m *MockSubscriber) Encode() ([]byte, error) {
ret := _m.ctrl.Call(_m, "Encode")
ret0, _ := ret[0].([]byte)
ret1, _ := ret[1].(error)
return ret0, ret1
}
func (_mr *_MockSubscriberRecorder) Encode() *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "Encode")
}
func (_m *MockSubscriber) Filter(_param0 map[string]string) bool {
ret := _m.ctrl.Call(_m, "Filter", _param0)
ret0, _ := ret[0].(bool)
return ret0
}
func (_mr *_MockSubscriberRecorder) Filter(arg0 interface{}) *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "Filter", arg0)
}
func (_m *MockSubscriber) Key() string {
ret := _m.ctrl.Call(_m, "Key")
ret0, _ := ret[0].(string)
return ret0
}
func (_mr *_MockSubscriberRecorder) Key() *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "Key")
}
func (_m *MockSubscriber) Loop(_param0 context.Context, _param1 connector.Queue) error {
ret := _m.ctrl.Call(_m, "Loop", _param0, _param1)
ret0, _ := ret[0].(error)
return ret0
}
func (_mr *_MockSubscriberRecorder) Loop(arg0, arg1 interface{}) *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "Loop", arg0, arg1)
}
func (_m *MockSubscriber) Reset() error {
ret := _m.ctrl.Call(_m, "Reset")
ret0, _ := ret[0].(error)
return ret0
}
func (_mr *_MockSubscriberRecorder) Reset() *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "Reset")
}
func (_m *MockSubscriber) Route() *router.Route {
ret := _m.ctrl.Call(_m, "Route")
ret0, _ := ret[0].(*router.Route)
return ret0
}
func (_mr *_MockSubscriberRecorder) Route() *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "Route")
}
func (_m *MockSubscriber) SetLastID(_param0 uint64) {
_m.ctrl.Call(_m, "SetLastID", _param0)
}
func (_mr *_MockSubscriberRecorder) SetLastID(arg0 interface{}) *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "SetLastID", arg0)
}
================================================
FILE: server/apns/mocks_kvstore_gen_test.go
================================================
// Automatically generated by MockGen. DO NOT EDIT!
// Source: github.com/smancke/guble/server/kvstore (interfaces: KVStore)
package apns
import (
gomock "github.com/golang/mock/gomock"
)
// Mock of KVStore interface
type MockKVStore struct {
ctrl *gomock.Controller
recorder *_MockKVStoreRecorder
}
// Recorder for MockKVStore (not exported)
type _MockKVStoreRecorder struct {
mock *MockKVStore
}
func NewMockKVStore(ctrl *gomock.Controller) *MockKVStore {
mock := &MockKVStore{ctrl: ctrl}
mock.recorder = &_MockKVStoreRecorder{mock}
return mock
}
func (_m *MockKVStore) EXPECT() *_MockKVStoreRecorder {
return _m.recorder
}
func (_m *MockKVStore) Delete(_param0 string, _param1 string) error {
ret := _m.ctrl.Call(_m, "Delete", _param0, _param1)
ret0, _ := ret[0].(error)
return ret0
}
func (_mr *_MockKVStoreRecorder) Delete(arg0, arg1 interface{}) *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "Delete", arg0, arg1)
}
func (_m *MockKVStore) Get(_param0 string, _param1 string) ([]byte, bool, error) {
ret := _m.ctrl.Call(_m, "Get", _param0, _param1)
ret0, _ := ret[0].([]byte)
ret1, _ := ret[1].(bool)
ret2, _ := ret[2].(error)
return ret0, ret1, ret2
}
func (_mr *_MockKVStoreRecorder) Get(arg0, arg1 interface{}) *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "Get", arg0, arg1)
}
func (_m *MockKVStore) Iterate(_param0 string, _param1 string) chan [2]string {
ret := _m.ctrl.Call(_m, "Iterate", _param0, _param1)
ret0, _ := ret[0].(chan [2]string)
return ret0
}
func (_mr *_MockKVStoreRecorder) Iterate(arg0, arg1 interface{}) *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "Iterate", arg0, arg1)
}
func (_m *MockKVStore) IterateKeys(_param0 string, _param1 string) chan string {
ret := _m.ctrl.Call(_m, "IterateKeys", _param0, _param1)
ret0, _ := ret[0].(chan string)
return ret0
}
func (_mr *_MockKVStoreRecorder) IterateKeys(arg0, arg1 interface{}) *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "IterateKeys", arg0, arg1)
}
func (_m *MockKVStore) Put(_param0 string, _param1 string, _param2 []byte) error {
ret := _m.ctrl.Call(_m, "Put", _param0, _param1, _param2)
ret0, _ := ret[0].(error)
return ret0
}
func (_mr *_MockKVStoreRecorder) Put(arg0, arg1, arg2 interface{}) *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "Put", arg0, arg1, arg2)
}
================================================
FILE: server/apns/mocks_pusher_gen_test.go
================================================
// Automatically generated by MockGen. DO NOT EDIT!
// Source: github.com/smancke/guble/server/apns (interfaces: Pusher)
package apns
import (
gomock "github.com/golang/mock/gomock"
apns2 "github.com/sideshow/apns2"
)
// Mock of Pusher interface
type MockPusher struct {
ctrl *gomock.Controller
recorder *_MockPusherRecorder
}
// Recorder for MockPusher (not exported)
type _MockPusherRecorder struct {
mock *MockPusher
}
func NewMockPusher(ctrl *gomock.Controller) *MockPusher {
mock := &MockPusher{ctrl: ctrl}
mock.recorder = &_MockPusherRecorder{mock}
return mock
}
func (_m *MockPusher) EXPECT() *_MockPusherRecorder {
return _m.recorder
}
func (_m *MockPusher) Push(_param0 *apns2.Notification) (*apns2.Response, error) {
ret := _m.ctrl.Call(_m, "Push", _param0)
ret0, _ := ret[0].(*apns2.Response)
ret1, _ := ret[1].(error)
return ret0, ret1
}
func (_mr *_MockPusherRecorder) Push(arg0 interface{}) *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "Push", arg0)
}
================================================
FILE: server/apns/mocks_router_gen_test.go
================================================
// Automatically generated by MockGen. DO NOT EDIT!
// Source: github.com/smancke/guble/server/router (interfaces: Router)
package apns
import (
"github.com/golang/mock/gomock"
"github.com/smancke/guble/protocol"
"github.com/smancke/guble/server/auth"
"github.com/smancke/guble/server/cluster"
"github.com/smancke/guble/server/kvstore"
"github.com/smancke/guble/server/router"
"github.com/smancke/guble/server/store"
)
// Mock of Router interface
type MockRouter struct {
ctrl *gomock.Controller
recorder *_MockRouterRecorder
}
// Recorder for MockRouter (not exported)
type _MockRouterRecorder struct {
mock *MockRouter
}
func NewMockRouter(ctrl *gomock.Controller) *MockRouter {
mock := &MockRouter{ctrl: ctrl}
mock.recorder = &_MockRouterRecorder{mock}
return mock
}
func (_m *MockRouter) EXPECT() *_MockRouterRecorder {
return _m.recorder
}
func (_m *MockRouter) AccessManager() (auth.AccessManager, error) {
ret := _m.ctrl.Call(_m, "AccessManager")
ret0, _ := ret[0].(auth.AccessManager)
ret1, _ := ret[1].(error)
return ret0, ret1
}
func (_mr *_MockRouterRecorder) AccessManager() *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "AccessManager")
}
func (_m *MockRouter) Cluster() *cluster.Cluster {
ret := _m.ctrl.Call(_m, "Cluster")
ret0, _ := ret[0].(*cluster.Cluster)
return ret0
}
func (_mr *_MockRouterRecorder) Cluster() *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "Cluster")
}
func (_m *MockRouter) Done() <-chan bool {
ret := _m.ctrl.Call(_m, "Done")
ret0, _ := ret[0].(<-chan bool)
return ret0
}
func (_mr *_MockRouterRecorder) Done() *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "Done")
}
func (_m *MockRouter) Fetch(_param0 *store.FetchRequest) error {
ret := _m.ctrl.Call(_m, "Fetch", _param0)
ret0, _ := ret[0].(error)
return ret0
}
func (_mr *_MockRouterRecorder) Fetch(arg0 interface{}) *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "Fetch", arg0)
}
func (_m *MockRouter) GetSubscribers(_param0 string) ([]byte, error) {
ret := _m.ctrl.Call(_m, "GetSubscribers", _param0)
ret0, _ := ret[0].([]byte)
ret1, _ := ret[1].(error)
return ret0, ret1
}
func (_mr *_MockRouterRecorder) GetSubscribers(arg0 interface{}) *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "GetSubscribers", arg0)
}
func (_m *MockRouter) HandleMessage(_param0 *protocol.Message) error {
ret := _m.ctrl.Call(_m, "HandleMessage", _param0)
ret0, _ := ret[0].(error)
return ret0
}
func (_mr *_MockRouterRecorder) HandleMessage(arg0 interface{}) *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "HandleMessage", arg0)
}
func (_m *MockRouter) KVStore() (kvstore.KVStore, error) {
ret := _m.ctrl.Call(_m, "KVStore")
ret0, _ := ret[0].(kvstore.KVStore)
ret1, _ := ret[1].(error)
return ret0, ret1
}
func (_mr *_MockRouterRecorder) KVStore() *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "KVStore")
}
func (_m *MockRouter) MessageStore() (store.MessageStore, error) {
ret := _m.ctrl.Call(_m, "MessageStore")
ret0, _ := ret[0].(store.MessageStore)
ret1, _ := ret[1].(error)
return ret0, ret1
}
func (_mr *_MockRouterRecorder) MessageStore() *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "MessageStore")
}
func (_m *MockRouter) Subscribe(_param0 *router.Route) (*router.Route, error) {
ret := _m.ctrl.Call(_m, "Subscribe", _param0)
ret0, _ := ret[0].(*router.Route)
ret1, _ := ret[1].(error)
return ret0, ret1
}
func (_mr *_MockRouterRecorder) Subscribe(arg0 interface{}) *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "Subscribe", arg0)
}
func (_m *MockRouter) Unsubscribe(_param0 *router.Route) {
_m.ctrl.Call(_m, "Unsubscribe", _param0)
}
func (_mr *_MockRouterRecorder) Unsubscribe(arg0 interface{}) *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "Unsubscribe", arg0)
}
================================================
FILE: server/auth/accessmanager.go
================================================
package auth
import (
"github.com/smancke/guble/protocol"
)
// AccessType permission required by the user
type AccessType int
const (
// READ permission
READ AccessType = iota
// WRITE permission
WRITE
)
// AccessManager interface allows to provide a custom authentication mechanism
type AccessManager interface {
IsAllowed(accessType AccessType, userID string, path protocol.Path) bool
}
================================================
FILE: server/auth/accessmanager_test.go
================================================
package auth
import (
"github.com/stretchr/testify/assert"
"net/http"
"net/http/httptest"
"testing"
)
func Test_AllowAllAccessManager(t *testing.T) {
a := assert.New(t)
am := AccessManager(NewAllowAllAccessManager(true))
a.True(am.IsAllowed(READ, "userid", "/path"))
am = AccessManager(NewAllowAllAccessManager(false))
a.False(am.IsAllowed(READ, "userid", "/path"))
}
func Test_RestAccessManagerAllowed(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("true"))
}))
defer ts.Close()
a := assert.New(t)
am := NewRestAccessManager(ts.URL)
a.True(am.IsAllowed(READ, "foo", "/foo"))
a.True(am.IsAllowed(WRITE, "foo", "/foo"))
}
func Test_RestAccessManagerNotAllowed(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("false"))
}))
defer ts.Close()
am := NewRestAccessManager(ts.URL)
a := assert.New(t)
a.False(am.IsAllowed(READ, "user", "/foo"))
}
func Test_RestAccessManagerNotAllowedWithServerNotStarted(t *testing.T) {
ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("false"))
}))
defer ts.Close()
am := NewRestAccessManager(ts.URL)
a := assert.New(t)
a.False(am.IsAllowed(READ, "user", "/foo"))
}
func Test_RestAccessManagerNotAllowedHttpReturningStatusForbidden(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusForbidden)
}))
defer ts.Close()
a := assert.New(t)
am := NewRestAccessManager(ts.URL)
a.False(am.IsAllowed(READ, "foo", "/foo"))
a.False(am.IsAllowed(WRITE, "foo", "/foo"))
}
================================================
FILE: server/auth/allow_all_accessmanager.go
================================================
package auth
import (
"github.com/smancke/guble/protocol"
)
//AllowAllAccessManager is a dummy implementation that grants access for everything.
type AllowAllAccessManager bool
//NewAllowAllAccessManager returns a new AllowAllAccessManager (depending on the passed parameter, always true or always false)
func NewAllowAllAccessManager(allowAll bool) AllowAllAccessManager {
return AllowAllAccessManager(allowAll)
}
//IsAllowed returns always the same value, given at construction time (true or false).
func (am AllowAllAccessManager) IsAllowed(accessType AccessType, userID string, path protocol.Path) bool {
return bool(am)
}
================================================
FILE: server/auth/logger.go
================================================
package auth
import (
log "github.com/Sirupsen/logrus"
)
var logger = log.WithFields(log.Fields{
"module": "accessManager",
})
================================================
FILE: server/auth/mocks_auth_gen_test.go
================================================
// Automatically generated by MockGen. DO NOT EDIT!
// Source: github.com/smancke/guble/server/auth (interfaces: AccessManager)
package auth
import (
"github.com/golang/mock/gomock"
"github.com/smancke/guble/protocol"
)
// Mock of AccessManager interface
type MockAccessManager struct {
ctrl *gomock.Controller
recorder *_MockAccessManagerRecorder
}
// Recorder for MockAccessManager (not exported)
type _MockAccessManagerRecorder struct {
mock *MockAccessManager
}
func NewMockAccessManager(ctrl *gomock.Controller) *MockAccessManager {
mock := &MockAccessManager{ctrl: ctrl}
mock.recorder = &_MockAccessManagerRecorder{mock}
return mock
}
func (_m *MockAccessManager) EXPECT() *_MockAccessManagerRecorder {
return _m.recorder
}
func (_m *MockAccessManager) IsAllowed(_param0 AccessType, _param1 string, _param2 protocol.Path) bool {
ret := _m.ctrl.Call(_m, "IsAllowed", _param0, _param1, _param2)
ret0, _ := ret[0].(bool)
return ret0
}
func (_mr *_MockAccessManagerRecorder) IsAllowed(arg0, arg1, arg2 interface{}) *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "IsAllowed", arg0, arg1, arg2)
}
================================================
FILE: server/auth/rest_accessmanager.go
================================================
package auth
import (
"github.com/smancke/guble/protocol"
log "github.com/Sirupsen/logrus"
"io/ioutil"
"net/http"
"net/url"
)
// RestAccessManager is a url for which the access is allowed or not.
type RestAccessManager string
// NewRestAccessManager returns a new RestAccessManager.
func NewRestAccessManager(url string) RestAccessManager {
return RestAccessManager(url)
}
// IsAllowed is an implementation of the AccessManager interface.
// The boolean result is based on matching between the desired AccessType, the userId and the path.
func (ram RestAccessManager) IsAllowed(accessType AccessType, userId string, path protocol.Path) bool {
u, _ := url.Parse(string(ram))
q := u.Query()
if accessType == READ {
q.Set("type", "read")
} else {
q.Set("type", "write")
}
q.Set("userId", userId)
q.Set("path", string(path))
resp, err := http.DefaultClient.Get(u.String())
if err != nil {
logger.WithError(err).WithField("module", "RestAccessManager").Warn("Write message failed")
return false
}
defer resp.Body.Close()
responseBody, err := ioutil.ReadAll(resp.Body)
if err != nil || resp.StatusCode != 200 {
logger.WithError(err).WithField("httpCode", resp.StatusCode).Info("Error getting permission")
logger.WithField("responseBody", responseBody).Debug("HTTP Response Body")
return false
}
logger.WithFields(log.Fields{
"access_type": accessType,
"userId": userId,
"path": path,
"responseBody": string(responseBody),
}).Debug("Access allowed")
return "true" == string(responseBody)
}
================================================
FILE: server/benchmarking_apns_test.go
================================================
package server
import (
"bytes"
"fmt"
log "github.com/Sirupsen/logrus"
"github.com/golang/mock/gomock"
"github.com/sideshow/apns2"
"github.com/smancke/guble/client"
"github.com/smancke/guble/server/apns"
"github.com/smancke/guble/server/connector"
"github.com/smancke/guble/server/router"
"github.com/smancke/guble/server/websocket"
"github.com/smancke/guble/testutil"
"github.com/stretchr/testify/assert"
"io/ioutil"
"net/http"
"os"
"strings"
"testing"
"time"
)
// APNS benchmarks
func BenchmarkAPNS_1Workers50MilliTimeout(b *testing.B) {
params := &benchParams{
B: b,
workers: 1,
subscriptions: 8,
timeout: 50 * time.Millisecond,
clients: 8,
sender: sendMessageSample,
}
params.throughputAPNS()
fmt.Println(params)
}
func BenchmarkAPNS_8Workers50MilliTimeout(b *testing.B) {
params := &benchParams{
B: b,
workers: 8,
subscriptions: 8,
timeout: 50 * time.Millisecond,
clients: 8,
sender: sendMessageSample,
}
params.throughputAPNS()
fmt.Println(params)
}
func BenchmarkAPNS_16Workers50MilliTimeout(b *testing.B) {
params := &benchParams{
B: b,
workers: 16,
subscriptions: 8,
timeout: 50 * time.Millisecond,
clients: 8,
sender: sendMessageSample,
}
params.throughputAPNS()
fmt.Println(params)
}
func BenchmarkAPNS_1Workers100MilliTimeout(b *testing.B) {
params := &benchParams{
B: b,
workers: 1,
subscriptions: 8,
timeout: 100 * time.Millisecond,
clients: 8,
sender: sendMessageSample,
}
params.throughputAPNS()
fmt.Println(params)
}
func BenchmarkAPNS_8Workers100MilliTimeout(b *testing.B) {
params := &benchParams{
B: b,
workers: 8,
subscriptions: 8,
timeout: 100 * time.Millisecond,
clients: 8,
sender: sendMessageSample,
}
params.throughputAPNS()
fmt.Println(params)
}
func BenchmarkAPNS_16Workers100MilliTimeout(b *testing.B) {
params := &benchParams{
B: b,
workers: 16,
subscriptions: 8,
timeout: 100 * time.Millisecond,
clients: 8,
sender: sendMessageSample,
}
params.throughputAPNS()
fmt.Println(params)
}
func (params *benchParams) throughputAPNS() {
defer testutil.EnableDebugForMethod()()
_, finish := testutil.NewMockBenchmarkCtrl(params.B)
defer finish()
defer testutil.ResetDefaultRegistryHealthCheck()
a := assert.New(params)
dir, errTempDir := ioutil.TempDir("", "guble_benchmarking_apns_test")
a.NoError(errTempDir)
*Config.HttpListen = "localhost:0"
*Config.KVS = "memory"
*Config.MS = "file"
*Config.StoragePath = dir
*Config.APNS.Enabled = true
*Config.APNS.AppTopic = "app.topic"
*Config.APNS.Prefix = "/apns/"
params.receiveC = make(chan bool)
CreateModules = createModulesWebsocketAndMockAPNSPusher(params.receiveC, params.timeout)
params.service = StartService()
var apnsConn connector.ResponsiveConnector
var ok bool
for _, iface := range params.service.ModulesSortedByStartOrder() {
apnsConn, ok = iface.(connector.ResponsiveConnector)
if ok {
break
}
}
if apnsConn == nil {
a.FailNow("There should be a module of type: APNS Connector")
}
urlFormat := fmt.Sprintf("http://%s/apns/apns-%%d/%%d/%%s", params.service.WebServer().GetAddr())
for i := 1; i <= params.subscriptions; i++ {
// create APNS subscription
response, errPost := http.Post(
fmt.Sprintf(urlFormat, i, i, strings.TrimPrefix(testTopic, "/")),
"text/plain",
bytes.NewBufferString(""),
)
a.NoError(errPost)
a.Equal(response.StatusCode, 200)
body, errReadAll := ioutil.ReadAll(response.Body)
a.NoError(errReadAll)
a.Equal("{\"subscribed\":\"/topic\"}", string(body))
}
clients := params.createClients()
// Report allocations also
params.ReportAllocs()
expectedMessagesNumber := params.N * params.clients * params.subscriptions
logger.WithFields(log.Fields{
"expectedMessagesNumber": expectedMessagesNumber,
"b.N": params.N,
}).Info("Expecting messages")
params.wg.Add(expectedMessagesNumber)
// start the receive loop (a select on receiveC and doneC)
params.doneC = make(chan struct{})
params.receiveLoop()
params.ResetTimer()
// send all messages, or fail on any error
for _, cl := range clients {
go func(cl client.Client) {
for i := 0; i < params.N; i++ {
err := params.sender(cl)
if err != nil {
a.FailNow("Message could not be sent")
}
params.sent++
}
}(cl)
}
// wait to receive all messages
params.wg.Wait()
// stop timer after the actual test
params.StopTimer()
close(params.doneC)
a.NoError(params.service.Stop())
params.service = nil
close(params.receiveC)
errRemove := os.RemoveAll(dir)
if errRemove != nil {
logger.WithError(errRemove).WithField("module", "testing").Error("Could not remove directory")
}
}
var createModulesWebsocketAndMockAPNSPusher = func(receiveC chan bool, simulatedLatency time.Duration) func(router router.Router) []interface{} {
return func(router router.Router) []interface{} {
var modules []interface{}
if wsHandler, err := websocket.NewWSHandler(router, "/stream/"); err != nil {
logger.WithError(err).Error("Error loading WSHandler module")
} else {
modules = append(modules, wsHandler)
}
if *Config.APNS.Enabled {
if *Config.APNS.AppTopic == "" {
logger.Panic("The Mobile App Topic (usually the bundle-id) has to be provided when APNS is enabled")
}
// create and use a mock Pusher - introducing a latency per each message
rsp := &apns2.Response{
ApnsID: "apns-id",
StatusCode: 200,
}
mPusher := NewMockPusher(testutil.MockCtrl)
mPusher.EXPECT().Push(gomock.Any()).
Do(func(notif *apns2.Notification) (*apns2.Response, error) {
time.Sleep(simulatedLatency)
receiveC <- true
return nil, nil
}).Return(rsp, nil).AnyTimes()
apnsSender, err := apns.NewSenderUsingPusher(mPusher, *Config.APNS.AppTopic)
if err != nil {
logger.Panic("APNS Sender could not be created")
}
if apnsConn, err := apns.New(router, apnsSender, Config.APNS); err != nil {
logger.WithError(err).Error("Error creating APNS connector")
} else {
modules = append(modules, apnsConn)
}
} else {
logger.Info("APNS: disabled")
}
return modules
}
}
================================================
FILE: server/benchmarking_common_test.go
================================================
package server
import (
"fmt"
"github.com/smancke/guble/client"
"github.com/smancke/guble/server/service"
"github.com/stretchr/testify/assert"
"strconv"
"sync"
"testing"
"time"
)
const (
testTopic = "/topic"
)
type sender func(c client.Client) error
func sendMessageSample(c client.Client) error {
return c.Send(testTopic, "test-body", "{id:id}")
}
type benchParams struct {
*testing.B
workers int // number of workers
subscriptions int // number of subscriptions listening on the topic
timeout time.Duration // timeout response
clients int // number of clients
sender sender // the function that will send the messages
sent int // sent messages
received int // received messages
service *service.Service
receiveC chan bool
doneC chan struct{}
wg sync.WaitGroup
start time.Time
end time.Time
}
func (params *benchParams) createClients() (clients []client.Client) {
wsURL := "ws://" + params.service.WebServer().GetAddr() + "/stream/user/"
for clientID := 0; clientID < params.clients; clientID++ {
location := wsURL + strconv.Itoa(clientID)
c, err := client.Open(location, "http://localhost/", 1000, true)
if err != nil {
assert.FailNow(params, "guble client could not connect to server")
}
clients = append(clients, c)
}
return
}
func (params *benchParams) receiveLoop() {
for i := 0; i <= params.workers; i++ {
go func() {
for {
select {
case <-params.receiveC:
params.received++
logger.WithField("received", params.received).Debug("Received a call")
params.wg.Done()
case <-params.doneC:
return
}
}
}()
}
}
func (params *benchParams) String() string {
return fmt.Sprintf(`
Throughput %.2f messages/second using:
%d workers
%d subscriptions
%s response timeout
%d clients
`, params.messagesPerSecond(), params.workers, params.subscriptions, params.timeout, params.clients)
}
func (params *benchParams) ResetTimer() {
params.start = time.Now()
params.B.ResetTimer()
}
func (params *benchParams) StopTimer() {
params.end = time.Now()
params.B.StopTimer()
}
func (params *benchParams) duration() time.Duration {
return params.end.Sub(params.start)
}
func (params *benchParams) messagesPerSecond() float64 {
return float64(params.received) / params.duration().Seconds()
}
================================================
FILE: server/benchmarking_fcm_test.go
================================================
package server
import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
"os"
"strings"
"testing"
"time"
log "github.com/Sirupsen/logrus"
"github.com/smancke/guble/client"
"github.com/smancke/guble/server/connector"
"github.com/smancke/guble/server/fcm"
"github.com/smancke/guble/testutil"
"github.com/stretchr/testify/assert"
)
// FCM benchmarks
// Default number of clients and subscriptions are 8, for tests that do not
// specify this in their name
func BenchmarkFCM_1Workers50MilliTimeout(b *testing.B) {
params := &benchParams{
B: b,
workers: 1,
subscriptions: 8,
timeout: 50 * time.Millisecond,
clients: 8,
sender: sendMessageSample,
}
params.throughputFCM()
fmt.Println(params)
}
func BenchmarkFCM_8Workers50MilliTimeout(b *testing.B) {
params := &benchParams{
B: b,
workers: 8,
subscriptions: 8,
timeout: 50 * time.Millisecond,
clients: 8,
sender: sendMessageSample,
}
params.throughputFCM()
fmt.Println(params)
}
func BenchmarkFCM_16Workers50MilliTimeout(b *testing.B) {
params := &benchParams{
B: b,
workers: 16,
subscriptions: 8,
timeout: 50 * time.Millisecond,
clients: 8,
sender: sendMessageSample,
}
params.throughputFCM()
fmt.Println(params)
}
func BenchmarkFCM_1Workers100MilliTimeout(b *testing.B) {
params := &benchParams{
B: b,
workers: 1,
subscriptions: 8,
timeout: 100 * time.Millisecond,
clients: 8,
sender: sendMessageSample,
}
params.throughputFCM()
fmt.Println(params)
}
func BenchmarkFCM_8Workers100MilliTimeout(b *testing.B) {
params := &benchParams{
B: b,
workers: 8,
subscriptions: 8,
timeout: 100 * time.Millisecond,
clients: 8,
sender: sendMessageSample,
}
params.throughputFCM()
fmt.Println(params)
}
func BenchmarkFCM_16Workers100MilliTimeout(b *testing.B) {
params := &benchParams{
B: b,
workers: 16,
subscriptions: 8,
timeout: 100 * time.Millisecond,
clients: 8,
sender: sendMessageSample,
}
params.throughputFCM()
fmt.Println(params)
}
func (params *benchParams) throughputFCM() {
defer testutil.ResetDefaultRegistryHealthCheck()
a := assert.New(params)
dir, errTempDir := ioutil.TempDir("", "guble_benchmarking_fcm_test")
a.NoError(errTempDir)
*Config.HttpListen = "localhost:0"
*Config.KVS = "memory"
*Config.MS = "file"
*Config.StoragePath = dir
*Config.FCM.Enabled = true
*Config.FCM.APIKey = "WILL BE OVERWRITTEN"
*Config.FCM.Workers = params.workers
params.service = StartService()
var fcmConn connector.ResponsiveConnector
var ok bool
for _, iface := range params.service.ModulesSortedByStartOrder() {
fcmConn, ok = iface.(connector.ResponsiveConnector)
if ok {
break
}
}
if fcmConn == nil {
a.FailNow("There should be a module of type: FCM Connector")
}
params.receiveC = make(chan bool)
sender, err := fcm.CreateFcmSender(fcm.SuccessFCMResponse, params.receiveC, params.timeout)
a.NoError(err)
fcmConn.SetSender(sender)
urlFormat := fmt.Sprintf("http://%s/fcm/%%d/gcmId%%d/subscribe/%%s", params.service.WebServer().GetAddr())
for i := 1; i <= params.subscriptions; i++ {
// create FCM subscription
response, errPost := http.Post(
fmt.Sprintf(urlFormat, i, i, strings.TrimPrefix(testTopic, "/")),
"text/plain",
bytes.NewBufferString(""),
)
a.NoError(errPost)
a.Equal(response.StatusCode, 200)
body, errReadAll := ioutil.ReadAll(response.Body)
a.NoError(errReadAll)
a.Equal("{\"subscribed\":\"/topic\"}", string(body))
}
clients := params.createClients()
// Report allocations also
params.ReportAllocs()
expectedMessagesNumber := params.N * params.clients * params.subscriptions
logger.WithFields(log.Fields{
"expectedMessagesNumber": expectedMessagesNumber,
"N": params.N,
}).Info("Expecting messages")
params.wg.Add(expectedMessagesNumber)
// start the receive loop (a select on receiveC and doneC)
params.doneC = make(chan struct{})
params.receiveLoop()
params.ResetTimer()
// send all messages, or fail on any error
for _, cl := range clients {
go func(cl client.Client) {
for i := 0; i < params.N; i++ {
err := params.sender(cl)
if err != nil {
a.FailNow("Message could not be sent")
}
params.sent++
}
}(cl)
}
// wait to receive all messages
params.wg.Wait()
// stop timer after the actual test
params.StopTimer()
close(params.doneC)
a.NoError(params.service.Stop())
params.service = nil
close(params.receiveC)
errRemove := os.RemoveAll(dir)
if errRemove != nil {
logger.WithError(errRemove).WithField("module", "testing").Error("Could not remove directory")
}
}
================================================
FILE: server/benchmarking_fetch_test.go
================================================
package server
import (
"github.com/smancke/guble/client"
"github.com/smancke/guble/testutil"
"github.com/stretchr/testify/assert"
"fmt"
"io/ioutil"
"os"
"strconv"
"testing"
"time"
)
func Benchmark_E2E_Fetch_HelloWorld_Messages(b *testing.B) {
defer testutil.ResetDefaultRegistryHealthCheck()
a := assert.New(b)
dir, _ := ioutil.TempDir("", "guble_benchmarking_fetch_test")
defer os.RemoveAll(dir)
*Config.HttpListen = "localhost:0"
*Config.KVS = "memory"
*Config.MS = "file"
*Config.StoragePath = dir
service := StartService()
defer service.Stop()
time.Sleep(time.Millisecond * 10)
// fill the topic
location := "ws://" + service.WebServer().GetAddr() + "/stream/user/xy"
c, err := client.Open(location, "http://localhost/", 1000, true)
a.NoError(err)
for i := 1; i <= b.N; i++ {
a.NoError(c.Send("/hello", fmt.Sprintf("Hello %v", i), ""))
select {
case <-c.StatusMessages():
// wait for, but ignore
case <-time.After(time.Millisecond * 100):
a.Fail("timeout on send notification")
return
}
}
start := time.Now()
b.ResetTimer()
c.WriteRawMessage([]byte("+ /hello 0 1000000"))
for i := 1; i <= b.N; i++ {
select {
case msg := <-c.Messages():
a.Equal(fmt.Sprintf("Hello %v", i), msg.BodyAsString())
case e := <-c.Errors():
a.Fail(string(e.Bytes()))
return
case <-time.After(time.Second):
a.Fail("timeout on message: " + strconv.Itoa(i))
return
}
}
b.StopTimer()
end := time.Now()
throughput := float64(b.N) / end.Sub(start).Seconds()
fmt.Printf("\n\tThroughput: %v/sec (%v message in %v)\n", int(throughput), b.N, end.Sub(start))
}
================================================
FILE: server/benchmarking_test.go
================================================
package server
import (
"fmt"
"io/ioutil"
"log"
"os"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/smancke/guble/client"
"github.com/smancke/guble/protocol"
"github.com/smancke/guble/testutil"
)
type testgroup struct {
t *testing.T
groupID int
addr string
done chan bool
messagesToSend int
consumer, publisher client.Client
topic string
}
func newTestgroup(t *testing.T, groupID int, addr string, messagesToSend int) *testgroup {
return &testgroup{
t: t,
groupID: groupID,
addr: addr,
done: make(chan bool),
messagesToSend: messagesToSend,
}
}
func TestThroughput(t *testing.T) {
// TODO: We disabled this test because the receiver implementation of fetching messages
// should be reimplemented according to the new message store
testutil.SkipIfDisabled(t)
testutil.SkipIfShort(t)
defer testutil.ResetDefaultRegistryHealthCheck()
dir, _ := ioutil.TempDir("", "guble_benchmarking_test")
*Config.HttpListen = "localhost:0"
*Config.KVS = "memory"
*Config.MS = "file"
*Config.StoragePath = dir
service := StartService()
testgroupCount := 4
messagesPerGroup := 100
log.Printf("init the %v testgroups", testgroupCount)
testgroups := make([]*testgroup, testgroupCount, testgroupCount)
for i := range testgroups {
testgroups[i] = newTestgroup(t, i, service.WebServer().GetAddr(), messagesPerGroup)
}
// init test
log.Print("init the testgroups")
for i := range testgroups {
testgroups[i].Init()
}
defer func() {
// cleanup tests
log.Print("cleanup the testgroups")
for i := range testgroups {
testgroups[i].Clean()
}
service.Stop()
os.RemoveAll(dir)
}()
// start test
log.Print("start the testgroups")
start := time.Now()
for i := range testgroups {
go testgroups[i].Start()
}
log.Print("wait for finishing")
for i, test := range testgroups {
select {
case successFlag := <-test.done:
if !successFlag {
t.Logf("testgroup %v returned with error", i)
t.FailNow()
return
}
case <-time.After(time.Second * 20):
t.Log("timeout. testgroups not ready before timeout")
t.Fail()
return
}
}
end := time.Now()
totalMessages := testgroupCount * messagesPerGroup
throughput := float64(totalMessages) / end.Sub(start).Seconds()
log.Printf("finished! Throughput: %v/sec (%v message in %v)", int(throughput), totalMessages, end.Sub(start))
time.Sleep(time.Second * 1)
}
func (tg *testgroup) Init() {
tg.topic = fmt.Sprintf("/%v-foo", tg.groupID)
var err error
location := "ws://" + tg.addr + "/stream/user/xy"
//location := "ws://gathermon.mancke.net:8080/stream/"
//location := "ws://127.0.0.1:8080/stream/"
tg.consumer, err = client.Open(location, "http://localhost/", 10, false)
if err != nil {
panic(err)
}
tg.publisher, err = client.Open(location, "http://localhost/", 10, false)
if err != nil {
panic(err)
}
tg.expectStatusMessage(protocol.SUCCESS_CONNECTED, "You are connected to the server.")
tg.consumer.Subscribe(tg.topic)
time.Sleep(time.Millisecond * 1)
//test.expectStatusMessage(protocol.SUCCESS_SUBSCRIBED_TO, test.topic)
}
func (tg *testgroup) expectStatusMessage(name string, arg string) {
select {
case notify := <-tg.consumer.StatusMessages():
assert.Equal(tg.t, name, notify.Name)
assert.Equal(tg.t, arg, notify.Arg)
case <-time.After(time.Second * 1):
tg.t.Logf("[%v] no notification of type %s until timeout", tg.groupID, name)
tg.done <- false
tg.t.Fail()
return
}
}
func (tg *testgroup) Start() {
go func() {
for i := 0; i < tg.messagesToSend; i++ {
body := fmt.Sprintf("Hallo-%d", i)
tg.publisher.Send(tg.topic, body, "")
}
}()
for i := 0; i < tg.messagesToSend; i++ {
body := fmt.Sprintf("Hallo-%d", i)
select {
case msg := <-tg.consumer.Messages():
assert.Equal(tg.t, tg.topic, string(msg.Path))
if !assert.Equal(tg.t, body, msg.BodyAsString()) {
tg.t.FailNow()
tg.done <- false
}
case msg := <-tg.consumer.Errors():
tg.t.Logf("[%v] received error: %v", tg.groupID, msg)
tg.done <- false
tg.t.Fail()
return
case <-time.After(time.Second * 5):
tg.t.Logf("[%v] no message received until timeout, expected message %v", tg.groupID, i)
tg.done <- false
tg.t.Fail()
return
}
}
tg.done <- true
}
func (tg *testgroup) Clean() {
tg.consumer.Close()
tg.publisher.Close()
}
================================================
FILE: server/cluster/cluster.go
================================================
package cluster
import (
"io/ioutil"
"github.com/smancke/guble/protocol"
"github.com/smancke/guble/server/store"
log "github.com/Sirupsen/logrus"
"github.com/hashicorp/memberlist"
"errors"
"fmt"
"net"
"strconv"
)
var (
ErrNodeNotFound = errors.New("Node not found.")
)
// Config is a struct used by the local node when creating and running the guble cluster
type Config struct {
ID uint8
Host string
Port int
Remotes []*net.TCPAddr
HealthScoreThreshold int
}
// router interface specify only the methods we require in cluster from the Router
// router is an interface used for handling messages in cluster.
// It is logically connected to the router.Router interface, by reusing the same func signature.
type router interface {
HandleMessage(message *protocol.Message) error
MessageStore() (store.MessageStore, error)
}
// Cluster is a struct for managing the `local view` of the guble cluster, as seen by a node.
type Cluster struct {
// Pointer to a Config struct, based on which the Cluster node is created and runs.
Config *Config
// Router is used for dispatching messages received by this node.
// Should be set after the node is created with New(), and before Start().
Router router
name string
memberlist *memberlist.Memberlist
broadcasts [][]byte
numJoins int
numLeaves int
numUpdates int
synchronizer *synchronizer
}
//New returns a new instance of the cluster, created using the given Config.
func New(config *Config) (*Cluster, error) {
c := &Cluster{
Config: config,
name: fmt.Sprintf("%d", config.ID),
}
memberlistConfig := memberlist.DefaultLANConfig()
memberlistConfig.Name = c.name
memberlistConfig.BindAddr = config.Host
memberlistConfig.BindPort = config.Port
//TODO Cosmin temporarily disabling any logging from memberlist, we might want to enable it again using logrus?
memberlistConfig.LogOutput = ioutil.Discard
ml, err := memberlist.Create(memberlistConfig)
if err != nil {
logger.WithField("error", err).Error("Error when creating the internal memberlist of the cluster")
return nil, err
}
c.memberlist = ml
memberlistConfig.Delegate = c
memberlistConfig.Conflict = c
memberlistConfig.Events = c
return c, nil
}
// Start the cluster module.
func (cluster *Cluster) Start() error {
logger.WithField("remotes", cluster.Config.Remotes).Debug("Starting Cluster")
if cluster.Router == nil {
errorMessage := "There should be a valid Router already set-up"
logger.Error(errorMessage)
return errors.New(errorMessage)
}
synchronizer, err := newSynchronizer(cluster)
if err != nil {
logger.WithError(err).Error("Error creating cluster synchronizer")
return err
}
cluster.synchronizer = synchronizer
num, err := cluster.memberlist.Join(cluster.remotesAsStrings())
if err != nil {
logger.WithField("error", err).Error("Error when this node wanted to join the cluster")
return err
}
if num == 0 {
errorMessage := "No remote hosts were successfully contacted when this node wanted to join the cluster"
logger.WithField("remotes", cluster.remotesAsStrings()).Error(errorMessage)
return errors.New(errorMessage)
}
logger.Debug("Started Cluster")
return nil
}
// Stop the cluster module.
func (cluster *Cluster) Stop() error {
if cluster.synchronizer != nil {
close(cluster.synchronizer.stopC)
}
return cluster.memberlist.Shutdown()
}
// Check returns a non-nil error if the health status of the cluster (as seen by this node) is not perfect.
func (cluster *Cluster) Check() error {
if healthScore := cluster.memberlist.GetHealthScore(); healthScore > cluster.Config.HealthScoreThreshold {
errorMessage := "Cluster Health Score is not perfect"
logger.WithField("healthScore", healthScore).Error(errorMessage)
return errors.New(errorMessage)
}
return nil
}
// newMessage returns a *message to be used in broadcasting or sending to a node
func (cluster *Cluster) newMessage(t messageType, body []byte) *message {
return &message{
NodeID: cluster.Config.ID,
Type: t,
Body: body,
}
}
func (cluster *Cluster) newEncoderMessage(t messageType, entity encoder) (*message, error) {
body, err := entity.encode()
if err != nil {
return nil, err
}
return cluster.newMessage(t, body), nil
}
// BroadcastString broadcasts a string to all the other nodes in the guble cluster
func (cluster *Cluster) BroadcastString(sMessage *string) error {
logger.WithField("string", sMessage).Debug("BroadcastString")
cMessage := &message{
NodeID: cluster.Config.ID,
Type: mtStringMessage,
Body: []byte(*sMessage),
}
return cluster.broadcastClusterMessage(cMessage)
}
// BroadcastMessage broadcasts a guble-protocol-message to all the other nodes in the guble cluster.
func (cluster *Cluster) BroadcastMessage(pMessage *protocol.Message) error {
logger.WithField("message", pMessage).Debug("BroadcastMessage")
cMessage := &message{
NodeID: cluster.Config.ID,
Type: mtGubleMessage,
Body: pMessage.Bytes(),
}
return cluster.broadcastClusterMessage(cMessage)
}
func (cluster *Cluster) broadcastClusterMessage(cMessage *message) error {
if cMessage == nil {
errorMessage := "Could not broadcast a nil cluster-message"
logger.Error(errorMessage)
return errors.New(errorMessage)
}
cMessageBytes, err := cMessage.encode()
if err != nil {
logger.WithError(err).Error("Could not encode and broadcast cluster-message")
return err
}
for _, node := range cluster.memberlist.Members() {
if cluster.name == node.Name {
continue
}
go cluster.sendToNode(node, cMessageBytes)
}
return nil
}
func (cluster *Cluster) sendToNode(node *memberlist.Node, msgBytes []byte) error {
logger.WithFields(log.Fields{
"node": cluster.Config.ID,
"to": node.Name,
}).Debug("Sending cluster-message to a node")
err := cluster.memberlist.SendToTCP(node, msgBytes)
if err != nil {
logger.WithFields(log.Fields{
"err": err,
"node": node,
}).Error("Error sending cluster-message to a node")
return err
}
return nil
}
func (cluster *Cluster) sendMessageToNode(node *memberlist.Node, cmsg *message) error {
logger.WithField("node", node.Name).Debug("Sending message to a node")
bytes, err := cmsg.encode()
if err != nil {
logger.WithError(err).Error("Could not encode and broadcast cluster-message")
return err
}
if err = cluster.memberlist.SendToTCP(node, bytes); err != nil {
logger.WithField("node", node.Name).WithError(err).Error("Error send message to node")
return err
}
return nil
}
func (cluster *Cluster) sendMessageToNodeID(nodeID uint8, cmsg *message) error {
node := cluster.GetNodeByID(nodeID)
if node == nil {
return ErrNodeNotFound
}
return cluster.sendMessageToNode(node, cmsg)
}
func (cluster *Cluster) GetNodeByID(id uint8) *memberlist.Node {
name := strconv.FormatUint(uint64(id), 10)
for _, node := range cluster.memberlist.Members() {
if node.Name == name {
return node
}
}
return nil
}
func (cluster *Cluster) remotesAsStrings() (strings []string) {
log.WithField("Remotes", cluster.Config.Remotes).Debug("Cluster remotes")
for _, remote := range cluster.Config.Remotes {
strings = append(strings, remote.IP.String()+":"+strconv.Itoa(remote.Port))
}
return
}
================================================
FILE: server/cluster/cluster_benchmarking_test.go
================================================
package cluster
import (
log "github.com/Sirupsen/logrus"
"github.com/hashicorp/memberlist"
"fmt"
"io/ioutil"
"testing"
"time"
)
func BenchmarkMemberListCluster(b *testing.B) {
benchmarkCluster(b, 36, 10*time.Second, 15000)
}
func benchmarkCluster(b *testing.B, num int, timeoutForAllJoins time.Duration, lowestPort int) {
startTime := time.Now()
var nodes []*memberlist.Memberlist
eventC := make(chan memberlist.NodeEvent, num)
addr := "127.0.0.1"
var firstMemberName string
for i := 0; i < num; i++ {
c := memberlist.DefaultLANConfig()
port := lowestPort + i
c.Name = fmt.Sprintf("%s:%d", addr, port)
c.BindAddr = addr
c.BindPort = port
c.ProbeInterval = 20 * time.Millisecond
c.ProbeTimeout = 100 * time.Millisecond
c.GossipInterval = 20 * time.Millisecond
c.PushPullInterval = 200 * time.Millisecond
c.LogOutput = ioutil.Discard
if i == 0 {
c.Events = &memberlist.ChannelEventDelegate{eventC}
firstMemberName = c.Name
}
newMember, err := memberlist.Create(c)
if err != nil {
log.WithField("error", err).Fatal("Unexpected error when creating the memberlist")
}
nodes = append(nodes, newMember)
defer newMember.Shutdown()
if i > 0 {
numContacted, err := newMember.Join([]string{firstMemberName})
if numContacted == 0 || err != nil {
log.WithField("error", err).Fatal("Unexpected fatal error when node wanted to join the cluster")
}
}
}
if convergence(nodes, num, eventC, timeoutForAllJoins) {
endTime := time.Now()
log.WithField("durationSeconds", endTime.Sub(startTime).Seconds()).Info("Cluster convergence reached")
}
b.StartTimer()
sendMessagesInCluster(nodes, b.N)
b.StopTimer()
}
func convergence(nodes []*memberlist.Memberlist, num int, eventC chan memberlist.NodeEvent, timeoutForAllJoins time.Duration) bool {
breakTimer := time.After(timeoutForAllJoins)
numJoins := 0
WAIT:
for {
select {
case e := <-eventC:
l := log.WithFields(log.Fields{
"node": *e.Node,
"numJoins": numJoins,
"numMembers": nodes[0].NumMembers(),
})
if e.Event == memberlist.NodeJoin {
l.Info("Node join")
numJoins++
if numJoins == num {
l.Info("All nodes joined")
break WAIT
}
} else {
l.Info("Node leave")
}
case <-breakTimer:
break WAIT
}
}
if numJoins != num {
log.WithFields(log.Fields{
"joinCounter": numJoins,
"num": num,
}).Error("Timeout before completing all joins")
}
convergence := false
for !convergence {
convergence = true
for idx, node := range nodes {
numSeenByNode := node.NumMembers()
if numSeenByNode != num {
log.WithFields(log.Fields{
"index": idx,
"expected": num,
"actual": numSeenByNode,
}).Debug("Wrong number of nodes")
convergence = false
break
}
}
}
return numJoins == num
}
func sendMessagesInCluster(nodes []*memberlist.Memberlist, numMessages int) {
for senderID, node := range nodes {
for receiverID, member := range node.Members() {
for i := 0; i < numMessages; i++ {
message := fmt.Sprintf("Hello from %v to %v !", senderID, receiverID)
log.WithField("message", message).Debug("SendToTCP")
node.SendToTCP(member, []byte(message))
}
}
}
}
================================================
FILE: server/cluster/cluster_conflict.go
================================================
package cluster
import (
log "github.com/Sirupsen/logrus"
"github.com/hashicorp/memberlist"
)
// =============================================================
// memberlist.ConflictDelegate implementation for cluster struct
// =============================================================
func (cluster *Cluster) NotifyConflict(existing, other *memberlist.Node) {
logger.WithFields(log.Fields{
"existing": *existing,
"other": *other,
}).Panic("NotifyConflict")
}
================================================
FILE: server/cluster/cluster_delegate.go
================================================
package cluster
import (
log "github.com/Sirupsen/logrus"
"github.com/smancke/guble/protocol"
)
// ======================================================
// memberslist.Delegate implementation for cluster struct
// ======================================================
// NotifyMsg is invoked each time a message is received by this node of the cluster;
// it decodes and dispatches the messages.
func (cluster *Cluster) NotifyMsg(data []byte) {
logger.WithField("msgAsBytes", data).Debug("NotifyMsg")
cmsg := new(message)
err := cmsg.decode(data)
if err != nil {
logger.WithError(err).Error("Decoding of cluster message failed")
return
}
logger.WithFields(log.Fields{
"senderNodeID": cmsg.NodeID,
"type": cmsg.Type,
}).Debug("NotifyMsg: Received cluster message")
switch cmsg.Type {
case mtGubleMessage:
cluster.handleGubleMessage(cmsg)
case mtSyncPartitions:
cluster.handleSyncPartitions(cmsg)
case mtSyncMessage:
cluster.handleSyncMessage(cmsg)
case mtSyncMessageRequest:
// cluster node is requesting to receive messages for sync
cluster.handleSyncMessageRequest(cmsg)
}
}
func (cluster *Cluster) GetBroadcasts(overhead, limit int) [][]byte {
b := cluster.broadcasts
cluster.broadcasts = nil
return b
}
func (cluster *Cluster) NodeMeta(limit int) []byte { return nil }
func (cluster *Cluster) LocalState(join bool) []byte { return nil }
func (cluster *Cluster) MergeRemoteState(s []byte, join bool) {}
// handles message received with type `mtGubleMessage`
func (cluster *Cluster) handleGubleMessage(cmsg *message) {
if cluster.Router == nil {
return
}
message, err := protocol.ParseMessage(cmsg.Body)
if err != nil {
logger.WithField("err", err).Error("Parsing of guble-message contained in cluster-message failed")
return
}
cluster.Router.HandleMessage(message)
}
// handles message received with type `mtSyncPartitions`
func (cluster *Cluster) handleSyncPartitions(cmsg *message) {
logger.WithField("message", cmsg).Debug("Received sync partitions message")
// Decode message
partitionsSlice := make(partitions, 0)
// Decode data into the new slice
err := partitionsSlice.decode(cmsg.Body)
if err != nil {
logger.WithError(err).Error("Error decoding partitions")
return
}
logger.WithFields(log.Fields{
"partitions": partitionsSlice,
"nodeID": cmsg.NodeID,
}).Debug("Partitions received")
// add to synchronizer
cluster.synchronizer.sync(cmsg.NodeID, partitionsSlice)
}
func (cluster *Cluster) handleSyncMessage(cmsg *message) {
logger.WithField("cmsg", cmsg).Debug("Handling sync message")
err := cluster.synchronizer.syncMessage(cmsg.NodeID, cmsg.Body)
if err != nil {
logger.WithError(err).Error("Error synchronizing messages")
}
}
func (cluster *Cluster) handleSyncMessageRequest(cmsg *message) {
logger.WithField("cmsg", cmsg).Debug("Handling sync message request")
err := cluster.synchronizer.messageRequest(cmsg.NodeID, cmsg.Body)
if err != nil {
logger.WithError(err).Error("Error send synchronization messages")
}
}
================================================
FILE: server/cluster/cluster_event_delegate.go
================================================
package cluster
import (
log "github.com/Sirupsen/logrus"
"github.com/hashicorp/memberlist"
)
// ==========================================================
// memberlist.EventDelegate implementation for cluster struct
// ==========================================================
func (cluster *Cluster) NotifyJoin(node *memberlist.Node) {
cluster.numJoins++
cluster.eventLog(node, "Cluster Node Join")
cluster.sendPartitions(node)
}
func (cluster *Cluster) NotifyLeave(node *memberlist.Node) {
cluster.numLeaves++
cluster.eventLog(node, "Cluster Node Leave")
}
func (cluster *Cluster) NotifyUpdate(node *memberlist.Node) {
cluster.numUpdates++
cluster.eventLog(node, "Cluster Node Update")
}
func (cluster *Cluster) eventLog(node *memberlist.Node, message string) {
logger.WithFields(log.Fields{
"node": node.Name,
"numJoins": cluster.numJoins,
"numLeaves": cluster.numLeaves,
"numUpdates": cluster.numUpdates,
}).Debug(message)
}
func (cluster *Cluster) sendPartitions(node *memberlist.Node) {
if _, inSync := cluster.synchronizer.inSync(node.Name); inSync {
logger.WithField("node", node.Name).Debug("Already in sync with node")
return
}
logger.WithField("node", node.Name).Debug("Sending partitions info")
// Send message partitions to the new node
store, err := cluster.Router.MessageStore()
if err != nil {
logger.WithError(err).Error("Error retriving message store to get partitions")
return
}
partitionsSlice := partitionsFromStore(store)
// sending partitions
data, err := partitionsSlice.encode()
if err != nil {
logger.WithError(err).Error("Error encoding partitions")
return
}
cmsg := cluster.newMessage(mtSyncPartitions, data)
// send message to node
err = cluster.sendMessageToNode(node, cmsg)
if err != nil {
logger.WithField("node", node.Name).WithError(err).Error("Error sending partitions info to node")
}
}
================================================
FILE: server/cluster/cluster_test.go
================================================
package cluster
import (
"io/ioutil"
"github.com/smancke/guble/server/store/filestore"
"github.com/smancke/guble/protocol"
"github.com/smancke/guble/server/store"
"github.com/hashicorp/go-multierror"
"github.com/stretchr/testify/assert"
"errors"
"net"
"testing"
"time"
)
const basePort = 10000
var (
index = 1
)
func testConfig() (config Config) {
remoteAddr := net.TCPAddr{IP: []byte{127, 0, 0, 1}, Port: basePort + index}
var remotes []*net.TCPAddr
remotes = append(remotes, &remoteAddr)
config = Config{ID: uint8(index), Host: "127.0.0.1", Port: basePort + index, Remotes: remotes}
index++
return
}
func testConfigAnother() (config Config) {
remoteAddr := net.TCPAddr{IP: []byte{127, 0, 0, 1}, Port: basePort + index - 1}
var remotes []*net.TCPAddr
remotes = append(remotes, &remoteAddr)
config = Config{ID: uint8(index), Host: "127.0.0.1", Port: basePort + index, Remotes: remotes}
index++
return
}
func TestCluster_StartCheckStop(t *testing.T) {
a := assert.New(t)
conf := testConfig()
node, err := New(&conf)
a.NoError(err, "No error should be raised when Creating the Cluster")
node.Router = newDummyRouter(t)
err = node.Start()
a.NoError(err, "No error should be raised when Starting the Cluster")
err = node.Check()
a.NoError(err, "Health-check score of a Cluster with a single node should be OK")
err = node.Stop()
a.NoError(err, "No error should be raised when Stopping the Cluster")
}
func TestCluster_BroadcastStringAndMessageAndCheck(t *testing.T) {
a := assert.New(t)
config1 := testConfig()
node1, err := New(&config1)
a.NoError(err, "No error should be raised when Creating the Cluster")
node1.Router = newDummyRouter(t)
//start the cluster node 1
defer node1.Stop()
err = node1.Start()
a.NoError(err, "No error should be raised when starting node 1 of the Cluster")
config2 := testConfigAnother()
node2, err := New(&config2)
a.NoError(err, "No error should be raised when Creating the Cluster")
node2.Router = newDummyRouter(t)
//start the cluster node 2
defer node2.Stop()
err = node2.Start()
a.NoError(err, "No error should be raised when starting node 2 of the Cluster")
// Send a String Message
str := "TEST"
err = node1.BroadcastString(&str)
a.NoError(err, "No error should be raised when sending a string to Cluster")
// and a protocol message
pmsg := protocol.Message{
ID: 1,
Path: "/stuff",
UserID: "id",
ApplicationID: "appId",
Time: time.Now().Unix(),
HeaderJSON: "{}",
Body: []byte("test"),
NodeID: 1}
err = node1.BroadcastMessage(&pmsg)
a.NoError(err, "No error should be raised when sending a protocol message to Cluster")
err = node1.Check()
a.NoError(err, "Health-check score of a Cluster with 2 nodes should be OK for node 1")
err = node2.Check()
a.NoError(err, "Health-check score of a Cluster with 2 nodes should be OK for node 2")
}
func TestCluster_NewShouldReturnErrorWhenPortIsInvalid(t *testing.T) {
a := assert.New(t)
remoteAddr := net.TCPAddr{IP: []byte{127, 0, 0, 1}, Port: basePort + index - 1}
var remotes []*net.TCPAddr
remotes = append(remotes, &remoteAddr)
index++
config := Config{ID: 1, Host: "localhost", Port: -1, Remotes: remotes}
_, err := New(&config)
if a.Error(err, "An error was expected when Creating the Cluster") {
a.Equal(err, errors.New("Failed to start TCP listener. Err: listen tcp :-1: bind: invalid argument"),
"Error should be precisely defined")
}
}
func TestCluster_StartShouldReturnErrorWhenNoRemotes(t *testing.T) {
a := assert.New(t)
var remotes []*net.TCPAddr
index++
config := Config{ID: 1, Host: "localhost", Port: basePort + index - 1, Remotes: remotes}
node, err := New(&config)
a.NoError(err, "No error should be raised when Creating the Cluster")
node.Router = newDummyRouter(t)
defer node.Stop()
err = node.Start()
if a.Error(err, "An error is expected when Starting the Cluster") {
a.Equal(err, errors.New("No remote hosts were successfully contacted when this node wanted to join the cluster"),
"Error should be precisely defined")
}
}
func TestCluster_StartShouldReturnErrorWhenInvalidRemotes(t *testing.T) {
a := assert.New(t)
remoteAddr := net.TCPAddr{IP: []byte{127, 0, 0, 1}, Port: 0}
var remotes []*net.TCPAddr
remotes = append(remotes, &remoteAddr)
index++
config := Config{ID: 1, Host: "localhost", Port: basePort + index - 1, Remotes: remotes}
node, err := New(&config)
a.NoError(err, "No error should be raised when Creating the Cluster")
node.Router = newDummyRouter(t)
defer node.Stop()
err = node.Start()
if a.Error(err, "An error is expected when Starting the Cluster") {
expected := multierror.Append(errors.New("Failed to join 127.0.0.1: dial tcp 127.0.0.1:0: getsockopt: connection refused"))
a.Equal(err, expected, "Error should be precisely defined")
}
}
func TestCluster_StartShouldReturnErrorWhenNoMessageHandler(t *testing.T) {
a := assert.New(t)
config := testConfig()
node, err := New(&config)
a.NoError(err, "No error should be raised when Creating the Cluster")
defer node.Stop()
err = node.Start()
if a.Error(err, "An error is expected when Starting the Cluster") {
expected := errors.New("There should be a valid Router already set-up")
a.Equal(expected, err, "Error should be precisely defined")
}
}
func TestCluster_NotifyMsgShouldSimplyReturnWhenDecodingInvalidMessage(t *testing.T) {
a := assert.New(t)
config := testConfig()
node, err := New(&config)
a.NoError(err, "No error should be raised when Creating the Cluster")
node.Router = newDummyRouter(t)
defer node.Stop()
err = node.Start()
a.NoError(err, "No error should be raised when Starting the Cluster")
node.NotifyMsg([]byte{})
//TODO Cosmin check that HandleMessage is not invoked (i.e. invalid message is not dispatched)
}
func TestCluster_broadcastClusterMessage(t *testing.T) {
a := assert.New(t)
config := testConfig()
node, err := New(&config)
a.NoError(err, "No error should be raised when Creating the Cluster")
node.Router = newDummyRouter(t)
defer node.Stop()
err = node.Start()
a.NoError(err, "No error should be raised when Starting the Cluster")
err = node.broadcastClusterMessage(nil)
if a.Error(err, "An error is expected from broadcastClusterMessage") {
expected := errors.New("Could not broadcast a nil cluster-message")
a.Equal(err, expected, "Error should be precisely defined")
}
}
type dummyRouter struct {
store store.MessageStore
}
func newDummyRouter(t *testing.T) *dummyRouter {
dir, err := ioutil.TempDir("", "guble_cluster_test")
assert.NoError(t, err)
return &dummyRouter{store: filestore.New(dir)}
}
func (_ *dummyRouter) HandleMessage(pmsg *protocol.Message) error {
return nil
}
func (d *dummyRouter) MessageStore() (store.MessageStore, error) {
return d.store, nil
}
================================================
FILE: server/cluster/codec.go
================================================
package cluster
import (
log "github.com/Sirupsen/logrus"
"github.com/ugorji/go/codec"
)
type messageType int
var h = &codec.MsgpackHandle{}
const (
// Guble protocol.Message
mtGubleMessage messageType = iota
// A node will send this message type when the body contains the partitions
// in it's store with the max message id for each ([]partitions)
mtSyncPartitions
// Sent this to request a node to give us the next message so we can save it
mtSyncMessageRequest
// Sent to synchronize a message, contains the message to synchonrize along with
// updated partition info
mtSyncMessage
mtStringMessage
)
type encoder interface {
encode() ([]byte, error)
}
type decoder interface {
decode(data []byte) error
}
type message struct {
NodeID uint8
Type messageType
Body []byte
}
func (cmsg *message) encode() ([]byte, error) {
logger.WithFields(log.Fields{
"nodeID": cmsg.NodeID,
"type": cmsg.Type,
"body": string(cmsg.Body),
}).Debug("Encoding cluster message")
return encode(cmsg)
}
func (cmsg *message) decode(data []byte) error {
logger.WithField("data", string(data)).Debug("decode")
return decode(cmsg, data)
}
func encode(entity interface{}) ([]byte, error) {
logger.WithField("entity", entity).Debug("Encoding")
var bytes []byte
encoder := codec.NewEncoderBytes(&bytes, h)
err := encoder.Encode(entity)
if err != nil {
logger.WithField("err", err).Error("Encoding failed")
return nil, err
}
return bytes, nil
}
func decode(o interface{}, data []byte) error {
logger.WithField("data", string(data)).Debug("Decoding")
decoder := codec.NewDecoderBytes(data, h)
err := decoder.Decode(o)
if err != nil {
logger.WithField("err", err).Error("Decoding failed")
return err
}
return nil
}
================================================
FILE: server/cluster/codec_test.go
================================================
package cluster
================================================
FILE: server/cluster/logger.go
================================================
package cluster
import (
log "github.com/Sirupsen/logrus"
)
var logger = log.WithFields(log.Fields{
"module": "cluster",
})
================================================
FILE: server/cluster/synchronizer.go
================================================
package cluster
import (
"errors"
"math"
"strconv"
"sync"
log "github.com/Sirupsen/logrus"
"github.com/smancke/guble/server/store"
"github.com/ugorji/go/codec"
)
const (
syncPartitionsProcessBuffer = 100
)
var (
ErrNodeNotInSync = errors.New("Node not found in syncPartitions list.")
ErrMissingSyncPartition = errors.New("Missing sync partition")
)
type synchronizer struct {
cluster *Cluster
store store.MessageStore
// map to keep track of nodes and remote partitions and local partitions
syncPartitions map[string]*syncPartition
nodes map[uint8]partitions // store the lastest info received from a node
sync.RWMutex
logger *log.Entry
stopC chan struct{}
}
func newSynchronizer(cluster *Cluster) (*synchronizer, error) {
store, err := cluster.Router.MessageStore()
if err != nil {
logger.WithError(err).Error("Error retriving message store for synchronizer")
return nil, err
}
return &synchronizer{
cluster: cluster,
store: store,
syncPartitions: make(map[string]*syncPartition),
nodes: make(map[uint8]partitions),
logger: logger.WithField("module", "synchronizer"),
stopC: make(chan struct{}),
}, nil
}
// add the partitions received from a node to the nodes list and start the loop
// for each partition
func (s *synchronizer) sync(nodeID uint8, partitions partitions) {
if s.inSyncID(nodeID) {
return
}
s.addNode(nodeID, partitions)
}
// inSync returns nodeID and a boolean value specifying if this node is already in sync
// returns 0 as nodeID and false if the node cannot be parsed
// the cluster should not send partitions nor should accept partitions information
// from a node that is already in sync
func (s *synchronizer) inSync(nodeID string) (uint8, bool) {
id, err := strconv.ParseUint(nodeID, 10, 8)
if err != nil {
logger.WithError(err).Error("Error parsing node ID")
return 0, false
}
ID := uint8(id)
return ID, s.inSyncID(ID)
}
func (s *synchronizer) inSyncID(nodeID uint8) bool {
s.RLock()
defer s.RUnlock()
_, in := s.nodes[nodeID]
return in
}
// addNode adds the node to the state with the missing partitions
func (s *synchronizer) addNode(nodeID uint8, partitions partitions) {
s.Lock()
defer s.Unlock()
s.nodes[nodeID] = partitions
for _, p := range partitions {
sp, exists := s.syncPartitions[p.Name]
if !exists {
localPartition, err := s.store.Partition(p.Name)
if err != nil {
logger.WithError(err).WithField("partition", p.Name).Error("Error retrieving local partition")
return
}
localMaxID := localPartition.MaxMessageID()
sp = &syncPartition{
synchronizer: s,
localPartition: localPartition,
localStartMaxID: localMaxID,
nodes: make(map[uint8]partition, 1),
lastID: localMaxID,
processC: make(chan *syncMessage, syncPartitionsProcessBuffer),
}
}
sp.nodes[nodeID] = p
s.syncPartitions[p.Name] = sp
go sp.run()
}
}
func (s *synchronizer) messageRequest(nodeID uint8, data []byte) error {
smr := &syncMessageRequest{}
err := smr.decode(data)
if err != nil {
return err
}
// start goroutine that will fetch messages from store and send them to the node
go s.requestLoop(nodeID, smr)
return nil
}
// requestLoop handles sending messages fetched from the store to the node
// that made the request a message sent from here will be received by the syncMessage
// method on the other node
func (s *synchronizer) requestLoop(nodeID uint8, smr *syncMessageRequest) {
s.logger.WithFields(log.Fields{
"requestNodeID": nodeID,
"syncMessageRequest": smr,
}).Debug("Sending requested messages")
req := &store.FetchRequest{
Partition: smr.Partition,
StartID: smr.StartID,
EndID: smr.EndID,
Direction: 1,
MessageC: make(chan *store.FetchedMessage, 10),
ErrorC: make(chan error),
StartC: make(chan int),
Count: math.MaxInt32,
}
s.store.Fetch(req)
var fetchedMessage *store.FetchedMessage
opened := true
for opened {
select {
case count := <-req.StartC:
logger.WithField("count", count).
Debug("Receiving messages for sync request from store")
case fetchedMessage, opened = <-req.MessageC:
if !opened {
s.logger.WithField("requestNodeID", nodeID).
Debug("Receive channel closed by the store for the sync request")
return
}
// send message to node
cmsg, err := s.cluster.newEncoderMessage(mtSyncMessage, &syncMessage{
Partition: smr.Partition,
ID: fetchedMessage.ID,
Message: fetchedMessage.Message,
})
if err != nil {
logger.WithError(err).
WithField("fetchedMessage", fetchedMessage).
Error("Error creating cluster message for fetched message")
continue
}
err = s.cluster.sendMessageToNodeID(nodeID, cmsg)
if err != nil {
logger.WithError(err).
WithField("clusterMesssage", cmsg).
Error("Error sending sync message to node")
continue
}
case <-s.stopC:
s.logger.WithField("requestNodeID", nodeID).Debug("Stopping synchronization request loop")
break
}
}
}
// syncMessage received data from another node after we made a request for a set
// of messages it will decode the data into a *syncMessage and send it into the
// appropriate syncPartition processC channel
func (s *synchronizer) syncMessage(nodeID uint8, data []byte) error {
if !s.inSyncID(nodeID) {
return ErrNodeNotInSync
}
sm := &syncMessage{}
err := sm.decode(data)
if err != nil {
logger.WithError(err).WithFields(log.Fields{
"nodeID": nodeID,
"data": string(data),
}).Error("Error decoding sync message received")
return err
}
s.RLock()
defer s.RUnlock()
syncPartition, exists := s.syncPartitions[sm.Partition]
if !exists {
return ErrMissingSyncPartition
}
s.logger.WithFields(log.Fields{
"sm": sm,
"syncPartiton": syncPartition,
}).Debug("Processing received message")
syncPartition.processC <- sm
return nil
}
// keep state of fetching for a partition
type syncPartition struct {
sync.RWMutex
synchronizer *synchronizer
localPartition store.MessagePartition
localStartMaxID uint64 // max message ID in the local store before the sync request
nodes map[uint8]partition // store nodes that have this partition and the info in does nodes
lastID uint64 // last fetched message ID
// processC channel will receive the message from the cluster and store it in the
// it's partition updating the lastID and sending a new request
processC chan *syncMessage
running bool
runningMu sync.RWMutex
}
// start the loop that will synchronize this partition with other nodes
func (sp *syncPartition) run() {
if sp.isRunning() {
return
}
sp.setRunning(true)
defer sp.setRunning(false)
sp.loop()
}
// syncLoop will start to sync the partition
// Each nodes job is to ask the messages it's missing from own store.
func (sp *syncPartition) loop() {
// send request for the missing messages
// get node with the highest message
maxID, nodeID := sp.maxIDNode()
partitionName := sp.localPartition.Name()
cmsg, err := sp.synchronizer.cluster.newEncoderMessage(mtSyncMessageRequest, &syncMessageRequest{
Partition: partitionName,
StartID: sp.lastID,
EndID: maxID,
})
if err != nil {
sp.synchronizer.logger.WithError(err).Error("Error creating sync message request")
return
}
err = sp.synchronizer.cluster.sendMessageToNodeID(nodeID, cmsg)
if err != nil {
sp.synchronizer.logger.WithError(err).WithFields(log.Fields{
"nodeID": nodeID,
"StartID": sp.lastID,
"EndID": maxID,
}).Error("Error sending sync message request to node")
return
}
for {
// wait to receive the message
// end the loop in case we are stopping the process or we finished synchronizing
select {
case sm := <-sp.processC:
err := sp.synchronizer.store.Store(partitionName, sm.ID, sm.Message)
if err != nil {
sp.synchronizer.logger.WithError(err).
WithField("messageID", sm.ID).
Error("Error storing synchronize message")
}
// end loop if we reached the end
sp.lastID = sm.ID
if sm.ID >= maxID {
return
}
case <-sp.synchronizer.stopC:
return
}
}
}
func (sp *syncPartition) maxIDNode() (max uint64, nodeID uint8) {
for nid, p := range sp.nodes {
if p.MaxID > max {
max = p.MaxID
nodeID = nid
}
}
return
}
func (sp *syncPartition) isRunning() bool {
sp.runningMu.RLock()
defer sp.runningMu.RUnlock()
return sp.running
}
func (sp *syncPartition) setRunning(r bool) {
sp.runningMu.Lock()
defer sp.runningMu.Unlock()
sp.running = r
}
type partition struct {
Name string
MaxID uint64
}
// syncPartitions will be sent by cluster server to notify the joining server
// on the partitions they store
type partitions []partition
func (p *partitions) encode() ([]byte, error) {
var bytes []byte
encoder := codec.NewEncoderBytes(&bytes, h)
err := encoder.Encode(p)
if err != nil {
logger.WithError(err).Error("Error encoding partitions")
return nil, err
}
return bytes, nil
}
// decode will decode the bytes into the receiver `p` in our case
// Example:
// ```
// p := make(partitions, 0)
// err := p.decode(data)
// if err != nil {
// ...
// }
// ```
func (p *partitions) decode(data []byte) error {
decoder := codec.NewDecoderBytes(data, h)
err := decoder.Decode(p)
if err != nil {
logger.WithError(err).Error("Error decoding partitions data")
return err
}
return nil
}
func partitionsFromStore(store store.MessageStore) *partitions {
messagePartitions, err := store.Partitions()
if err != nil {
logger.WithError(err).Error("Error retrieving store localPartitions")
return nil
}
localPartitions := make(partitions, 0, len(messagePartitions))
for _, p := range messagePartitions {
localPartitions = append(localPartitions, partition{
Name: p.Name(),
MaxID: p.MaxMessageID(),
})
}
return &localPartitions
}
// send this struct to a node to request the messages between StartID and EndID
type syncMessageRequest struct {
Partition string
StartID uint64
EndID uint64
}
func (smr *syncMessageRequest) encode() ([]byte, error) {
return encode(smr)
}
func (smr *syncMessageRequest) decode(data []byte) error {
return decode(smr, data)
}
type syncMessage struct {
// Hold updated partition info
Partition string
ID uint64
Message []byte
}
func (sm *syncMessage) encode() ([]byte, error) {
return encode(sm)
}
func (sm *syncMessage) decode(data []byte) error {
return decode(sm, data)
}
================================================
FILE: server/cluster_integration_test.go
================================================
package server
import (
log "github.com/Sirupsen/logrus"
"github.com/smancke/guble/protocol"
"github.com/smancke/guble/testutil"
"github.com/stretchr/testify/assert"
"testing"
"time"
gitextract_j4dqg380/
├── .gitignore
├── .travis.yml
├── Dockerfile
├── LICENSE
├── README.md
├── api/
│ └── swagger.yaml
├── client/
│ ├── client.go
│ ├── client_test.go
│ └── mocks_client_gen_test.go
├── guble-cli/
│ ├── README.md
│ ├── main.go
│ └── main_test.go
├── logformatter/
│ ├── logstash_formatter.go
│ └── logstash_formatter_test.go
├── main.go
├── protocol/
│ ├── cmd.go
│ ├── cmd_test.go
│ ├── log.go
│ ├── log_test.go
│ ├── message.go
│ ├── message_test.go
│ └── path.go
├── restclient/
│ ├── guble_sender.go
│ ├── guble_sender_test.go
│ ├── logger.go
│ └── sender.go
├── scripts/
│ ├── Dockerfile-cluster
│ ├── compose.cluster.test.yml
│ ├── compose.postgres.test.yml
│ ├── cov.sh
│ ├── dependencies_graph.sh
│ ├── file-hex.sh
│ ├── generate_coverage.sh
│ └── generate_mocks.sh
├── server/
│ ├── apns/
│ │ ├── apns.go
│ │ ├── apns_metrics.go
│ │ ├── apns_pusher.go
│ │ ├── apns_sender.go
│ │ ├── apns_sender_test.go
│ │ ├── apns_test.go
│ │ ├── logger.go
│ │ ├── mocks_connector_gen_test.go
│ │ ├── mocks_kvstore_gen_test.go
│ │ ├── mocks_pusher_gen_test.go
│ │ └── mocks_router_gen_test.go
│ ├── auth/
│ │ ├── accessmanager.go
│ │ ├── accessmanager_test.go
│ │ ├── allow_all_accessmanager.go
│ │ ├── logger.go
│ │ ├── mocks_auth_gen_test.go
│ │ └── rest_accessmanager.go
│ ├── benchmarking_apns_test.go
│ ├── benchmarking_common_test.go
│ ├── benchmarking_fcm_test.go
│ ├── benchmarking_fetch_test.go
│ ├── benchmarking_test.go
│ ├── cluster/
│ │ ├── cluster.go
│ │ ├── cluster_benchmarking_test.go
│ │ ├── cluster_conflict.go
│ │ ├── cluster_delegate.go
│ │ ├── cluster_event_delegate.go
│ │ ├── cluster_test.go
│ │ ├── codec.go
│ │ ├── codec_test.go
│ │ ├── logger.go
│ │ └── synchronizer.go
│ ├── cluster_integration_test.go
│ ├── config.go
│ ├── config_test.go
│ ├── connector/
│ │ ├── connector.go
│ │ ├── connector_test.go
│ │ ├── logger.go
│ │ ├── manager.go
│ │ ├── manager_test.go
│ │ ├── mocks_connector_gen_test.go
│ │ ├── mocks_kvstore_gen_test.go
│ │ ├── mocks_router_gen_test.go
│ │ ├── queue.go
│ │ ├── request.go
│ │ ├── subscriber.go
│ │ └── substitution.go
│ ├── fcm/
│ │ ├── fcm.go
│ │ ├── fcm_metrics.go
│ │ ├── fcm_sender.go
│ │ ├── fcm_test.go
│ │ ├── json_error.go
│ │ ├── logger.go
│ │ ├── mocks_gcm_gen_test.go
│ │ ├── mocks_kvstore_gen_test.go
│ │ ├── mocks_router_gen_test.go
│ │ ├── mocks_store_gen_test.go
│ │ └── testutil.go
│ ├── fcm_integration_test.go
│ ├── gubled.go
│ ├── gubled_test.go
│ ├── integration_test.go
│ ├── kvstore/
│ │ ├── common_test.go
│ │ ├── gorm.go
│ │ ├── kvstore.go
│ │ ├── memory.go
│ │ ├── memory_test.go
│ │ ├── postgres.go
│ │ ├── postgres_config.go
│ │ ├── postgres_config_test.go
│ │ ├── postgres_test.go
│ │ ├── sqlite.go
│ │ └── sqlite_test.go
│ ├── logger.go
│ ├── metrics/
│ │ ├── average.go
│ │ ├── average_test.go
│ │ ├── disabled.go
│ │ ├── enabled.go
│ │ ├── enabled_test.go
│ │ ├── int.go
│ │ ├── map.go
│ │ ├── metrics.go
│ │ ├── metrics_test.go
│ │ ├── ns.go
│ │ ├── rate.go
│ │ ├── rate_test.go
│ │ ├── time.go
│ │ └── zero.go
│ ├── mocks_apns_pusher_gen_test.go
│ ├── mocks_auth_gen_test.go
│ ├── mocks_router_gen_test.go
│ ├── mocks_store_gen_test.go
│ ├── redundancy_test.go
│ ├── rest/
│ │ ├── mocks_router_gen_test.go
│ │ ├── rest_message_api.go
│ │ └── rest_message_api_test.go
│ ├── router/
│ │ ├── errors.go
│ │ ├── logger.go
│ │ ├── message_queue.go
│ │ ├── mocks_auth_gen_test.go
│ │ ├── mocks_checker_gen_test.go
│ │ ├── mocks_kvstore_gen_test.go
│ │ ├── mocks_router_gen_test.go
│ │ ├── mocks_store_gen_test.go
│ │ ├── route.go
│ │ ├── route_config.go
│ │ ├── route_config_test.go
│ │ ├── route_params.go
│ │ ├── route_test.go
│ │ ├── router.go
│ │ ├── router_metrics.go
│ │ └── router_test.go
│ ├── service/
│ │ ├── logger.go
│ │ ├── mocks_checker_gen_test.go
│ │ ├── mocks_router_gen_test.go
│ │ ├── module.go
│ │ ├── service.go
│ │ └── service_test.go
│ ├── sms/
│ │ ├── logger.go
│ │ ├── mocks_router_gen_test.go
│ │ ├── mocks_sender_gen_test.go
│ │ ├── mocks_store_gen_test.go
│ │ ├── nexmo_sms.go
│ │ ├── nexmo_sms_sender.go
│ │ ├── nexmo_sms_sender_test.go
│ │ ├── sms_gateway.go
│ │ ├── sms_gateway_test.go
│ │ └── sms_metrics.go
│ ├── store/
│ │ ├── dummystore/
│ │ │ ├── dummy_message_store.go
│ │ │ └── dummy_message_store_test.go
│ │ ├── fetch_request.go
│ │ ├── filestore/
│ │ │ ├── cache.go
│ │ │ ├── index_list.go
│ │ │ ├── index_list_test.go
│ │ │ ├── logger.go
│ │ │ ├── message_partition.go
│ │ │ ├── message_partition_robustness_test.go
│ │ │ ├── message_partition_test.go
│ │ │ ├── message_store.go
│ │ │ └── message_store_test.go
│ │ └── store.go
│ ├── utils_test.go
│ ├── webserver/
│ │ ├── logger.go
│ │ ├── web_server.go
│ │ └── web_server_test.go
│ └── websocket/
│ ├── logger.go
│ ├── mocks_auth_gen_test.go
│ ├── mocks_router_gen_test.go
│ ├── mocks_store_gen_test.go
│ ├── mocks_websocket_gen_test.go
│ ├── receiver.go
│ ├── receiver_test.go
│ ├── websocket_connector.go
│ └── websocket_connector_test.go
├── test.sh
└── testutil/
└── testutil.go
SYMBOL INDEX (1703 symbols across 159 files)
FILE: client/client.go
type WSConnection (line 18) | type WSConnection interface
function DefaultConnectionFactory (line 24) | func DefaultConnectionFactory(url string, origin string) (WSConnection, ...
type WSConnectionFactory (line 37) | type WSConnectionFactory
type Client (line 39) | type Client interface
type client (line 58) | type client struct
method SetWSConnectionFactory (line 94) | func (c *client) SetWSConnectionFactory(connection WSConnectionFactory) {
method IsConnected (line 98) | func (c *client) IsConnected() bool {
method setIsConnected (line 104) | func (c *client) setIsConnected(connected bool) {
method Start (line 113) | func (c *client) Start() error {
method startWithReconnect (line 126) | func (c *client) startWithReconnect() {
method readLoop (line 155) | func (c *client) readLoop() error {
method shouldStop (line 175) | func (c *client) shouldStop() bool {
method handleIncomingMessage (line 188) | func (c *client) handleIncomingMessage(msg []byte) {
method Subscribe (line 214) | func (c *client) Subscribe(path string) error {
method Unsubscribe (line 223) | func (c *client) Unsubscribe(path string) error {
method Send (line 232) | func (c *client) Send(path string, body string, header string) error {
method SendBytes (line 236) | func (c *client) SendBytes(path string, body []byte, header string) er...
method WriteRawMessage (line 247) | func (c *client) WriteRawMessage(message []byte) error {
method Messages (line 251) | func (c *client) Messages() chan *protocol.Message {
method StatusMessages (line 255) | func (c *client) StatusMessages() chan *protocol.NotificationMessage {
method Errors (line 259) | func (c *client) Errors() chan *protocol.NotificationMessage {
method Close (line 263) | func (c *client) Close() {
function Open (line 75) | func Open(url, origin string, channelSize int, autoReconnect bool) (Clie...
function New (line 82) | func New(url, origin string, channelSize int, autoReconnect bool) Client {
function clientErrorMessage (line 268) | func clientErrorMessage(message string) *protocol.NotificationMessage {
FILE: client/client_test.go
function MockConnectionFactory (line 23) | func MockConnectionFactory(connectionMock *MockWSConnection) func(string...
function TestConnectErrorWithoutReconnection (line 29) | func TestConnectErrorWithoutReconnection(t *testing.T) {
function TestConnectErrorWithoutReconnectionUsingOpen (line 52) | func TestConnectErrorWithoutReconnectionUsingOpen(t *testing.T) {
function TestConnectErrorWithReconnection (line 69) | func TestConnectErrorWithReconnection(t *testing.T) {
function TestStopableClient (line 106) | func TestStopableClient(t *testing.T) {
function TestReceiveAMessage (line 142) | func TestReceiveAMessage(t *testing.T) {
function TestSendAMessage (line 219) | func TestSendAMessage(t *testing.T) {
function TestSendSubscribeMessage (line 246) | func TestSendSubscribeMessage(t *testing.T) {
function TestSendUnSubscribeMessage (line 272) | func TestSendUnSubscribeMessage(t *testing.T) {
FILE: client/mocks_client_gen_test.go
type MockWSConnection (line 13) | type MockWSConnection struct
method EXPECT (line 29) | func (_m *MockWSConnection) EXPECT() *_MockWSConnectionRecorder {
method Close (line 33) | func (_m *MockWSConnection) Close() error {
method ReadMessage (line 43) | func (_m *MockWSConnection) ReadMessage() (int, []byte, error) {
method WriteMessage (line 55) | func (_m *MockWSConnection) WriteMessage(_param0 int, _param1 []byte) ...
type _MockWSConnectionRecorder (line 19) | type _MockWSConnectionRecorder struct
method Close (line 39) | func (_mr *_MockWSConnectionRecorder) Close() *gomock.Call {
method ReadMessage (line 51) | func (_mr *_MockWSConnectionRecorder) ReadMessage() *gomock.Call {
method WriteMessage (line 61) | func (_mr *_MockWSConnectionRecorder) WriteMessage(arg0, arg1 interfac...
function NewMockWSConnection (line 23) | func NewMockWSConnection(ctrl *gomock.Controller) *MockWSConnection {
type MockClient (line 66) | type MockClient struct
method EXPECT (line 82) | func (_m *MockClient) EXPECT() *_MockClientRecorder {
method Close (line 86) | func (_m *MockClient) Close() {
method Errors (line 94) | func (_m *MockClient) Errors() chan *protocol.NotificationMessage {
method IsConnected (line 104) | func (_m *MockClient) IsConnected() bool {
method Messages (line 114) | func (_m *MockClient) Messages() chan *protocol.Message {
method Send (line 124) | func (_m *MockClient) Send(_param0 string, _param1 string, _param2 str...
method SendBytes (line 134) | func (_m *MockClient) SendBytes(_param0 string, _param1 []byte, _param...
method SetWSConnectionFactory (line 144) | func (_m *MockClient) SetWSConnectionFactory(_param0 WSConnectionFacto...
method Start (line 152) | func (_m *MockClient) Start() error {
method StatusMessages (line 162) | func (_m *MockClient) StatusMessages() chan *protocol.NotificationMess...
method Subscribe (line 172) | func (_m *MockClient) Subscribe(_param0 string) error {
method Unsubscribe (line 182) | func (_m *MockClient) Unsubscribe(_param0 string) error {
method WriteRawMessage (line 192) | func (_m *MockClient) WriteRawMessage(_param0 []byte) error {
type _MockClientRecorder (line 72) | type _MockClientRecorder struct
method Close (line 90) | func (_mr *_MockClientRecorder) Close() *gomock.Call {
method Errors (line 100) | func (_mr *_MockClientRecorder) Errors() *gomock.Call {
method IsConnected (line 110) | func (_mr *_MockClientRecorder) IsConnected() *gomock.Call {
method Messages (line 120) | func (_mr *_MockClientRecorder) Messages() *gomock.Call {
method Send (line 130) | func (_mr *_MockClientRecorder) Send(arg0, arg1, arg2 interface{}) *go...
method SendBytes (line 140) | func (_mr *_MockClientRecorder) SendBytes(arg0, arg1, arg2 interface{}...
method SetWSConnectionFactory (line 148) | func (_mr *_MockClientRecorder) SetWSConnectionFactory(arg0 interface{...
method Start (line 158) | func (_mr *_MockClientRecorder) Start() *gomock.Call {
method StatusMessages (line 168) | func (_mr *_MockClientRecorder) StatusMessages() *gomock.Call {
method Subscribe (line 178) | func (_mr *_MockClientRecorder) Subscribe(arg0 interface{}) *gomock.Ca...
method Unsubscribe (line 188) | func (_mr *_MockClientRecorder) Unsubscribe(arg0 interface{}) *gomock....
method WriteRawMessage (line 198) | func (_mr *_MockClientRecorder) WriteRawMessage(arg0 interface{}) *gom...
function NewMockClient (line 76) | func NewMockClient(ctrl *gomock.Controller) *MockClient {
FILE: guble-cli/main.go
function logLevels (line 32) | func logLevels() (levels []string) {
function main (line 40) | func main() {
function readLoop (line 69) | func readLoop(client client.Client) {
function writeLoop (line 87) | func writeLoop(client client.Client) {
function waitForTermination (line 132) | func waitForTermination(callback func()) {
function printHelp (line 140) | func printHelp() {
function removeTrailingSlash (line 157) | func removeTrailingSlash(path string) string {
FILE: guble-cli/main_test.go
function Test_PrintHelp (line 11) | func Test_PrintHelp(t *testing.T) {
function Test_removeTrailingSlash (line 42) | func Test_removeTrailingSlash(t *testing.T) {
FILE: logformatter/logstash_formatter.go
constant defaultServiceName (line 13) | defaultServiceName = "guble"
constant defaultLogType (line 14) | defaultLogType = "application"
constant defaultApplicationType (line 15) | defaultApplicationType = "service"
type LogstashFormatter (line 20) | type LogstashFormatter struct
method Format (line 42) | func (f *LogstashFormatter) Format(entry *logrus.Entry) ([]byte, error) {
FILE: logformatter/logstash_formatter_test.go
function TestLogstashFormatter_Format (line 12) | func TestLogstashFormatter_Format(t *testing.T) {
FILE: main.go
function main (line 7) | func main() {
FILE: protocol/cmd.go
constant CmdSend (line 11) | CmdSend = ">"
constant CmdReceive (line 12) | CmdReceive = "+"
constant CmdCancel (line 13) | CmdCancel = "-"
type Cmd (line 17) | type Cmd struct
method Bytes (line 61) | func (cmd *Cmd) Bytes() []byte {
function ParseCmd (line 33) | func ParseCmd(message []byte) (*Cmd, error) {
FILE: protocol/cmd_test.go
function TestParsingASendCommand (line 14) | func TestParsingASendCommand(t *testing.T) {
function TestSerializeASendCommand (line 26) | func TestSerializeASendCommand(t *testing.T) {
function Test_Cmd_EmptyCommand_Error (line 37) | func Test_Cmd_EmptyCommand_Error(t *testing.T) {
function TestParsingASubscribeCommand (line 43) | func TestParsingASubscribeCommand(t *testing.T) {
function TestSerializeASubscribeCommand (line 55) | func TestSerializeASubscribeCommand(t *testing.T) {
FILE: protocol/log.go
function PanicLogger (line 11) | func PanicLogger() {
function identifyLogOrigin (line 18) | func identifyLogOrigin() string {
function getStackTraceMessage (line 46) | func getStackTraceMessage(msg string) string {
FILE: protocol/log_test.go
function Test_log_functions_panic_logger (line 11) | func Test_log_functions_panic_logger(t *testing.T) {
function raisePanic (line 25) | func raisePanic() {
FILE: protocol/message.go
type Message (line 14) | type Message struct
method Metadata (line 49) | func (msg *Message) Metadata() string {
method String (line 55) | func (msg *Message) String() string {
method BodyAsString (line 59) | func (msg *Message) BodyAsString() string {
method Bytes (line 64) | func (msg *Message) Bytes() []byte {
method writeMetadata (line 85) | func (msg *Message) writeMetadata(buff *bytes.Buffer) {
method encodeFilters (line 101) | func (msg *Message) encodeFilters() []byte {
method decodeFilters (line 113) | func (msg *Message) decodeFilters(data []byte) {
method SetFilter (line 124) | func (msg *Message) SetFilter(key, value string) {
type MessageDeliveryCallback (line 46) | type MessageDeliveryCallback
constant SUCCESS_CONNECTED (line 133) | SUCCESS_CONNECTED = "connected"
constant SUCCESS_SEND (line 134) | SUCCESS_SEND = "send"
constant SUCCESS_FETCH_START (line 135) | SUCCESS_FETCH_START = "fetch-start"
constant SUCCESS_FETCH_END (line 136) | SUCCESS_FETCH_END = "fetch-end"
constant SUCCESS_SUBSCRIBED_TO (line 137) | SUCCESS_SUBSCRIBED_TO = "subscribed-to"
constant SUCCESS_CANCELED (line 138) | SUCCESS_CANCELED = "canceled"
constant ERROR_SUBSCRIBED_TO (line 139) | ERROR_SUBSCRIBED_TO = "error-subscribed-to"
constant ERROR_BAD_REQUEST (line 140) | ERROR_BAD_REQUEST = "error-bad-request"
constant ERROR_INTERNAL_SERVER (line 141) | ERROR_INTERNAL_SERVER = "error-server-internal"
type NotificationMessage (line 145) | type NotificationMessage struct
method Bytes (line 161) | func (msg *NotificationMessage) Bytes() []byte {
function Decode (line 185) | func Decode(message []byte) (interface{}, error) {
function ParseMessage (line 192) | func ParseMessage(message []byte) (*Message, error) {
function parseNotificationMessage (line 244) | func parseNotificationMessage(message []byte) (*NotificationMessage, err...
FILE: protocol/message_test.go
function TestParsingANormalMessage (line 24) | func TestParsingANormalMessage(t *testing.T) {
function TestSerializeANormalMessage (line 43) | func TestSerializeANormalMessage(t *testing.T) {
function TestSerializeAMinimalMessage (line 65) | func TestSerializeAMinimalMessage(t *testing.T) {
function TestSerializeAMinimalMessageWithBody (line 75) | func TestSerializeAMinimalMessageWithBody(t *testing.T) {
function TestParsingAMinimalMessage (line 86) | func TestParsingAMinimalMessage(t *testing.T) {
function TestErrorsOnParsingMessages (line 105) | func TestErrorsOnParsingMessages(t *testing.T) {
function TestParsingNotificationMessage (line 129) | func TestParsingNotificationMessage(t *testing.T) {
function TestSerializeANotificationMessage (line 143) | func TestSerializeANotificationMessage(t *testing.T) {
function TestSerializeAnErrorMessage (line 154) | func TestSerializeAnErrorMessage(t *testing.T) {
function TestSerializeANotificationMessageWithEmptyArg (line 164) | func TestSerializeANotificationMessageWithEmptyArg(t *testing.T) {
function TestParsingErrorNotificationMessage (line 174) | func TestParsingErrorNotificationMessage(t *testing.T) {
function Test_Message_getPartitionFromTopic (line 190) | func Test_Message_getPartitionFromTopic(t *testing.T) {
function TestMessage_Filters (line 198) | func TestMessage_Filters(t *testing.T) {
function TestMessage_decodeFilters (line 212) | func TestMessage_decodeFilters(t *testing.T) {
FILE: protocol/path.go
type Path (line 6) | type Path
method Partition (line 9) | func (path Path) Partition() string {
method RemovePrefixSlash (line 16) | func (path Path) RemovePrefixSlash() string {
FILE: restclient/guble_sender.go
type gubleSender (line 15) | type gubleSender struct
method GetSubscribers (line 28) | func (gs gubleSender) GetSubscribers(topic string) ([]byte, error) {
method Check (line 69) | func (gs gubleSender) Check() bool {
method Send (line 84) | func (gs gubleSender) Send(topic string, body []byte, userID string, p...
function New (line 21) | func New(endpoint string) Sender {
function getURL (line 112) | func getURL(endpoint, topic, userID string, params map[string]string) st...
function trimPrefixSlash (line 125) | func trimPrefixSlash(topic string) string {
FILE: restclient/guble_sender_test.go
function TestGetURL (line 9) | func TestGetURL(t *testing.T) {
FILE: restclient/sender.go
type Sender (line 4) | type Sender interface
FILE: server/apns/apns.go
constant schema (line 15) | schema = "apns_registration"
type Config (line 23) | type Config struct
type apns (line 36) | type apns struct
method Start (line 66) | func (a *apns) Start() error {
method startMetrics (line 74) | func (a *apns) startMetrics() {
method startIntervalMetric (line 92) | func (a *apns) startIntervalMetric(m metrics.Map, td time.Duration) {
method HandleResponse (line 96) | func (a *apns) HandleResponse(request connector.Request, responseIface...
function New (line 42) | func New(router router.Router, sender connector.Sender, config Config) (...
FILE: server/apns/apns_metrics.go
constant currentTotalMessagesLatenciesKey (line 25) | currentTotalMessagesLatenciesKey = "current_messages_total_latencies_nanos"
constant currentTotalMessagesKey (line 26) | currentTotalMessagesKey = "current_messages_count"
constant currentTotalErrorsLatenciesKey (line 27) | currentTotalErrorsLatenciesKey = "current_errors_total_latencies_nanos"
constant currentTotalErrorsKey (line 28) | currentTotalErrorsKey = "current_errors_count"
function processAndResetIntervalMetrics (line 31) | func processAndResetIntervalMetrics(m metrics.Map, td time.Duration, t t...
function resetIntervalMetrics (line 47) | func resetIntervalMetrics(m metrics.Map, t time.Time) {
function addToLatenciesAndCountsMaps (line 55) | func addToLatenciesAndCountsMaps(latenciesKey string, countKey string, l...
FILE: server/apns/apns_pusher.go
constant tlsDialTimeout (line 16) | tlsDialTimeout = 20 * time.Second
constant httpClientTimeout (line 17) | httpClientTimeout = 30 * time.Second
type Pusher (line 20) | type Pusher interface
type closable (line 24) | type closable interface
function newPusher (line 28) | func newPusher(c Config) (Pusher, error) {
function newProductionClient (line 59) | func newProductionClient(certificate tls.Certificate) *apns2Client {
function newDevelopmentClient (line 67) | func newDevelopmentClient(certificate tls.Certificate) *apns2Client {
type apns2Client (line 75) | type apns2Client struct
method CloseTLS (line 122) | func (c *apns2Client) CloseTLS() {
function newApns2Client (line 82) | func newApns2Client(certificate tls.Certificate) *apns2Client {
FILE: server/apns/apns_sender.go
constant deviceIDKey (line 14) | deviceIDKey = "device_token"
constant userIDKey (line 15) | userIDKey = "user_id"
type sender (line 23) | type sender struct
method Send (line 47) | func (s sender) Send(request connector.Request) (interface{}, error) {
function NewSender (line 28) | func NewSender(config Config) (connector.Sender, error) {
function NewSenderUsingPusher (line 37) | func NewSenderUsingPusher(pusher Pusher, appTopic string) (connector.Sen...
type retryable (line 82) | type retryable struct
method execute (line 87) | func (r *retryable) execute(op func() (interface{}, error)) (interface...
FILE: server/apns/apns_sender_test.go
function TestNewSender_ErrorBytes (line 13) | func TestNewSender_ErrorBytes(t *testing.T) {
function TestNewSender_ErrorFile (line 32) | func TestNewSender_ErrorFile(t *testing.T) {
function TestSender_Send (line 51) | func TestSender_Send(t *testing.T) {
function TestSender_Retry (line 91) | func TestSender_Retry(t *testing.T) {
type resultpair (line 134) | type resultpair struct
function Test_Retriable (line 139) | func Test_Retriable(t *testing.T) {
type mockTimeout (line 184) | type mockTimeout struct
method Error (line 186) | func (e *mockTimeout) Error() string { return "mock i/o timeout" }
method Timeout (line 187) | func (e *mockTimeout) Timeout() bool { return true }
method Temporary (line 188) | func (e *mockTimeout) Temporary() bool { return true }
FILE: server/apns/apns_test.go
function TestNew_WithoutKVStore (line 16) | func TestNew_WithoutKVStore(t *testing.T) {
function TestConn_HandleResponseOnSendError (line 41) | func TestConn_HandleResponseOnSendError(t *testing.T) {
function TestConn_HandleResponse (line 57) | func TestConn_HandleResponse(t *testing.T) {
function TestNew_HandleResponseHandleSubscriber (line 92) | func TestNew_HandleResponseHandleSubscriber(t *testing.T) {
function TestNew_HandleResponseDoNotHandleSubscriber (line 138) | func TestNew_HandleResponseDoNotHandleSubscriber(t *testing.T) {
function newAPNSConnector (line 203) | func newAPNSConnector(t *testing.T) (c connector.ResponsiveConnector, mK...
FILE: server/apns/mocks_connector_gen_test.go
type MockSender (line 15) | type MockSender struct
method EXPECT (line 31) | func (_m *MockSender) EXPECT() *_MockSenderRecorder {
method Send (line 35) | func (_m *MockSender) Send(_param0 connector.Request) (interface{}, er...
type _MockSenderRecorder (line 21) | type _MockSenderRecorder struct
method Send (line 42) | func (_mr *_MockSenderRecorder) Send(arg0 interface{}) *gomock.Call {
function NewMockSender (line 25) | func NewMockSender(ctrl *gomock.Controller) *MockSender {
type MockRequest (line 47) | type MockRequest struct
method EXPECT (line 63) | func (_m *MockRequest) EXPECT() *_MockRequestRecorder {
method Message (line 67) | func (_m *MockRequest) Message() *protocol.Message {
method Subscriber (line 77) | func (_m *MockRequest) Subscriber() connector.Subscriber {
type _MockRequestRecorder (line 53) | type _MockRequestRecorder struct
method Message (line 73) | func (_mr *_MockRequestRecorder) Message() *gomock.Call {
method Subscriber (line 83) | func (_mr *_MockRequestRecorder) Subscriber() *gomock.Call {
function NewMockRequest (line 57) | func NewMockRequest(ctrl *gomock.Controller) *MockRequest {
type MockSubscriber (line 88) | type MockSubscriber struct
method EXPECT (line 104) | func (_m *MockSubscriber) EXPECT() *_MockSubscriberRecorder {
method Cancel (line 108) | func (_m *MockSubscriber) Cancel() {
method Encode (line 116) | func (_m *MockSubscriber) Encode() ([]byte, error) {
method Filter (line 127) | func (_m *MockSubscriber) Filter(_param0 map[string]string) bool {
method Key (line 137) | func (_m *MockSubscriber) Key() string {
method Loop (line 147) | func (_m *MockSubscriber) Loop(_param0 context.Context, _param1 connec...
method Reset (line 157) | func (_m *MockSubscriber) Reset() error {
method Route (line 167) | func (_m *MockSubscriber) Route() *router.Route {
method SetLastID (line 177) | func (_m *MockSubscriber) SetLastID(_param0 uint64) {
type _MockSubscriberRecorder (line 94) | type _MockSubscriberRecorder struct
method Cancel (line 112) | func (_mr *_MockSubscriberRecorder) Cancel() *gomock.Call {
method Encode (line 123) | func (_mr *_MockSubscriberRecorder) Encode() *gomock.Call {
method Filter (line 133) | func (_mr *_MockSubscriberRecorder) Filter(arg0 interface{}) *gomock.C...
method Key (line 143) | func (_mr *_MockSubscriberRecorder) Key() *gomock.Call {
method Loop (line 153) | func (_mr *_MockSubscriberRecorder) Loop(arg0, arg1 interface{}) *gomo...
method Reset (line 163) | func (_mr *_MockSubscriberRecorder) Reset() *gomock.Call {
method Route (line 173) | func (_mr *_MockSubscriberRecorder) Route() *gomock.Call {
method SetLastID (line 181) | func (_mr *_MockSubscriberRecorder) SetLastID(arg0 interface{}) *gomoc...
function NewMockSubscriber (line 98) | func NewMockSubscriber(ctrl *gomock.Controller) *MockSubscriber {
FILE: server/apns/mocks_kvstore_gen_test.go
type MockKVStore (line 11) | type MockKVStore struct
method EXPECT (line 27) | func (_m *MockKVStore) EXPECT() *_MockKVStoreRecorder {
method Delete (line 31) | func (_m *MockKVStore) Delete(_param0 string, _param1 string) error {
method Get (line 41) | func (_m *MockKVStore) Get(_param0 string, _param1 string) ([]byte, bo...
method Iterate (line 53) | func (_m *MockKVStore) Iterate(_param0 string, _param1 string) chan [2...
method IterateKeys (line 63) | func (_m *MockKVStore) IterateKeys(_param0 string, _param1 string) cha...
method Put (line 73) | func (_m *MockKVStore) Put(_param0 string, _param1 string, _param2 []b...
type _MockKVStoreRecorder (line 17) | type _MockKVStoreRecorder struct
method Delete (line 37) | func (_mr *_MockKVStoreRecorder) Delete(arg0, arg1 interface{}) *gomoc...
method Get (line 49) | func (_mr *_MockKVStoreRecorder) Get(arg0, arg1 interface{}) *gomock.C...
method Iterate (line 59) | func (_mr *_MockKVStoreRecorder) Iterate(arg0, arg1 interface{}) *gomo...
method IterateKeys (line 69) | func (_mr *_MockKVStoreRecorder) IterateKeys(arg0, arg1 interface{}) *...
method Put (line 79) | func (_mr *_MockKVStoreRecorder) Put(arg0, arg1, arg2 interface{}) *go...
function NewMockKVStore (line 21) | func NewMockKVStore(ctrl *gomock.Controller) *MockKVStore {
FILE: server/apns/mocks_pusher_gen_test.go
type MockPusher (line 12) | type MockPusher struct
method EXPECT (line 28) | func (_m *MockPusher) EXPECT() *_MockPusherRecorder {
method Push (line 32) | func (_m *MockPusher) Push(_param0 *apns2.Notification) (*apns2.Respon...
type _MockPusherRecorder (line 18) | type _MockPusherRecorder struct
method Push (line 39) | func (_mr *_MockPusherRecorder) Push(arg0 interface{}) *gomock.Call {
function NewMockPusher (line 22) | func NewMockPusher(ctrl *gomock.Controller) *MockPusher {
FILE: server/apns/mocks_router_gen_test.go
type MockRouter (line 17) | type MockRouter struct
method EXPECT (line 33) | func (_m *MockRouter) EXPECT() *_MockRouterRecorder {
method AccessManager (line 37) | func (_m *MockRouter) AccessManager() (auth.AccessManager, error) {
method Cluster (line 48) | func (_m *MockRouter) Cluster() *cluster.Cluster {
method Done (line 58) | func (_m *MockRouter) Done() <-chan bool {
method Fetch (line 68) | func (_m *MockRouter) Fetch(_param0 *store.FetchRequest) error {
method GetSubscribers (line 78) | func (_m *MockRouter) GetSubscribers(_param0 string) ([]byte, error) {
method HandleMessage (line 89) | func (_m *MockRouter) HandleMessage(_param0 *protocol.Message) error {
method KVStore (line 99) | func (_m *MockRouter) KVStore() (kvstore.KVStore, error) {
method MessageStore (line 110) | func (_m *MockRouter) MessageStore() (store.MessageStore, error) {
method Subscribe (line 121) | func (_m *MockRouter) Subscribe(_param0 *router.Route) (*router.Route,...
method Unsubscribe (line 132) | func (_m *MockRouter) Unsubscribe(_param0 *router.Route) {
type _MockRouterRecorder (line 23) | type _MockRouterRecorder struct
method AccessManager (line 44) | func (_mr *_MockRouterRecorder) AccessManager() *gomock.Call {
method Cluster (line 54) | func (_mr *_MockRouterRecorder) Cluster() *gomock.Call {
method Done (line 64) | func (_mr *_MockRouterRecorder) Done() *gomock.Call {
method Fetch (line 74) | func (_mr *_MockRouterRecorder) Fetch(arg0 interface{}) *gomock.Call {
method GetSubscribers (line 85) | func (_mr *_MockRouterRecorder) GetSubscribers(arg0 interface{}) *gomo...
method HandleMessage (line 95) | func (_mr *_MockRouterRecorder) HandleMessage(arg0 interface{}) *gomoc...
method KVStore (line 106) | func (_mr *_MockRouterRecorder) KVStore() *gomock.Call {
method MessageStore (line 117) | func (_mr *_MockRouterRecorder) MessageStore() *gomock.Call {
method Subscribe (line 128) | func (_mr *_MockRouterRecorder) Subscribe(arg0 interface{}) *gomock.Ca...
method Unsubscribe (line 136) | func (_mr *_MockRouterRecorder) Unsubscribe(arg0 interface{}) *gomock....
function NewMockRouter (line 27) | func NewMockRouter(ctrl *gomock.Controller) *MockRouter {
FILE: server/auth/accessmanager.go
type AccessType (line 8) | type AccessType
constant READ (line 12) | READ AccessType = iota
constant WRITE (line 15) | WRITE
type AccessManager (line 19) | type AccessManager interface
FILE: server/auth/accessmanager_test.go
function Test_AllowAllAccessManager (line 10) | func Test_AllowAllAccessManager(t *testing.T) {
function Test_RestAccessManagerAllowed (line 19) | func Test_RestAccessManagerAllowed(t *testing.T) {
function Test_RestAccessManagerNotAllowed (line 31) | func Test_RestAccessManagerNotAllowed(t *testing.T) {
function Test_RestAccessManagerNotAllowedWithServerNotStarted (line 42) | func Test_RestAccessManagerNotAllowedWithServerNotStarted(t *testing.T) {
function Test_RestAccessManagerNotAllowedHttpReturningStatusForbidden (line 53) | func Test_RestAccessManagerNotAllowedHttpReturningStatusForbidden(t *tes...
FILE: server/auth/allow_all_accessmanager.go
type AllowAllAccessManager (line 8) | type AllowAllAccessManager
method IsAllowed (line 16) | func (am AllowAllAccessManager) IsAllowed(accessType AccessType, userI...
function NewAllowAllAccessManager (line 11) | func NewAllowAllAccessManager(allowAll bool) AllowAllAccessManager {
FILE: server/auth/mocks_auth_gen_test.go
type MockAccessManager (line 12) | type MockAccessManager struct
method EXPECT (line 28) | func (_m *MockAccessManager) EXPECT() *_MockAccessManagerRecorder {
method IsAllowed (line 32) | func (_m *MockAccessManager) IsAllowed(_param0 AccessType, _param1 str...
type _MockAccessManagerRecorder (line 18) | type _MockAccessManagerRecorder struct
method IsAllowed (line 38) | func (_mr *_MockAccessManagerRecorder) IsAllowed(arg0, arg1, arg2 inte...
function NewMockAccessManager (line 22) | func NewMockAccessManager(ctrl *gomock.Controller) *MockAccessManager {
FILE: server/auth/rest_accessmanager.go
type RestAccessManager (line 14) | type RestAccessManager
method IsAllowed (line 23) | func (ram RestAccessManager) IsAllowed(accessType AccessType, userId s...
function NewRestAccessManager (line 17) | func NewRestAccessManager(url string) RestAccessManager {
FILE: server/benchmarking_apns_test.go
function BenchmarkAPNS_1Workers50MilliTimeout (line 25) | func BenchmarkAPNS_1Workers50MilliTimeout(b *testing.B) {
function BenchmarkAPNS_8Workers50MilliTimeout (line 38) | func BenchmarkAPNS_8Workers50MilliTimeout(b *testing.B) {
function BenchmarkAPNS_16Workers50MilliTimeout (line 51) | func BenchmarkAPNS_16Workers50MilliTimeout(b *testing.B) {
function BenchmarkAPNS_1Workers100MilliTimeout (line 64) | func BenchmarkAPNS_1Workers100MilliTimeout(b *testing.B) {
function BenchmarkAPNS_8Workers100MilliTimeout (line 77) | func BenchmarkAPNS_8Workers100MilliTimeout(b *testing.B) {
function BenchmarkAPNS_16Workers100MilliTimeout (line 90) | func BenchmarkAPNS_16Workers100MilliTimeout(b *testing.B) {
method throughputAPNS (line 103) | func (params *benchParams) throughputAPNS() {
FILE: server/benchmarking_common_test.go
constant testTopic (line 15) | testTopic = "/topic"
type sender (line 18) | type sender
function sendMessageSample (line 20) | func sendMessageSample(c client.Client) error {
type benchParams (line 24) | type benchParams struct
method createClients (line 43) | func (params *benchParams) createClients() (clients []client.Client) {
method receiveLoop (line 56) | func (params *benchParams) receiveLoop() {
method String (line 73) | func (params *benchParams) String() string {
method ResetTimer (line 83) | func (params *benchParams) ResetTimer() {
method StopTimer (line 88) | func (params *benchParams) StopTimer() {
method duration (line 93) | func (params *benchParams) duration() time.Duration {
method messagesPerSecond (line 97) | func (params *benchParams) messagesPerSecond() float64 {
FILE: server/benchmarking_fcm_test.go
function BenchmarkFCM_1Workers50MilliTimeout (line 24) | func BenchmarkFCM_1Workers50MilliTimeout(b *testing.B) {
function BenchmarkFCM_8Workers50MilliTimeout (line 37) | func BenchmarkFCM_8Workers50MilliTimeout(b *testing.B) {
function BenchmarkFCM_16Workers50MilliTimeout (line 50) | func BenchmarkFCM_16Workers50MilliTimeout(b *testing.B) {
function BenchmarkFCM_1Workers100MilliTimeout (line 63) | func BenchmarkFCM_1Workers100MilliTimeout(b *testing.B) {
function BenchmarkFCM_8Workers100MilliTimeout (line 76) | func BenchmarkFCM_8Workers100MilliTimeout(b *testing.B) {
function BenchmarkFCM_16Workers100MilliTimeout (line 89) | func BenchmarkFCM_16Workers100MilliTimeout(b *testing.B) {
method throughputFCM (line 102) | func (params *benchParams) throughputFCM() {
FILE: server/benchmarking_fetch_test.go
function Benchmark_E2E_Fetch_HelloWorld_Messages (line 17) | func Benchmark_E2E_Fetch_HelloWorld_Messages(b *testing.B) {
FILE: server/benchmarking_test.go
type testgroup (line 18) | type testgroup struct
method Init (line 111) | func (tg *testgroup) Init() {
method expectStatusMessage (line 133) | func (tg *testgroup) expectStatusMessage(name string, arg string) {
method Start (line 146) | func (tg *testgroup) Start() {
method Clean (line 179) | func (tg *testgroup) Clean() {
function newTestgroup (line 28) | func newTestgroup(t *testing.T, groupID int, addr string, messagesToSend...
function TestThroughput (line 38) | func TestThroughput(t *testing.T) {
FILE: server/cluster/cluster.go
type Config (line 23) | type Config struct
type router (line 34) | type router interface
type Cluster (line 40) | type Cluster struct
method Start (line 88) | func (cluster *Cluster) Start() error {
method Stop (line 121) | func (cluster *Cluster) Stop() error {
method Check (line 129) | func (cluster *Cluster) Check() error {
method newMessage (line 139) | func (cluster *Cluster) newMessage(t messageType, body []byte) *message {
method newEncoderMessage (line 147) | func (cluster *Cluster) newEncoderMessage(t messageType, entity encode...
method BroadcastString (line 156) | func (cluster *Cluster) BroadcastString(sMessage *string) error {
method BroadcastMessage (line 167) | func (cluster *Cluster) BroadcastMessage(pMessage *protocol.Message) e...
method broadcastClusterMessage (line 177) | func (cluster *Cluster) broadcastClusterMessage(cMessage *message) err...
method sendToNode (line 199) | func (cluster *Cluster) sendToNode(node *memberlist.Node, msgBytes []b...
method sendMessageToNode (line 218) | func (cluster *Cluster) sendMessageToNode(node *memberlist.Node, cmsg ...
method sendMessageToNodeID (line 235) | func (cluster *Cluster) sendMessageToNodeID(nodeID uint8, cmsg *messag...
method GetNodeByID (line 244) | func (cluster *Cluster) GetNodeByID(id uint8) *memberlist.Node {
method remotesAsStrings (line 254) | func (cluster *Cluster) remotesAsStrings() (strings []string) {
function New (line 60) | func New(config *Config) (*Cluster, error) {
FILE: server/cluster/cluster_benchmarking_test.go
function BenchmarkMemberListCluster (line 13) | func BenchmarkMemberListCluster(b *testing.B) {
function benchmarkCluster (line 17) | func benchmarkCluster(b *testing.B, num int, timeoutForAllJoins time.Dur...
function convergence (line 67) | func convergence(nodes []*memberlist.Memberlist, num int, eventC chan me...
function sendMessagesInCluster (line 120) | func sendMessagesInCluster(nodes []*memberlist.Memberlist, numMessages i...
FILE: server/cluster/cluster_conflict.go
method NotifyConflict (line 13) | func (cluster *Cluster) NotifyConflict(existing, other *memberlist.Node) {
FILE: server/cluster/cluster_delegate.go
method NotifyMsg (line 15) | func (cluster *Cluster) NotifyMsg(data []byte) {
method GetBroadcasts (line 43) | func (cluster *Cluster) GetBroadcasts(overhead, limit int) [][]byte {
method NodeMeta (line 49) | func (cluster *Cluster) NodeMeta(limit int) []byte { return nil }
method LocalState (line 51) | func (cluster *Cluster) LocalState(join bool) []byte { return nil }
method MergeRemoteState (line 53) | func (cluster *Cluster) MergeRemoteState(s []byte, join bool) {}
method handleGubleMessage (line 56) | func (cluster *Cluster) handleGubleMessage(cmsg *message) {
method handleSyncPartitions (line 69) | func (cluster *Cluster) handleSyncPartitions(cmsg *message) {
method handleSyncMessage (line 90) | func (cluster *Cluster) handleSyncMessage(cmsg *message) {
method handleSyncMessageRequest (line 98) | func (cluster *Cluster) handleSyncMessageRequest(cmsg *message) {
FILE: server/cluster/cluster_event_delegate.go
method NotifyJoin (line 13) | func (cluster *Cluster) NotifyJoin(node *memberlist.Node) {
method NotifyLeave (line 20) | func (cluster *Cluster) NotifyLeave(node *memberlist.Node) {
method NotifyUpdate (line 25) | func (cluster *Cluster) NotifyUpdate(node *memberlist.Node) {
method eventLog (line 30) | func (cluster *Cluster) eventLog(node *memberlist.Node, message string) {
method sendPartitions (line 39) | func (cluster *Cluster) sendPartitions(node *memberlist.Node) {
FILE: server/cluster/cluster_test.go
constant basePort (line 20) | basePort = 10000
function testConfig (line 26) | func testConfig() (config Config) {
function testConfigAnother (line 35) | func testConfigAnother() (config Config) {
function TestCluster_StartCheckStop (line 44) | func TestCluster_StartCheckStop(t *testing.T) {
function TestCluster_BroadcastStringAndMessageAndCheck (line 63) | func TestCluster_BroadcastStringAndMessageAndCheck(t *testing.T) {
function TestCluster_NewShouldReturnErrorWhenPortIsInvalid (line 113) | func TestCluster_NewShouldReturnErrorWhenPortIsInvalid(t *testing.T) {
function TestCluster_StartShouldReturnErrorWhenNoRemotes (line 129) | func TestCluster_StartShouldReturnErrorWhenNoRemotes(t *testing.T) {
function TestCluster_StartShouldReturnErrorWhenInvalidRemotes (line 149) | func TestCluster_StartShouldReturnErrorWhenInvalidRemotes(t *testing.T) {
function TestCluster_StartShouldReturnErrorWhenNoMessageHandler (line 171) | func TestCluster_StartShouldReturnErrorWhenNoMessageHandler(t *testing.T) {
function TestCluster_NotifyMsgShouldSimplyReturnWhenDecodingInvalidMessage (line 186) | func TestCluster_NotifyMsgShouldSimplyReturnWhenDecodingInvalidMessage(t...
function TestCluster_broadcastClusterMessage (line 204) | func TestCluster_broadcastClusterMessage(t *testing.T) {
type dummyRouter (line 224) | type dummyRouter struct
method HandleMessage (line 234) | func (_ *dummyRouter) HandleMessage(pmsg *protocol.Message) error {
method MessageStore (line 238) | func (d *dummyRouter) MessageStore() (store.MessageStore, error) {
function newDummyRouter (line 228) | func newDummyRouter(t *testing.T) *dummyRouter {
FILE: server/cluster/codec.go
type messageType (line 9) | type messageType
constant mtGubleMessage (line 15) | mtGubleMessage messageType = iota
constant mtSyncPartitions (line 19) | mtSyncPartitions
constant mtSyncMessageRequest (line 22) | mtSyncMessageRequest
constant mtSyncMessage (line 26) | mtSyncMessage
constant mtStringMessage (line 28) | mtStringMessage
type encoder (line 31) | type encoder interface
type decoder (line 35) | type decoder interface
type message (line 39) | type message struct
method encode (line 45) | func (cmsg *message) encode() ([]byte, error) {
method decode (line 54) | func (cmsg *message) decode(data []byte) error {
function encode (line 59) | func encode(entity interface{}) ([]byte, error) {
function decode (line 74) | func decode(o interface{}, data []byte) error {
FILE: server/cluster/synchronizer.go
constant syncPartitionsProcessBuffer (line 16) | syncPartitionsProcessBuffer = 100
type synchronizer (line 25) | type synchronizer struct
method sync (line 59) | func (s *synchronizer) sync(nodeID uint8, partitions partitions) {
method inSync (line 71) | func (s *synchronizer) inSync(nodeID string) (uint8, bool) {
method inSyncID (line 81) | func (s *synchronizer) inSyncID(nodeID uint8) bool {
method addNode (line 90) | func (s *synchronizer) addNode(nodeID uint8, partitions partitions) {
method messageRequest (line 123) | func (s *synchronizer) messageRequest(nodeID uint8, data []byte) error {
method requestLoop (line 138) | func (s *synchronizer) requestLoop(nodeID uint8, smr *syncMessageReque...
method syncMessage (line 202) | func (s *synchronizer) syncMessage(nodeID uint8, data []byte) error {
function newSynchronizer (line 39) | func newSynchronizer(cluster *Cluster) (*synchronizer, error) {
type syncPartition (line 235) | type syncPartition struct
method run (line 252) | func (sp *syncPartition) run() {
method loop (line 264) | func (sp *syncPartition) loop() {
method maxIDNode (line 313) | func (sp *syncPartition) maxIDNode() (max uint64, nodeID uint8) {
method isRunning (line 323) | func (sp *syncPartition) isRunning() bool {
method setRunning (line 330) | func (sp *syncPartition) setRunning(r bool) {
type partition (line 337) | type partition struct
type partitions (line 344) | type partitions
method encode (line 346) | func (p *partitions) encode() ([]byte, error) {
method decode (line 368) | func (p *partitions) decode(data []byte) error {
function partitionsFromStore (line 378) | func partitionsFromStore(store store.MessageStore) *partitions {
type syncMessageRequest (line 396) | type syncMessageRequest struct
method encode (line 402) | func (smr *syncMessageRequest) encode() ([]byte, error) {
method decode (line 406) | func (smr *syncMessageRequest) decode(data []byte) error {
type syncMessage (line 410) | type syncMessage struct
method encode (line 417) | func (sm *syncMessage) encode() ([]byte, error) {
method decode (line 421) | func (sm *syncMessage) decode(data []byte) error {
FILE: server/cluster_integration_test.go
function Test_Cluster_Subscribe_To_Random_Node (line 12) | func Test_Cluster_Subscribe_To_Random_Node(t *testing.T) {
function Test_Cluster_Integration (line 52) | func Test_Cluster_Integration(t *testing.T) {
function TestSynchronizerIntegration (line 134) | func TestSynchronizerIntegration(t *testing.T) {
FILE: server/config.go
constant defaultHttpListen (line 20) | defaultHttpListen = ":8080"
constant defaultHealthEndpoint (line 21) | defaultHealthEndpoint = "/admin/healthcheck"
constant defaultMetricsEndpoint (line 22) | defaultMetricsEndpoint = "/admin/metrics"
constant defaultKVSBackend (line 23) | defaultKVSBackend = "file"
constant defaultMSBackend (line 24) | defaultMSBackend = "file"
constant defaultStoragePath (line 25) | defaultStoragePath = "/var/lib/guble"
constant defaultNodePort (line 26) | defaultNodePort = "10000"
constant development (line 27) | development = "dev"
constant integration (line 28) | integration = "int"
constant preproduction (line 29) | preproduction = "pre"
constant production (line 30) | production = "prod"
constant memProfile (line 31) | memProfile = "mem"
constant cpuProfile (line 32) | cpuProfile = "cpu"
constant blockProfile (line 33) | blockProfile = "block"
type PostgresConfig (line 46) | type PostgresConfig struct
type ClusterConfig (line 54) | type ClusterConfig struct
type GubleConfig (line 60) | type GubleConfig struct
function logLevels (line 224) | func logLevels() (levels []string) {
function parseConfig (line 234) | func parseConfig() {
type tcpAddrList (line 243) | type tcpAddrList
method Set (line 245) | func (h *tcpAddrList) Set(value string) error {
method String (line 271) | func (h *tcpAddrList) String() string {
function tcpAddrListParser (line 265) | func tcpAddrListParser(s kingpin.Settings) (target *tcpAddrList) {
FILE: server/config_test.go
function TestParsingOfEnvironmentVariables (line 10) | func TestParsingOfEnvironmentVariables(t *testing.T) {
function TestParsingArgs (line 100) | func TestParsingArgs(t *testing.T) {
function assertArguments (line 143) | func assertArguments(a *assert.Assertions) {
function assertClusterRemotes (line 178) | func assertClusterRemotes(a *assert.Assertions) {
FILE: server/connector/connector.go
constant DefaultWorkers (line 20) | DefaultWorkers = 1
constant SubstitutePath (line 21) | SubstitutePath = "/substitute/"
type Sender (line 29) | type Sender interface
type SenderSetter (line 34) | type SenderSetter interface
type Metadata (line 39) | type Metadata struct
type ResponseHandler (line 43) | type ResponseHandler interface
type ResponseHandlerSetter (line 48) | type ResponseHandlerSetter interface
type Runner (line 53) | type Runner interface
type Connector (line 57) | type Connector interface
type ResponsiveConnector (line 68) | type ResponsiveConnector interface
type connector (line 73) | type connector struct
method initMuxRouter (line 120) | func (c *connector) initMuxRouter() {
method ServeHTTP (line 133) | func (c *connector) ServeHTTP(w http.ResponseWriter, req *http.Request) {
method GetPrefix (line 141) | func (c *connector) GetPrefix() string {
method GetList (line 146) | func (c *connector) GetList(w http.ResponseWriter, req *http.Request) {
method Post (line 179) | func (c *connector) Post(w http.ResponseWriter, req *http.Request) {
method Delete (line 205) | func (c *connector) Delete(w http.ResponseWriter, req *http.Request) {
method Substitute (line 230) | func (c *connector) Substitute(w http.ResponseWriter, req *http.Reques...
method Start (line 261) | func (c *connector) Start() error {
method Run (line 282) | func (c *connector) Run(s Subscriber) {
method restart (line 329) | func (c *connector) restart(s Subscriber) error {
method Stop (line 341) | func (c *connector) Stop() error {
method Manager (line 350) | func (c *connector) Manager() Manager {
method Context (line 354) | func (c *connector) Context() context.Context {
method ResponseHandler (line 358) | func (c *connector) ResponseHandler() ResponseHandler {
method SetResponseHandler (line 362) | func (c *connector) SetResponseHandler(handler ResponseHandler) {
method Sender (line 367) | func (c *connector) Sender() Sender {
method SetSender (line 371) | func (c *connector) SetSender(s Sender) {
type Config (line 90) | type Config struct
function NewConnector (line 98) | func NewConnector(router router.Router, sender Sender, config Config) (C...
FILE: server/connector/connector_test.go
type connectorMocks (line 18) | type connectorMocks struct
function TestConnector_PostSubscription (line 27) | func TestConnector_PostSubscription(t *testing.T) {
function TestConnector_PostSubscriptionNoMocks (line 72) | func TestConnector_PostSubscriptionNoMocks(t *testing.T) {
function TestConnector_DeleteSubscription (line 109) | func TestConnector_DeleteSubscription(t *testing.T) {
function TestConnector_GetList_And_Getters (line 138) | func TestConnector_GetList_And_Getters(t *testing.T) {
function TestConnector_GetListWithFilters (line 165) | func TestConnector_GetListWithFilters(t *testing.T) {
function TestConnector_StartWithSubscriptions (line 193) | func TestConnector_StartWithSubscriptions(t *testing.T) {
function TestConnector_Substitute (line 240) | func TestConnector_Substitute(t *testing.T) {
function TestConnector_SubstituteWrongPostBody (line 285) | func TestConnector_SubstituteWrongPostBody(t *testing.T) {
function createSubscriptions (line 328) | func createSubscriptions(t *testing.T, conn Connector, count int) {
function TestConnector_StartAndStopWithoutSubscribers (line 343) | func TestConnector_StartAndStopWithoutSubscribers(t *testing.T) {
function getTestConnector (line 366) | func getTestConnector(t *testing.T, config Config, mockManager bool, moc...
FILE: server/connector/manager.go
type Manager (line 11) | type Manager interface
type manager (line 23) | type manager struct
method Load (line 38) | func (m *manager) Load() error {
method Find (line 51) | func (m *manager) Find(key string) Subscriber {
method Create (line 61) | func (m *manager) Create(topic protocol.Path, params router.RouteParam...
method List (line 82) | func (m *manager) List() []Subscriber {
method Filter (line 93) | func (m *manager) Filter(filters map[string]string) (subscribers []Sub...
method Add (line 105) | func (m *manager) Add(s Subscriber) error {
method Update (line 121) | func (m *manager) Update(s Subscriber) error {
method putSubscriber (line 137) | func (m *manager) putSubscriber(s Subscriber) {
method deleteSubscriber (line 143) | func (m *manager) deleteSubscriber(s Subscriber) {
method Exists (line 149) | func (m *manager) Exists(key string) bool {
method Remove (line 157) | func (m *manager) Remove(s Subscriber) error {
method cancelSubscriber (line 175) | func (m *manager) cancelSubscriber(s Subscriber) {
method updateStore (line 182) | func (m *manager) updateStore(s Subscriber) error {
method removeStore (line 192) | func (m *manager) removeStore(s Subscriber) error {
function NewManager (line 30) | func NewManager(schema string, kvstore kvstore.KVStore) Manager {
FILE: server/connector/mocks_connector_gen_test.go
type MockConnector (line 16) | type MockConnector struct
method EXPECT (line 32) | func (_m *MockConnector) EXPECT() *_MockConnectorRecorder {
method Context (line 36) | func (_m *MockConnector) Context() context.Context {
method GetPrefix (line 46) | func (_m *MockConnector) GetPrefix() string {
method Manager (line 56) | func (_m *MockConnector) Manager() Manager {
method ResponseHandler (line 66) | func (_m *MockConnector) ResponseHandler() ResponseHandler {
method Run (line 76) | func (_m *MockConnector) Run(_param0 Subscriber) {
method Sender (line 84) | func (_m *MockConnector) Sender() Sender {
method ServeHTTP (line 94) | func (_m *MockConnector) ServeHTTP(_param0 http.ResponseWriter, _param...
method SetResponseHandler (line 102) | func (_m *MockConnector) SetResponseHandler(_param0 ResponseHandler) {
method SetSender (line 110) | func (_m *MockConnector) SetSender(_param0 Sender) {
method Start (line 118) | func (_m *MockConnector) Start() error {
method Stop (line 128) | func (_m *MockConnector) Stop() error {
type _MockConnectorRecorder (line 22) | type _MockConnectorRecorder struct
method Context (line 42) | func (_mr *_MockConnectorRecorder) Context() *gomock.Call {
method GetPrefix (line 52) | func (_mr *_MockConnectorRecorder) GetPrefix() *gomock.Call {
method Manager (line 62) | func (_mr *_MockConnectorRecorder) Manager() *gomock.Call {
method ResponseHandler (line 72) | func (_mr *_MockConnectorRecorder) ResponseHandler() *gomock.Call {
method Run (line 80) | func (_mr *_MockConnectorRecorder) Run(arg0 interface{}) *gomock.Call {
method Sender (line 90) | func (_mr *_MockConnectorRecorder) Sender() *gomock.Call {
method ServeHTTP (line 98) | func (_mr *_MockConnectorRecorder) ServeHTTP(arg0, arg1 interface{}) *...
method SetResponseHandler (line 106) | func (_mr *_MockConnectorRecorder) SetResponseHandler(arg0 interface{}...
method SetSender (line 114) | func (_mr *_MockConnectorRecorder) SetSender(arg0 interface{}) *gomock...
method Start (line 124) | func (_mr *_MockConnectorRecorder) Start() *gomock.Call {
method Stop (line 134) | func (_mr *_MockConnectorRecorder) Stop() *gomock.Call {
function NewMockConnector (line 26) | func NewMockConnector(ctrl *gomock.Controller) *MockConnector {
type MockSender (line 139) | type MockSender struct
method EXPECT (line 155) | func (_m *MockSender) EXPECT() *_MockSenderRecorder {
method Send (line 159) | func (_m *MockSender) Send(_param0 Request) (interface{}, error) {
type _MockSenderRecorder (line 145) | type _MockSenderRecorder struct
method Send (line 166) | func (_mr *_MockSenderRecorder) Send(arg0 interface{}) *gomock.Call {
function NewMockSender (line 149) | func NewMockSender(ctrl *gomock.Controller) *MockSender {
type MockResponseHandler (line 171) | type MockResponseHandler struct
method EXPECT (line 187) | func (_m *MockResponseHandler) EXPECT() *_MockResponseHandlerRecorder {
method HandleResponse (line 191) | func (_m *MockResponseHandler) HandleResponse(_param0 Request, _param1...
type _MockResponseHandlerRecorder (line 177) | type _MockResponseHandlerRecorder struct
method HandleResponse (line 197) | func (_mr *_MockResponseHandlerRecorder) HandleResponse(arg0, arg1, ar...
function NewMockResponseHandler (line 181) | func NewMockResponseHandler(ctrl *gomock.Controller) *MockResponseHandler {
type MockManager (line 202) | type MockManager struct
method EXPECT (line 218) | func (_m *MockManager) EXPECT() *_MockManagerRecorder {
method Add (line 222) | func (_m *MockManager) Add(_param0 Subscriber) error {
method Create (line 232) | func (_m *MockManager) Create(_param0 protocol.Path, _param1 router.Ro...
method Exists (line 243) | func (_m *MockManager) Exists(_param0 string) bool {
method Filter (line 253) | func (_m *MockManager) Filter(_param0 map[string]string) []Subscriber {
method Find (line 263) | func (_m *MockManager) Find(_param0 string) Subscriber {
method List (line 273) | func (_m *MockManager) List() []Subscriber {
method Load (line 283) | func (_m *MockManager) Load() error {
method Remove (line 293) | func (_m *MockManager) Remove(_param0 Subscriber) error {
method Update (line 303) | func (_m *MockManager) Update(_param0 Subscriber) error {
type _MockManagerRecorder (line 208) | type _MockManagerRecorder struct
method Add (line 228) | func (_mr *_MockManagerRecorder) Add(arg0 interface{}) *gomock.Call {
method Create (line 239) | func (_mr *_MockManagerRecorder) Create(arg0, arg1 interface{}) *gomoc...
method Exists (line 249) | func (_mr *_MockManagerRecorder) Exists(arg0 interface{}) *gomock.Call {
method Filter (line 259) | func (_mr *_MockManagerRecorder) Filter(arg0 interface{}) *gomock.Call {
method Find (line 269) | func (_mr *_MockManagerRecorder) Find(arg0 interface{}) *gomock.Call {
method List (line 279) | func (_mr *_MockManagerRecorder) List() *gomock.Call {
method Load (line 289) | func (_mr *_MockManagerRecorder) Load() *gomock.Call {
method Remove (line 299) | func (_mr *_MockManagerRecorder) Remove(arg0 interface{}) *gomock.Call {
method Update (line 309) | func (_mr *_MockManagerRecorder) Update(arg0 interface{}) *gomock.Call {
function NewMockManager (line 212) | func NewMockManager(ctrl *gomock.Controller) *MockManager {
type MockQueue (line 314) | type MockQueue struct
method EXPECT (line 330) | func (_m *MockQueue) EXPECT() *_MockQueueRecorder {
method Push (line 334) | func (_m *MockQueue) Push(_param0 Request) error {
method ResponseHandler (line 344) | func (_m *MockQueue) ResponseHandler() ResponseHandler {
method Sender (line 354) | func (_m *MockQueue) Sender() Sender {
method SetResponseHandler (line 364) | func (_m *MockQueue) SetResponseHandler(_param0 ResponseHandler) {
method SetSender (line 372) | func (_m *MockQueue) SetSender(_param0 Sender) {
method Start (line 380) | func (_m *MockQueue) Start() error {
method Stop (line 390) | func (_m *MockQueue) Stop() error {
type _MockQueueRecorder (line 320) | type _MockQueueRecorder struct
method Push (line 340) | func (_mr *_MockQueueRecorder) Push(arg0 interface{}) *gomock.Call {
method ResponseHandler (line 350) | func (_mr *_MockQueueRecorder) ResponseHandler() *gomock.Call {
method Sender (line 360) | func (_mr *_MockQueueRecorder) Sender() *gomock.Call {
method SetResponseHandler (line 368) | func (_mr *_MockQueueRecorder) SetResponseHandler(arg0 interface{}) *g...
method SetSender (line 376) | func (_mr *_MockQueueRecorder) SetSender(arg0 interface{}) *gomock.Call {
method Start (line 386) | func (_mr *_MockQueueRecorder) Start() *gomock.Call {
method Stop (line 396) | func (_mr *_MockQueueRecorder) Stop() *gomock.Call {
function NewMockQueue (line 324) | func NewMockQueue(ctrl *gomock.Controller) *MockQueue {
type MockRequest (line 401) | type MockRequest struct
method EXPECT (line 417) | func (_m *MockRequest) EXPECT() *_MockRequestRecorder {
method Message (line 421) | func (_m *MockRequest) Message() *protocol.Message {
method Subscriber (line 431) | func (_m *MockRequest) Subscriber() Subscriber {
type _MockRequestRecorder (line 407) | type _MockRequestRecorder struct
method Message (line 427) | func (_mr *_MockRequestRecorder) Message() *gomock.Call {
method Subscriber (line 437) | func (_mr *_MockRequestRecorder) Subscriber() *gomock.Call {
function NewMockRequest (line 411) | func NewMockRequest(ctrl *gomock.Controller) *MockRequest {
type MockSubscriber (line 442) | type MockSubscriber struct
method EXPECT (line 458) | func (_m *MockSubscriber) EXPECT() *_MockSubscriberRecorder {
method Cancel (line 462) | func (_m *MockSubscriber) Cancel() {
method Encode (line 470) | func (_m *MockSubscriber) Encode() ([]byte, error) {
method Filter (line 481) | func (_m *MockSubscriber) Filter(_param0 map[string]string) bool {
method Key (line 491) | func (_m *MockSubscriber) Key() string {
method Loop (line 501) | func (_m *MockSubscriber) Loop(_param0 context.Context, _param1 Queue)...
method Reset (line 511) | func (_m *MockSubscriber) Reset() error {
method Route (line 521) | func (_m *MockSubscriber) Route() *router.Route {
method SetLastID (line 531) | func (_m *MockSubscriber) SetLastID(_param0 uint64) {
type _MockSubscriberRecorder (line 448) | type _MockSubscriberRecorder struct
method Cancel (line 466) | func (_mr *_MockSubscriberRecorder) Cancel() *gomock.Call {
method Encode (line 477) | func (_mr *_MockSubscriberRecorder) Encode() *gomock.Call {
method Filter (line 487) | func (_mr *_MockSubscriberRecorder) Filter(arg0 interface{}) *gomock.C...
method Key (line 497) | func (_mr *_MockSubscriberRecorder) Key() *gomock.Call {
method Loop (line 507) | func (_mr *_MockSubscriberRecorder) Loop(arg0, arg1 interface{}) *gomo...
method Reset (line 517) | func (_mr *_MockSubscriberRecorder) Reset() *gomock.Call {
method Route (line 527) | func (_mr *_MockSubscriberRecorder) Route() *gomock.Call {
method SetLastID (line 535) | func (_mr *_MockSubscriberRecorder) SetLastID(arg0 interface{}) *gomoc...
function NewMockSubscriber (line 452) | func NewMockSubscriber(ctrl *gomock.Controller) *MockSubscriber {
FILE: server/connector/mocks_kvstore_gen_test.go
type MockKVStore (line 11) | type MockKVStore struct
method EXPECT (line 27) | func (_m *MockKVStore) EXPECT() *_MockKVStoreRecorder {
method Delete (line 31) | func (_m *MockKVStore) Delete(_param0 string, _param1 string) error {
method Get (line 41) | func (_m *MockKVStore) Get(_param0 string, _param1 string) ([]byte, bo...
method Iterate (line 53) | func (_m *MockKVStore) Iterate(_param0 string, _param1 string) chan [2...
method IterateKeys (line 63) | func (_m *MockKVStore) IterateKeys(_param0 string, _param1 string) cha...
method Put (line 73) | func (_m *MockKVStore) Put(_param0 string, _param1 string, _param2 []b...
type _MockKVStoreRecorder (line 17) | type _MockKVStoreRecorder struct
method Delete (line 37) | func (_mr *_MockKVStoreRecorder) Delete(arg0, arg1 interface{}) *gomoc...
method Get (line 49) | func (_mr *_MockKVStoreRecorder) Get(arg0, arg1 interface{}) *gomock.C...
method Iterate (line 59) | func (_mr *_MockKVStoreRecorder) Iterate(arg0, arg1 interface{}) *gomo...
method IterateKeys (line 69) | func (_mr *_MockKVStoreRecorder) IterateKeys(arg0, arg1 interface{}) *...
method Put (line 79) | func (_mr *_MockKVStoreRecorder) Put(arg0, arg1, arg2 interface{}) *go...
function NewMockKVStore (line 21) | func NewMockKVStore(ctrl *gomock.Controller) *MockKVStore {
FILE: server/connector/mocks_router_gen_test.go
type MockRouter (line 17) | type MockRouter struct
method EXPECT (line 33) | func (_m *MockRouter) EXPECT() *_MockRouterRecorder {
method AccessManager (line 37) | func (_m *MockRouter) AccessManager() (auth.AccessManager, error) {
method Cluster (line 48) | func (_m *MockRouter) Cluster() *cluster.Cluster {
method Done (line 58) | func (_m *MockRouter) Done() <-chan bool {
method Fetch (line 68) | func (_m *MockRouter) Fetch(_param0 *store.FetchRequest) error {
method GetSubscribers (line 78) | func (_m *MockRouter) GetSubscribers(_param0 string) ([]byte, error) {
method HandleMessage (line 89) | func (_m *MockRouter) HandleMessage(_param0 *protocol.Message) error {
method KVStore (line 99) | func (_m *MockRouter) KVStore() (kvstore.KVStore, error) {
method MessageStore (line 110) | func (_m *MockRouter) MessageStore() (store.MessageStore, error) {
method Subscribe (line 121) | func (_m *MockRouter) Subscribe(_param0 *router.Route) (*router.Route,...
method Unsubscribe (line 132) | func (_m *MockRouter) Unsubscribe(_param0 *router.Route) {
type _MockRouterRecorder (line 23) | type _MockRouterRecorder struct
method AccessManager (line 44) | func (_mr *_MockRouterRecorder) AccessManager() *gomock.Call {
method Cluster (line 54) | func (_mr *_MockRouterRecorder) Cluster() *gomock.Call {
method Done (line 64) | func (_mr *_MockRouterRecorder) Done() *gomock.Call {
method Fetch (line 74) | func (_mr *_MockRouterRecorder) Fetch(arg0 interface{}) *gomock.Call {
method GetSubscribers (line 85) | func (_mr *_MockRouterRecorder) GetSubscribers(arg0 interface{}) *gomo...
method HandleMessage (line 95) | func (_mr *_MockRouterRecorder) HandleMessage(arg0 interface{}) *gomoc...
method KVStore (line 106) | func (_mr *_MockRouterRecorder) KVStore() *gomock.Call {
method MessageStore (line 117) | func (_mr *_MockRouterRecorder) MessageStore() *gomock.Call {
method Subscribe (line 128) | func (_mr *_MockRouterRecorder) Subscribe(arg0 interface{}) *gomock.Ca...
method Unsubscribe (line 136) | func (_mr *_MockRouterRecorder) Unsubscribe(arg0 interface{}) *gomock....
function NewMockRouter (line 27) | func NewMockRouter(ctrl *gomock.Controller) *MockRouter {
FILE: server/connector/queue.go
type Queue (line 12) | type Queue interface
type queue (line 21) | type queue struct
method SetResponseHandler (line 40) | func (q *queue) SetResponseHandler(rh ResponseHandler) {
method ResponseHandler (line 44) | func (q *queue) ResponseHandler() ResponseHandler {
method Sender (line 48) | func (q *queue) Sender() Sender {
method SetSender (line 52) | func (q *queue) SetSender(s Sender) {
method Start (line 57) | func (q *queue) Start() error {
method worker (line 65) | func (q *queue) worker(i int) {
method handle (line 72) | func (q *queue) handle(request Request) {
method Push (line 101) | func (q *queue) Push(request Request) error {
method Stop (line 118) | func (q *queue) Stop() error {
function NewQueue (line 31) | func NewQueue(sender Sender, nWorkers int) Queue {
FILE: server/connector/request.go
type Request (line 5) | type Request interface
type request (line 10) | type request struct
method Subscriber (line 19) | func (r *request) Subscriber() Subscriber {
method Message (line 23) | func (r *request) Message() *protocol.Message {
function NewRequest (line 15) | func NewRequest(s Subscriber, m *protocol.Message) Request {
FILE: server/connector/subscriber.go
type Subscriber (line 25) | type Subscriber interface
type SubscriberData (line 38) | type SubscriberData struct
method newRoute (line 44) | func (sd *SubscriberData) newRoute() *router.Route {
type subscriber (line 56) | type subscriber struct
method String (line 88) | func (s *subscriber) String() string {
method Reset (line 92) | func (s *subscriber) Reset() error {
method Key (line 98) | func (s *subscriber) Key() string {
method Filter (line 105) | func (s *subscriber) Filter(filters map[string]string) bool {
method Route (line 109) | func (s *subscriber) Route() *router.Route {
method Loop (line 113) | func (s *subscriber) Loop(ctx context.Context, q Queue) error {
method SetLastID (line 142) | func (s *subscriber) SetLastID(ID uint64) {
method Cancel (line 146) | func (s *subscriber) Cancel() {
method Encode (line 152) | func (s *subscriber) Encode() ([]byte, error) {
function NewSubscriber (line 64) | func NewSubscriber(topic protocol.Path, params router.RouteParams, lastI...
function NewSubscriberFromData (line 72) | func NewSubscriberFromData(data SubscriberData) Subscriber {
function NewSubscriberFromJSON (line 79) | func NewSubscriberFromJSON(data []byte) (Subscriber, error) {
function GenerateKey (line 156) | func GenerateKey(topic string, params map[string]string) string {
FILE: server/connector/substitution.go
type substitution (line 3) | type substitution struct
method isValid (line 9) | func (s *substitution) isValid() bool {
FILE: server/fcm/fcm.go
constant schema (line 16) | schema = "fcm_registration"
constant deviceTokenKey (line 18) | deviceTokenKey = "device_token"
constant userIDKEy (line 19) | userIDKEy = "user_id"
type Config (line 23) | type Config struct
type fcm (line 34) | type fcm struct
method Start (line 58) | func (f *fcm) Start() error {
method startMetrics (line 66) | func (f *fcm) startMetrics() {
method startIntervalMetric (line 82) | func (f *fcm) startIntervalMetric(m metrics.Map, td time.Duration) {
method HandleResponse (line 86) | func (f *fcm) HandleResponse(request connector.Request, responseIface ...
method replaceCanonical (line 142) | func (f *fcm) replaceCanonical(subscriber connector.Subscriber, newTok...
function New (line 40) | func New(router router.Router, sender connector.Sender, config Config) (...
FILE: server/fcm/fcm_metrics.go
constant currentTotalMessagesLatenciesKey (line 23) | currentTotalMessagesLatenciesKey = "current_messages_total_latencies_nanos"
constant currentTotalMessagesKey (line 24) | currentTotalMessagesKey = "current_messages_count"
constant currentTotalErrorsLatenciesKey (line 25) | currentTotalErrorsLatenciesKey = "current_errors_total_latencies_nanos"
constant currentTotalErrorsKey (line 26) | currentTotalErrorsKey = "current_errors_count"
function processAndResetIntervalMetrics (line 29) | func processAndResetIntervalMetrics(m metrics.Map, td time.Duration, t t...
function resetIntervalMetrics (line 45) | func resetIntervalMetrics(m metrics.Map, t time.Time) {
function addToLatenciesAndCountsMaps (line 53) | func addToLatenciesAndCountsMaps(latenciesKey string, countKey string, l...
FILE: server/fcm/fcm_sender.go
constant sendRetries (line 16) | sendRetries = 5
constant sendTimeout (line 19) | sendTimeout = time.Second
type sender (line 22) | type sender struct
method Send (line 32) | func (s *sender) Send(request connector.Request) (interface{}, error) {
function NewSender (line 26) | func NewSender(apiKey string) *sender {
function fcmMessage (line 40) | func fcmMessage(message *protocol.Message) *gcm.Message {
function isValidResponseError (line 66) | func isValidResponseError(err error) bool {
FILE: server/fcm/fcm_test.go
type mocks (line 31) | type mocks struct
function TestConnector_GetErrorMessageFromFCM (line 37) | func TestConnector_GetErrorMessageFromFCM(t *testing.T) {
function TestFCMFormatMessage (line 97) | func TestFCMFormatMessage(t *testing.T) {
function testFCM (line 181) | func testFCM(t *testing.T, mockStore bool) (connector.ResponsiveConnecto...
function postSubscription (line 216) | func postSubscription(t *testing.T, fcmConn connector.ResponsiveConnecto...
function deleteSubscription (line 228) | func deleteSubscription(t *testing.T, fcmConn connector.ResponsiveConnec...
function removeTrailingSlash (line 240) | func removeTrailingSlash(path string) string {
FILE: server/fcm/json_error.go
type jsonError (line 3) | type jsonError struct
method Error (line 7) | func (e *jsonError) Error() string {
FILE: server/fcm/mocks_gcm_gen_test.go
type MockSender (line 12) | type MockSender struct
method EXPECT (line 28) | func (_m *MockSender) EXPECT() *_MockSenderRecorder {
method Send (line 32) | func (_m *MockSender) Send(_param0 *gcm.Message) (*gcm.Response, error) {
type _MockSenderRecorder (line 18) | type _MockSenderRecorder struct
method Send (line 39) | func (_mr *_MockSenderRecorder) Send(arg0 interface{}) *gomock.Call {
function NewMockSender (line 22) | func NewMockSender(ctrl *gomock.Controller) *MockSender {
FILE: server/fcm/mocks_kvstore_gen_test.go
type MockKVStore (line 11) | type MockKVStore struct
method EXPECT (line 27) | func (_m *MockKVStore) EXPECT() *_MockKVStoreRecorder {
method Delete (line 31) | func (_m *MockKVStore) Delete(_param0 string, _param1 string) error {
method Get (line 41) | func (_m *MockKVStore) Get(_param0 string, _param1 string) ([]byte, bo...
method Iterate (line 53) | func (_m *MockKVStore) Iterate(_param0 string, _param1 string) chan [2...
method IterateKeys (line 63) | func (_m *MockKVStore) IterateKeys(_param0 string, _param1 string) cha...
method Put (line 73) | func (_m *MockKVStore) Put(_param0 string, _param1 string, _param2 []b...
type _MockKVStoreRecorder (line 17) | type _MockKVStoreRecorder struct
method Delete (line 37) | func (_mr *_MockKVStoreRecorder) Delete(arg0, arg1 interface{}) *gomoc...
method Get (line 49) | func (_mr *_MockKVStoreRecorder) Get(arg0, arg1 interface{}) *gomock.C...
method Iterate (line 59) | func (_mr *_MockKVStoreRecorder) Iterate(arg0, arg1 interface{}) *gomo...
method IterateKeys (line 69) | func (_mr *_MockKVStoreRecorder) IterateKeys(arg0, arg1 interface{}) *...
method Put (line 79) | func (_mr *_MockKVStoreRecorder) Put(arg0, arg1, arg2 interface{}) *go...
function NewMockKVStore (line 21) | func NewMockKVStore(ctrl *gomock.Controller) *MockKVStore {
FILE: server/fcm/mocks_router_gen_test.go
type MockRouter (line 17) | type MockRouter struct
method EXPECT (line 33) | func (_m *MockRouter) EXPECT() *_MockRouterRecorder {
method AccessManager (line 37) | func (_m *MockRouter) AccessManager() (auth.AccessManager, error) {
method Cluster (line 48) | func (_m *MockRouter) Cluster() *cluster.Cluster {
method Done (line 58) | func (_m *MockRouter) Done() <-chan bool {
method Fetch (line 68) | func (_m *MockRouter) Fetch(_param0 *store.FetchRequest) error {
method GetSubscribers (line 78) | func (_m *MockRouter) GetSubscribers(_param0 string) ([]byte, error) {
method HandleMessage (line 89) | func (_m *MockRouter) HandleMessage(_param0 *protocol.Message) error {
method KVStore (line 99) | func (_m *MockRouter) KVStore() (kvstore.KVStore, error) {
method MessageStore (line 110) | func (_m *MockRouter) MessageStore() (store.MessageStore, error) {
method Subscribe (line 121) | func (_m *MockRouter) Subscribe(_param0 *router.Route) (*router.Route,...
method Unsubscribe (line 132) | func (_m *MockRouter) Unsubscribe(_param0 *router.Route) {
type _MockRouterRecorder (line 23) | type _MockRouterRecorder struct
method AccessManager (line 44) | func (_mr *_MockRouterRecorder) AccessManager() *gomock.Call {
method Cluster (line 54) | func (_mr *_MockRouterRecorder) Cluster() *gomock.Call {
method Done (line 64) | func (_mr *_MockRouterRecorder) Done() *gomock.Call {
method Fetch (line 74) | func (_mr *_MockRouterRecorder) Fetch(arg0 interface{}) *gomock.Call {
method GetSubscribers (line 85) | func (_mr *_MockRouterRecorder) GetSubscribers(arg0 interface{}) *gomo...
method HandleMessage (line 95) | func (_mr *_MockRouterRecorder) HandleMessage(arg0 interface{}) *gomoc...
method KVStore (line 106) | func (_mr *_MockRouterRecorder) KVStore() *gomock.Call {
method MessageStore (line 117) | func (_mr *_MockRouterRecorder) MessageStore() *gomock.Call {
method Subscribe (line 128) | func (_mr *_MockRouterRecorder) Subscribe(arg0 interface{}) *gomock.Ca...
method Unsubscribe (line 136) | func (_mr *_MockRouterRecorder) Unsubscribe(arg0 interface{}) *gomock....
function NewMockRouter (line 27) | func NewMockRouter(ctrl *gomock.Controller) *MockRouter {
FILE: server/fcm/mocks_store_gen_test.go
type MockMessageStore (line 13) | type MockMessageStore struct
method EXPECT (line 29) | func (_m *MockMessageStore) EXPECT() *_MockMessageStoreRecorder {
method DoInTx (line 33) | func (_m *MockMessageStore) DoInTx(_param0 string, _param1 func(uint64...
method Fetch (line 43) | func (_m *MockMessageStore) Fetch(_param0 *store.FetchRequest) {
method GenerateNextMsgID (line 51) | func (_m *MockMessageStore) GenerateNextMsgID(_param0 string, _param1 ...
method MaxMessageID (line 63) | func (_m *MockMessageStore) MaxMessageID(_param0 string) (uint64, erro...
method Partition (line 74) | func (_m *MockMessageStore) Partition(_param0 string) (store.MessagePa...
method Partitions (line 85) | func (_m *MockMessageStore) Partitions() ([]store.MessagePartition, er...
method Store (line 96) | func (_m *MockMessageStore) Store(_param0 string, _param1 uint64, _par...
method StoreMessage (line 106) | func (_m *MockMessageStore) StoreMessage(_param0 *protocol.Message, _p...
type _MockMessageStoreRecorder (line 19) | type _MockMessageStoreRecorder struct
method DoInTx (line 39) | func (_mr *_MockMessageStoreRecorder) DoInTx(arg0, arg1 interface{}) *...
method Fetch (line 47) | func (_mr *_MockMessageStoreRecorder) Fetch(arg0 interface{}) *gomock....
method GenerateNextMsgID (line 59) | func (_mr *_MockMessageStoreRecorder) GenerateNextMsgID(arg0, arg1 int...
method MaxMessageID (line 70) | func (_mr *_MockMessageStoreRecorder) MaxMessageID(arg0 interface{}) *...
method Partition (line 81) | func (_mr *_MockMessageStoreRecorder) Partition(arg0 interface{}) *gom...
method Partitions (line 92) | func (_mr *_MockMessageStoreRecorder) Partitions() *gomock.Call {
method Store (line 102) | func (_mr *_MockMessageStoreRecorder) Store(arg0, arg1, arg2 interface...
method StoreMessage (line 113) | func (_mr *_MockMessageStoreRecorder) StoreMessage(arg0, arg1 interfac...
function NewMockMessageStore (line 23) | func NewMockMessageStore(ctrl *gomock.Controller) *MockMessageStore {
FILE: server/fcm/testutil.go
constant SuccessFCMResponse (line 13) | SuccessFCMResponse = `{
constant ErrorFCMResponse (line 27) | ErrorFCMResponse = `{
function NewSenderWithMock (line 43) | func NewSenderWithMock(gcmSender gcm.Sender) *sender {
type FCMSender (line 47) | type FCMSender
method Send (line 49) | func (fcms FCMSender) Send(message *gcm.Message) (*gcm.Response, error) {
function CreateFcmSender (line 53) | func CreateFcmSender(body string, doneC chan bool, to time.Duration) (co...
FILE: server/fcm_integration_test.go
type fcmMetricsMap (line 28) | type fcmMetricsMap struct
type fcmMetrics (line 35) | type fcmMetrics struct
type routerMetrics (line 43) | type routerMetrics struct
type expectedValues (line 48) | type expectedValues struct
function TestFCMRestart (line 56) | func TestFCMRestart(t *testing.T) {
function serviceSetUp (line 129) | func serviceSetUp(t *testing.T) (*service.Service, func()) {
function clientSetUp (line 159) | func clientSetUp(t *testing.T, service *service.Service) client.Client {
function subscriptionSetUp (line 166) | func subscriptionSetUp(t *testing.T, service *service.Service) {
function assertMetrics (line 184) | func assertMetrics(a *assert.Assertions, s *service.Service, expected ex...
FILE: server/gubled.go
constant fileOption (line 39) | fileOption = "file"
function Main (line 203) | func Main() {
function StartService (line 254) | func StartService() *service.Service {
function exitIfInvalidClusterParams (line 300) | func exitIfInvalidClusterParams(nodeID uint8, nodePort int, remotes []*n...
function waitForTermination (line 311) | func waitForTermination(callback func()) {
FILE: server/gubled_test.go
function TestValidateStoragePath (line 17) | func TestValidateStoragePath(t *testing.T) {
function TestCreateKVStoreBackend (line 35) | func TestCreateKVStoreBackend(t *testing.T) {
function TestFCMOnlyStartedIfEnabled (line 50) | func TestFCMOnlyStartedIfEnabled(t *testing.T) {
function containsFCMModule (line 68) | func containsFCMModule(modules []interface{}) bool {
function TestPanicOnMissingFCMApiKey (line 77) | func TestPanicOnMissingFCMApiKey(t *testing.T) {
function TestCreateStoreBackendPanicInvalidBackend (line 94) | func TestCreateStoreBackendPanicInvalidBackend(t *testing.T) {
function TestStartServiceModules (line 107) | func TestStartServiceModules(t *testing.T) {
function initRouterMock (line 136) | func initRouterMock() *MockRouter {
FILE: server/integration_test.go
function initServerAndClients (line 23) | func initServerAndClients(t *testing.T) (*service.Service, client.Client...
function expectStatusMessage (line 55) | func expectStatusMessage(t *testing.T, client client.Client, name string...
function checkConnectedNotificationJSON (line 68) | func checkConnectedNotificationJSON(t *testing.T, user string, connected...
type Subscriber (line 79) | type Subscriber struct
function TestSubscribersIntegration (line 84) | func TestSubscribersIntegration(t *testing.T) {
function subscribeMultipleClients (line 113) | func subscribeMultipleClients(t *testing.T, service *service.Service, no...
FILE: server/kvstore/common_test.go
function CommonTestPutGetDelete (line 17) | func CommonTestPutGetDelete(t *testing.T, kvs1 KVStore, kvs2 KVStore) {
function CommonTestIterate (line 42) | func CommonTestIterate(t *testing.T, kvs1 KVStore, kvs2 KVStore) {
function assertChannelContainsEntries (line 68) | func assertChannelContainsEntries(a *assert.Assertions, entryC chan [2]s...
function CommonTestIterateKeys (line 90) | func CommonTestIterateKeys(t *testing.T, kvs1 KVStore, kvs2 KVStore) {
function assertChannelContains (line 113) | func assertChannelContains(a *assert.Assertions, entryC chan string, exp...
function CommonBenchmarkPutGet (line 135) | func CommonBenchmarkPutGet(b *testing.B, s KVStore) {
function assertGet (line 149) | func assertGet(a *assert.Assertions, s KVStore, schema string, key strin...
function assertGetNoExist (line 156) | func assertGetNoExist(a *assert.Assertions, s KVStore, schema string, ke...
function randString (line 163) | func randString(n int) string {
function tempFilename (line 173) | func tempFilename() string {
FILE: server/kvstore/gorm.go
constant responseChannelSize (line 12) | responseChannelSize = 100
type kvEntry (line 15) | type kvEntry struct
type kvStore (line 22) | type kvStore struct
method Stop (line 27) | func (store *kvStore) Stop() error {
method Check (line 36) | func (store *kvStore) Check() error {
method Put (line 49) | func (store *kvStore) Put(schema, key string, value []byte) error {
method Get (line 57) | func (store *kvStore) Get(schema, key string) ([]byte, bool, error) {
method Iterate (line 69) | func (store *kvStore) Iterate(schema string, keyPrefix string) chan [2...
method IterateKeys (line 89) | func (store *kvStore) IterateKeys(schema string, keyPrefix string) cha...
method Delete (line 109) | func (store *kvStore) Delete(schema, key string) error {
FILE: server/kvstore/kvstore.go
type KVStore (line 4) | type KVStore interface
FILE: server/kvstore/memory.go
type MemoryKVStore (line 9) | type MemoryKVStore struct
method Put (line 22) | func (kvStore *MemoryKVStore) Put(schema, key string, value []byte) er...
method Get (line 31) | func (kvStore *MemoryKVStore) Get(schema, key string) ([]byte, bool, e...
method Delete (line 42) | func (kvStore *MemoryKVStore) Delete(schema, key string) error {
method Iterate (line 52) | func (kvStore *MemoryKVStore) Iterate(schema string, keyPrefix string)...
method IterateKeys (line 72) | func (kvStore *MemoryKVStore) IterateKeys(schema string, keyPrefix str...
method getSchema (line 91) | func (kvStore *MemoryKVStore) getSchema(schema string) map[string][]by...
function NewMemoryKVStore (line 15) | func NewMemoryKVStore() *MemoryKVStore {
FILE: server/kvstore/memory_test.go
function TestMemoryPutGetDelete (line 7) | func TestMemoryPutGetDelete(t *testing.T) {
function TestMemoryIterateKeys (line 12) | func TestMemoryIterateKeys(t *testing.T) {
function TestMemoryIterate (line 17) | func TestMemoryIterate(t *testing.T) {
function BenchmarkMemoryPutGet (line 22) | func BenchmarkMemoryPutGet(b *testing.B) {
FILE: server/kvstore/postgres.go
constant postgresGormLogMode (line 12) | postgresGormLogMode = false
type PostgresKVStore (line 15) | type PostgresKVStore struct
method Open (line 29) | func (kvStore *PostgresKVStore) Open() error {
function NewPostgresKVStore (line 21) | func NewPostgresKVStore(postgresConfig PostgresConfig) *PostgresKVStore {
FILE: server/kvstore/postgres_config.go
type PostgresConfig (line 7) | type PostgresConfig struct
method connectionString (line 13) | func (pc PostgresConfig) connectionString() string {
FILE: server/kvstore/postgres_config_test.go
function TestPostgresConfig_String (line 8) | func TestPostgresConfig_String(t *testing.T) {
FILE: server/kvstore/postgres_test.go
function BenchmarkPostgresKVStore_PutGet (line 8) | func BenchmarkPostgresKVStore_PutGet(b *testing.B) {
function TestPostgresKVStore_PutGetDelete (line 14) | func TestPostgresKVStore_PutGetDelete(t *testing.T) {
function TestPostgresKVStore_Iterate (line 20) | func TestPostgresKVStore_Iterate(t *testing.T) {
function TestPostgresKVStore_IterateKeys (line 26) | func TestPostgresKVStore_IterateKeys(t *testing.T) {
function TestPostgresKVStore_Check (line 32) | func TestPostgresKVStore_Check(t *testing.T) {
function TestPostgresKVStore_Open (line 47) | func TestPostgresKVStore_Open(t *testing.T) {
function aPostgresConfig (line 54) | func aPostgresConfig() PostgresConfig {
function invalidPostgresConfig (line 68) | func invalidPostgresConfig() PostgresConfig {
FILE: server/kvstore/sqlite.go
constant sqliteMaxIdleConns (line 19) | sqliteMaxIdleConns = 2
constant sqliteMaxOpenConns (line 20) | sqliteMaxOpenConns = 5
constant sqliteGormLogMode (line 21) | sqliteGormLogMode = false
type SqliteKVStore (line 27) | type SqliteKVStore struct
method Open (line 47) | func (kvStore *SqliteKVStore) Open() error {
function NewSqliteKVStore (line 34) | func NewSqliteKVStore(filename string, syncOnWrite bool) *SqliteKVStore {
function ensureWriteableDirectory (line 90) | func ensureWriteableDirectory(dir string) error {
FILE: server/kvstore/sqlite_test.go
function BenchmarkSqlitePutGet (line 9) | func BenchmarkSqlitePutGet(b *testing.B) {
function TestSqlitePutGetDelete (line 18) | func TestSqlitePutGetDelete(t *testing.T) {
function TestSqliteIterate (line 27) | func TestSqliteIterate(t *testing.T) {
function TestSqliteIterateKeys (line 37) | func TestSqliteIterateKeys(t *testing.T) {
function TestCheck_SqlKVStore (line 47) | func TestCheck_SqlKVStore(t *testing.T) {
FILE: server/metrics/average.go
type average (line 7) | type average struct
method String (line 18) | func (a average) String() string {
function newAverage (line 11) | func newAverage(total, cases, scale int64, defaultAverageJSONValue strin...
FILE: server/metrics/average_test.go
function TestAverage_String (line 9) | func TestAverage_String(t *testing.T) {
FILE: server/metrics/disabled.go
type dummyInt (line 10) | type dummyInt struct
method Add (line 13) | func (v *dummyInt) Add(delta int64) {}
method Set (line 14) | func (v *dummyInt) Set(value int64) {}
function NewInt (line 17) | func NewInt(name string) Int {
type dummyMap (line 21) | type dummyMap struct
method Init (line 24) | func (v *dummyMap) Init() *expvar.Map { return nil }
method Get (line 25) | func (v *dummyMap) Get(key string) expvar.Var { return nil }
method Set (line 26) | func (v *dummyMap) Set(key string, av expvar.Var) {}
method Add (line 27) | func (v *dummyMap) Add(key string, delta int64) {}
function NewMap (line 30) | func NewMap(name string) Map {
function RegisterInterval (line 34) | func RegisterInterval(m Map, td time.Duration, reset func(Map, time.Time...
FILE: server/metrics/enabled.go
function NewInt (line 12) | func NewInt(name string) Int {
function NewMap (line 16) | func NewMap(name string) Map {
function RegisterInterval (line 20) | func RegisterInterval(ctx context.Context, m Map, td time.Duration, rese...
FILE: server/metrics/enabled_test.go
function TestNewInt (line 10) | func TestNewInt(t *testing.T) {
FILE: server/metrics/int.go
type Int (line 4) | type Int interface
FILE: server/metrics/map.go
type Map (line 10) | type Map interface
function SetRate (line 17) | func SetRate(m Map, key string, value expvar.Var, timeframe, unit time.D...
function SetAverage (line 29) | func SetAverage(m Map, key string, totalVar, casesVar expvar.Var, scale ...
function AddToMaps (line 42) | func AddToMaps(key string, value int64, maps ...Map) {
FILE: server/metrics/metrics.go
constant DefaultAverageLatencyJSONValue (line 22) | DefaultAverageLatencyJSONValue = "\"\""
constant MilliPerNano (line 23) | MilliPerNano = 1000000
function HttpHandler (line 27) | func HttpHandler(rw http.ResponseWriter, r *http.Request) {
function writeMetrics (line 32) | func writeMetrics(w io.Writer) {
function LogOnDebugLevel (line 47) | func LogOnDebugLevel() {
FILE: server/metrics/metrics_test.go
function TestHttpHandler_MetricsNotEnabled (line 15) | func TestHttpHandler_MetricsNotEnabled(t *testing.T) {
function TestLogOnDebugLevel_Debug (line 27) | func TestLogOnDebugLevel_Debug(t *testing.T) {
function TestLogOnDebugLevel_Info (line 44) | func TestLogOnDebugLevel_Info(t *testing.T) {
FILE: server/metrics/ns.go
constant sep (line 3) | sep = "."
type NS (line 6) | type NS
method NewInt (line 8) | func (ns NS) NewInt(key string) Int {
method NewMap (line 12) | func (ns NS) NewMap(key string) Map {
method NewNS (line 16) | func (ns NS) NewNS(childKey string) NS {
FILE: server/metrics/rate.go
type rate (line 8) | type rate struct
method String (line 19) | func (r rate) String() string {
function newRate (line 12) | func newRate(value int64, timeframe, scale time.Duration) rate {
FILE: server/metrics/rate_test.go
function TestRate_String (line 9) | func TestRate_String(t *testing.T) {
FILE: server/metrics/time.go
type Time (line 8) | type Time struct
method String (line 16) | func (t Time) String() string {
function NewTime (line 12) | func NewTime(timeValue time.Time) Time {
FILE: server/metrics/zero.go
type zeroVar (line 3) | type zeroVar struct
method String (line 6) | func (z zeroVar) String() string {
FILE: server/mocks_apns_pusher_gen_test.go
type MockPusher (line 12) | type MockPusher struct
method EXPECT (line 28) | func (_m *MockPusher) EXPECT() *_MockPusherRecorder {
method Push (line 32) | func (_m *MockPusher) Push(_param0 *apns2.Notification) (*apns2.Respon...
type _MockPusherRecorder (line 18) | type _MockPusherRecorder struct
method Push (line 39) | func (_mr *_MockPusherRecorder) Push(arg0 interface{}) *gomock.Call {
function NewMockPusher (line 22) | func NewMockPusher(ctrl *gomock.Controller) *MockPusher {
FILE: server/mocks_auth_gen_test.go
type MockAccessManager (line 13) | type MockAccessManager struct
method EXPECT (line 29) | func (_m *MockAccessManager) EXPECT() *_MockAccessManagerRecorder {
method IsAllowed (line 33) | func (_m *MockAccessManager) IsAllowed(_param0 auth.AccessType, _param...
type _MockAccessManagerRecorder (line 19) | type _MockAccessManagerRecorder struct
method IsAllowed (line 39) | func (_mr *_MockAccessManagerRecorder) IsAllowed(arg0, arg1, arg2 inte...
function NewMockAccessManager (line 23) | func NewMockAccessManager(ctrl *gomock.Controller) *MockAccessManager {
FILE: server/mocks_router_gen_test.go
type MockRouter (line 17) | type MockRouter struct
method EXPECT (line 33) | func (_m *MockRouter) EXPECT() *_MockRouterRecorder {
method AccessManager (line 37) | func (_m *MockRouter) AccessManager() (auth.AccessManager, error) {
method Cluster (line 48) | func (_m *MockRouter) Cluster() *cluster.Cluster {
method Done (line 58) | func (_m *MockRouter) Done() <-chan bool {
method Fetch (line 68) | func (_m *MockRouter) Fetch(_param0 *store.FetchRequest) error {
method GetSubscribers (line 78) | func (_m *MockRouter) GetSubscribers(_param0 string) ([]byte, error) {
method HandleMessage (line 89) | func (_m *MockRouter) HandleMessage(_param0 *protocol.Message) error {
method KVStore (line 99) | func (_m *MockRouter) KVStore() (kvstore.KVStore, error) {
method MessageStore (line 110) | func (_m *MockRouter) MessageStore() (store.MessageStore, error) {
method Subscribe (line 121) | func (_m *MockRouter) Subscribe(_param0 *router.Route) (*router.Route,...
method Unsubscribe (line 132) | func (_m *MockRouter) Unsubscribe(_param0 *router.Route) {
type _MockRouterRecorder (line 23) | type _MockRouterRecorder struct
method AccessManager (line 44) | func (_mr *_MockRouterRecorder) AccessManager() *gomock.Call {
method Cluster (line 54) | func (_mr *_MockRouterRecorder) Cluster() *gomock.Call {
method Done (line 64) | func (_mr *_MockRouterRecorder) Done() *gomock.Call {
method Fetch (line 74) | func (_mr *_MockRouterRecorder) Fetch(arg0 interface{}) *gomock.Call {
method GetSubscribers (line 85) | func (_mr *_MockRouterRecorder) GetSubscribers(arg0 interface{}) *gomo...
method HandleMessage (line 95) | func (_mr *_MockRouterRecorder) HandleMessage(arg0 interface{}) *gomoc...
method KVStore (line 106) | func (_mr *_MockRouterRecorder) KVStore() *gomock.Call {
method MessageStore (line 117) | func (_mr *_MockRouterRecorder) MessageStore() *gomock.Call {
method Subscribe (line 128) | func (_mr *_MockRouterRecorder) Subscribe(arg0 interface{}) *gomock.Ca...
method Unsubscribe (line 136) | func (_mr *_MockRouterRecorder) Unsubscribe(arg0 interface{}) *gomock....
function NewMockRouter (line 27) | func NewMockRouter(ctrl *gomock.Controller) *MockRouter {
FILE: server/mocks_store_gen_test.go
type MockMessageStore (line 13) | type MockMessageStore struct
method EXPECT (line 29) | func (_m *MockMessageStore) EXPECT() *_MockMessageStoreRecorder {
method DoInTx (line 33) | func (_m *MockMessageStore) DoInTx(_param0 string, _param1 func(uint64...
method Fetch (line 43) | func (_m *MockMessageStore) Fetch(_param0 *store.FetchRequest) {
method GenerateNextMsgID (line 51) | func (_m *MockMessageStore) GenerateNextMsgID(_param0 string, _param1 ...
method MaxMessageID (line 63) | func (_m *MockMessageStore) MaxMessageID(_param0 string) (uint64, erro...
method Partition (line 74) | func (_m *MockMessageStore) Partition(_param0 string) (store.MessagePa...
method Partitions (line 85) | func (_m *MockMessageStore) Partitions() ([]store.MessagePartition, er...
method Store (line 96) | func (_m *MockMessageStore) Store(_param0 string, _param1 uint64, _par...
method StoreMessage (line 106) | func (_m *MockMessageStore) StoreMessage(_param0 *protocol.Message, _p...
type _MockMessageStoreRecorder (line 19) | type _MockMessageStoreRecorder struct
method DoInTx (line 39) | func (_mr *_MockMessageStoreRecorder) DoInTx(arg0, arg1 interface{}) *...
method Fetch (line 47) | func (_mr *_MockMessageStoreRecorder) Fetch(arg0 interface{}) *gomock....
method GenerateNextMsgID (line 59) | func (_mr *_MockMessageStoreRecorder) GenerateNextMsgID(arg0, arg1 int...
method MaxMessageID (line 70) | func (_mr *_MockMessageStoreRecorder) MaxMessageID(arg0 interface{}) *...
method Partition (line 81) | func (_mr *_MockMessageStoreRecorder) Partition(arg0 interface{}) *gom...
method Partitions (line 92) | func (_mr *_MockMessageStoreRecorder) Partitions() *gomock.Call {
method Store (line 102) | func (_mr *_MockMessageStoreRecorder) Store(arg0, arg1, arg2 interface...
method StoreMessage (line 113) | func (_mr *_MockMessageStoreRecorder) StoreMessage(arg0, arg1 interfac...
function NewMockMessageStore (line 23) | func NewMockMessageStore(ctrl *gomock.Controller) *MockMessageStore {
FILE: server/redundancy_test.go
function Test_Subscribe_on_random_node (line 12) | func Test_Subscribe_on_random_node(t *testing.T) {
function Test_Subscribe_working_After_Node_Restart (line 53) | func Test_Subscribe_working_After_Node_Restart(t *testing.T) {
function Test_Independent_Receiving (line 119) | func Test_Independent_Receiving(t *testing.T) {
function Test_NoReceiving_After_Unsubscribe (line 173) | func Test_NoReceiving_After_Unsubscribe(t *testing.T) {
FILE: server/rest/mocks_router_gen_test.go
type MockRouter (line 17) | type MockRouter struct
method EXPECT (line 33) | func (_m *MockRouter) EXPECT() *_MockRouterRecorder {
method AccessManager (line 37) | func (_m *MockRouter) AccessManager() (auth.AccessManager, error) {
method Cluster (line 48) | func (_m *MockRouter) Cluster() *cluster.Cluster {
method Done (line 58) | func (_m *MockRouter) Done() <-chan bool {
method Fetch (line 68) | func (_m *MockRouter) Fetch(_param0 *store.FetchRequest) error {
method GetSubscribers (line 78) | func (_m *MockRouter) GetSubscribers(_param0 string) ([]byte, error) {
method HandleMessage (line 89) | func (_m *MockRouter) HandleMessage(_param0 *protocol.Message) error {
method KVStore (line 99) | func (_m *MockRouter) KVStore() (kvstore.KVStore, error) {
method MessageStore (line 110) | func (_m *MockRouter) MessageStore() (store.MessageStore, error) {
method Subscribe (line 121) | func (_m *MockRouter) Subscribe(_param0 *router.Route) (*router.Route,...
method Unsubscribe (line 132) | func (_m *MockRouter) Unsubscribe(_param0 *router.Route) {
type _MockRouterRecorder (line 23) | type _MockRouterRecorder struct
method AccessManager (line 44) | func (_mr *_MockRouterRecorder) AccessManager() *gomock.Call {
method Cluster (line 54) | func (_mr *_MockRouterRecorder) Cluster() *gomock.Call {
method Done (line 64) | func (_mr *_MockRouterRecorder) Done() *gomock.Call {
method Fetch (line 74) | func (_mr *_MockRouterRecorder) Fetch(arg0 interface{}) *gomock.Call {
method GetSubscribers (line 85) | func (_mr *_MockRouterRecorder) GetSubscribers(arg0 interface{}) *gomo...
method HandleMessage (line 95) | func (_mr *_MockRouterRecorder) HandleMessage(arg0 interface{}) *gomoc...
method KVStore (line 106) | func (_mr *_MockRouterRecorder) KVStore() *gomock.Call {
method MessageStore (line 117) | func (_mr *_MockRouterRecorder) MessageStore() *gomock.Call {
method Subscribe (line 128) | func (_mr *_MockRouterRecorder) Subscribe(arg0 interface{}) *gomock.Ca...
method Unsubscribe (line 136) | func (_mr *_MockRouterRecorder) Unsubscribe(arg0 interface{}) *gomock....
function NewMockRouter (line 27) | func NewMockRouter(ctrl *gomock.Controller) *MockRouter {
FILE: server/rest/rest_message_api.go
constant xHeaderPrefix (line 23) | xHeaderPrefix = "x-guble-"
constant filterPrefix (line 24) | filterPrefix = "filter"
constant subscribersPrefix (line 25) | subscribersPrefix = "/subscribers"
type RestMessageAPI (line 31) | type RestMessageAPI struct
method GetPrefix (line 43) | func (api *RestMessageAPI) GetPrefix() string {
method ServeHTTP (line 49) | func (api *RestMessageAPI) ServeHTTP(w http.ResponseWriter, r *http.Re...
method extractTopic (line 116) | func (api *RestMessageAPI) extractTopic(path string, requestTypeTopicP...
method setFilters (line 131) | func (api *RestMessageAPI) setFilters(r *http.Request, msg *protocol.M...
function NewRestMessageAPI (line 37) | func NewRestMessageAPI(router router.Router, prefix string) *RestMessage...
function q (line 140) | func q(r *http.Request, name string) string {
function filterName (line 149) | func filterName(name string) string {
function headersToJSON (line 153) | func headersToJSON(header http.Header) string {
function removeTrailingSlash (line 175) | func removeTrailingSlash(path string) string {
FILE: server/rest/rest_message_api_test.go
function TestServerHTTP (line 22) | func TestServerHTTP(t *testing.T) {
function TestServeHTTP_GetError (line 58) | func TestServeHTTP_GetError(t *testing.T) {
function TestServeHTTP_GetSubscribers (line 81) | func TestServeHTTP_GetSubscribers(t *testing.T) {
function TestHeadersToJSON (line 106) | func TestHeadersToJSON(t *testing.T) {
function TestRemoveTrailingSlash (line 129) | func TestRemoveTrailingSlash(t *testing.T) {
function TestExtractTopic (line 135) | func TestExtractTopic(t *testing.T) {
function TestRestMessageAPI_setFilters (line 163) | func TestRestMessageAPI_setFilters(t *testing.T) {
function TestRestMessageAPI_SetFiltersWhenServing (line 190) | func TestRestMessageAPI_SetFiltersWhenServing(t *testing.T) {
FILE: server/router/errors.go
type PermissionDeniedError (line 28) | type PermissionDeniedError struct
method Error (line 40) | func (e *PermissionDeniedError) Error() string {
type ModuleStoppingError (line 45) | type ModuleStoppingError struct
method Error (line 49) | func (m *ModuleStoppingError) Error() string {
FILE: server/router/message_queue.go
constant defaultQueueCap (line 10) | defaultQueueCap = 50
type queue (line 13) | type queue struct
method push (line 29) | func (q *queue) push(m *protocol.Message) {
method remove (line 37) | func (q *queue) remove() {
method poll (line 48) | func (q *queue) poll() (*protocol.Message, error) {
method size (line 59) | func (q *queue) size() int {
function newQueue (line 20) | func newQueue(size int) *queue {
FILE: server/router/mocks_auth_gen_test.go
type MockAccessManager (line 13) | type MockAccessManager struct
method EXPECT (line 29) | func (_m *MockAccessManager) EXPECT() *_MockAccessManagerRecorder {
method IsAllowed (line 33) | func (_m *MockAccessManager) IsAllowed(_param0 auth.AccessType, _param...
type _MockAccessManagerRecorder (line 19) | type _MockAccessManagerRecorder struct
method IsAllowed (line 39) | func (_mr *_MockAccessManagerRecorder) IsAllowed(arg0, arg1, arg2 inte...
function NewMockAccessManager (line 23) | func NewMockAccessManager(ctrl *gomock.Controller) *MockAccessManager {
FILE: server/router/mocks_checker_gen_test.go
type MockChecker (line 11) | type MockChecker struct
method EXPECT (line 27) | func (_m *MockChecker) EXPECT() *_MockCheckerRecorder {
method Check (line 31) | func (_m *MockChecker) Check() error {
type _MockCheckerRecorder (line 17) | type _MockCheckerRecorder struct
method Check (line 37) | func (_mr *_MockCheckerRecorder) Check() *gomock.Call {
function NewMockChecker (line 21) | func NewMockChecker(ctrl *gomock.Controller) *MockChecker {
FILE: server/router/mocks_kvstore_gen_test.go
type MockKVStore (line 11) | type MockKVStore struct
method EXPECT (line 27) | func (_m *MockKVStore) EXPECT() *_MockKVStoreRecorder {
method Delete (line 31) | func (_m *MockKVStore) Delete(_param0 string, _param1 string) error {
method Get (line 41) | func (_m *MockKVStore) Get(_param0 string, _param1 string) ([]byte, bo...
method Iterate (line 53) | func (_m *MockKVStore) Iterate(_param0 string, _param1 string) chan [2...
method IterateKeys (line 63) | func (_m *MockKVStore) IterateKeys(_param0 string, _param1 string) cha...
method Put (line 73) | func (_m *MockKVStore) Put(_param0 string, _param1 string, _param2 []b...
type _MockKVStoreRecorder (line 17) | type _MockKVStoreRecorder struct
method Delete (line 37) | func (_mr *_MockKVStoreRecorder) Delete(arg0, arg1 interface{}) *gomoc...
method Get (line 49) | func (_mr *_MockKVStoreRecorder) Get(arg0, arg1 interface{}) *gomock.C...
method Iterate (line 59) | func (_mr *_MockKVStoreRecorder) Iterate(arg0, arg1 interface{}) *gomo...
method IterateKeys (line 69) | func (_mr *_MockKVStoreRecorder) IterateKeys(arg0, arg1 interface{}) *...
method Put (line 79) | func (_mr *_MockKVStoreRecorder) Put(arg0, arg1, arg2 interface{}) *go...
function NewMockKVStore (line 21) | func NewMockKVStore(ctrl *gomock.Controller) *MockKVStore {
FILE: server/router/mocks_router_gen_test.go
type MockRouter (line 17) | type MockRouter struct
method EXPECT (line 33) | func (_m *MockRouter) EXPECT() *_MockRouterRecorder {
method AccessManager (line 37) | func (_m *MockRouter) AccessManager() (auth.AccessManager, error) {
method Cluster (line 48) | func (_m *MockRouter) Cluster() *cluster.Cluster {
method Done (line 58) | func (_m *MockRouter) Done() <-chan bool {
method Fetch (line 68) | func (_m *MockRouter) Fetch(_param0 *store.FetchRequest) error {
method GetSubscribers (line 78) | func (_m *MockRouter) GetSubscribers(_param0 string) ([]byte, error) {
method HandleMessage (line 89) | func (_m *MockRouter) HandleMessage(_param0 *protocol.Message) error {
method KVStore (line 99) | func (_m *MockRouter) KVStore() (kvstore.KVStore, error) {
method MessageStore (line 110) | func (_m *MockRouter) MessageStore() (store.MessageStore, error) {
method Subscribe (line 121) | func (_m *MockRouter) Subscribe(_param0 *Route) (*Route, error) {
method Unsubscribe (line 132) | func (_m *MockRouter) Unsubscribe(_param0 *Route) {
type _MockRouterRecorder (line 23) | type _MockRouterRecorder struct
method AccessManager (line 44) | func (_mr *_MockRouterRecorder) AccessManager() *gomock.Call {
method Cluster (line 54) | func (_mr *_MockRouterRecorder) Cluster() *gomock.Call {
method Done (line 64) | func (_mr *_MockRouterRecorder) Done() *gomock.Call {
method Fetch (line 74) | func (_mr *_MockRouterRecorder) Fetch(arg0 interface{}) *gomock.Call {
method GetSubscribers (line 85) | func (_mr *_MockRouterRecorder) GetSubscribers(arg0 interface{}) *gomo...
method HandleMessage (line 95) | func (_mr *_MockRouterRecorder) HandleMessage(arg0 interface{}) *gomoc...
method KVStore (line 106) | func (_mr *_MockRouterRecorder) KVStore() *gomock.Call {
method MessageStore (line 117) | func (_mr *_MockRouterRecorder) MessageStore() *gomock.Call {
method Subscribe (line 128) | func (_mr *_MockRouterRecorder) Subscribe(arg0 interface{}) *gomock.Ca...
method Unsubscribe (line 136) | func (_mr *_MockRouterRecorder) Unsubscribe(arg0 interface{}) *gomock....
function NewMockRouter (line 27) | func NewMockRouter(ctrl *gomock.Controller) *MockRouter {
FILE: server/router/mocks_store_gen_test.go
type MockMessageStore (line 13) | type MockMessageStore struct
method EXPECT (line 29) | func (_m *MockMessageStore) EXPECT() *_MockMessageStoreRecorder {
method DoInTx (line 33) | func (_m *MockMessageStore) DoInTx(_param0 string, _param1 func(uint64...
method Fetch (line 43) | func (_m *MockMessageStore) Fetch(_param0 *store.FetchRequest) {
method GenerateNextMsgID (line 51) | func (_m *MockMessageStore) GenerateNextMsgID(_param0 string, _param1 ...
method MaxMessageID (line 63) | func (_m *MockMessageStore) MaxMessageID(_param0 string) (uint64, erro...
method Partition (line 74) | func (_m *MockMessageStore) Partition(_param0 string) (store.MessagePa...
method Partitions (line 85) | func (_m *MockMessageStore) Partitions() ([]store.MessagePartition, er...
method Store (line 96) | func (_m *MockMessageStore) Store(_param0 string, _param1 uint64, _par...
method StoreMessage (line 106) | func (_m *MockMessageStore) StoreMessage(_param0 *protocol.Message, _p...
type _MockMessageStoreRecorder (line 19) | type _MockMessageStoreRecorder struct
method DoInTx (line 39) | func (_mr *_MockMessageStoreRecorder) DoInTx(arg0, arg1 interface{}) *...
method Fetch (line 47) | func (_mr *_MockMessageStoreRecorder) Fetch(arg0 interface{}) *gomock....
method GenerateNextMsgID (line 59) | func (_mr *_MockMessageStoreRecorder) GenerateNextMsgID(arg0, arg1 int...
method MaxMessageID (line 70) | func (_mr *_MockMessageStoreRecorder) MaxMessageID(arg0 interface{}) *...
method Partition (line 81) | func (_mr *_MockMessageStoreRecorder) Partition(arg0 interface{}) *gom...
method Partitions (line 92) | func (_mr *_MockMessageStoreRecorder) Partitions() *gomock.Call {
method Store (line 102) | func (_mr *_MockMessageStoreRecorder) Store(arg0, arg1, arg2 interface...
method StoreMessage (line 113) | func (_mr *_MockMessageStoreRecorder) StoreMessage(arg0, arg1 interfac...
function NewMockMessageStore (line 23) | func NewMockMessageStore(ctrl *gomock.Controller) *MockMessageStore {
FILE: server/router/route.go
type Route (line 24) | type Route struct
method Key (line 63) | func (r *Route) Key() string {
method String (line 70) | func (r *Route) String() string {
method Deliver (line 77) | func (r *Route) Deliver(msg *protocol.Message, isFromStore bool) error {
method MessagesChannel (line 112) | func (r *Route) MessagesChannel() <-chan *protocol.Message {
method Provide (line 119) | func (r *Route) Provide(router Router, subscribe bool) error {
method handleFetch (line 134) | func (r *Route) handleFetch(router Router) error {
method handleSubscribe (line 202) | func (r *Route) handleSubscribe(router Router) error {
method Close (line 208) | func (r *Route) Close() error {
method Equal (line 227) | func (r *Route) Equal(other *Route, keys ...string) bool {
method isInvalid (line 232) | func (r *Route) isInvalid() bool {
method setInvalid (line 238) | func (r *Route) setInvalid(invalid bool) {
method isConsuming (line 244) | func (r *Route) isConsuming() bool {
method setConsuming (line 250) | func (r *Route) setConsuming(consuming bool) {
method consume (line 258) | func (r *Route) consume() {
method send (line 306) | func (r *Route) send(msg *protocol.Message) error {
method invalidRecover (line 331) | func (r *Route) invalidRecover() error {
method sendDirect (line 340) | func (r *Route) sendDirect(msg *protocol.Message, store bool) error {
function NewRoute (line 45) | func NewRoute(config RouteConfig) *Route {
FILE: server/router/route_config.go
type Matcher (line 12) | type Matcher
type RouteConfig (line 14) | type RouteConfig struct
method Equal (line 39) | func (rc *RouteConfig) Equal(other RouteConfig, keys ...string) bool {
method messageFilter (line 47) | func (rc *RouteConfig) messageFilter(m *protocol.Message) bool {
method Filter (line 56) | func (rc *RouteConfig) Filter(filters map[string]string) bool {
FILE: server/router/route_config_test.go
type routeConfig (line 10) | type routeConfig struct
function TestRouteConfig_Equal (line 15) | func TestRouteConfig_Equal(t *testing.T) {
function TestRouteConfig_messageFilter (line 148) | func TestRouteConfig_messageFilter(t *testing.T) {
FILE: server/router/route_params.go
type RouteParams (line 9) | type RouteParams
method String (line 11) | func (rp *RouteParams) String() string {
method Key (line 19) | func (rp *RouteParams) Key() string {
method orderedKeys (line 29) | func (rp *RouteParams) orderedKeys() []string {
method Equal (line 43) | func (rp *RouteParams) Equal(other RouteParams, keys ...string) bool {
method partialEqual (line 60) | func (rp *RouteParams) partialEqual(other RouteParams, fields []string...
method Get (line 71) | func (rp *RouteParams) Get(key string) string {
method Set (line 75) | func (rp *RouteParams) Set(key, value string) {
method Copy (line 79) | func (rp *RouteParams) Copy() RouteParams {
FILE: server/router/route_test.go
function TestRouteDeliver_sendDirect (line 32) | func TestRouteDeliver_sendDirect(t *testing.T) {
function TestRouteDeliver_Invalid (line 76) | func TestRouteDeliver_Invalid(t *testing.T) {
function TestRouteDeliver_QueueSize (line 85) | func TestRouteDeliver_QueueSize(t *testing.T) {
function TestRouteDeliver_WithTimeout (line 114) | func TestRouteDeliver_WithTimeout(t *testing.T) {
function TestRoute_CloseTwice (line 147) | func TestRoute_CloseTwice(t *testing.T) {
function TestQueue_ShiftEmpty (line 158) | func TestQueue_ShiftEmpty(t *testing.T) {
function testRoute (line 164) | func testRoute() *Route {
function TestRoute_messageFilter (line 176) | func TestRoute_messageFilter(t *testing.T) {
function isMessageReceived (line 233) | func isMessageReceived(route *Route, msg *protocol.Message) bool {
function TestRoute_Provide_ErrMissingFetchRequest (line 247) | func TestRoute_Provide_ErrMissingFetchRequest(t *testing.T) {
function TestRoute_Provide_Fetch (line 262) | func TestRoute_Provide_Fetch(t *testing.T) {
function TestRoute_Provide_WithSubscribe (line 319) | func TestRoute_Provide_WithSubscribe(t *testing.T) {
type startable (line 391) | type startable interface
type stopable (line 395) | type stopable interface
function TestRoute_Provide_MultipleFetch (line 401) | func TestRoute_Provide_MultipleFetch(t *testing.T) {
function TestRoute_Provide_EndIDSubscribe (line 486) | func TestRoute_Provide_EndIDSubscribe(t *testing.T) {
FILE: server/router/router.go
constant overloadedHandleChannelRatio (line 24) | overloadedHandleChannelRatio = 0.9
constant handleChannelCapacity (line 25) | handleChannelCapacity = 500
constant subscribeChannelCapacity (line 26) | subscribeChannelCapacity = 10
constant unsubscribeChannelCapacity (line 27) | unsubscribeChannelCapacity = 10
constant prefix (line 28) | prefix = "/admin/router"
type Router (line 32) | type Router interface
type subRequest (line 48) | type subRequest struct
type router (line 53) | type router struct
method Start (line 87) | func (router *router) Start() error {
method Stop (line 127) | func (router *router) Stop() error {
method Check (line 135) | func (router *router) Check() error {
method HandleMessage (line 159) | func (router *router) HandleMessage(message *protocol.Message) error {
method Subscribe (line 199) | func (router *router) Subscribe(r *Route) (*Route, error) {
method Unsubscribe (line 227) | func (router *router) Unsubscribe(r *Route) {
method GetSubscribers (line 241) | func (router *router) GetSubscribers(topicPath string) ([]byte, error) {
method subscribe (line 256) | func (router *router) subscribe(r *Route) {
method unsubscribe (line 281) | func (router *router) unsubscribe(r *Route) {
method panicIfInternalDependenciesAreNil (line 305) | func (router *router) panicIfInternalDependenciesAreNil() {
method channelsAreEmpty (line 312) | func (router *router) channelsAreEmpty() bool {
method setStopping (line 316) | func (router *router) setStopping(v bool) {
method Done (line 323) | func (router *router) Done() <-chan bool {
method isStopping (line 327) | func (router *router) isStopping() error {
method handleMessage (line 338) | func (router *router) handleMessage(message *protocol.Message) {
method closeRoutes (line 366) | func (router *router) closeRoutes() {
method handleOverloadedChannel (line 378) | func (router *router) handleOverloadedChannel() {
method Fetch (line 413) | func (router *router) Fetch(req *store.FetchRequest) error {
method AccessManager (line 423) | func (router *router) AccessManager() (auth.AccessManager, error) {
method MessageStore (line 431) | func (router *router) MessageStore() (store.MessageStore, error) {
method KVStore (line 439) | func (router *router) KVStore() (kvstore.KVStore, error) {
method Cluster (line 447) | func (router *router) Cluster() *cluster.Cluster {
method ServeHTTP (line 451) | func (router *router) ServeHTTP(w http.ResponseWriter, req *http.Reque...
method GetPrefix (line 467) | func (router *router) GetPrefix() string {
function New (line 71) | func New(accessManager auth.AccessManager, messageStore store.MessageSto...
function matchesTopic (line 389) | func matchesTopic(messagePath, routePath protocol.Path) bool {
function removeIfMatching (line 399) | func removeIfMatching(slice []*Route, route *Route) ([]*Route, bool) {
FILE: server/router/router_metrics.go
function resetRouterMetrics (line 28) | func resetRouterMetrics() {
FILE: server/router/router_test.go
type msChecker (line 21) | type msChecker struct
function newMSChecker (line 26) | func newMSChecker() *msChecker {
type kvsChecker (line 33) | type kvsChecker struct
function newKVSChecker (line 38) | func newKVSChecker() *kvsChecker {
function TestRouter_AddAndRemoveRoutes (line 45) | func TestRouter_AddAndRemoveRoutes(t *testing.T) {
function TestRouter_SubscribeNotAllowed (line 97) | func TestRouter_SubscribeNotAllowed(t *testing.T) {
function TestRouter_HandleMessageNotAllowed (line 137) | func TestRouter_HandleMessageNotAllowed(t *testing.T) {
function TestRouter_ReplacingOfRoutesMatchingAppID (line 188) | func TestRouter_ReplacingOfRoutesMatchingAppID(t *testing.T) {
function TestRouter_SimpleMessageSending (line 220) | func TestRouter_SimpleMessageSending(t *testing.T) {
function TestRouter_RoutingWithSubTopics (line 247) | func TestRouter_RoutingWithSubTopics(t *testing.T) {
function TestMatchesTopic (line 293) | func TestMatchesTopic(t *testing.T) {
function TestRoute_IsRemovedIfChannelIsFull (line 312) | func TestRoute_IsRemovedIfChannelIsFull(t *testing.T) {
function TestRouter_CleanShutdown (line 373) | func TestRouter_CleanShutdown(t *testing.T) {
function TestRouter_Check (line 452) | func TestRouter_Check(t *testing.T) {
function TestPanicOnInternalDependencies (line 511) | func TestPanicOnInternalDependencies(t *testing.T) {
function aStartedRouter (line 517) | func aStartedRouter() (*router, auth.AccessManager, store.MessageStore, ...
function aRouterRoute (line 526) | func aRouterRoute(unused int) (*router, *Route) {
function assertChannelContainsMessage (line 538) | func assertChannelContainsMessage(a *assert.Assertions, c <-chan *protoc...
FILE: server/service/mocks_checker_gen_test.go
type MockChecker (line 11) | type MockChecker struct
method EXPECT (line 27) | func (_m *MockChecker) EXPECT() *_MockCheckerRecorder {
method Check (line 31) | func (_m *MockChecker) Check() error {
type _MockCheckerRecorder (line 17) | type _MockCheckerRecorder struct
method Check (line 37) | func (_mr *_MockCheckerRecorder) Check() *gomock.Call {
function NewMockChecker (line 21) | func NewMockChecker(ctrl *gomock.Controller) *MockChecker {
FILE: server/service/mocks_router_gen_test.go
type MockRouter (line 17) | type MockRouter struct
method EXPECT (line 33) | func (_m *MockRouter) EXPECT() *_MockRouterRecorder {
method AccessManager (line 37) | func (_m *MockRouter) AccessManager() (auth.AccessManager, error) {
method Cluster (line 48) | func (_m *MockRouter) Cluster() *cluster.Cluster {
method Done (line 58) | func (_m *MockRouter) Done() <-chan bool {
method Fetch (line 68) | func (_m *MockRouter) Fetch(_param0 *store.FetchRequest) error {
method GetSubscribers (line 78) | func (_m *MockRouter) GetSubscribers(_param0 string) ([]byte, error) {
method HandleMessage (line 89) | func (_m *MockRouter) HandleMessage(_param0 *protocol.Message) error {
method KVStore (line 99) | func (_m *MockRouter) KVStore() (kvstore.KVStore, error) {
method MessageStore (line 110) | func (_m *MockRouter) MessageStore() (store.MessageStore, error) {
method Subscribe (line 121) | func (_m *MockRouter) Subscribe(_param0 *router.Route) (*router.Route,...
method Unsubscribe (line 132) | func (_m *MockRouter) Unsubscribe(_param0 *router.Route) {
type _MockRouterRecorder (line 23) | type _MockRouterRecorder struct
method AccessManager (line 44) | func (_mr *_MockRouterRecorder) AccessManager() *gomock.Call {
method Cluster (line 54) | func (_mr *_MockRouterRecorder) Cluster() *gomock.Call {
method Done (line 64) | func (_mr *_MockRouterRecorder) Done() *gomock.Call {
method Fetch (line 74) | func (_mr *_MockRouterRecorder) Fetch(arg0 interface{}) *gomock.Call {
method GetSubscribers (line 85) | func (_mr *_MockRouterRecorder) GetSubscribers(arg0 interface{}) *gomo...
method HandleMessage (line 95) | func (_mr *_MockRouterRecorder) HandleMessage(arg0 interface{}) *gomoc...
method KVStore (line 106) | func (_mr *_MockRouterRecorder) KVStore() *gomock.Call {
method MessageStore (line 117) | func (_mr *_MockRouterRecorder) MessageStore() *gomock.Call {
method Subscribe (line 128) | func (_mr *_MockRouterRecorder) Subscribe(arg0 interface{}) *gomock.Ca...
method Unsubscribe (line 136) | func (_mr *_MockRouterRecorder) Unsubscribe(arg0 interface{}) *gomock....
function NewMockRouter (line 27) | func NewMockRouter(ctrl *gomock.Controller) *MockRouter {
FILE: server/service/module.go
type Startable (line 9) | type Startable interface
type Stopable (line 14) | type Stopable interface
type Endpoint (line 19) | type Endpoint interface
type module (line 24) | type module struct
type by (line 30) | type by
method sort (line 37) | func (criteria by) sort(modules []module) {
type moduleSorter (line 32) | type moduleSorter struct
method Len (line 47) | func (s *moduleSorter) Len() int { return len(s.modules) }
method Swap (line 48) | func (s *moduleSorter) Swap(i, j int) { s.modules[i], s.modules[j...
method Less (line 49) | func (s *moduleSorter) Less(i, j int) bool { return s.by(&s.modules[i]...
FILE: server/service/service.go
constant defaultHealthFrequency (line 18) | defaultHealthFrequency = time.Second * 60
constant defaultHealthThreshold (line 19) | defaultHealthThreshold = 1
type Service (line 23) | type Service struct
method RegisterModules (line 55) | func (s *Service) RegisterModules(startOrder int, stopOrder int, iface...
method HealthEndpoint (line 72) | func (s *Service) HealthEndpoint(endpointPrefix string) *Service {
method MetricsEndpoint (line 78) | func (s *Service) MetricsEndpoint(endpointPrefix string) *Service {
method Start (line 87) | func (s *Service) Start() error {
method Stop (line 126) | func (s *Service) Stop() error {
method WebServer (line 143) | func (s *Service) WebServer() *webserver.WebServer {
method ModulesSortedByStartOrder (line 148) | func (s *Service) ModulesSortedByStartOrder() []interface{} {
method modulesSortedBy (line 153) | func (s *Service) modulesSortedBy(criteria by) []interface{} {
function New (line 36) | func New(router router.Router, webserver *webserver.WebServer) *Service {
FILE: server/service/service_test.go
function TestStartingOfModules (line 20) | func TestStartingOfModules(t *testing.T) {
function TestStoppingOfModules (line 38) | func TestStoppingOfModules(t *testing.T) {
function TestEndpointRegisterAndServing (line 54) | func TestEndpointRegisterAndServing(t *testing.T) {
function TestHealthUp (line 79) | func TestHealthUp(t *testing.T) {
function TestHealthDown (line 106) | func TestHealthDown(t *testing.T) {
function TestMetricsEnabled (line 138) | func TestMetricsEnabled(t *testing.T) {
function aMockedServiceWithMockedRouterStandalone (line 165) | func aMockedServiceWithMockedRouterStandalone() (*Service, kvstore.KVSto...
type testEndpoint (line 174) | type testEndpoint struct
method GetPrefix (line 177) | func (*testEndpoint) GetPrefix() string {
method ServeHTTP (line 181) | func (*testEndpoint) ServeHTTP(w http.ResponseWriter, r *http.Request) {
type testStartable (line 186) | type testStartable struct
method Start (line 189) | func (*testStartable) Start() error {
type testStopable (line 193) | type testStopable struct
method Stop (line 196) | func (*testStopable) Stop() error {
FILE: server/sms/mocks_router_gen_test.go
type MockRouter (line 17) | type MockRouter struct
method EXPECT (line 33) | func (_m *MockRouter) EXPECT() *_MockRouterRecorder {
method AccessManager (line 37) | func (_m *MockRouter) AccessManager() (auth.AccessManager, error) {
method Cluster (line 48) | func (_m *MockRouter) Cluster() *cluster.Cluster {
method Done (line 58) | func (_m *MockRouter) Done() <-chan bool {
method Fetch (line 68) | func (_m *MockRouter) Fetch(_param0 *store.FetchRequest) error {
method GetSubscribers (line 78) | func (_m *MockRouter) GetSubscribers(_param0 string) ([]byte, error) {
method HandleMessage (line 89) | func (_m *MockRouter) HandleMessage(_param0 *protocol.Message) error {
method KVStore (line 99) | func (_m *MockRouter) KVStore() (kvstore.KVStore, error) {
method MessageStore (line 110) | func (_m *MockRouter) MessageStore() (store.MessageStore, error) {
method Subscribe (line 121) | func (_m *MockRouter) Subscribe(_param0 *router.Route) (*router.Route,...
method Unsubscribe (line 132) | func (_m *MockRouter) Unsubscribe(_param0 *router.Route) {
type _MockRouterRecorder (line 23) | type _MockRouterRecorder struct
method AccessManager (line 44) | func (_mr *_MockRouterRecorder) AccessManager() *gomock.Call {
method Cluster (line 54) | func (_mr *_MockRouterRecorder) Cluster() *gomock.Call {
method Done (line 64) | func (_mr *_MockRouterRecorder) Done() *gomock.Call {
method Fetch (line 74) | func (_mr *_MockRouterRecorder) Fetch(arg0 interface{}) *gomock.Call {
method GetSubscribers (line 85) | func (_mr *_MockRouterRecorder) GetSubscribers(arg0 interface{}) *gomo...
method HandleMessage (line 95) | func (_mr *_MockRouterRecorder) HandleMessage(arg0 interface{}) *gomoc...
method KVStore (line 106) | func (_mr *_MockRouterRecorder) KVStore() *gomock.Call {
method MessageStore (line 117) | func (_mr *_MockRouterRecorder) MessageStore() *gomock.Call {
method Subscribe (line 128) | func (_mr *_MockRouterRecorder) Subscribe(arg0 interface{}) *gomock.Ca...
method Unsubscribe (line 136) | func (_mr *_MockRouterRecorder) Unsubscribe(arg0 interface{}) *gomock....
function NewMockRouter (line 27) | func NewMockRouter(ctrl *gomock.Controller) *MockRouter {
FILE: server/sms/mocks_sender_gen_test.go
type MockSender (line 12) | type MockSender struct
method EXPECT (line 28) | func (_m *MockSender) EXPECT() *_MockSenderRecorder {
method Send (line 32) | func (_m *MockSender) Send(_param0 *protocol.Message) error {
type _MockSenderRecorder (line 18) | type _MockSenderRecorder struct
method Send (line 38) | func (_mr *_MockSenderRecorder) Send(arg0 interface{}) *gomock.Call {
function NewMockSender (line 22) | func NewMockSender(ctrl *gomock.Controller) *MockSender {
FILE: server/sms/mocks_store_gen_test.go
type MockMessageStore (line 13) | type MockMessageStore struct
method EXPECT (line 29) | func (_m *MockMessageStore) EXPECT() *_MockMessageStoreRecorder {
method DoInTx (line 33) | func (_m *MockMessageStore) DoInTx(_param0 string, _param1 func(uint64...
method Fetch (line 43) | func (_m *MockMessageStore) Fetch(_param0 *store.FetchRequest) {
method GenerateNextMsgID (line 51) | func (_m *MockMessageStore) GenerateNextMsgID(_param0 string, _param1 ...
method MaxMessageID (line 63) | func (_m *MockMessageStore) MaxMessageID(_param0 string) (uint64, erro...
method Partition (line 74) | func (_m *MockMessageStore) Partition(_param0 string) (store.MessagePa...
method Partitions (line 85) | func (_m *MockMessageStore) Partitions() ([]store.MessagePartition, er...
method Store (line 96) | func (_m *MockMessageStore) Store(_param0 string, _param1 uint64, _par...
method StoreMessage (line 106) | func (_m *MockMessageStore) StoreMessage(_param0 *protocol.Message, _p...
type _MockMessageStoreRecorder (line 19) | type _MockMessageStoreRecorder struct
method DoInTx (line 39) | func (_mr *_MockMessageStoreRecorder) DoInTx(arg0, arg1 interface{}) *...
method Fetch (line 47) | func (_mr *_MockMessageStoreRecorder) Fetch(arg0 interface{}) *gomock....
method GenerateNextMsgID (line 59) | func (_mr *_MockMessageStoreRecorder) GenerateNextMsgID(arg0, arg1 int...
method MaxMessageID (line 70) | func (_mr *_MockMessageStoreRecorder) MaxMessageID(arg0 interface{}) *...
method Partition (line 81) | func (_mr *_MockMessageStoreRecorder) Partition(arg0 interface{}) *gom...
method Partitions (line 92) | func (_mr *_MockMessageStoreRecorder) Partitions() *gomock.Call {
method Store (line 102) | func (_mr *_MockMessageStoreRecorder) Store(arg0, arg1, arg2 interface...
method StoreMessage (line 113) | func (_mr *_MockMessageStoreRecorder) StoreMessage(arg0, arg1 interfac...
function NewMockMessageStore (line 23) | func NewMockMessageStore(ctrl *gomock.Controller) *MockMessageStore {
FILE: server/sms/nexmo_sms.go
type NexmoSms (line 5) | type NexmoSms struct
method EncodeNexmoSms (line 13) | func (sms *NexmoSms) EncodeNexmoSms(apiKey, apiSecret string) ([]byte,...
FILE: server/sms/nexmo_sms_sender.go
type ResponseCode (line 22) | type ResponseCode
method String (line 74) | func (c ResponseCode) String() string {
constant ResponseSuccess (line 25) | ResponseSuccess ResponseCode = iota
constant ResponseThrottled (line 26) | ResponseThrottled
constant ResponseMissingParams (line 27) | ResponseMissingParams
constant ResponseInvalidParams (line 28) | ResponseInvalidParams
constant ResponseInvalidCredentials (line 29) | ResponseInvalidCredentials
constant ResponseInternalError (line 30) | ResponseInternalError
constant ResponseInvalidMessage (line 31) | ResponseInvalidMessage
constant ResponseNumberBarred (line 32) | ResponseNumberBarred
constant ResponsePartnerAcctBarred (line 33) | ResponsePartnerAcctBarred
constant ResponsePartnerQuotaExceeded (line 34) | ResponsePartnerQuotaExceeded
constant ResponseUnused (line 35) | ResponseUnused
constant ResponseRESTNotEnabled (line 36) | ResponseRESTNotEnabled
constant ResponseMessageTooLong (line 37) | ResponseMessageTooLong
constant ResponseCommunicationFailed (line 38) | ResponseCommunicationFailed
constant ResponseInvalidSignature (line 39) | ResponseInvalidSignature
constant ResponseInvalidSenderAddress (line 40) | ResponseInvalidSenderAddress
constant ResponseInvalidTTL (line 41) | ResponseInvalidTTL
constant ResponseFacilityNotAllowed (line 42) | ResponseFacilityNotAllowed
constant ResponseInvalidMessageClass (line 43) | ResponseInvalidMessageClass
type NexmoMessageReport (line 79) | type NexmoMessageReport struct
type NexmoMessageResponse (line 90) | type NexmoMessageResponse struct
method Check (line 95) | func (nm NexmoMessageResponse) Check() error {
type NexmoSender (line 115) | type NexmoSender struct
method Send (line 133) | func (ns *NexmoSender) Send(msg *protocol.Message) error {
method sendSms (line 150) | func (ns *NexmoSender) sendSms(sms *NexmoSms) (*NexmoMessageResponse, ...
method createHttpClient (line 192) | func (ns *NexmoSender) createHttpClient() {
function NewNexmoSender (line 123) | func NewNexmoSender(apiKey, apiSecret string) (*NexmoSender, error) {
FILE: server/sms/nexmo_sms_sender_test.go
constant KEY (line 14) | KEY = "ce40b46d"
constant SECRET (line 15) | SECRET = "153d2b2c72985370"
function TestNexmoSender_Send (line 18) | func TestNexmoSender_Send(t *testing.T) {
function TestNexmoSender_SendWithError (line 35) | func TestNexmoSender_SendWithError(t *testing.T) {
FILE: server/sms/sms_gateway.go
constant SMSSchema (line 20) | SMSSchema = "sms_notifications"
constant SMSDefaultTopic (line 21) | SMSDefaultTopic = "/sms"
type Sender (line 28) | type Sender interface
type Config (line 32) | type Config struct
type gateway (line 44) | type gateway struct
method Start (line 73) | func (g *gateway) Start() error {
method initRoute (line 92) | func (g *gateway) initRoute() {
method fetchRequest (line 100) | func (g *gateway) fetchRequest() (fr *store.FetchRequest) {
method Run (line 111) | func (g *gateway) Run() {
method proxyLoop (line 173) | func (g *gateway) proxyLoop() (*protocol.Message, error) {
method retry (line 206) | func (g *gateway) retry(msg *protocol.Message) error {
method send (line 226) | func (g *gateway) send(receivedMsg *protocol.Message) error {
method Restart (line 238) | func (g *gateway) Restart() error {
method Stop (line 257) | func (g *gateway) Stop() error {
method SetLastSentID (line 264) | func (g *gateway) SetLastSentID(ID uint64) error {
method ReadLastID (line 287) | func (g *gateway) ReadLastID() error {
method Cancel (line 315) | func (g *gateway) Cancel() {
method startMetrics (line 321) | func (g *gateway) startMetrics() {
method startIntervalMetric (line 334) | func (g *gateway) startIntervalMetric(m metrics.Map, td time.Duration) {
function New (line 59) | func New(router router.Router, sender Sender, config Config) (*gateway, ...
function isRestartableErr (line 164) | func isRestartableErr(err error) bool {
FILE: server/sms/sms_gateway_test.go
function Test_StartStop (line 22) | func Test_StartStop(t *testing.T) {
function Test_SendOneSms (line 64) | func Test_SendOneSms(t *testing.T) {
function Test_Restart (line 134) | func Test_Restart(t *testing.T) {
function TestReadLastID (line 200) | func TestReadLastID(t *testing.T) {
FILE: server/sms/sms_metrics.go
constant currentTotalMessagesLatenciesKey (line 20) | currentTotalMessagesLatenciesKey = "current_messages_total_latencies_nanos"
constant currentTotalMessagesKey (line 21) | currentTotalMessagesKey = "current_messages_count"
constant currentTotalErrorsLatenciesKey (line 22) | currentTotalErrorsLatenciesKey = "current_errors_total_latencies_nanos"
constant currentTotalErrorsKey (line 23) | currentTotalErrorsKey = "current_errors_count"
function processAndResetIntervalMetrics (line 26) | func processAndResetIntervalMetrics(m metrics.Map, td time.Duration, t t...
function resetIntervalMetrics (line 42) | func resetIntervalMetrics(m metrics.Map, t time.Time) {
FILE: server/store/dummystore/dummy_message_store.go
constant topicSchema (line 14) | topicSchema = "topic_sequence"
type DummyMessageStore (line 21) | type DummyMessageStore struct
method Start (line 45) | func (dms *DummyMessageStore) Start() error {
method Stop (line 52) | func (dms *DummyMessageStore) Stop() error {
method StoreMessage (line 62) | func (dms *DummyMessageStore) StoreMessage(message *protocol.Message, ...
method Store (line 79) | func (dms *DummyMessageStore) Store(partition string, msgID uint64, ms...
method store (line 85) | func (dms *DummyMessageStore) store(partition string, msgId uint64, ms...
method Fetch (line 100) | func (dms *DummyMessageStore) Fetch(req *store.FetchRequest) {
method MaxMessageID (line 104) | func (dms *DummyMessageStore) MaxMessageID(partition string) (uint64, ...
method DoInTx (line 111) | func (dms *DummyMessageStore) DoInTx(partition string, fnToExecute fun...
method GenerateNextMsgID (line 122) | func (dms *DummyMessageStore) GenerateNextMsgID(partitionName string, ...
method maxMessageID (line 135) | func (dms *DummyMessageStore) maxMessageID(partition string) (uint64, ...
method setID (line 156) | func (dms *DummyMessageStore) setID(partition string, id uint64) {
method startSequenceSync (line 160) | func (dms *DummyMessageStore) startSequenceSync() {
method Check (line 193) | func (dms *DummyMessageStore) Check() error {
method Partition (line 197) | func (dms *DummyMessageStore) Partition(name string) (store.MessagePar...
method Partitions (line 201) | func (dms *DummyMessageStore) Partitions() ([]store.MessagePartition, ...
function New (line 34) | func New(kvStore kvstore.KVStore) *DummyMessageStore {
FILE: server/store/dummystore/dummy_message_store_test.go
function Test_DummyMessageStore_IncreaseOnStore (line 12) | func Test_DummyMessageStore_IncreaseOnStore(t *testing.T) {
function Test_DummyMessageStore_ErrorOnWrongMessageId (line 23) | func Test_DummyMessageStore_ErrorOnWrongMessageId(t *testing.T) {
function Test_DummyMessageStore_InitIdsFromKvStore (line 32) | func Test_DummyMessageStore_InitIdsFromKvStore(t *testing.T) {
function Test_DummyMessageStore_SyncIds (line 46) | func Test_DummyMessageStore_SyncIds(t *testing.T) {
function Test_DummyMessageStore_SyncIdsOnStop (line 76) | func Test_DummyMessageStore_SyncIdsOnStop(t *testing.T) {
function fne (line 108) | func fne(args ...interface{}) interface{} {
FILE: server/store/fetch_request.go
constant DirectionOneMessage (line 12) | DirectionOneMessage FetchDirection = 0
constant DirectionForward (line 13) | DirectionForward FetchDirection = 1
constant DirectionBackwards (line 14) | DirectionBackwards FetchDirection = -1
constant FetchBufferSize (line 17) | FetchBufferSize = 10
type FetchDirection (line 20) | type FetchDirection
type FetchedMessage (line 23) | type FetchedMessage struct
type FetchRequest (line 29) | type FetchRequest struct
method Init (line 80) | func (fr *FetchRequest) Init() {
method Ready (line 92) | func (fr *FetchRequest) Ready() int {
method Messages (line 96) | func (fr *FetchRequest) Messages() <-chan *FetchedMessage {
method Errors (line 100) | func (fr *FetchRequest) Errors() <-chan error {
method Error (line 104) | func (fr *FetchRequest) Error(err error) {
method Push (line 108) | func (fr *FetchRequest) Push(id uint64, message []byte) {
method PushFetchMessage (line 112) | func (fr *FetchRequest) PushFetchMessage(fm *FetchedMessage) {
method PushError (line 116) | func (fr *FetchRequest) PushError(err error) {
method IsDone (line 120) | func (fr *FetchRequest) IsDone() bool {
method Done (line 126) | func (fr *FetchRequest) Done() {
function NewFetchRequest (line 66) | func NewFetchRequest(partition string, start, end uint64, direction Fetc...
FILE: server/store/filestore/cache.go
type cache (line 9) | type cache struct
method length (line 21) | func (c *cache) length() int {
method add (line 28) | func (c *cache) add(entry *cacheEntry) {
function newCache (line 14) | func newCache() *cache {
type cacheEntry (line 35) | type cacheEntry struct
method Contains (line 41) | func (entry *cacheEntry) Contains(req *store.FetchRequest) bool {
FILE: server/store/filestore/index_list.go
type indexList (line 12) | type indexList struct
method len (line 22) | func (l *indexList) len() int {
method insertList (line 29) | func (l *indexList) insertList(other *indexList) {
method insert (line 34) | func (l *indexList) insert(items ...*index) {
method insertElem (line 40) | func (l *indexList) insertElem(elem *index) {
method clear (line 71) | func (l *indexList) clear() {
method search (line 83) | func (l *indexList) search(searchID uint64) (bool, int, int, *index) {
method back (line 114) | func (l *indexList) back() *index {
method front (line 126) | func (l *indexList) front() *index {
method toSliceArray (line 137) | func (l *indexList) toSliceArray() []*index {
method get (line 145) | func (l *indexList) get(pos int) *index {
method mapWithPredicate (line 160) | func (l *indexList) mapWithPredicate(predicate func(elem *index, i int...
method String (line 173) | func (l *indexList) String() string {
method contains (line 185) | func (l *indexList) contains(id uint64) bool {
method extract (line 201) | func (l *indexList) extract(req *store.FetchRequest) *indexList {
function newIndexList (line 18) | func newIndexList(size int) *indexList {
function abs (line 239) | func abs(m1, m2 uint64) uint64 {
FILE: server/store/filestore/index_list_test.go
function Test_SortedListSanity (line 11) | func Test_SortedListSanity(t *testing.T) {
FILE: server/store/filestore/message_partition.go
constant gubleNodeIdBits (line 28) | gubleNodeIdBits = 3
constant sequenceBits (line 29) | sequenceBits = 12
constant gubleNodeIdShift (line 30) | gubleNodeIdShift = sequenceBits
constant timestampLeftShift (line 31) | timestampLeftShift = sequenceBits + gubleNodeIdBits
constant gubleEpoch (line 32) | gubleEpoch = 1467714505012
type index (line 35) | type index struct
type messagePartition (line 42) | type messagePartition struct
method Name (line 68) | func (p *messagePartition) Name() string {
method MaxMessageID (line 72) | func (p *messagePartition) MaxMessageID() uint64 {
method Count (line 79) | func (p *messagePartition) Count() uint64 {
method initialize (line 86) | func (p *messagePartition) initialize() error {
method readIdxFiles (line 103) | func (p *messagePartition) readIdxFiles() error {
method closeAppendFiles (line 174) | func (p *messagePartition) closeAppendFiles() error {
method createNextAppendFiles (line 219) | func (p *messagePartition) createNextAppendFiles() error {
method generateNextMsgID (line 261) | func (p *messagePartition) generateNextMsgID(nodeID uint8) (uint64, in...
method Close (line 293) | func (p *messagePartition) Close() error {
method DoInTx (line 300) | func (p *messagePartition) DoInTx(fnToExecute func(maxMessageId uint64...
method Store (line 306) | func (p *messagePartition) Store(msgID uint64, msg []byte) error {
method store (line 313) | func (p *messagePartition) store(messageID uint64, data []byte) error {
method Fetch (line 407) | func (p *messagePartition) Fetch(req *store.FetchRequest) {
method fetchByFetchlist (line 438) | func (p *messagePartition) fetchByFetchlist(fetchList *indexList, req ...
method calculateFetchList (line 467) | func (p *messagePartition) calculateFetchList(req *store.FetchRequest)...
method rewriteSortedIdxFile (line 512) | func (p *messagePartition) rewriteSortedIdxFile(filename string) error {
method loadLastIndexList (line 603) | func (p *messagePartition) loadLastIndexList(filename string) error {
method loadIndexList (line 619) | func (p *messagePartition) loadIndexList(fileID int) (*indexList, erro...
method composeMsgFilenameForPosition (line 662) | func (p *messagePartition) composeMsgFilenameForPosition(value uint64)...
method composeIdxFilenameForPosition (line 666) | func (p *messagePartition) composeIdxFilenameForPosition(value uint64)...
function newMessagePartition (line 58) | func newMessagePartition(basedir string, storeName string) (*messagePart...
function readCacheEntryFromIdxFile (line 194) | func readCacheEntryFromIdxFile(filename string) (entry *cacheEntry, err ...
function readIndexEntry (line 554) | func readIndexEntry(file *os.File, position int64) (uint64, uint64, uint...
function writeIndexEntry (line 572) | func writeIndexEntry(w io.WriterAt, id uint64, offset uint64, size uint3...
function calculateNoEntries (line 592) | func calculateNoEntries(filename string) (uint64, error) {
FILE: server/store/filestore/message_partition_robustness_test.go
function Test_MessagePartition_forConcurrentWriteAndReads (line 16) | func Test_MessagePartition_forConcurrentWriteAndReads(t *testing.T) {
function messagePartitionWriter (line 52) | func messagePartitionWriter(a *assert.Assertions, store *messagePartitio...
function messagePartitionReader (line 60) | func messagePartitionReader(name string, a *assert.Assertions, mStore *m...
FILE: server/store/filestore/message_partition_test.go
function TestFileMessageStore_GenerateNextMsgId (line 18) | func TestFileMessageStore_GenerateNextMsgId(t *testing.T) {
function TestFileMessageStore_GenerateNextMsgIdMultipleNodes (line 38) | func TestFileMessageStore_GenerateNextMsgIdMultipleNodes(t *testing.T) {
function Test_MessagePartition_loadFiles (line 74) | func Test_MessagePartition_loadFiles(t *testing.T) {
function Test_MessagePartition_correctIdAfterRestart (line 121) | func Test_MessagePartition_correctIdAfterRestart(t *testing.T) {
function Benchmark_Storing_HelloWorld_Messages (line 139) | func Benchmark_Storing_HelloWorld_Messages(b *testing.B) {
function Benchmark_Storing_1Kb_Messages (line 153) | func Benchmark_Storing_1Kb_Messages(b *testing.B) {
function Benchmark_Storing_1MB_Messages (line 172) | func Benchmark_Storing_1MB_Messages(b *testing.B) {
function Test_calculateFetchList (line 191) | func Test_calculateFetchList(t *testing.T) {
function matchSortedList (line 330) | func matchSortedList(t *testing.T, expected, actual indexList) bool {
function Test_Partition_Fetch (line 349) | func Test_Partition_Fetch(t *testing.T) {
function TestFilenameGeneration (line 479) | func TestFilenameGeneration(t *testing.T) {
FILE: server/store/filestore/message_store.go
type FileMessageStore (line 20) | type FileMessageStore struct
method MaxMessageID (line 35) | func (fms *FileMessageStore) MaxMessageID(partition string) (uint64, e...
method Stop (line 45) | func (fms *FileMessageStore) Stop() error {
method GenerateNextMsgID (line 66) | func (fms *FileMessageStore) GenerateNextMsgID(partitionName string, n...
method StoreMessage (line 75) | func (fms *FileMessageStore) StoreMessage(message *protocol.Message, n...
method Store (line 120) | func (fms *FileMessageStore) Store(partition string, msgID uint64, msg...
method Fetch (line 130) | func (fms *FileMessageStore) Fetch(req *store.FetchRequest) {
method DoInTx (line 140) | func (fms *FileMessageStore) DoInTx(partition string, fnToExecute func...
method Partitions (line 151) | func (fms *FileMessageStore) Partitions() (partitions []store.MessageP...
method Partition (line 171) | func (fms *FileMessageStore) Partition(partition string) (store.Messag...
method Check (line 201) | func (fms *FileMessageStore) Check() error {
function New (line 27) | func New(basedir string) *FileMessageStore {
function extractPartitionName (line 227) | func extractPartitionName(p string) string {
FILE: server/store/filestore/message_store_test.go
function Test_Fetch (line 14) | func Test_Fetch(t *testing.T) {
function Test_MessageStore_Close (line 78) | func Test_MessageStore_Close(t *testing.T) {
function Test_MaxMessageId (line 95) | func Test_MaxMessageId(t *testing.T) {
function Test_MaxMessageIdError (line 111) | func Test_MaxMessageIdError(t *testing.T) {
function Test_MessagePartitionReturningError (line 119) | func Test_MessagePartitionReturningError(t *testing.T) {
function Test_FetchWithError (line 132) | func Test_FetchWithError(t *testing.T) {
function Test_StoreWithError (line 143) | func Test_StoreWithError(t *testing.T) {
function Test_DoInTx (line 151) | func Test_DoInTx(t *testing.T) {
function Test_DoInTxError (line 163) | func Test_DoInTxError(t *testing.T) {
function Test_Check (line 171) | func Test_Check(t *testing.T) {
FILE: server/store/store.go
type MessageStore (line 6) | type MessageStore interface
type MessagePartition (line 41) | type MessagePartition interface
FILE: server/utils_test.go
type testClusterNodeConfig (line 26) | type testClusterNodeConfig struct
method parseConfig (line 36) | func (tnc *testClusterNodeConfig) parseConfig() error {
type testClusterNode (line 84) | type testClusterNode struct
method client (line 125) | func (tcn *testClusterNode) client(userID string, bufferSize int, auto...
method Subscribe (line 133) | func (tcn *testClusterNode) Subscribe(topic, id string) {
method Unsubscribe (line 137) | func (tcn *testClusterNode) Unsubscribe(topic, id string) {
method cleanup (line 141) | func (tcn *testClusterNode) cleanup(removeDir bool) {
function newTestClusterNode (line 91) | func newTestClusterNode(t *testing.T, nodeConfig testClusterNodeConfig) ...
type TestFCM (line 152) | type TestFCM struct
method setupRoundTripper (line 161) | func (tfcm *TestFCM) setupRoundTripper(timeout time.Duration, bufferSi...
method subscribe (line 171) | func (tfcm *TestFCM) subscribe(addr, topic, id string) {
method unsubscribe (line 188) | func (tfcm *TestFCM) unsubscribe(addr, topic, id string) {
method wait (line 213) | func (tfcm *TestFCM) wait(count int) {
method receive (line 219) | func (tfcm *TestFCM) receive() error {
method checkReceived (line 237) | func (tfcm *TestFCM) checkReceived(expected int) {
method reset (line 244) | func (tfcm *TestFCM) reset() {
method cleanup (line 250) | func (tfcm *TestFCM) cleanup() {
FILE: server/webserver/web_server.go
type WebServer (line 11) | type WebServer struct
method Start (line 27) | func (ws *WebServer) Start() (err error) {
method Stop (line 47) | func (ws *WebServer) Stop() (err error) {
method Handle (line 59) | func (ws *WebServer) Handle(prefix string, handler http.Handler) {
method GetAddr (line 65) | func (ws *WebServer) GetAddr() string {
function New (line 19) | func New(addr string) *WebServer {
type tcpKeepAliveListener (line 77) | type tcpKeepAliveListener struct
method Accept (line 81) | func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
FILE: server/webserver/web_server_test.go
function TestStartAndStopWebServer (line 12) | func TestStartAndStopWebServer(t *testing.T) {
FILE: server/websocket/mocks_auth_gen_test.go
type MockAccessManager (line 13) | type MockAccessManager struct
method EXPECT (line 29) | func (_m *MockAccessManager) EXPECT() *_MockAccessManagerRecorder {
method IsAllowed (line 33) | func (_m *MockAccessManager) IsAllowed(_param0 auth.AccessType, _param...
type _MockAccessManagerRecorder (line 19) | type _MockAccessManagerRecorder struct
method IsAllowed (line 39) | func (_mr *_MockAccessManagerRecorder) IsAllowed(arg0, arg1, arg2 inte...
function NewMockAccessManager (line 23) | func NewMockAccessManager(ctrl *gomock.Controller) *MockAccessManager {
FILE: server/websocket/mocks_router_gen_test.go
type MockRouter (line 17) | type MockRouter struct
method EXPECT (line 33) | func (_m *MockRouter) EXPECT() *_MockRouterRecorder {
method AccessManager (line 37) | func (_m *MockRouter) AccessManager() (auth.AccessManager, error) {
method Cluster (line 48) | func (_m *MockRouter) Cluster() *cluster.Cluster {
method Done (line 58) | func (_m *MockRouter) Done() <-chan bool {
method Fetch (line 68) | func (_m *MockRouter) Fetch(_param0 *store.FetchRequest) error {
method GetSubscribers (line 78) | func (_m *MockRouter) GetSubscribers(_param0 string) ([]byte, error) {
method HandleMessage (line 89) | func (_m *MockRouter) HandleMessage(_param0 *protocol.Message) error {
method KVStore (line 99) | func (_m *MockRouter) KVStore() (kvstore.KVStore, error) {
method MessageStore (line 110) | func (_m *MockRouter) MessageStore() (store.MessageStore, error) {
method Subscribe (line 121) | func (_m *MockRouter) Subscribe(_param0 *router.Route) (*router.Route,...
method Unsubscribe (line 132) | func (_m *MockRouter) Unsubscribe(_param0 *router.Route) {
type _MockRouterRecorder (line 23) | type _MockRouterRecorder struct
method AccessManager (line 44) | func (_mr *_MockRouterRecorder) AccessManager() *gomock.Call {
method Cluster (line 54) | func (_mr *_MockRouterRecorder) Cluster() *gomock.Call {
method Done (line 64) | func (_mr *_MockRouterRecorder) Done() *gomock.Call {
method Fetch (line 74) | func (_mr *_MockRouterRecorder) Fetch(arg0 interface{}) *gomock.Call {
method GetSubscribers (line 85) | func (_mr *_MockRouterRecorder) GetSubscribers(arg0 interface{}) *gomo...
method HandleMessage (line 95) | func (_mr *_MockRouterRecorder) HandleMessage(arg0 interface{}) *gomoc...
method KVStore (line 106) | func (_mr *_MockRouterRecorder) KVStore() *gomock.Call {
method MessageStore (line 117) | func (_mr *_MockRouterRecorder) MessageStore() *gomock.Call {
method Subscribe (line 128) | func (_mr *_MockRouterRecorder) Subscribe(arg0 interface{}) *gomock.Ca...
method Unsubscribe (line 136) | func (_mr *_MockRouterRecorder) Unsubscribe(arg0 interface{}) *gomock....
function NewMockRouter (line 27) | func NewMockRouter(ctrl *gomock.Controller) *MockRouter {
FILE: server/websocket/mocks_store_gen_test.go
type MockMessageStore (line 13) | type MockMessageStore struct
method EXPECT (line 29) | func (_m *MockMessageStore) EXPECT() *_MockMessageStoreRecorder {
method DoInTx (line 33) | func (_m *MockMessageStore) DoInTx(_param0 string, _param1 func(uint64...
method Fetch (line 43) | func (_m *MockMessageStore) Fetch(_param0 *store.FetchRequest) {
method GenerateNextMsgID (line 51) | func (_m *MockMessageStore) GenerateNextMsgID(_param0 string, _param1 ...
method MaxMessageID (line 63) | func (_m *MockMessageStore) MaxMessageID(_param0 string) (uint64, erro...
method Partition (line 74) | func (_m *MockMessageStore) Partition(_param0 string) (store.MessagePa...
method Partitions (line 85) | func (_m *MockMessageStore) Partitions() ([]store.MessagePartition, er...
method Store (line 96) | func (_m *MockMessageStore) Store(_param0 string, _param1 uint64, _par...
method StoreMessage (line 106) | func (_m *MockMessageStore) StoreMessage(_param0 *protocol.Message, _p...
type _MockMessageStoreRecorder (line 19) | type _MockMessageStoreRecorder struct
method DoInTx (line 39) | func (_mr *_MockMessageStoreRecorder) DoInTx(arg0, arg1 interface{}) *...
method Fetch (line 47) | func (_mr *_MockMessageStoreRecorder) Fetch(arg0 interface{}) *gomock....
method GenerateNextMsgID (line 59) | func (_mr *_MockMessageStoreRecorder) GenerateNextMsgID(arg0, arg1 int...
method MaxMessageID (line 70) | func (_mr *_MockMessageStoreRecorder) MaxMessageID(arg0 interface{}) *...
method Partition (line 81) | func (_mr *_MockMessageStoreRecorder) Partition(arg0 interface{}) *gom...
method Partitions (line 92) | func (_mr *_MockMessageStoreRecorder) Partitions() *gomock.Call {
method Store (line 102) | func (_mr *_MockMessageStoreRecorder) Store(arg0, arg1, arg2 interface...
method StoreMessage (line 113) | func (_mr *_MockMessageStoreRecorder) StoreMessage(arg0, arg1 interfac...
function NewMockMessageStore (line 23) | func NewMockMessageStore(ctrl *gomock.Controller) *MockMessageStore {
FILE: server/websocket/mocks_websocket_gen_test.go
type MockWSConnection (line 11) | type MockWSConnection struct
method EXPECT (line 27) | func (_m *MockWSConnection) EXPECT() *_MockWSConnectionRecorder {
method Close (line 31) | func (_m *MockWSConnection) Close() {
method Receive (line 39) | func (_m *MockWSConnection) Receive(_param0 *[]byte) error {
method Send (line 49) | func (_m *MockWSConnection) Send(_param0 []byte) error {
type _MockWSConnectionRecorder (line 17) | type _MockWSConnectionRecorder struct
method Close (line 35) | func (_mr *_MockWSConnectionRecorder) Close() *gomock.Call {
method Receive (line 45) | func (_mr *_MockWSConnectionRecorder) Receive(arg0 interface{}) *gomoc...
method Send (line 55) | func (_mr *_MockWSConnectionRecorder) Send(arg0 interface{}) *gomock.C...
function NewMockWSConnection (line 21) | func NewMockWSConnection(ctrl *gomock.Controller) *MockWSConnection {
FILE: server/websocket/receiver.go
type Receiver (line 21) | type Receiver struct
method Start (line 89) | func (rec *Receiver) Start() error {
method subscriptionLoop (line 99) | func (rec *Receiver) subscriptionLoop() {
method subscribeIfNoUnreadMessagesAvailable (line 139) | func (rec *Receiver) subscribeIfNoUnreadMessagesAvailable(maxMessageID...
method subscribe (line 147) | func (rec *Receiver) subscribe() {
method receiveFromSubscription (line 164) | func (rec *Receiver) receiveFromSubscription() {
method fetchOnlyLoop (line 199) | func (rec *Receiver) fetchOnlyLoop() {
method fetch (line 207) | func (rec *Receiver) fetch() error {
method Stop (line 266) | func (rec *Receiver) Stop() error {
method sendError (line 271) | func (rec *Receiver) sendError(name string, argPattern string, params ...
method sendOK (line 280) | func (rec *Receiver) sendOK(name string, argPattern string, params ......
function NewReceiverFromCmd (line 40) | func NewReceiverFromCmd(
FILE: server/websocket/receiver_test.go
function Test_Receiver_error_handling_on_create (line 18) | func Test_Receiver_error_handling_on_create(t *testing.T) {
function Test_Receiver_Fetch_Subscribe_Fetch_Subscribe (line 32) | func Test_Receiver_Fetch_Subscribe_Fetch_Subscribe(t *testing.T) {
function Test_Receiver_Fetch_Returns_Correct_Messages (line 155) | func Test_Receiver_Fetch_Returns_Correct_Messages(t *testing.T) {
function Test_Receiver_Fetch_Produces_Correct_Fetch_Requests (line 192) | func Test_Receiver_Fetch_Produces_Correct_Fetch_Requests(t *testing.T) {
function Test_Receiver_Fetch_Sends_error_on_failure (line 252) | func Test_Receiver_Fetch_Sends_error_on_failure(t *testing.T) {
function Test_Receiver_Fetch_Sends_error_on_failure_in_MaxMessageId (line 276) | func Test_Receiver_Fetch_Sends_error_on_failure_in_MaxMessageId(t *testi...
function aMockedReceiver (line 294) | func aMockedReceiver(arg string) (*Receiver, chan []byte, *MockRouter, *...
function expectMessages (line 307) | func expectMessages(a *assert.Assertions, msgChannel chan []byte, messag...
FILE: server/websocket/websocket_connector.go
type WSHandler (line 23) | type WSHandler struct
method GetPrefix (line 44) | func (handler *WSHandler) GetPrefix() string {
method ServeHTTP (line 50) | func (handler *WSHandler) ServeHTTP(w http.ResponseWriter, r *http.Req...
function NewWSHandler (line 30) | func NewWSHandler(router router.Router, prefix string) (*WSHandler, erro...
type WSConnection (line 63) | type WSConnection interface
type wsconn (line 71) | type wsconn struct
method Close (line 76) | func (conn *wsconn) Close() {
method Send (line 81) | func (conn *wsconn) Send(bytes []byte) error {
method Receive (line 86) | func (conn *wsconn) Receive(bytes *[]byte) (err error) {
type WebSocket (line 92) | type WebSocket struct
method Start (line 115) | func (ws *WebSocket) Start() error {
method sendLoop (line 122) | func (ws *WebSocket) sendLoop() {
method checkAccess (line 140) | func (ws *WebSocket) checkAccess(raw []byte) bool {
method receiveLoop (line 160) | func (ws *WebSocket) receiveLoop() {
method sendConnectionMessage (line 193) | func (ws *WebSocket) sendConnectionMessage() {
method handleReceiveCmd (line 202) | func (ws *WebSocket) handleReceiveCmd(cmd *protocol.Cmd) {
method handleCancelCmd (line 219) | func (ws *WebSocket) handleCancelCmd(cmd *protocol.Cmd) {
method handleSendCmd (line 232) | func (ws *WebSocket) handleSendCmd(cmd *protocol.Cmd) {
method cleanAndClose (line 256) | func (ws *WebSocket) cleanAndClose() {
method sendError (line 270) | func (ws *WebSocket) sendError(name string, argPattern string, params ...
method sendOK (line 279) | func (ws *WebSocket) sendOK(name string, argPattern string, params ......
function NewWebSocket (line 102) | func NewWebSocket(handler *WSHandler, wsConn WSConnection, userID string...
function getPathFromRawMessage (line 155) | func getPathFromRawMessage(raw []byte) protocol.Path {
function extractUserID (line 292) | func extractUserID(uri string) string {
FILE: server/websocket/websocket_connector_test.go
function Test_WebSocket_SubscribeAndUnsubscribe (line 26) | func Test_WebSocket_SubscribeAndUnsubscribe(t *testing.T) {
function Test_SendMessage (line 64) | func Test_SendMessage(t *testing.T) {
function Test_AnIncomingMessageIsDelivered (line 77) | func Test_AnIncomingMessageIsDelivered(t *testing.T) {
function Test_AnIncomingMessageIsNotAllowed (line 91) | func Test_AnIncomingMessageIsNotAllowed(t *testing.T) {
function Test_BadCommands (line 124) | func Test_BadCommands(t *testing.T) {
function TestExtractUserId (line 156) | func TestExtractUserId(t *testing.T) {
function testWSHandler (line 162) | func testWSHandler(
function runNewWebSocket (line 173) | func runNewWebSocket(
function createDefaultMocks (line 196) | func createDefaultMocks(inputMessages []string) (
type routeMatcher (line 221) | type routeMatcher struct
method Matches (line 225) | func (n routeMatcher) Matches(x interface{}) bool {
method String (line 229) | func (n routeMatcher) String() string {
type messageMatcher (line 234) | type messageMatcher struct
method Matches (line 241) | func (n messageMatcher) Matches(x interface{}) bool {
method String (line 248) | func (n messageMatcher) String() string {
type connectedNotificationMatcher (line 253) | type connectedNotificationMatcher struct
method Matches (line 256) | func (notify connectedNotificationMatcher) Matches(x interface{}) bool {
method String (line 260) | func (notify connectedNotificationMatcher) String() string {
FILE: testutil/testutil.go
function init (line 21) | func init() {
function NewMockCtrl (line 33) | func NewMockCtrl(t *testing.T) (*gomock.Controller, func()) {
function NewMockBenchmarkCtrl (line 38) | func NewMockBenchmarkCtrl(b *testing.B) (*gomock.Controller, func()) {
function EnableDebugForMethod (line 46) | func EnableDebugForMethod() func() {
function EnableInfoForMethod (line 55) | func EnableInfoForMethod() func() {
function ExpectDone (line 63) | func ExpectDone(a *assert.Assertions, doneChannel chan bool) {
function ExpectPanic (line 73) | func ExpectPanic(t *testing.T) {
function ResetDefaultRegistryHealthCheck (line 80) | func ResetDefaultRegistryHealthCheck() {
function SkipIfShort (line 85) | func SkipIfShort(t *testing.T) {
function SkipIfDisabled (line 92) | func SkipIfDisabled(t *testing.T) {
function PprofDebug (line 98) | func PprofDebug() {
Condensed preview — 190 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (676K chars).
[
{
"path": ".gitignore",
"chars": 60,
"preview": "cover.out\n.goxc.json\n/.idea\n*.iml\nguble\nguble-cli/guble-cli\n"
},
{
"path": ".travis.yml",
"chars": 2976,
"preview": "language: go\ngo:\n - tip\nsudo: required\nservices:\n - docker\n - postgresql\nbefore_script:\n - psql -c 'create database "
},
{
"path": "Dockerfile",
"chars": 165,
"preview": "FROM alpine\nCOPY ./guble ./guble-cli/guble-cli /usr/local/bin/\nRUN mkdir -p /var/lib/guble\nVOLUME [\"/var/lib/guble\"]\nENT"
},
{
"path": "LICENSE",
"chars": 1083,
"preview": "The MIT License (MIT)\nCopyright (c) 2015 Sebastian Mancke\n\nPermission is hereby granted, free of charge, to any person o"
},
{
"path": "README.md",
"chars": 18786,
"preview": "# Guble Messaging Server\n\nGuble is a simple user-facing messaging and data replication server written in Go.\n\n[![Codacy "
},
{
"path": "api/swagger.yaml",
"chars": 10500,
"preview": "swagger: '2.0'\n\ninfo:\n version: \"0.0.1\"\n title: Guble API\n \nschemes:\n - http\n \npaths:\n \n /api/subscribers/{topic}"
},
{
"path": "client/client.go",
"chars": 6225,
"preview": "package client\n\nimport (\n\t\"github.com/smancke/guble/protocol\"\n\n\tlog \"github.com/Sirupsen/logrus\"\n\t\"github.com/gorilla/we"
},
{
"path": "client/client_test.go",
"chars": 7076,
"preview": "package client\n\nimport (\n\t\"github.com/smancke/guble/testutil\"\n\n\t\"fmt\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/gorill"
},
{
"path": "client/mocks_client_gen_test.go",
"chars": 5513,
"preview": "// Automatically generated by MockGen. DO NOT EDIT!\n// Source: github.com/smancke/guble/client (interfaces: WSConnection"
},
{
"path": "guble-cli/README.md",
"chars": 1705,
"preview": "# The guble command line client\n\nThis is the command line client for the guble messaging server. It is intended\nfor demo"
},
{
"path": "guble-cli/main.go",
"chars": 3852,
"preview": "package main\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"os\"\n\t\"os/signal\"\n\t\"strings\"\n\t\"syscall\"\n\n\tlog \"github.com/Sirupsen/logrus\"\n\t\"git"
},
{
"path": "guble-cli/main_test.go",
"chars": 1172,
"preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"io/ioutil\"\n\t\"os\"\n\t\"testing\"\n)\n\nfunc Test_PrintHelp"
},
{
"path": "logformatter/logstash_formatter.go",
"chars": 2784,
"preview": "package logformatter\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\n\t\"github.com/Sirupsen/logrus\"\n\t\"os\"\n\t\"time\"\n)\n\nconst (\n\tdefaultS"
},
{
"path": "logformatter/logstash_formatter_test.go",
"chars": 1203,
"preview": "package logformatter\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"testing\"\n\n\t\"github.com/Sirupsen/logrus\"\n\t\"github.com/stretchr"
},
{
"path": "main.go",
"chars": 92,
"preview": "package main\n\nimport (\n\t\"github.com/smancke/guble/server\"\n)\n\nfunc main() {\n\tserver.Main()\n}\n"
},
{
"path": "protocol/cmd.go",
"chars": 1407,
"preview": "package protocol\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"strings\"\n)\n\n// Valid command names\nconst (\n\tCmdSend = \">\"\n\tCmdReceive = "
},
{
"path": "protocol/cmd_test.go",
"chars": 1291,
"preview": "package protocol\n\nimport (\n\tassert \"github.com/stretchr/testify/assert\"\n\t\"testing\"\n)\n\nvar aSendCommand = `> /foo\n{\"meta\""
},
{
"path": "protocol/log.go",
"chars": 1324,
"preview": "package protocol\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\tlog \"github.com/Sirupsen/logrus\"\n\t\"runtime\"\n\t\"strings\"\n)\n\nfunc PanicLogger()"
},
{
"path": "protocol/log_test.go",
"chars": 480,
"preview": "package protocol\n\nimport (\n\t\"bytes\"\n\tlog \"github.com/Sirupsen/logrus\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"os\"\n\t\"test"
},
{
"path": "protocol/message.go",
"chars": 6465,
"preview": "package protocol\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\n\tlog \"github.com/Sirupsen/logrus\"\n)\n\n"
},
{
"path": "protocol/message_test.go",
"chars": 6063,
"preview": "package protocol\n\nimport (\n\t\"strings\"\n\t\"testing\"\n\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nvar aNormalMessage ="
},
{
"path": "protocol/path.go",
"chars": 384,
"preview": "package protocol\n\nimport \"strings\"\n\n// Path is the path of a topic\ntype Path string\n\n// Partition returns the parsed par"
},
{
"path": "restclient/guble_sender.go",
"chars": 3096,
"preview": "package restclient\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n\n\t\"io/ioutil\"\n\t\"net/url\"\n\n\tlog \"github.com/Sirupsen/"
},
{
"path": "restclient/guble_sender_test.go",
"chars": 2498,
"preview": "package restclient\n\nimport (\n\t\"github.com/stretchr/testify/assert\"\n\t\"net/url\"\n\t\"testing\"\n)\n\nfunc TestGetURL(t *testing.T"
},
{
"path": "restclient/logger.go",
"chars": 134,
"preview": "package restclient\n\nimport (\n\tlog \"github.com/Sirupsen/logrus\"\n)\n\nvar logger = log.WithFields(log.Fields{\n\t\"module\": \"re"
},
{
"path": "restclient/sender.go",
"chars": 540,
"preview": "package restclient\n\n// Sender is an interface used to send a message to the guble server.\ntype Sender interface {\n\t// Se"
},
{
"path": "scripts/Dockerfile-cluster",
"chars": 222,
"preview": "# this Dockerfile requires u to build the app locally with the name `guble`\nFROM phusion/baseimage\nRUN mkdir -p /var/lib"
},
{
"path": "scripts/compose.cluster.test.yml",
"chars": 614,
"preview": "version: '2'\nservices:\n cluster_1:\n build:\n context: ..\n dockerfile: scripts/Dockerfile-cluster\n entryp"
},
{
"path": "scripts/compose.postgres.test.yml",
"chars": 585,
"preview": "# docker-compose file to run test(s) using dockerized Postgresql\n# Start Postgres from root of project with following co"
},
{
"path": "scripts/cov.sh",
"chars": 367,
"preview": "#!/bin/bash -e\n# Run from parent directory via:\n# ./scripts/cov.sh\n# Requires installation of:\n# go get golang.o"
},
{
"path": "scripts/dependencies_graph.sh",
"chars": 295,
"preview": "#!/bin/bash -e\n# Requires installation of package: graphviz\n\n(echo \"digraph G {\"\ngo list -f '{{range .Imports}}{{printf "
},
{
"path": "scripts/file-hex.sh",
"chars": 44,
"preview": "#!/usr/bin/env bash\n\nxxd -p $1 | tr -d '\\n'\n"
},
{
"path": "scripts/generate_coverage.sh",
"chars": 459,
"preview": "#!/bin/bash -e\n# Requires local installation of: `github.com/wadey/gocovmerge`\n\ncd $GOPATH/src/github.com/smancke/guble\n"
},
{
"path": "scripts/generate_mocks.sh",
"chars": 6624,
"preview": "#!/bin/bash -xe\n\n# Prerequisites: mockgen should be installed\n# go get github.com/golang/mock/mockgen\n\nif [ -z \"$GOPAT"
},
{
"path": "server/apns/apns.go",
"chars": 4290,
"preview": "package apns\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/sideshow/apns2\"\n\t\"github.com/smancke/guble/server/connector\"\n\t\"gith"
},
{
"path": "server/apns/apns_metrics.go",
"chars": 2643,
"preview": "package apns\n\nimport (\n\t\"github.com/smancke/guble/server/metrics\"\n\t\"time\"\n)\n\nvar (\n\tns = m"
},
{
"path": "server/apns/apns_pusher.go",
"chars": 3076,
"preview": "package apns\n\nimport (\n\t\"crypto/tls\"\n\t\"github.com/sideshow/apns2\"\n\t\"github.com/sideshow/apns2/certificate\"\n\t\"golang.org/"
},
{
"path": "server/apns/apns_sender.go",
"chars": 2532,
"preview": "package apns\n\nimport (\n\t\"errors\"\n\t\"github.com/jpillora/backoff\"\n\t\"github.com/sideshow/apns2\"\n\t\"github.com/smancke/guble/"
},
{
"path": "server/apns/apns_sender_test.go",
"chars": 4428,
"preview": "package apns\n\nimport (\n\t\"errors\"\n\t\"github.com/golang/mock/gomock\"\n\t\"github.com/smancke/guble/protocol\"\n\t\"github.com/sman"
},
{
"path": "server/apns/apns_test.go",
"chars": 5543,
"preview": "package apns\n\nimport (\n\t\"errors\"\n\t\"github.com/golang/mock/gomock\"\n\t\"github.com/sideshow/apns2\"\n\t\"github.com/smancke/gubl"
},
{
"path": "server/apns/logger.go",
"chars": 105,
"preview": "package apns\n\nimport (\n\tlog \"github.com/Sirupsen/logrus\"\n)\n\nvar logger = log.WithField(\"module\", \"apns\")\n"
},
{
"path": "server/apns/mocks_connector_gen_test.go",
"chars": 4624,
"preview": "// Automatically generated by MockGen. DO NOT EDIT!\n// Source: github.com/smancke/guble/server/connector (interfaces: Se"
},
{
"path": "server/apns/mocks_kvstore_gen_test.go",
"chars": 2358,
"preview": "// Automatically generated by MockGen. DO NOT EDIT!\n// Source: github.com/smancke/guble/server/kvstore (interfaces: KVSt"
},
{
"path": "server/apns/mocks_pusher_gen_test.go",
"chars": 1004,
"preview": "// Automatically generated by MockGen. DO NOT EDIT!\n// Source: github.com/smancke/guble/server/apns (interfaces: Pusher)"
},
{
"path": "server/apns/mocks_router_gen_test.go",
"chars": 3843,
"preview": "// Automatically generated by MockGen. DO NOT EDIT!\n// Source: github.com/smancke/guble/server/router (interfaces: Route"
},
{
"path": "server/auth/accessmanager.go",
"chars": 400,
"preview": "package auth\n\nimport (\n\t\"github.com/smancke/guble/protocol\"\n)\n\n// AccessType permission required by the user\ntype Access"
},
{
"path": "server/auth/accessmanager_test.go",
"chars": 1726,
"preview": "package auth\n\nimport (\n\t\"github.com/stretchr/testify/assert\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\nfunc Test_Al"
},
{
"path": "server/auth/allow_all_accessmanager.go",
"chars": 634,
"preview": "package auth\n\nimport (\n\t\"github.com/smancke/guble/protocol\"\n)\n\n//AllowAllAccessManager is a dummy implementation that gr"
},
{
"path": "server/auth/logger.go",
"chars": 131,
"preview": "package auth\n\nimport (\n\tlog \"github.com/Sirupsen/logrus\"\n)\n\nvar logger = log.WithFields(log.Fields{\n\t\"module\": \"accessMa"
},
{
"path": "server/auth/mocks_auth_gen_test.go",
"chars": 1132,
"preview": "// Automatically generated by MockGen. DO NOT EDIT!\n// Source: github.com/smancke/guble/server/auth (interfaces: AccessM"
},
{
"path": "server/auth/rest_accessmanager.go",
"chars": 1554,
"preview": "package auth\n\nimport (\n\t\"github.com/smancke/guble/protocol\"\n\n\tlog \"github.com/Sirupsen/logrus\"\n\n\t\"io/ioutil\"\n\t\"net/http\""
},
{
"path": "server/benchmarking_apns_test.go",
"chars": 6345,
"preview": "package server\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\tlog \"github.com/Sirupsen/logrus\"\n\t\"github.com/golang/mock/gomock\"\n\t\"github.com"
},
{
"path": "server/benchmarking_common_test.go",
"chars": 2400,
"preview": "package server\n\nimport (\n\t\"fmt\"\n\t\"github.com/smancke/guble/client\"\n\t\"github.com/smancke/guble/server/service\"\n\t\"github.c"
},
{
"path": "server/benchmarking_fcm_test.go",
"chars": 4781,
"preview": "package server\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"net/http\"\n\t\"os\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\tlog \"github.com/"
},
{
"path": "server/benchmarking_fetch_test.go",
"chars": 1621,
"preview": "package server\n\nimport (\n\t\"github.com/smancke/guble/client\"\n\t\"github.com/smancke/guble/testutil\"\n\n\t\"github.com/stretchr/"
},
{
"path": "server/benchmarking_test.go",
"chars": 4457,
"preview": "package server\n\nimport (\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"os\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\n\t\"g"
},
{
"path": "server/cluster/cluster.go",
"chars": 7276,
"preview": "package cluster\n\nimport (\n\t\"io/ioutil\"\n\n\t\"github.com/smancke/guble/protocol\"\n\t\"github.com/smancke/guble/server/store\"\n\n\t"
},
{
"path": "server/cluster/cluster_benchmarking_test.go",
"chars": 3231,
"preview": "package cluster\n\nimport (\n\tlog \"github.com/Sirupsen/logrus\"\n\t\"github.com/hashicorp/memberlist\"\n\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"te"
},
{
"path": "server/cluster/cluster_conflict.go",
"chars": 478,
"preview": "package cluster\n\nimport (\n\tlog \"github.com/Sirupsen/logrus\"\n\n\t\"github.com/hashicorp/memberlist\"\n)\n\n// =================="
},
{
"path": "server/cluster/cluster_delegate.go",
"chars": 3041,
"preview": "package cluster\n\nimport (\n\tlog \"github.com/Sirupsen/logrus\"\n\n\t\"github.com/smancke/guble/protocol\"\n)\n\n// ================"
},
{
"path": "server/cluster/cluster_event_delegate.go",
"chars": 1899,
"preview": "package cluster\n\nimport (\n\tlog \"github.com/Sirupsen/logrus\"\n\n\t\"github.com/hashicorp/memberlist\"\n)\n\n// =================="
},
{
"path": "server/cluster/cluster_test.go",
"chars": 6854,
"preview": "package cluster\n\nimport (\n\t\"io/ioutil\"\n\n\t\"github.com/smancke/guble/server/store/filestore\"\n\n\t\"github.com/smancke/guble/p"
},
{
"path": "server/cluster/codec.go",
"chars": 1763,
"preview": "package cluster\n\nimport (\n\tlog \"github.com/Sirupsen/logrus\"\n\n\t\"github.com/ugorji/go/codec\"\n)\n\ntype messageType int\n\nvar "
},
{
"path": "server/cluster/codec_test.go",
"chars": 16,
"preview": "package cluster\n"
},
{
"path": "server/cluster/logger.go",
"chars": 128,
"preview": "package cluster\n\nimport (\n\tlog \"github.com/Sirupsen/logrus\"\n)\n\nvar logger = log.WithFields(log.Fields{\n\t\"module\": \"clust"
},
{
"path": "server/cluster/synchronizer.go",
"chars": 10535,
"preview": "package cluster\n\nimport (\n\t\"errors\"\n\t\"math\"\n\t\"strconv\"\n\t\"sync\"\n\n\tlog \"github.com/Sirupsen/logrus\"\n\n\t\"github.com/smancke/"
},
{
"path": "server/cluster_integration_test.go",
"chars": 4773,
"preview": "package server\n\nimport (\n\tlog \"github.com/Sirupsen/logrus\"\n\t\"github.com/smancke/guble/protocol\"\n\t\"github.com/smancke/gub"
},
{
"path": "server/config.go",
"chars": 9007,
"preview": "package server\n\nimport (\n\t\"github.com/Bogh/gcm\"\n\tlog \"github.com/Sirupsen/logrus\"\n\t\"gopkg.in/alecthomas/kingpin.v2\"\n\n\t\"f"
},
{
"path": "server/config_test.go",
"chars": 4914,
"preview": "package server\n\nimport (\n\t\"github.com/stretchr/testify/assert\"\n\t\"net\"\n\t\"os\"\n\t\"testing\"\n)\n\nfunc TestParsingOfEnvironmentV"
},
{
"path": "server/connector/connector.go",
"chars": 9118,
"preview": "package connector\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"sync\"\n\t\"time\"\n\n\tlog \"github.com/Sirupsen/lo"
},
{
"path": "server/connector/connector_test.go",
"chars": 10699,
"preview": "package connector\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/golang/m"
},
{
"path": "server/connector/logger.go",
"chars": 115,
"preview": "package connector\n\nimport (\n\tlog \"github.com/Sirupsen/logrus\"\n)\n\nvar logger = log.WithField(\"module\", \"connector\")\n"
},
{
"path": "server/connector/manager.go",
"chars": 4236,
"preview": "package connector\n\nimport (\n\t\"sync\"\n\n\t\"github.com/smancke/guble/protocol\"\n\t\"github.com/smancke/guble/server/kvstore\"\n\t\"g"
},
{
"path": "server/connector/manager_test.go",
"chars": 18,
"preview": "package connector\n"
},
{
"path": "server/connector/mocks_connector_gen_test.go",
"chars": 13974,
"preview": "// Automatically generated by MockGen. DO NOT EDIT!\n// Source: github.com/smancke/guble/server/connector (interfaces: Co"
},
{
"path": "server/connector/mocks_kvstore_gen_test.go",
"chars": 2363,
"preview": "// Automatically generated by MockGen. DO NOT EDIT!\n// Source: github.com/smancke/guble/server/kvstore (interfaces: KVSt"
},
{
"path": "server/connector/mocks_router_gen_test.go",
"chars": 3848,
"preview": "// Automatically generated by MockGen. DO NOT EDIT!\n// Source: github.com/smancke/guble/server/router (interfaces: Route"
},
{
"path": "server/connector/queue.go",
"chars": 2623,
"preview": "package connector\n\nimport (\n\t\"sync\"\n\n\t\"time\"\n\n\tlog \"github.com/Sirupsen/logrus\"\n)\n\n// Queue is an interface modeling a t"
},
{
"path": "server/connector/request.go",
"chars": 446,
"preview": "package connector\n\nimport \"github.com/smancke/guble/protocol\"\n\ntype Request interface {\n\tSubscriber() Subscriber\n\tMessag"
},
{
"path": "server/connector/subscriber.go",
"chars": 3659,
"preview": "package connector\n\nimport (\n\t\"context\"\n\t\"crypto/sha1\"\n\t\"encoding/hex\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"sort\"\n\n\t"
},
{
"path": "server/connector/substitution.go",
"chars": 264,
"preview": "package connector\n\ntype substitution struct {\n\tFieldName string `json:\"field\"`\n\tOldValue string `json:\"old_value\"`\n\tNew"
},
{
"path": "server/fcm/fcm.go",
"chars": 4652,
"preview": "package fcm\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/Bogh/gcm\"\n\t\"github.com/smancke/guble/protocol\"\n\t\"github.com/smancke/guble/ser"
},
{
"path": "server/fcm/fcm_metrics.go",
"chars": 2504,
"preview": "package fcm\n\nimport (\n\t\"github.com/smancke/guble/server/metrics\"\n\t\"time\"\n)\n\nvar (\n\tns = m"
},
{
"path": "server/fcm/fcm_sender.go",
"chars": 1673,
"preview": "package fcm\n\nimport (\n\t\"encoding/json\"\n\t\"time\"\n\n\tlog \"github.com/Sirupsen/logrus\"\n\n\t\"github.com/Bogh/gcm\"\n\t\"github.com/s"
},
{
"path": "server/fcm/fcm_test.go",
"chars": 6196,
"preview": "package fcm\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/Bogh/gcm"
},
{
"path": "server/fcm/json_error.go",
"chars": 107,
"preview": "package fcm\n\ntype jsonError struct {\n\tjson string\n}\n\nfunc (e *jsonError) Error() string {\n\treturn e.json\n}\n"
},
{
"path": "server/fcm/logger.go",
"chars": 103,
"preview": "package fcm\n\nimport (\n\tlog \"github.com/Sirupsen/logrus\"\n)\n\nvar logger = log.WithField(\"module\", \"fcm\")\n"
},
{
"path": "server/fcm/mocks_gcm_gen_test.go",
"chars": 967,
"preview": "// Automatically generated by MockGen. DO NOT EDIT!\n// Source: github.com/Bogh/gcm (interfaces: Sender)\n\npackage fcm\n\nim"
},
{
"path": "server/fcm/mocks_kvstore_gen_test.go",
"chars": 2357,
"preview": "// Automatically generated by MockGen. DO NOT EDIT!\n// Source: github.com/smancke/guble/server/kvstore (interfaces: KVSt"
},
{
"path": "server/fcm/mocks_router_gen_test.go",
"chars": 3842,
"preview": "// Automatically generated by MockGen. DO NOT EDIT!\n// Source: github.com/smancke/guble/server/router (interfaces: Route"
},
{
"path": "server/fcm/mocks_store_gen_test.go",
"chars": 3640,
"preview": "// Automatically generated by MockGen. DO NOT EDIT!\n// Source: github.com/smancke/guble/server/store (interfaces: Messag"
},
{
"path": "server/fcm/testutil.go",
"chars": 1424,
"preview": "package fcm\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/Bogh/gcm\"\n\t\"github.com/smancke/guble/server/connecto"
},
{
"path": "server/fcm_integration_test.go",
"chars": 6751,
"preview": "package server\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"net/http\"\n\t\"os\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"encoding/json\"\n"
},
{
"path": "server/gubled.go",
"chars": 9940,
"preview": "package server\n\nimport (\n\tlog \"github.com/Sirupsen/logrus\"\n\n\t\"github.com/smancke/guble/logformatter\"\n\t\"github.com/smanck"
},
{
"path": "server/gubled_test.go",
"chars": 3492,
"preview": "package server\n\nimport (\n\t\"github.com/smancke/guble/server/kvstore\"\n\n\t\"github.com/smancke/guble/testutil\"\n\t\"github.com/s"
},
{
"path": "server/integration_test.go",
"chars": 3658,
"preview": "package server\n\nimport (\n\t\"github.com/smancke/guble/client\"\n\t\"github.com/smancke/guble/protocol\"\n\t\"github.com/smancke/gu"
},
{
"path": "server/kvstore/common_test.go",
"chars": 4334,
"preview": "package kvstore\n\nimport (\n\t\"github.com/stretchr/testify/assert\"\n\n\t\"crypto/rand\"\n\t\"io/ioutil\"\n\t\"os\"\n\t\"testing\"\n\t\"time\"\n)\n"
},
{
"path": "server/kvstore/gorm.go",
"chars": 2744,
"preview": "package kvstore\n\nimport (\n\tlog \"github.com/Sirupsen/logrus\"\n\t\"github.com/jinzhu/gorm\"\n\n\t\"errors\"\n\t\"time\"\n)\n\nconst (\n\tres"
},
{
"path": "server/kvstore/kvstore.go",
"chars": 907,
"preview": "package kvstore\n\n// KVStore is an interface for a persistence backend, storing key-value pairs.\ntype KVStore interface {"
},
{
"path": "server/kvstore/memory.go",
"chars": 2587,
"preview": "package kvstore\n\nimport (\n\t\"strings\"\n\t\"sync\"\n)\n\n// MemoryKVStore is a struct representing an in-memory key-value store.\n"
},
{
"path": "server/kvstore/memory_test.go",
"chars": 461,
"preview": "package kvstore\n\nimport (\n\t\"testing\"\n)\n\nfunc TestMemoryPutGetDelete(t *testing.T) {\n\tmkvs := NewMemoryKVStore()\n\tCommonT"
},
{
"path": "server/kvstore/postgres.go",
"chars": 1716,
"preview": "package kvstore\n\nimport (\n\tlog \"github.com/Sirupsen/logrus\"\n\n\t\"github.com/jinzhu/gorm\"\n\n\t// use gorm's postgres dialect\n"
},
{
"path": "server/kvstore/postgres_config.go",
"chars": 509,
"preview": "package kvstore\n\nimport \"strings\"\n\n// PostgresConfig is a map-based configuration of a Postgresql connection (dbname, ho"
},
{
"path": "server/kvstore/postgres_config_test.go",
"chars": 539,
"preview": "package kvstore\n\nimport (\n\t\"github.com/stretchr/testify/assert\"\n\t\"testing\"\n)\n\nfunc TestPostgresConfig_String(t *testing."
},
{
"path": "server/kvstore/postgres_test.go",
"chars": 1682,
"preview": "package kvstore\n\nimport (\n\t\"github.com/stretchr/testify/assert\"\n\t\"testing\"\n)\n\nfunc BenchmarkPostgresKVStore_PutGet(b *te"
},
{
"path": "server/kvstore/sqlite.go",
"chars": 2817,
"preview": "package kvstore\n\nimport (\n\t// use this as gorm's sqlite dialect / implementation\n\t_ \"github.com/mattn/go-sqlite3\"\n\n\t\"git"
},
{
"path": "server/kvstore/sqlite_test.go",
"chars": 1052,
"preview": "package kvstore\n\nimport (\n\t\"github.com/stretchr/testify/assert\"\n\t\"os\"\n\t\"testing\"\n)\n\nfunc BenchmarkSqlitePutGet(b *testin"
},
{
"path": "server/logger.go",
"chars": 126,
"preview": "package server\n\nimport (\n\tlog \"github.com/Sirupsen/logrus\"\n)\n\nvar logger = log.WithFields(log.Fields{\n\t\"module\": \"server"
},
{
"path": "server/metrics/average.go",
"chars": 364,
"preview": "package metrics\n\nimport (\n\t\"fmt\"\n)\n\ntype average struct {\n\tvalue string\n}\n\nfunc newAverage(total, cases, scale int64, de"
},
{
"path": "server/metrics/average_test.go",
"chars": 405,
"preview": "package metrics\n\nimport (\n\t\"github.com/stretchr/testify/assert\"\n\n\t\"testing\"\n)\n\nfunc TestAverage_String(t *testing.T) {\n\t"
},
{
"path": "server/metrics/disabled.go",
"chars": 933,
"preview": "// +build disablemetrics\n\npackage metrics\n\nimport (\n\t\"expvar\"\n\t\"time\"\n)\n\ntype dummyInt struct{}\n\n// Dummy functions on d"
},
{
"path": "server/metrics/enabled.go",
"chars": 732,
"preview": "// +build !disablemetrics\n\npackage metrics\n\nimport (\n\t\"context\"\n\t\"expvar\"\n\t\"time\"\n)\n\n// NewInt returns an expvar Int, de"
},
{
"path": "server/metrics/enabled_test.go",
"chars": 183,
"preview": "package metrics\n\nimport (\n\t\"github.com/stretchr/testify/assert\"\n\n\t\"expvar\"\n\t\"testing\"\n)\n\nfunc TestNewInt(t *testing.T) {"
},
{
"path": "server/metrics/int.go",
"chars": 136,
"preview": "package metrics\n\n// Int is an interface for some of the operations defined on expvar.Int\ntype Int interface {\n\tAdd(int64"
},
{
"path": "server/metrics/map.go",
"chars": 1082,
"preview": "package metrics\n\nimport (\n\t\"expvar\"\n\t\"strconv\"\n\t\"time\"\n)\n\n// Map is an interface for some of the operations defined on e"
},
{
"path": "server/metrics/metrics.go",
"chars": 1326,
"preview": "// Package metrics implements simple general counter-metrics.\n// Metrics are enabled by default. If you want to disable "
},
{
"path": "server/metrics/metrics_test.go",
"chars": 1118,
"preview": "package metrics\n\nimport (\n\t\"github.com/stretchr/testify/assert\"\n\n\tlog \"github.com/Sirupsen/logrus\"\n\n\t\"bytes\"\n\t\"io/ioutil"
},
{
"path": "server/metrics/ns.go",
"chars": 313,
"preview": "package metrics\n\nconst sep = \".\"\n\n//NS is a namespace\ntype NS string\n\nfunc (ns NS) NewInt(key string) Int {\n\treturn NewI"
},
{
"path": "server/metrics/rate.go",
"chars": 370,
"preview": "package metrics\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\ntype rate struct {\n\tvalue string\n}\n\nfunc newRate(value int64, timeframe, sca"
},
{
"path": "server/metrics/rate_test.go",
"chars": 399,
"preview": "package metrics\n\nimport (\n\t\"github.com/stretchr/testify/assert\"\n\n\t\"testing\"\n)\n\nfunc TestRate_String(t *testing.T) {\n\tass"
},
{
"path": "server/metrics/time.go",
"chars": 243,
"preview": "package metrics\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\ntype Time struct {\n\ttimeValue time.Time\n}\n\nfunc NewTime(timeValue time.Time)"
},
{
"path": "server/metrics/zero.go",
"chars": 114,
"preview": "package metrics\n\ntype zeroVar struct {\n}\n\nfunc (z zeroVar) String() string {\n\treturn \"0\"\n}\n\nvar zeroValue zeroVar\n"
},
{
"path": "server/mocks_apns_pusher_gen_test.go",
"chars": 1006,
"preview": "// Automatically generated by MockGen. DO NOT EDIT!\n// Source: github.com/smancke/guble/server/apns (interfaces: Pusher)"
},
{
"path": "server/mocks_auth_gen_test.go",
"chars": 1200,
"preview": "// Automatically generated by MockGen. DO NOT EDIT!\n// Source: github.com/smancke/guble/server/auth (interfaces: AccessM"
},
{
"path": "server/mocks_router_gen_test.go",
"chars": 3845,
"preview": "// Automatically generated by MockGen. DO NOT EDIT!\n// Source: github.com/smancke/guble/server/router (interfaces: Route"
},
{
"path": "server/mocks_store_gen_test.go",
"chars": 3643,
"preview": "// Automatically generated by MockGen. DO NOT EDIT!\n// Source: github.com/smancke/guble/server/store (interfaces: Messag"
},
{
"path": "server/redundancy_test.go",
"chars": 6272,
"preview": "package server\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/smancke/guble/server/fcm\"\n\t\"github.com/smancke/guble/testutil\""
},
{
"path": "server/rest/mocks_router_gen_test.go",
"chars": 3843,
"preview": "// Automatically generated by MockGen. DO NOT EDIT!\n// Source: github.com/smancke/guble/server/router (interfaces: Route"
},
{
"path": "server/rest/rest_message_api.go",
"chars": 4342,
"preview": "package rest\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/azer/snakecase\"\n\n\t\"github.com/smancke/guble/protocol\"\n\t\"github.com"
},
{
"path": "server/rest/rest_message_api_test.go",
"chars": 5534,
"preview": "package rest\n\nimport (\n\t\"github.com/smancke/guble/protocol\"\n\t\"github.com/smancke/guble/testutil\"\n\n\t\"github.com/golang/mo"
},
{
"path": "server/router/errors.go",
"chars": 1467,
"preview": "package router\n\nimport (\n\t\"github.com/smancke/guble/protocol\"\n\t\"github.com/smancke/guble/server/auth\"\n\n\t\"errors\"\n\t\"fmt\"\n"
},
{
"path": "server/router/logger.go",
"chars": 109,
"preview": "package router\n\nimport (\n\tlog \"github.com/Sirupsen/logrus\"\n)\n\nvar logger = log.WithField(\"module\", \"router\")\n"
},
{
"path": "server/router/message_queue.go",
"chars": 1052,
"preview": "package router\n\nimport (\n\t\"github.com/smancke/guble/protocol\"\n\n\t\"sync\"\n)\n\nconst (\n\tdefaultQueueCap = 50\n)\n\ntype queue st"
},
{
"path": "server/router/mocks_auth_gen_test.go",
"chars": 1200,
"preview": "// Automatically generated by MockGen. DO NOT EDIT!\n// Source: github.com/smancke/guble/server/auth (interfaces: AccessM"
},
{
"path": "server/router/mocks_checker_gen_test.go",
"chars": 871,
"preview": "// Automatically generated by MockGen. DO NOT EDIT!\n// Source: github.com/docker/distribution/health (interfaces: Checke"
},
{
"path": "server/router/mocks_kvstore_gen_test.go",
"chars": 2360,
"preview": "// Automatically generated by MockGen. DO NOT EDIT!\n// Source: github.com/smancke/guble/server/kvstore (interfaces: KVSt"
},
{
"path": "server/router/mocks_router_gen_test.go",
"chars": 3776,
"preview": "// Automatically generated by MockGen. DO NOT EDIT!\n// Source: github.com/smancke/guble/server/router (interfaces: Route"
},
{
"path": "server/router/mocks_store_gen_test.go",
"chars": 3643,
"preview": "// Automatically generated by MockGen. DO NOT EDIT!\n// Source: github.com/smancke/guble/server/store (interfaces: Messag"
},
{
"path": "server/router/route.go",
"chars": 8399,
"preview": "package router\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"runtime\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\tlog \"github.com/Sirupsen/logrus\"\n\t\"git"
},
{
"path": "server/router/route_config.go",
"chars": 1739,
"preview": "package router\n\nimport (\n\t\"time\"\n\n\t\"github.com/smancke/guble/protocol\"\n\t\"github.com/smancke/guble/server/store\"\n)\n\n// Ma"
},
{
"path": "server/router/route_config_test.go",
"chars": 4064,
"preview": "package router\n\nimport (\n\t\"testing\"\n\n\t\"github.com/smancke/guble/protocol\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\ntype "
},
{
"path": "server/router/route_params.go",
"chars": 1751,
"preview": "package router\n\nimport (\n\t\"fmt\"\n\t\"sort\"\n\t\"strings\"\n)\n\ntype RouteParams map[string]string\n\nfunc (rp *RouteParams) String("
},
{
"path": "server/router/route_test.go",
"chars": 13201,
"preview": "package router\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/golang/mock/gomock\"\n\t\"github.com/"
},
{
"path": "server/router/router.go",
"chars": 12953,
"preview": "package router\n\nimport (\n\t\"fmt\"\n\t\"runtime\"\n\t\"strings\"\n\t\"sync\"\n\n\tlog \"github.com/Sirupsen/logrus\"\n\t\"github.com/docker/dis"
},
{
"path": "server/router/router_metrics.go",
"chars": 2532,
"preview": "package router\n\nimport (\n\t\"github.com/smancke/guble/server/metrics\"\n)\n\nvar (\n\tmTotalSubscriptionAttempts "
},
{
"path": "server/router/router_test.go",
"chars": 14266,
"preview": "package router\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/smancke/guble/protocol\"\n\t\"github.com/smancke/guble/s"
},
{
"path": "server/service/logger.go",
"chars": 128,
"preview": "package service\n\nimport (\n\tlog \"github.com/Sirupsen/logrus\"\n)\n\nvar logger = log.WithFields(log.Fields{\n\t\"module\": \"servi"
},
{
"path": "server/service/mocks_checker_gen_test.go",
"chars": 872,
"preview": "// Automatically generated by MockGen. DO NOT EDIT!\n// Source: github.com/docker/distribution/health (interfaces: Checke"
},
{
"path": "server/service/mocks_router_gen_test.go",
"chars": 3846,
"preview": "// Automatically generated by MockGen. DO NOT EDIT!\n// Source: github.com/smancke/guble/server/router (interfaces: Route"
},
{
"path": "server/service/module.go",
"chars": 1223,
"preview": "package service\n\nimport (\n\t\"net/http\"\n\t\"sort\"\n)\n\n// Startable interface for modules which provide a start mechanism\ntype"
},
{
"path": "server/service/service.go",
"chars": 5398,
"preview": "package service\n\nimport (\n\tlog \"github.com/Sirupsen/logrus\"\n\t\"github.com/docker/distribution/health\"\n\n\t\"github.com/smanc"
},
{
"path": "server/service/service_test.go",
"chars": 5116,
"preview": "package service\n\nimport (\n\t\"github.com/smancke/guble/server/kvstore\"\n\t\"github.com/smancke/guble/server/store\"\n\t\"github.c"
},
{
"path": "server/sms/logger.go",
"chars": 103,
"preview": "package sms\n\nimport (\n\tlog \"github.com/Sirupsen/logrus\"\n)\n\nvar logger = log.WithField(\"module\", \"sms\")\n"
},
{
"path": "server/sms/mocks_router_gen_test.go",
"chars": 3842,
"preview": "// Automatically generated by MockGen. DO NOT EDIT!\n// Source: github.com/smancke/guble/server/router (interfaces: Route"
},
{
"path": "server/sms/mocks_sender_gen_test.go",
"chars": 949,
"preview": "// Automatically generated by MockGen. DO NOT EDIT!\n// Source: github.com/smancke/guble/server/sms (interfaces: Sender)\n"
},
{
"path": "server/sms/mocks_store_gen_test.go",
"chars": 3640,
"preview": "// Automatically generated by MockGen. DO NOT EDIT!\n// Source: github.com/smancke/guble/server/store (interfaces: Messag"
},
{
"path": "server/sms/nexmo_sms.go",
"chars": 543,
"preview": "package sms\n\nimport \"encoding/json\"\n\ntype NexmoSms struct {\n\tApiKey string `json:\"api_key,omitempty\"`\n\tApiSecret stri"
},
{
"path": "server/sms/nexmo_sms_sender.go",
"chars": 5960,
"preview": "package sms\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"io/ioutil\"\n\t\"net/http\"\n\t\"strconv\"\n\t\"time\"\n\n\tlog \"github.com/"
},
{
"path": "server/sms/nexmo_sms_sender_test.go",
"chars": 1297,
"preview": "package sms\n\nimport (\n\t\"encoding/json\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/smancke/guble/protocol\"\n\t\"github.com/smancke/gub"
},
{
"path": "server/sms/sms_gateway.go",
"chars": 7981,
"preview": "package sms\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\n\t\"github.com/smancke/guble/server/connector\"\n\n\t\"time\"\n\n\tlog"
},
{
"path": "server/sms/sms_gateway_test.go",
"chars": 5258,
"preview": "package sms\n\nimport (\n\t\"testing\"\n\n\t\"encoding/json\"\n\n\t\"github.com/smancke/guble/server/kvstore\"\n\t\"github.com/smancke/gubl"
},
{
"path": "server/sms/sms_metrics.go",
"chars": 1992,
"preview": "package sms\n\nimport (\n\t\"github.com/smancke/guble/server/metrics\"\n\t\"time\"\n)\n\nvar (\n\tns = metric"
},
{
"path": "server/store/dummystore/dummy_message_store.go",
"chars": 5654,
"preview": "package dummystore\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/smancke/guble/protocol\"\n\t\"github.com/smanck"
},
{
"path": "server/store/dummystore/dummy_message_store_test.go",
"chars": 2905,
"preview": "package dummystore\n\nimport (\n\t\"strconv\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/smancke/guble/server/kvstore\"\n\t\"github.com/stre"
},
{
"path": "server/store/fetch_request.go",
"chars": 3056,
"preview": "package store\n\nimport (\n\t\"errors\"\n\t\"math\"\n\t\"sync\"\n)\n\nvar ErrRequestDone = errors.New(\"Fetch request is done\")\n\nconst (\n\t"
},
{
"path": "server/store/filestore/cache.go",
"chars": 865,
"preview": "package filestore\n\nimport (\n\t\"sync\"\n\n\t\"github.com/smancke/guble/server/store\"\n)\n\ntype cache struct {\n\tentries []*cacheEn"
},
{
"path": "server/store/filestore/index_list.go",
"chars": 5107,
"preview": "package filestore\n\nimport (\n\t\"fmt\"\n\t\"sync\"\n\n\tlog \"github.com/Sirupsen/logrus\"\n\t\"github.com/smancke/guble/server/store\"\n)"
},
{
"path": "server/store/filestore/index_list_test.go",
"chars": 1388,
"preview": "package filestore\n\nimport (\n\t\"math/rand\"\n\t\"testing\"\n\n\t\"github.com/Sirupsen/logrus\"\n\t\"github.com/stretchr/testify/assert\""
},
{
"path": "server/store/filestore/logger.go",
"chars": 115,
"preview": "package filestore\n\nimport (\n\tlog \"github.com/Sirupsen/logrus\"\n)\n\nvar logger = log.WithField(\"module\", \"filestore\")\n"
},
{
"path": "server/store/filestore/message_partition.go",
"chars": 16769,
"preview": "package filestore\n\nimport (\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\""
},
{
"path": "server/store/filestore/message_partition_robustness_test.go",
"chars": 2529,
"preview": "package filestore\n\nimport (\n\tlog \"github.com/Sirupsen/logrus\"\n\t\"github.com/smancke/guble/server/store\"\n\t\"github.com/sman"
},
{
"path": "server/store/filestore/message_partition_test.go",
"chars": 15622,
"preview": "package filestore\n\nimport (\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"os\"\n\t\"path\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/smancke/guble/server/stor"
},
{
"path": "server/store/filestore/message_store.go",
"chars": 6558,
"preview": "// Package filestore is a filesystem-based implementation of the MessageStore interface.\npackage filestore\n\nimport (\n\t\"e"
},
{
"path": "server/store/filestore/message_store_test.go",
"chars": 4948,
"preview": "package filestore\n\nimport (\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/smancke/guble/server/store\"\n\n\t\"github.c"
},
{
"path": "server/store/store.go",
"chars": 1949,
"preview": "package store\n\nimport \"github.com/smancke/guble/protocol\"\n\n// MessageStore is an interface for a persistence backend sto"
},
{
"path": "server/utils_test.go",
"chars": 5722,
"preview": "package server\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"net/http\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\""
},
{
"path": "server/webserver/logger.go",
"chars": 132,
"preview": "package webserver\n\nimport (\n\tlog \"github.com/Sirupsen/logrus\"\n)\n\nvar logger = log.WithFields(log.Fields{\n\t\"module\": \"web"
},
{
"path": "server/webserver/web_server.go",
"chars": 2231,
"preview": "package webserver\n\nimport (\n\t\"net\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n)\n\n// WebServer is a struct representing a HTTP Server"
},
{
"path": "server/webserver/web_server_test.go",
"chars": 1100,
"preview": "package webserver\n\nimport (\n\t\"bytes\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"io/ioutil\"\n\t\"net/http\"\n\t\"testing\"\n\t\"time\"\n)"
},
{
"path": "server/websocket/logger.go",
"chars": 132,
"preview": "package websocket\n\nimport (\n\tlog \"github.com/Sirupsen/logrus\"\n)\n\nvar logger = log.WithFields(log.Fields{\n\t\"module\": \"web"
},
{
"path": "server/websocket/mocks_auth_gen_test.go",
"chars": 1203,
"preview": "// Automatically generated by MockGen. DO NOT EDIT!\n// Source: github.com/smancke/guble/server/auth (interfaces: AccessM"
},
{
"path": "server/websocket/mocks_router_gen_test.go",
"chars": 3848,
"preview": "// Automatically generated by MockGen. DO NOT EDIT!\n// Source: github.com/smancke/guble/server/router (interfaces: Route"
},
{
"path": "server/websocket/mocks_store_gen_test.go",
"chars": 3646,
"preview": "// Automatically generated by MockGen. DO NOT EDIT!\n// Source: github.com/smancke/guble/server/store (interfaces: Messag"
},
{
"path": "server/websocket/mocks_websocket_gen_test.go",
"chars": 1469,
"preview": "// Automatically generated by MockGen. DO NOT EDIT!\n// Source: github.com/smancke/guble/server/websocket (interfaces: WS"
},
{
"path": "server/websocket/receiver.go",
"chars": 7607,
"preview": "package websocket\n\nimport (\n\t\"github.com/smancke/guble/protocol\"\n\t\"github.com/smancke/guble/server/router\"\n\t\"github.com/"
},
{
"path": "server/websocket/receiver_test.go",
"chars": 9020,
"preview": "package websocket\n\nimport (\n\t\"github.com/smancke/guble/protocol\"\n\t\"github.com/smancke/guble/server/router\"\n\t\"github.com/"
},
{
"path": "server/websocket/websocket_connector.go",
"chars": 7375,
"preview": "package websocket\n\nimport (\n\t\"github.com/smancke/guble/protocol\"\n\t\"github.com/smancke/guble/server/auth\"\n\t\"github.com/sm"
},
{
"path": "server/websocket/websocket_connector_test.go",
"chars": 6637,
"preview": "package websocket\n\nimport (\n\t\"github.com/smancke/guble/protocol\"\n\t\"github.com/smancke/guble/server/auth\"\n\t\"github.com/sm"
},
{
"path": "test.sh",
"chars": 368,
"preview": "#!/usr/bin/env bash\n\nGO_TEST_DISABLED=true go test -short ./...\nTESTRESULT=$?\n\nRED='\\033[0;31m'\nGREEN='\\033[0;32m'\nNOCO"
},
{
"path": "testutil/testutil.go",
"chars": 2736,
"preview": "package testutil\n\nimport (\n\t//used for pprof server\n\t_ \"net/http/pprof\"\n\n\tlog \"github.com/Sirupsen/logrus\"\n\t\"github.com/"
}
]
About this extraction
This page contains the full source code of the smancke/guble GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 190 files (597.1 KB), approximately 171.9k tokens, and a symbol index with 1703 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.