server.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. /**
  2. * This file provided by Facebook is for non-commercial testing and evaluation
  3. * purposes only. Facebook reserves all rights not expressly granted.
  4. *
  5. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  6. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  7. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  8. * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  9. * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  10. * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  11. */
  12. package main
  13. import (
  14. "bytes"
  15. "encoding/json"
  16. "fmt"
  17. "io"
  18. "io/ioutil"
  19. "log"
  20. "net/http"
  21. "os"
  22. "sync"
  23. "time"
  24. )
  25. type comment struct {
  26. ID int64 `json:"id"`
  27. Author string `json:"author"`
  28. Text string `json:"text"`
  29. }
  30. const dataFile = "./comments.json"
  31. var commentMutex = new(sync.Mutex)
  32. func updateComments(w http.ResponseWriter, r *http.Request, commentData []byte, fi os.FileInfo) {
  33. // Decode the JSON data
  34. var comments []comment
  35. if err := json.Unmarshal(commentData, &comments); err != nil {
  36. http.Error(w, fmt.Sprintf("Unable to Unmarshal comments from data file (%s): %s", dataFile, err), http.StatusInternalServerError)
  37. return
  38. }
  39. // Add a new comment to the in memory slice of comments
  40. comments = append(comments, comment{ID: time.Now().UnixNano() / 1000000, Author: r.FormValue("author"), Text: r.FormValue("text")})
  41. // Marshal the comments to indented json.
  42. commentData, err := json.MarshalIndent(comments, "", " ")
  43. if err != nil {
  44. http.Error(w, fmt.Sprintf("Unable to marshal comments to json: %s", err), http.StatusInternalServerError)
  45. return
  46. }
  47. // Write out the comments to the file, preserving permissions.
  48. err = ioutil.WriteFile(dataFile, commentData, fi.Mode())
  49. if err != nil {
  50. http.Error(w, fmt.Sprintf("Unable to write comments to data file (%s): %s", dataFile, err), http.StatusInternalServerError)
  51. return
  52. }
  53. w.Header().Set("Content-Type", "application/json")
  54. w.Header().Set("Cache-Control", "no-cache")
  55. io.Copy(w, bytes.NewReader(commentData))
  56. }
  57. // Handle comments
  58. func handleComments(w http.ResponseWriter, r *http.Request) {
  59. // Since multiple requests could come in at once, ensure we have a lock
  60. // around all file operations
  61. commentMutex.Lock()
  62. defer commentMutex.Unlock()
  63. // Stat the file, so we can find its current permissions
  64. fi, err := os.Stat(dataFile)
  65. if err != nil {
  66. http.Error(w, fmt.Sprintf("Unable to stat the data file (%s): %s", dataFile, err), http.StatusInternalServerError)
  67. return
  68. }
  69. // Read the comments from the file.
  70. commentData, err := ioutil.ReadFile(dataFile)
  71. if err != nil {
  72. http.Error(w, fmt.Sprintf("Unable to read the data file (%s): %s", dataFile, err), http.StatusInternalServerError)
  73. return
  74. }
  75. switch r.Method {
  76. case "POST":
  77. updateComments(w, r, commentData, fi);
  78. case "GET":
  79. w.Header().Set("Content-Type", "application/json")
  80. w.Header().Set("Cache-Control", "no-cache")
  81. // stream the contents of the file to the response
  82. io.Copy(w, bytes.NewReader(commentData))
  83. default:
  84. // Don't know the method, so error.
  85. http.Error(w, fmt.Sprintf("Unsupported method: %s", r.Method), http.StatusMethodNotAllowed)
  86. }
  87. }
  88. func main() {
  89. port := os.Getenv("PORT")
  90. if port == "" {
  91. port = "3000"
  92. }
  93. http.HandleFunc("/api/comments", handleComments)
  94. http.Handle("/", http.FileServer(http.Dir("./public")))
  95. log.Println("Server started: http://localhost:" + port)
  96. log.Fatal(http.ListenAndServe(":" + port, nil))
  97. }