http.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. package main
  2. import (
  3. "errors"
  4. "fmt"
  5. "log"
  6. "net/http"
  7. "strconv"
  8. "time"
  9. "git.lattuga.net/boyska/circolog"
  10. "github.com/gorilla/websocket"
  11. "gopkg.in/mcuadros/go-syslog.v2/format"
  12. )
  13. // TODO: return a server
  14. func setupHTTP(hub circolog.Hub) {
  15. http.HandleFunc("/", getHTTPHandler(hub))
  16. http.HandleFunc("/ws", getWSHandler(hub))
  17. }
  18. func parseParameterL(r *http.Request) (int, error) {
  19. var requestMessageLen int = -1
  20. var err error
  21. if reqL, ok := r.Form["l"]; ok {
  22. if len(reqL) == 1 {
  23. requestMessageLen, err = strconv.Atoi(reqL[0])
  24. if err != nil {
  25. return 0, err
  26. }
  27. if requestMessageLen <= 0 {
  28. return 0, errors.New("malformed request")
  29. }
  30. } else {
  31. return 0, errors.New("malformed request")
  32. }
  33. }
  34. return requestMessageLen, nil
  35. }
  36. func parseParameters(r *http.Request) (circolog.ClientOptions, error) {
  37. var opts circolog.ClientOptions
  38. err := r.ParseForm()
  39. if err != nil {
  40. log.Println("error parsing http request", err)
  41. return opts, err
  42. }
  43. l, err := parseParameterL(r)
  44. if err != nil {
  45. return opts, err
  46. }
  47. opts.BacklogLength = l
  48. return opts, err
  49. }
  50. type renderOptions struct { // those are options relevant to the rendered (that is, the HTTP side of circologd)
  51. Format renderFormat
  52. }
  53. func parseRenderParameters(r *http.Request) (renderOptions, error) {
  54. opts := renderOptions{}
  55. err := r.ParseForm()
  56. if err != nil {
  57. log.Println("error parsing http request", err)
  58. return opts, err
  59. }
  60. if val, ok := r.Form["fmt"]; ok {
  61. if len(val) != 1 {
  62. return opts, errors.New("Format repeated multiple times")
  63. }
  64. format, err := parseRenderFormat(val[0])
  65. if err != nil {
  66. return opts, err
  67. }
  68. opts.Format = format
  69. }
  70. return opts, nil
  71. }
  72. func getHTTPHandler(hub circolog.Hub) http.HandlerFunc {
  73. return func(w http.ResponseWriter, r *http.Request) {
  74. // Looking for known parameter in the request
  75. render_opts, err := parseRenderParameters(r)
  76. if err != nil {
  77. log.Println("Error parsing:", err)
  78. w.WriteHeader(400)
  79. fmt.Fprintln(w, err)
  80. return
  81. }
  82. opts, err := parseParameters(r)
  83. if err != nil {
  84. log.Println("Error on request parameter \"l\":", err)
  85. w.WriteHeader(400)
  86. fmt.Fprintln(w, err)
  87. return
  88. }
  89. opts.Nofollow = true
  90. client := circolog.Client{
  91. Messages: make(chan format.LogParts, 20),
  92. Options: opts,
  93. }
  94. hub.Register <- client
  95. for x := range client.Messages {
  96. writeFormatted(w, render_opts.Format, x)
  97. if render_opts.Format != formatJSON { // bleah
  98. w.Write([]byte("\n"))
  99. }
  100. }
  101. }
  102. }
  103. func getWSHandler(hub circolog.Hub) http.HandlerFunc {
  104. var upgrader = websocket.Upgrader{
  105. ReadBufferSize: 1024,
  106. WriteBufferSize: 1024,
  107. }
  108. return func(w http.ResponseWriter, r *http.Request) {
  109. render_opts, err := parseRenderParameters(r)
  110. if err != nil {
  111. log.Println("Error parsing:", err)
  112. w.WriteHeader(400)
  113. fmt.Fprintln(w, err)
  114. return
  115. }
  116. opts, err := parseParameters(r)
  117. if err != nil {
  118. log.Println("Error on request parameter \"l\":", err)
  119. w.WriteHeader(400)
  120. fmt.Fprintln(w, err)
  121. return
  122. }
  123. opts.Nofollow = false
  124. conn, err := upgrader.Upgrade(w, r, nil)
  125. if err != nil {
  126. return
  127. }
  128. client := circolog.Client{
  129. Messages: make(chan format.LogParts, 20),
  130. Options: opts,
  131. }
  132. hub.Register <- client
  133. // Allow collection of memory referenced by the caller by doing all work in
  134. // new goroutines.
  135. go func(conn *websocket.Conn, c circolog.Client) {
  136. defer func() {
  137. hub.Unregister <- c
  138. conn.Close()
  139. }()
  140. for {
  141. select {
  142. case message, ok := <-c.Messages:
  143. conn.SetWriteDeadline(time.Now().Add(10 * time.Second))
  144. if !ok {
  145. // The hub closed the channel.
  146. conn.WriteMessage(websocket.CloseMessage, []byte{})
  147. return
  148. }
  149. w, err := conn.NextWriter(websocket.TextMessage)
  150. if err != nil {
  151. return
  152. }
  153. writeFormatted(w, render_opts.Format, message)
  154. if err := w.Close(); err != nil {
  155. return
  156. }
  157. // TODO: ticker/ping
  158. }
  159. }
  160. }(conn, client)
  161. }
  162. }