github.com/philippseith/signalr@v0.6.3/README.md (about) 1 # SignalR 2 3 [![Actions Status](https://github.com/philippseith/signalr/workflows/Build%20and%20Test/badge.svg)](https://github.com/philippseith/signalr/actions) 4 [![codecov](https://codecov.io/gh/philippseith/signalr/branch/master/graph/badge.svg)](https://codecov.io/gh/philippseith/signalr) 5 [![PkgGoDev](https://pkg.go.dev/badge/github.com/philippseith/signalr)](https://pkg.go.dev/github.com/philippseith/signalr) 6 7 SignalR is an open-source library that simplifies adding real-time web functionality to apps. 8 Real-time web functionality enables server-side code to push content to clients instantly. 9 10 Historically it was tied to ASP.NET Core but the 11 [protocol](https://github.com/aspnet/AspNetCore/tree/master/src/SignalR/docs/specs) is open and implementable in any language. 12 13 This repository contains an implementation of a SignalR server and a SignalR client in go. 14 The implementation is based on the work of David Fowler at https://github.com/davidfowl/signalr-ports. 15 Client and server support transport over WebSockets, Server Sent Events and raw TCP. 16 Protocol encoding in JSON and MessagePack is fully supported. 17 18 - [Install](#install) 19 - [Getting Started](#getting-started) 20 - [Server side](#server-side) 21 - [Implement the HubInterface](#implement-the-hubinterface) 22 - [Serve with http.ServeMux](#serve-with-httpservemux) 23 - [Client side: JavaScript/TypeScript](#client-side-javascripttypescript) 24 - [Grab copies of the signalr scripts](#grab-copies-of-the-signalr-scripts) 25 - [Use a HubConnection to connect to your server Hub](#use-a-hubconnection-to-connect-to-your-server-hub) 26 - [Client side: go](#client-side-go) 27 - [Debugging](#debugging) 28 29 ## Install 30 31 With a [correctly configured](https://golang.org/doc/install#testing) Go toolchain: 32 33 ```sh 34 go get -u github.com/philippseith/signalr 35 ``` 36 37 ## Getting Started 38 39 SignalR uses a `signalr.HubInterface` instance to anchor the connection on the server and a javascript `HubConnection` object to anchor the connection on the client. 40 41 ### Server side 42 43 #### Implement the HubInterface 44 45 The easiest way to implement the `signalr.HubInterface` in your project is to declare your own type and embed `signalr.Hub` which implements that interface and will take care of all the signalr plumbing. You can call your custom type anything you want so long as it implements the `signalr.HubInterface` interface. 46 47 ```go 48 package main 49 50 import "github.com/philippseith/signalr" 51 52 type AppHub struct { 53 signalr.Hub 54 } 55 ``` 56 57 Add functions with your custom hub type as a receiver. 58 59 ```go 60 func (h *AppHub) SendChatMessage(message string) { 61 h.Clients().All().Send("chatMessageReceived", message) 62 } 63 ``` 64 65 These functions must be public so that they can be seen by the signalr server package but can be invoked client-side as lowercase message names. We'll explain setting up the client side in a moment, but as a preview, here's an example of calling our `AppHub.SendChatMessage(...)` method from the client: 66 67 ```js 68 // javascript snippet invoking that AppHub.Send method from the client 69 connection.invoke('sendChatMessage', val); 70 ``` 71 72 The `signalr.HubInterface` contains a pair of methods you can implement to handle connection and disconnection events. `signalr.Hub` contains empty implementations of them to satisfy the interface, but you can "override" those defaults by implementing your own functions with your custom hub type as a receiver: 73 74 ```go 75 func (c *chat) OnConnected(connectionID string) { 76 fmt.Printf("%s connected\n", connectionID) 77 } 78 79 func (c *chat) OnDisconnected(connectionID string) { 80 fmt.Printf("%s disconnected\n", connectionID) 81 } 82 ``` 83 84 #### Serve with http.ServeMux 85 86 ```go 87 import ( 88 "net/http" 89 90 "github.com/philippseith/signalr" 91 ) 92 93 func runHTTPServer() { 94 address := 'localhost:8080' 95 96 // create an instance of your hub 97 hub := AppHub{} 98 99 // build a signalr.Server using your hub 100 // and any server options you may need 101 server, _ := signalr.NewServer(context.TODO(), 102 signalr.SimpleHubFactory(hub) 103 signalr.KeepAliveInterval(2*time.Second), 104 signalr.Logger(kitlog.NewLogfmtLogger(os.Stderr), true)) 105 ) 106 107 // create a new http.ServerMux to handle your app's http requests 108 router := http.NewServeMux() 109 110 // ask the signalr server to map it's server 111 // api routes to your custom baseurl 112 server.MapHTTP(signalr.WithHTTPServeMux(router), "/chat") 113 114 // in addition to mapping the signalr routes 115 // your mux will need to serve the static files 116 // which make up your client-side app, including 117 // the signalr javascript files. here is an example 118 // of doing that using a local `public` package 119 // which was created with the go:embed directive 120 // 121 // fmt.Printf("Serving static content from the embedded filesystem\n") 122 // router.Handle("/", http.FileServer(http.FS(public.FS))) 123 124 // bind your mux to a given address and start handling requests 125 fmt.Printf("Listening for websocket connections on http://%s\n", address) 126 if err := http.ListenAndServe(address, router); err != nil { 127 log.Fatal("ListenAndServe:", err) 128 } 129 } 130 ``` 131 132 ### Client side: JavaScript/TypeScript 133 134 #### Grab copies of the signalr scripts 135 136 Microsoft has published the client-side libraries as a node package with embedded typescript annotations: `@microsoft/signalr`. 137 138 You can install `@microsoft/signalr` through any node package manager: 139 140 | package manager | command | 141 | --------------- | ------- | 142 | [npm](https://www.npmjs.com/) | `npm install @microsoft/signalr@latest` | 143 | [yarn](https://yarnpkg.com/) | `yarn add @microsoft/signalr@latest` | 144 | [LibMan](https://docs.microsoft.com/en-us/aspnet/core/client-side/libman/libman-cli)| `libman install @microsoft/signalr@latest -p unpkg -d wwwroot/js/signalr --files dist/browser/signalr.js --files dist/browser/signalr.min.js --files dist/browser/signalr.map.js` | 145 | none | you can download the version we are using in our `chatsample` from [here](https://raw.githubusercontent.com/philippseith/signalr/master/chatsample/public/js/signalr.js) (the minified version is [here](https://raw.githubusercontent.com/philippseith/signalr/master/chatsample/public/js/signalr.min.js))| 146 147 #### Use a HubConnection to connect to your server Hub 148 149 How you format your client UI is going to depend on your application use case but here is a simple example. It illustrates the basic steps of connecting to your server hub: 150 151 1. import the `signalr.js` library (or `signalr.min.js`); 152 1. create a connection object using the `HubConnectionBuilder`; 153 1. bind events 154 - UI event handlers can use `connection.invoke(targetMethod, payload)` to send invoke functions on the server hub; 155 - connection event handlers can react to the messages sent from the server hub; 156 157 1. start your connection 158 159 ```html 160 <html> 161 <body> 162 <!-- you may want the content you send to be dynamic --> 163 <input type="text" id="message" /> 164 165 <!-- you may need a trigger to initiate the send --> 166 <input type="button" value="Send" id="send" /> 167 168 <!-- you may want some container to display received messages --> 169 <ul id="messages"> 170 </ul> 171 172 <!-- 1. you need to import the signalr script which provides 173 the HubConnectionBuilder and handles the connection 174 plumbing. 175 --> 176 <script src="js/signalr.js"></script> 177 <script> 178 (async function () { 179 // 2. use the signalr.HubConnectionBuilder to build a hub connection 180 // and point it at the baseurl which you configured in your mux 181 const connection = new signalR.HubConnectionBuilder() 182 .withUrl('/chat') 183 .build(); 184 185 // 3. bind events: 186 // - UI events can invoke (i.e. dispatch to) functions on the server hub 187 document.getElementById('send').addEventListener('click', sendClicked); 188 // - connection events can handle messages received from the server hub 189 connection.on('chatMessageReceived', onChatMessageReceived); 190 191 // 4. call start to initiate the connection and start streaming events 192 // between your server hub and your client connection 193 connection.start(); 194 195 // that's it! your server and client should be able to communicate 196 // through the signalr.Hub <--> connection pipeline managed by the 197 // signalr package and client-side library. 198 199 // -------------------------------------------------------------------- 200 201 // example UI event handler 202 function sendClicked() { 203 // prepare your target payload 204 const msg = document.getElementById('message').value; 205 if (msg) { 206 // call invoke on your connection object to dispatch 207 // messages to the server hub with two arguments: 208 // - target: name of the hub func to invoke 209 // - payload: the message body 210 // 211 const target = 'sendChatMessage'; 212 connection.invoke(target, msg); 213 } 214 } 215 216 // example server event handler 217 function onChatMessageReceived(payload) { 218 // the payload is whatever was passed to the inner 219 // clients' `Send(...)` method in your server-side 220 // hub function. 221 222 const li = document.createElement('li'); 223 li.innerText = payload; 224 document.getElementById('messages').appendChild(li); 225 } 226 })(); 227 </script> 228 </body> 229 </html> 230 ``` 231 232 ### Client side: go 233 234 To handle callbacks from the server, create a receiver class which gets the server callbacks mapped 235 to its methods: 236 ```go 237 type receiver struct { 238 signalr.Hub 239 } 240 241 func (c *receiver) Receive(msg string) { 242 fmt.Println(msg) 243 } 244 ``` 245 `Receive` gets called when the server does something like this: 246 ```go 247 hub.Clients().Caller().Send("receive", message) 248 ``` 249 250 The client itself might be used like that: 251 ```go 252 // Create a Connection (with timeout for the negotiation process) 253 creationCtx, _ := context.WithTimeout(ctx, 2 * time.Second) 254 conn, err := signalr.NewHTTPConnection(creationCtx, address) 255 if err != nil { 256 return err 257 } 258 // Create the client and set a receiver for callbacks from the server 259 client, err := signalr.NewClient(ctx, 260 signalr.WithConnection(conn), 261 signalr.WithReceiver(receiver)) 262 if err != nil { 263 return err 264 } 265 // Start the client loop 266 c.Start() 267 // Do some client work 268 ch := <-c.Invoke("update", data) 269 // ch gets the result of the update operation 270 ``` 271 272 ## Debugging 273 274 Server, Client and the protocol implementations are able to log most of their operations. The logging option is disabled 275 by default in all tests. To configure logging, edit the `testLogConf.json` file: 276 ```json 277 { 278 "Enabled": false, 279 "Debug": false 280 } 281 ``` 282 - If `Enabled` is set to `true`, the logging will be enabled. The tests will log to `os.Stderr`. 283 - If `Debug` ist set to `true`, the logging will be more detailed.