Index: tools/bug_chomper/src/server/server.go |
diff --git a/tools/bug_chomper/src/server/server.go b/tools/bug_chomper/src/server/server.go |
deleted file mode 100644 |
index 7fdae93c7bd17662d6831c32eed2a93087bcd816..0000000000000000000000000000000000000000 |
--- a/tools/bug_chomper/src/server/server.go |
+++ /dev/null |
@@ -1,393 +0,0 @@ |
-// Copyright (c) 2014 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-/* |
- Serves a webpage for easy management of Skia bugs. |
- |
- WARNING: This server is NOT secure and should not be made publicly |
- accessible. |
-*/ |
- |
-package main |
- |
-import ( |
- "encoding/json" |
- "flag" |
- "fmt" |
- "html/template" |
- "issue_tracker" |
- "log" |
- "net/http" |
- "net/url" |
- "os" |
- "path/filepath" |
- "runtime" |
- "strconv" |
- "strings" |
- "time" |
-) |
- |
-import "github.com/gorilla/securecookie" |
- |
-const ( |
- certFile = "certs/cert.pem" |
- keyFile = "certs/key.pem" |
- issueComment = "Edited by BugChomper" |
- oauthCallbackPath = "/oauth2callback" |
- oauthConfigFile = "oauth_client_secret.json" |
- localHost = "127.0.0.1" |
- maxSessionLen = time.Duration(3600 * time.Second) |
- priorityPrefix = "Priority-" |
- project = "skia" |
- cookieName = "BugChomperCookie" |
-) |
- |
-// Flags: |
-var ( |
- port = flag.String("port", ":8000", "HTTP service address (e.g., ':8000')") |
- public = flag.Bool("public", false, "Make this server publicly accessible.") |
-) |
- |
-var ( |
- // templates is the list of html templates used by bug_chomper. |
- templates *template.Template = nil |
- |
- scheme = "http" |
- hashKey = securecookie.GenerateRandomKey(32) |
- blockKey = securecookie.GenerateRandomKey(32) |
- secureCookie = securecookie.New(hashKey, blockKey) |
-) |
- |
-func init() { |
- // Change the current working directory to two directories up from this |
- // source file so that we can read templates. |
- _, filename, _, _ := runtime.Caller(0) |
- cwd := filepath.Join(filepath.Dir(filename), "../..") |
- if err := os.Chdir(cwd); err != nil { |
- log.Fatal(err) |
- } |
- |
- templates = template.Must(template.ParseFiles( |
- filepath.Join(cwd, "templates/bug_chomper.html"), |
- filepath.Join(cwd, "templates/submitted.html"), |
- filepath.Join(cwd, "templates/error.html"), |
- )) |
-} |
- |
-// SessionState contains data for a given session. |
-type SessionState struct { |
- IssueTracker *issue_tracker.IssueTracker |
- OrigRequestURL string |
- SessionStart time.Time |
-} |
- |
-// getAbsoluteURL returns the absolute URL of the given Request. |
-func getAbsoluteURL(r *http.Request) string { |
- return scheme + "://" + r.Host + r.URL.Path |
-} |
- |
-// getOAuth2CallbackURL returns a callback URL to be used by the OAuth2 login |
-// page. |
-func getOAuth2CallbackURL(r *http.Request) string { |
- return scheme + "://" + r.Host + oauthCallbackPath |
-} |
- |
-func saveSession(session *SessionState, w http.ResponseWriter, r *http.Request) error { |
- encodedSession, err := secureCookie.Encode(cookieName, session) |
- if err != nil { |
- return fmt.Errorf("unable to encode session state: %s", err) |
- } |
- cookie := &http.Cookie{ |
- Name: cookieName, |
- Value: encodedSession, |
- Domain: strings.Split(r.Host, ":")[0], |
- Path: "/", |
- HttpOnly: true, |
- } |
- http.SetCookie(w, cookie) |
- return nil |
-} |
- |
-// makeSession creates a new session for the Request. |
-func makeSession(w http.ResponseWriter, r *http.Request) (*SessionState, error) { |
- log.Println("Creating new session.") |
- // Create the session state. |
- issueTracker, err := issue_tracker.MakeIssueTracker( |
- oauthConfigFile, getOAuth2CallbackURL(r)) |
- if err != nil { |
- return nil, fmt.Errorf("unable to create IssueTracker for session: %s", err) |
- } |
- session := SessionState{ |
- IssueTracker: issueTracker, |
- OrigRequestURL: getAbsoluteURL(r), |
- SessionStart: time.Now(), |
- } |
- |
- // Encode and store the session state. |
- if err := saveSession(&session, w, r); err != nil { |
- return nil, err |
- } |
- |
- return &session, nil |
-} |
- |
-// getSession retrieves the active SessionState or creates and returns a new |
-// SessionState. |
-func getSession(w http.ResponseWriter, r *http.Request) (*SessionState, error) { |
- cookie, err := r.Cookie(cookieName) |
- if err != nil { |
- log.Println("No cookie found! Starting new session.") |
- return makeSession(w, r) |
- } |
- var session SessionState |
- if err := secureCookie.Decode(cookieName, cookie.Value, &session); err != nil { |
- log.Printf("Invalid or corrupted session. Starting another: %s", err.Error()) |
- return makeSession(w, r) |
- } |
- |
- currentTime := time.Now() |
- if currentTime.Sub(session.SessionStart) > maxSessionLen { |
- log.Printf("Session starting at %s is expired. Starting another.", |
- session.SessionStart.Format(time.RFC822)) |
- return makeSession(w, r) |
- } |
- saveSession(&session, w, r) |
- return &session, nil |
-} |
- |
-// reportError serves the error page with the given message. |
-func reportError(w http.ResponseWriter, msg string, code int) { |
- errData := struct { |
- Code int |
- CodeString string |
- Message string |
- }{ |
- Code: code, |
- CodeString: http.StatusText(code), |
- Message: msg, |
- } |
- w.WriteHeader(code) |
- err := templates.ExecuteTemplate(w, "error.html", errData) |
- if err != nil { |
- log.Println("Failed to display error.html!!") |
- } |
-} |
- |
-// makeBugChomperPage builds and serves the BugChomper page. |
-func makeBugChomperPage(w http.ResponseWriter, r *http.Request) { |
- session, err := getSession(w, r) |
- if err != nil { |
- reportError(w, err.Error(), http.StatusInternalServerError) |
- return |
- } |
- issueTracker := session.IssueTracker |
- user, err := issueTracker.GetLoggedInUser() |
- if err != nil { |
- reportError(w, err.Error(), http.StatusInternalServerError) |
- return |
- } |
- log.Println("Loading bugs for " + user) |
- bugList, err := issueTracker.GetBugs(project, user) |
- if err != nil { |
- reportError(w, err.Error(), http.StatusInternalServerError) |
- return |
- } |
- bugsById := make(map[string]*issue_tracker.Issue) |
- bugsByPriority := make(map[string][]*issue_tracker.Issue) |
- for _, bug := range bugList.Items { |
- bugsById[strconv.Itoa(bug.Id)] = bug |
- var bugPriority string |
- for _, label := range bug.Labels { |
- if strings.HasPrefix(label, priorityPrefix) { |
- bugPriority = label[len(priorityPrefix):] |
- } |
- } |
- if _, ok := bugsByPriority[bugPriority]; !ok { |
- bugsByPriority[bugPriority] = make( |
- []*issue_tracker.Issue, 0) |
- } |
- bugsByPriority[bugPriority] = append( |
- bugsByPriority[bugPriority], bug) |
- } |
- bugsJson, err := json.Marshal(bugsById) |
- if err != nil { |
- reportError(w, err.Error(), http.StatusInternalServerError) |
- return |
- } |
- data := struct { |
- Title string |
- User string |
- BugsJson template.JS |
- BugsByPriority *map[string][]*issue_tracker.Issue |
- Priorities []string |
- PriorityPrefix string |
- }{ |
- Title: "BugChomper", |
- User: user, |
- BugsJson: template.JS(string(bugsJson)), |
- BugsByPriority: &bugsByPriority, |
- Priorities: issue_tracker.BugPriorities, |
- PriorityPrefix: priorityPrefix, |
- } |
- |
- if err := templates.ExecuteTemplate(w, "bug_chomper.html", data); err != nil { |
- reportError(w, err.Error(), http.StatusInternalServerError) |
- return |
- } |
-} |
- |
-// authIfNeeded determines whether the current user is logged in. If not, it |
-// redirects to a login page. Returns true if the user is redirected and false |
-// otherwise. |
-func authIfNeeded(w http.ResponseWriter, r *http.Request) bool { |
- session, err := getSession(w, r) |
- if err != nil { |
- reportError(w, err.Error(), http.StatusInternalServerError) |
- return false |
- } |
- issueTracker := session.IssueTracker |
- if !issueTracker.IsAuthenticated() { |
- loginURL := issueTracker.MakeAuthRequestURL() |
- log.Println("Redirecting for login:", loginURL) |
- http.Redirect(w, r, loginURL, http.StatusTemporaryRedirect) |
- return true |
- } |
- return false |
-} |
- |
-// submitData attempts to submit data from a POST request to the IssueTracker. |
-func submitData(w http.ResponseWriter, r *http.Request) { |
- session, err := getSession(w, r) |
- if err != nil { |
- reportError(w, err.Error(), http.StatusInternalServerError) |
- return |
- } |
- issueTracker := session.IssueTracker |
- edits := r.FormValue("all_edits") |
- var editsMap map[string]*issue_tracker.Issue |
- if err := json.Unmarshal([]byte(edits), &editsMap); err != nil { |
- errMsg := "Could not parse edits from form response: " + err.Error() |
- reportError(w, errMsg, http.StatusInternalServerError) |
- return |
- } |
- data := struct { |
- Title string |
- Message string |
- BackLink string |
- }{} |
- if len(editsMap) == 0 { |
- data.Title = "No Changes Submitted" |
- data.Message = "You didn't change anything!" |
- data.BackLink = "" |
- if err := templates.ExecuteTemplate(w, "submitted.html", data); err != nil { |
- reportError(w, err.Error(), http.StatusInternalServerError) |
- return |
- } |
- return |
- } |
- errorList := make([]error, 0) |
- for issueId, newIssue := range editsMap { |
- log.Println("Editing issue " + issueId) |
- if err := issueTracker.SubmitIssueChanges(newIssue, issueComment); err != nil { |
- errorList = append(errorList, err) |
- } |
- } |
- if len(errorList) > 0 { |
- errorStrings := "" |
- for _, err := range errorList { |
- errorStrings += err.Error() + "\n" |
- } |
- errMsg := "Not all changes could be submitted: \n" + errorStrings |
- reportError(w, errMsg, http.StatusInternalServerError) |
- return |
- } |
- data.Title = "Submitted Changes" |
- data.Message = "Your changes were submitted to the issue tracker." |
- data.BackLink = "" |
- if err := templates.ExecuteTemplate(w, "submitted.html", data); err != nil { |
- reportError(w, err.Error(), http.StatusInternalServerError) |
- return |
- } |
- return |
-} |
- |
-// handleBugChomper handles HTTP requests for the bug_chomper page. |
-func handleBugChomper(w http.ResponseWriter, r *http.Request) { |
- if authIfNeeded(w, r) { |
- return |
- } |
- switch r.Method { |
- case "GET": |
- makeBugChomperPage(w, r) |
- case "POST": |
- submitData(w, r) |
- } |
-} |
- |
-// handleOAuth2Callback handles callbacks from the OAuth2 sign-in. |
-func handleOAuth2Callback(w http.ResponseWriter, r *http.Request) { |
- session, err := getSession(w, r) |
- if err != nil { |
- reportError(w, err.Error(), http.StatusInternalServerError) |
- } |
- issueTracker := session.IssueTracker |
- invalidLogin := "Invalid login credentials" |
- params, err := url.ParseQuery(r.URL.RawQuery) |
- if err != nil { |
- reportError(w, invalidLogin+": "+err.Error(), http.StatusForbidden) |
- return |
- } |
- code, ok := params["code"] |
- if !ok { |
- reportError(w, invalidLogin+": redirect did not include auth code.", |
- http.StatusForbidden) |
- return |
- } |
- log.Println("Upgrading auth token:", code[0]) |
- if err := issueTracker.UpgradeCode(code[0]); err != nil { |
- errMsg := "failed to upgrade token: " + err.Error() |
- reportError(w, errMsg, http.StatusForbidden) |
- return |
- } |
- if err := saveSession(session, w, r); err != nil { |
- reportError(w, "failed to save session: "+err.Error(), |
- http.StatusInternalServerError) |
- return |
- } |
- http.Redirect(w, r, session.OrigRequestURL, http.StatusTemporaryRedirect) |
- return |
-} |
- |
-// handleRoot is the handler function for all HTTP requests at the root level. |
-func handleRoot(w http.ResponseWriter, r *http.Request) { |
- log.Println("Fetching " + r.URL.Path) |
- if r.URL.Path == "/" || r.URL.Path == "/index.html" { |
- handleBugChomper(w, r) |
- return |
- } |
- http.NotFound(w, r) |
-} |
- |
-// Run the BugChomper server. |
-func main() { |
- flag.Parse() |
- |
- http.HandleFunc("/", handleRoot) |
- http.HandleFunc(oauthCallbackPath, handleOAuth2Callback) |
- http.Handle("/res/", http.FileServer(http.Dir("./"))) |
- log.Println("Server is running at " + scheme + "://" + localHost + *port) |
- var err error |
- if *public { |
- log.Println("WARNING: This server is not secure and should not be made " + |
- "publicly accessible.") |
- scheme = "https" |
- err = http.ListenAndServeTLS(*port, certFile, keyFile, nil) |
- } else { |
- scheme = "http" |
- err = http.ListenAndServe(localHost+*port, nil) |
- } |
- if err != nil { |
- log.Println(err.Error()) |
- } |
-} |