publisher.go 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. package sessions
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "fmt"
  6. "net/http"
  7. "github.com/bugsnag/bugsnag-go/headers"
  8. )
  9. // sessionPayloadVersion defines the current version of the payload that's
  10. // being sent to the session server.
  11. const sessionPayloadVersion = "1.0"
  12. type sessionPublisher interface {
  13. publish(sessions []*Session) error
  14. }
  15. type httpClient interface {
  16. Do(*http.Request) (*http.Response, error)
  17. }
  18. type publisher struct {
  19. config *SessionTrackingConfiguration
  20. client httpClient
  21. }
  22. // publish builds a payload from the given sessions and publishes them to the
  23. // session server. Returns any errors that happened as part of publishing.
  24. func (p *publisher) publish(sessions []*Session) error {
  25. if p.config.Endpoint == "" {
  26. // Session tracking is disabled, likely because the notify endpoint was
  27. // changed without changing the sessions endpoint
  28. // We've already logged a warning in this case, so no need to spam the
  29. // log every minute
  30. return nil
  31. }
  32. if apiKey := p.config.APIKey; len(apiKey) != 32 {
  33. return fmt.Errorf("bugsnag/sessions/publisher.publish invalid API key: '%s'", apiKey)
  34. }
  35. nrs, rs := p.config.NotifyReleaseStages, p.config.ReleaseStage
  36. if rs != "" && (nrs != nil && !contains(nrs, rs)) {
  37. // Always send sessions if the release stage is not set, but don't send any
  38. // sessions when notify release stages don't match the current release stage
  39. return nil
  40. }
  41. if len(sessions) == 0 {
  42. return fmt.Errorf("bugsnag/sessions/publisher.publish requested publication of 0")
  43. }
  44. p.config.mutex.Lock()
  45. defer p.config.mutex.Unlock()
  46. payload := makeSessionPayload(sessions, p.config)
  47. buf, err := json.Marshal(payload)
  48. if err != nil {
  49. return fmt.Errorf("bugsnag/sessions/publisher.publish unable to marshal json: %v", err)
  50. }
  51. req, err := http.NewRequest("POST", p.config.Endpoint, bytes.NewBuffer(buf))
  52. if err != nil {
  53. return fmt.Errorf("bugsnag/sessions/publisher.publish unable to create request: %v", err)
  54. }
  55. for k, v := range headers.PrefixedHeaders(p.config.APIKey, sessionPayloadVersion) {
  56. req.Header.Add(k, v)
  57. }
  58. res, err := p.client.Do(req)
  59. if err != nil {
  60. return fmt.Errorf("bugsnag/sessions/publisher.publish unable to deliver session: %v", err)
  61. }
  62. defer func(res *http.Response) {
  63. if err := res.Body.Close(); err != nil {
  64. p.config.logf("%v", err)
  65. }
  66. }(res)
  67. if res.StatusCode != 202 {
  68. return fmt.Errorf("bugsnag/session.publish expected 202 response status, got HTTP %s", res.Status)
  69. }
  70. return nil
  71. }
  72. func contains(coll []string, e string) bool {
  73. for _, s := range coll {
  74. if s == e {
  75. return true
  76. }
  77. }
  78. return false
  79. }