From 0b1f8ea0464387b0f8d1e68a52bf40748b694689 Mon Sep 17 00:00:00 2001 From: nemunaire Date: Fri, 9 Dec 2016 09:36:18 +0100 Subject: [PATCH] frontend: refactor and dispatch in many routes --- frontend/chname.go | 39 ++++++++++++++++ frontend/main.go | 8 +++- frontend/register.go | 33 +++++++++++++ frontend/save.go | 81 ++++++++++++++++++++++++++++++++ frontend/submit.go | 107 +++---------------------------------------- 5 files changed, 167 insertions(+), 101 deletions(-) create mode 100644 frontend/chname.go create mode 100644 frontend/register.go create mode 100644 frontend/save.go diff --git a/frontend/chname.go b/frontend/chname.go new file mode 100644 index 00000000..e266b1c8 --- /dev/null +++ b/frontend/chname.go @@ -0,0 +1,39 @@ +package main + +import ( + "log" + "net/http" + "strings" +) + +type ChNameHandler struct {} + +func (n ChNameHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + log.Printf("Handling %s name change request from %s: %s [%s]\n", r.Method, r.RemoteAddr, r.URL.Path, r.UserAgent()) + + w.Header().Set("Content-Type", "application/json") + + // Check request type and size + if r.Method != "POST" { + http.Error(w, "{\"errmsg\":\"Requête invalide.\"}", http.StatusBadRequest) + return + } else if r.ContentLength < 0 || r.ContentLength > 1023 { + http.Error(w, "{\"errmsg\":\"Requête trop longue ou de taille inconnue\"}", http.StatusRequestEntityTooLarge) + return + } + + // Extract URL arguments + var sURL = strings.Split(r.URL.Path, "/") + + if len(sURL) != 1 { + http.Error(w, "{\"errmsg\":\"Requête invalide.\"}", http.StatusBadRequest) + return + } + + team := sURL[0] + + // Enqueue file for backend treatment + if saveTeamFile(SubmissionDir, team, "name", w, r) { + http.Error(w, "{\"errmsg\":\"Demande de changement de nom acceptée\"}", http.StatusAccepted) + } +} diff --git a/frontend/main.go b/frontend/main.go index ff0a4d83..306549a9 100644 --- a/frontend/main.go +++ b/frontend/main.go @@ -73,7 +73,13 @@ func main() { if *resolutionRoute { http.Handle(fmt.Sprintf("%s/resolution/", *prefix), http.StripPrefix(fmt.Sprintf("%s/resolution/", *prefix), ResolutionHandler{})) } - http.Handle(fmt.Sprintf("%s/", *prefix), http.StripPrefix(*prefix, SubmissionHandler{end, *denyChName, *allowRegistration})) + if *allowRegistration { + http.Handle(fmt.Sprintf("%s/registration", *prefix), http.StripPrefix(fmt.Sprintf("%s/registration", *prefix), RegistrationHandler{})) + } + if !*denyChName { + http.Handle(fmt.Sprintf("%s/chname/", *prefix), http.StripPrefix(fmt.Sprintf("%s/chname/", *prefix), ChNameHandler{})) + } + http.Handle(fmt.Sprintf("%s/submission/", *prefix), http.StripPrefix(fmt.Sprintf("%s/submission/", *prefix), SubmissionHandler{end})) log.Println(fmt.Sprintf("Ready, listening on %s", *bind)) if err := http.ListenAndServe(*bind, nil); err != nil { diff --git a/frontend/register.go b/frontend/register.go new file mode 100644 index 00000000..3080b7ff --- /dev/null +++ b/frontend/register.go @@ -0,0 +1,33 @@ +package main + +import ( + "log" + "net/http" + "path" +) + +type RegistrationHandler struct {} + +func (e RegistrationHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + log.Printf("Handling %s registration request from %s: %s [%s]\n", r.Method, r.RemoteAddr, r.URL.Path, r.UserAgent()) + + w.Header().Set("Content-Type", "application/json") + + // Check request type and size + if r.Method != "POST" { + http.Error(w, "{\"errmsg\":\"Requête invalide.\"}", http.StatusBadRequest) + return + } else if r.ContentLength < 0 || r.ContentLength > 1023 { + http.Error(w, "{\"errmsg\":\"Requête trop longue ou de taille inconnue\"}", http.StatusRequestEntityTooLarge) + return + } + + // Enqueue file for backend treatment + if err := saveFile(path.Join(SubmissionDir, "_registration"), "", r); err != nil { + log.Println("Unable to open registration file:", err) + http.Error(w, "{\"errmsg\":\"Internal server error. Please retry in few seconds.\"}", http.StatusInternalServerError) + return + } + + http.Error(w, "{\"errmsg\":\"Demande d'enregistrement acceptée\"}", http.StatusAccepted) +} diff --git a/frontend/save.go b/frontend/save.go new file mode 100644 index 00000000..8e87cbc4 --- /dev/null +++ b/frontend/save.go @@ -0,0 +1,81 @@ +package main + +import ( + "io/ioutil" + "log" + "net/http" + "os" + "path" +) + +func saveTeamFile(dirname string, team string, filename string, w http.ResponseWriter, r *http.Request) bool { + if _, err := os.Stat(path.Join(dirname, team)); os.IsNotExist(err) || len(team) < 1 || team[0] == '_' { + http.Error(w, "{\"errmsg\":\"Requête invalide.\"}", http.StatusBadRequest) + return false + } else { + if len(filename) <= 0 { + log.Println("EMPTY $EXERCICE RECEIVED:", filename) + http.Error(w, "{\"errmsg\":\"Internal server error. Please retry in few seconds.\"}", http.StatusInternalServerError) + return false + } + + // Previous submission not treated + if _, err := os.Stat(path.Join(SubmissionDir, team, filename)); !os.IsNotExist(err) { + http.Error(w, "{\"errmsg\":\"Du calme ! une requête est déjà en cours de traitement.\"}", http.StatusPaymentRequired) + return false + } + + if err := saveFile(path.Join(SubmissionDir, team), filename, r); err != nil { + log.Println("Unable to handle submission file:", err) + http.Error(w, "{\"errmsg\":\"Internal server error. Please retry in few seconds.\"}", http.StatusInternalServerError) + return false + } + + return true + } +} + +func saveFile(dirname string, filename string, r *http.Request) error { + if _, err := os.Stat(dirname); os.IsNotExist(err) { + if err := os.MkdirAll(dirname, 0777); err != nil { + return err + } + } + + var f *os.File + + if filename == "" { + if fd, err := ioutil.TempFile(dirname, ""); err != nil { + return err + } else { + defer f.Close() + f = fd + } + } else { + if fd, err := os.Create(path.Join(dirname, filename)); err != nil { + return err + } else { + defer f.Close() + f = fd + } + } + + // Read request body + var body []byte + if r.ContentLength > 0 { + tmp := make([]byte, 1024) + for { + n, err := r.Body.Read(tmp) + for j := 0; j < n; j++ { + body = append(body, tmp[j]) + } + if err != nil || n <= 0 { + break + } + } + } + + f.Write(body) + + return nil +} diff --git a/frontend/submit.go b/frontend/submit.go index c39ec0f8..31380d0c 100644 --- a/frontend/submit.go +++ b/frontend/submit.go @@ -2,11 +2,8 @@ package main import ( "fmt" - "io/ioutil" "log" "net/http" - "os" - "path" "strconv" "strings" "time" @@ -14,8 +11,6 @@ import ( type SubmissionHandler struct { ChallengeEnd time.Time - DenyChName bool - AllowRegistration bool } func (s SubmissionHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { @@ -35,113 +30,25 @@ func (s SubmissionHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { // Extract URL arguments var sURL = strings.Split(r.URL.Path, "/") - if len(sURL) == 2 && s.AllowRegistration && sURL[1] == "registration" { - if _, err := os.Stat(path.Join(SubmissionDir, "_registration")); os.IsNotExist(err) { - log.Println("Creating _registration directory") - if err := os.MkdirAll(path.Join(SubmissionDir, "_registration"), 0777); err != nil { - log.Println("Unable to create _registration directory: ", err) - http.Error(w, "{\"errmsg\":\"Internal server error. Please retry in few seconds.\"}", http.StatusInternalServerError) - return - } - } - - if f, err := ioutil.TempFile(path.Join(SubmissionDir, "_registration"), ""); err != nil { - log.Println("Unable to open registration file:", err) - http.Error(w, "{\"errmsg\":\"Internal server error. Please retry in few seconds..\"}", http.StatusInternalServerError) - } else { - // Read request body - var body []byte - if r.ContentLength > 0 { - tmp := make([]byte, 1024) - for { - n, err := r.Body.Read(tmp) - for j := 0; j < n; j++ { - body = append(body, tmp[j]) - } - if err != nil || n <= 0 { - break - } - } - } - - f.Write(body) - f.Close() - } - - http.Error(w, "{\"errmsg\":\"Demande d'enregistrement acceptée\"}", http.StatusAccepted) - return - } else if len(sURL) != 3 { - http.Error(w, "{\"errmsg\":\"Requête invalide.\"}", http.StatusBadRequest) + if len(sURL) != 2 { + http.Error(w, "{\"errmsg\":\"Arguments manquants.\"}", http.StatusBadRequest) return } + team := sURL[0] if time.Now().Sub(s.ChallengeEnd) > 0 { http.Error(w, "{\"errmsg\":\"Vous ne pouvez plus soumettre, le challenge est terminé.\"}", http.StatusForbidden) return } - // Parse arguments - if _, err := os.Stat(path.Join(TeamsDir, sURL[1])); os.IsNotExist(err) || len(sURL[1]) < 1 || sURL[1][0] == '_' { - http.Error(w, "{\"errmsg\":\"Requête invalide.\"}", http.StatusBadRequest) - return - } else if pex, err := strconv.Atoi(sURL[2]); (s.DenyChName || sURL[2] != "name") && err != nil { + if pex, err := strconv.Atoi(sURL[1]); err != nil { http.Error(w, "{\"errmsg\":\"Requête invalide.\"}", http.StatusBadRequest) return } else { - team := sURL[1] + exercice := fmt.Sprintf("%d", pex) - var exercice string - if sURL[2] == "name" { - exercice = sURL[2] - } else { - exercice = fmt.Sprintf("%d", pex) + if saveTeamFile(TeamsDir, team, exercice, w, r) { + http.Error(w, "{\"errmsg\":\"Son traitement est en cours...\"}", http.StatusAccepted) } - - if len(exercice) <= 0 { - log.Println("EMPTY $EXERCICE RECEIVED:", exercice) - http.Error(w, "{\"errmsg\":\"Internal server error. Please retry in few seconds.\"}", http.StatusInternalServerError) - return - } - - if _, err := os.Stat(path.Join(SubmissionDir, team)); os.IsNotExist(err) { - log.Println("Creating submission directory for", team) - if err := os.MkdirAll(path.Join(SubmissionDir, team), 0777); err != nil { - log.Println("Unable to create submission directory: ", err) - http.Error(w, "{\"errmsg\":\"Internal server error. Please retry in few seconds.\"}", http.StatusInternalServerError) - return - } - } - - // Previous submission not treated - if _, err := os.Stat(path.Join(SubmissionDir, team, exercice)); !os.IsNotExist(err) { - http.Error(w, "{\"errmsg\":\"Du calme ! une requête est déjà en cours de traitement.\"}", http.StatusPaymentRequired) - return - } - - // Read request body - var body []byte - if r.ContentLength > 0 { - tmp := make([]byte, 1024) - for { - n, err := r.Body.Read(tmp) - for j := 0; j < n; j++ { - body = append(body, tmp[j]) - } - if err != nil || n <= 0 { - break - } - } - } - - // Store content in file - if file, err := os.Create(path.Join(SubmissionDir, team, exercice)); err != nil { - log.Println("Unable to open exercice file:", err) - http.Error(w, "{\"errmsg\":\"Internal server error. Please retry in few seconds.\"}", http.StatusInternalServerError) - return - } else { - file.Write(body) - file.Close() - } - http.Error(w, "{\"errmsg\":\"Son traitement est en cours...\"}", http.StatusAccepted) } }