title: golang websocket how to be notified when browser closed unexpectly
body: <p>I am writing a websocket service in golang.
  The program use gollira websocket to accept ws request, and in each request handler, it listen to rabbitmq queue for messages.</p>
  
  <p>The problem is, when i close browser window, the handler thread is still running, i guess there is an mechanism to be notified when connection disconnected.</p>
  
  <p>I try to listen to channel request.Context().Done(), when i doesn't work.</p>
  
  <pre><code>package main
  
  import (
      "fmt"
      "log"
      "net/http"
  
      "github.com/gorilla/websocket"
      "github.com/streadway/amqp"
  )
  
  var (
      addr     = "localhost:9999"
      upgrader = websocket.Upgrader{
          CheckOrigin: func(r *http.Request) bool { return true },
      }
  )
  
  var conn *amqp.Connection
  
  func watch(w http.ResponseWriter, r *http.Request) {
      ns := r.URL.Query().Get("ns")
      if ns == "" {
          return
      }
      c, err := upgrader.Upgrade(w, r, nil)
      if err != nil {
          log.Print("upgrade:", err)
          return
      }
      defer c.Close()
  
      ch, err := conn.Channel()
      failOnError(err, "Failed to open a channel")
      defer ch.Close()
  
      err = ch.ExchangeDeclare(
          "notify", // name
          "fanout",           // type
          true,               // durable
          false,              // auto-deleted
          false,              // internal
          false,              // no-wait
          nil,                // arguments
      )
      failOnError(err, "Failed to declare an exchange")
  
      q, err := ch.QueueDeclare(
          "",    // name
          false, // durable
          false, // delete when usused
          true,  // exclusive
          false, // no-wait
          nil,   // arguments
      )
      failOnError(err, "Failed to declare a queue")
  
      err = ch.QueueBind(
          q.Name,             // queue name
          ns,                 // routing key
          "dsm_tasks_notify", // exchange
          false,
          nil)
      failOnError(err, "Failed to bind a queue")
  
      msgs, err := ch.Consume(
          q.Name, // queue
          "",     // consumer
          true,   // auto-ack
          false,  // exclusive
          false,  // no-local
          false,  // no-wait
          nil,    // args
      )
      failOnError(err, "Failed to register a consumer")
  
      for {
          select {
          case d := &lt;-msgs:
              err = c.WriteMessage(websocket.TextMessage, d.Body)
              if err != nil {
                  log.Println("write:", err)
                  break
              }
          case &lt;-r.Context().Done():
              log.Println("Disconnect")
              return
          }
      }
  
  }
  
  func failOnError(err error, msg string) {
      if err != nil {
          log.Fatalf("%s: %s", msg, err)
          panic(fmt.Sprintf("%s: %s", msg, err))
      }
  }
  
  func main() {
      var err error
      conn, err = amqp.Dial("amqp://guest:guest@localhost:5672/")
      failOnError(err, "Failed to connect to RabbitMQ")
      defer conn.Close()
  
      http.HandleFunc("/watch", watch)
      log.Fatal(http.ListenAndServe(addr, nil))
  }
  </code></pre>