http_log.go 4.1 KB

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