server.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  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. // Handle comments
  33. func handleComments(w http.ResponseWriter, r *http.Request) {
  34. // Since multiple requests could come in at once, ensure we have a lock
  35. // around all file operations
  36. commentMutex.Lock()
  37. defer commentMutex.Unlock()
  38. // Stat the file, so we can find its current permissions
  39. fi, err := os.Stat(dataFile)
  40. if err != nil {
  41. http.Error(w, fmt.Sprintf("Unable to stat the data file (%s): %s", dataFile, err), http.StatusInternalServerError)
  42. return
  43. }
  44. // Read the comments from the file.
  45. commentData, err := ioutil.ReadFile(dataFile)
  46. if err != nil {
  47. http.Error(w, fmt.Sprintf("Unable to read the data file (%s): %s", dataFile, err), http.StatusInternalServerError)
  48. return
  49. }
  50. switch r.Method {
  51. case "POST":
  52. // Decode the JSON data
  53. var comments []comment
  54. if err := json.Unmarshal(commentData, &comments); err != nil {
  55. http.Error(w, fmt.Sprintf("Unable to Unmarshal comments from data file (%s): %s", dataFile, err), http.StatusInternalServerError)
  56. return
  57. }
  58. // Add a new comment to the in memory slice of comments
  59. comments = append(comments, comment{ID: time.Now().UnixNano() / 1000000, Author: r.FormValue("author"), Text: r.FormValue("text")})
  60. // Marshal the comments to indented json.
  61. commentData, err = json.MarshalIndent(comments, "", " ")
  62. if err != nil {
  63. http.Error(w, fmt.Sprintf("Unable to marshal comments to json: %s", err), http.StatusInternalServerError)
  64. return
  65. }
  66. // Write out the comments to the file, preserving permissions
  67. err := ioutil.WriteFile(dataFile, commentData, fi.Mode())
  68. if err != nil {
  69. http.Error(w, fmt.Sprintf("Unable to write comments to data file (%s): %s", dataFile, err), http.StatusInternalServerError)
  70. return
  71. }
  72. w.Header().Set("Content-Type", "application/json")
  73. w.Header().Set("Cache-Control", "no-cache")
  74. io.Copy(w, bytes.NewReader(commentData))
  75. case "GET":
  76. w.Header().Set("Content-Type", "application/json")
  77. w.Header().Set("Cache-Control", "no-cache")
  78. // stream the contents of the file to the response
  79. io.Copy(w, bytes.NewReader(commentData))
  80. default:
  81. // Don't know the method, so error
  82. http.Error(w, fmt.Sprintf("Unsupported method: %s", r.Method), http.StatusMethodNotAllowed)
  83. }
  84. }
  85. func main() {
  86. port := os.Getenv("PORT")
  87. if port == "" {
  88. port = "3000"
  89. }
  90. http.HandleFunc("/api/comments", handleComments)
  91. http.Handle("/", http.FileServer(http.Dir("./public")))
  92. log.Println("Server started: http://localhost:" + port)
  93. log.Fatal(http.ListenAndServe(":" + port, nil))
  94. }