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.