stcrashreceiver.go 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. // Copyright (C) 2019 The Syncthing Authors.
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this file,
  5. // You can obtain one at https://mozilla.org/MPL/2.0/.
  6. package main
  7. import (
  8. "io"
  9. "log"
  10. "net/http"
  11. "path"
  12. "strings"
  13. )
  14. type crashReceiver struct {
  15. store *diskStore
  16. sentry *sentryService
  17. }
  18. func (r *crashReceiver) ServeHTTP(w http.ResponseWriter, req *http.Request) {
  19. // The final path component should be a SHA256 hash in hex, so 64 hex
  20. // characters. We don't care about case on the request but use lower
  21. // case internally.
  22. reportID := strings.ToLower(path.Base(req.URL.Path))
  23. if len(reportID) != 64 {
  24. http.Error(w, "Bad request", http.StatusBadRequest)
  25. return
  26. }
  27. for _, c := range reportID {
  28. if c >= 'a' && c <= 'f' {
  29. continue
  30. }
  31. if c >= '0' && c <= '9' {
  32. continue
  33. }
  34. http.Error(w, "Bad request", http.StatusBadRequest)
  35. return
  36. }
  37. switch req.Method {
  38. case http.MethodGet:
  39. r.serveGet(reportID, w, req)
  40. case http.MethodHead:
  41. r.serveHead(reportID, w, req)
  42. case http.MethodPut:
  43. r.servePut(reportID, w, req)
  44. default:
  45. http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
  46. }
  47. }
  48. // serveGet responds to GET requests by serving the uncompressed report.
  49. func (r *crashReceiver) serveGet(reportID string, w http.ResponseWriter, _ *http.Request) {
  50. bs, err := r.store.Get(reportID)
  51. if err != nil {
  52. http.Error(w, "Not found", http.StatusNotFound)
  53. return
  54. }
  55. w.Write(bs)
  56. }
  57. // serveHead responds to HEAD requests by checking if the named report
  58. // already exists in the system.
  59. func (r *crashReceiver) serveHead(reportID string, w http.ResponseWriter, _ *http.Request) {
  60. if !r.store.Exists(reportID) {
  61. http.Error(w, "Not found", http.StatusNotFound)
  62. }
  63. }
  64. // servePut accepts and stores the given report.
  65. func (r *crashReceiver) servePut(reportID string, w http.ResponseWriter, req *http.Request) {
  66. // Read at most maxRequestSize of report data.
  67. log.Println("Receiving report", reportID)
  68. lr := io.LimitReader(req.Body, maxRequestSize)
  69. bs, err := io.ReadAll(lr)
  70. if err != nil {
  71. log.Println("Reading report:", err)
  72. http.Error(w, "Internal server error", http.StatusInternalServerError)
  73. return
  74. }
  75. // Store the report
  76. if !r.store.Put(reportID, bs) {
  77. log.Println("Failed to store report (queue full):", reportID)
  78. }
  79. // Send the report to Sentry
  80. if !r.sentry.Send(reportID, bs) {
  81. log.Println("Failed to send report to sentry (queue full):", reportID)
  82. }
  83. }