http_log.go 3.9 KB

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