[build] internal/relui: replace httprouter use with http.ServeMux

2 views
Skip to first unread message

Dmitri Shuralyov (Gerrit)

unread,
Dec 23, 2025, 5:13:00 PM (2 days ago) Dec 23
to Michael Knyszek, goph...@pubsubhelper.golang.org, Dmitri Shuralyov, golang-co...@googlegroups.com
Attention needed from Michael Knyszek

Dmitri Shuralyov has uploaded the change for review

Dmitri Shuralyov would like Michael Knyszek to review this change.

Commit message

internal/relui: replace httprouter use with http.ServeMux

ServeMux in the net/http package of the Go standard library gained
useful features in Go 1.22, including the ability to match wildcards
in patterns, and register handlers with an HTTP method. The httprouter
package has these features and was used for relui's routing needs prior
to Go 1.22.

By now, it works quite well to migrate back to net/http and its standard
ServeMux router. This is done to simplify the code and reduce the number
of dependencies slightly.

For golang/go#76860.
Change-Id: Ib5ac645c5db2c6f2406fcde596f4978a410198f2

Change diff

diff --git a/go.mod b/go.mod
index 9de1606..db5673e 100644
--- a/go.mod
+++ b/go.mod
@@ -42,7 +42,6 @@
github.com/jackc/pgconn v1.14.3
github.com/jackc/pgx/v4 v4.18.3
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1
- github.com/julienschmidt/httprouter v1.3.0
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
github.com/mailjet/mailjet-apiv3-go/v4 v4.0.7
github.com/mattn/go-sqlite3 v1.14.14
@@ -122,6 +121,7 @@
github.com/jackc/pgtype v1.14.0 // indirect
github.com/jackc/puddle v1.3.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
+ github.com/julienschmidt/httprouter v1.3.0 // indirect
github.com/klauspost/asmfmt v1.3.2 // indirect
github.com/klauspost/compress v1.16.7 // indirect
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
diff --git a/internal/relui/metrics.go b/internal/relui/metrics.go
index 79c6460..b7c9f06 100644
--- a/internal/relui/metrics.go
+++ b/internal/relui/metrics.go
@@ -6,16 +6,13 @@

import (
"context"
- "fmt"
- "mime"
+ "io/fs"
"net/http"
- "path"
"strings"
"time"

"github.com/jackc/pgconn"
"github.com/jackc/pgx/v4"
- "github.com/julienschmidt/httprouter"
"go.opencensus.io/plugin/ochttp"
"go.opencensus.io/stats"
"go.opencensus.io/stats/view"
@@ -54,92 +51,34 @@
},
}

-// metricsRouter wraps an *httprouter.Router with telemetry.
+// metricsRouter wraps an *http.ServeMux with telemetry.
type metricsRouter struct {
- router *httprouter.Router
+ mux *http.ServeMux
}

-// GET is shorthand for Handle(http.MethodGet, path, handle)
-func (r *metricsRouter) GET(path string, handle httprouter.Handle) {
- r.Handle(http.MethodGet, path, handle)
+// Handle is like (*http.ServeMux).Handle but with additional metrics reporting.
+func (r *metricsRouter) Handle(pattern string, handler http.Handler) {
+ r.mux.Handle(pattern, ochttp.WithRouteTag(handler, pattern))
}

-// HEAD is shorthand for Handle(http.MethodHead, path, handle)
-func (r *metricsRouter) HEAD(path string, handle httprouter.Handle) {
- r.Handle(http.MethodHead, path, handle)
+// HandleFunc is like (*http.ServeMux).HandleFunc but with additional metrics reporting.
+func (r *metricsRouter) HandleFunc(pattern string, handler http.HandlerFunc) {
+ r.Handle(pattern, handler)
}

-// OPTIONS is shorthand for Handle(http.MethodOptions, path, handle)
-func (r *metricsRouter) OPTIONS(path string, handle httprouter.Handle) {
- r.Handle(http.MethodOptions, path, handle)
-}
-
-// POST is shorthand for Handle(http.MethodPost, path, handle)
-func (r *metricsRouter) POST(path string, handle httprouter.Handle) {
- r.Handle(http.MethodPost, path, handle)
-}
-
-// PUT is shorthand for Handle(http.MethodPut, path, handle)
-func (r *metricsRouter) PUT(path string, handle httprouter.Handle) {
- r.Handle(http.MethodPut, path, handle)
-}
-
-// PATCH is shorthand for Handle(http.MethodPatch, path, handle)
-func (r *metricsRouter) PATCH(path string, handle httprouter.Handle) {
- r.Handle(http.MethodPatch, path, handle)
-}
-
-// DELETE is shorthand for Handle(http.MethodDelete, path, handle)
-func (r *metricsRouter) DELETE(path string, handle httprouter.Handle) {
- r.Handle(http.MethodDelete, path, handle)
-}
-
-// Handler wraps *httprouter.Handler with recorded metrics.
-func (r *metricsRouter) Handler(method, path string, handler http.Handler) {
- r.router.Handler(method, path, ochttp.WithRouteTag(handler, path))
-}
-
-// HandlerFunc wraps *httprouter.HandlerFunc with recorded metrics.
-func (r *metricsRouter) HandlerFunc(method, path string, handler http.HandlerFunc) {
- r.Handler(method, path, handler)
-}
-
-// ServeFiles serves files at the specified root. The provided path
-// must end in /*filepath.
-//
-// Unlike *httprouter.ServeFiles, this method sets a Content-Type and
-// Cache-Control to "no-cache, private, max-age=0". This handler
-// also does not strip the prefix of the request path.
-func (r *metricsRouter) ServeFiles(p string, root http.FileSystem) {
- if len(p) < 10 || p[len(p)-10:] != "/*filepath" {
- panic(fmt.Sprintf("p must end with /*filepath in path %q", p))
- }
-
- s := http.FileServer(root)
- r.GET(p, func(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
- w.Header().Set("Content-Type", mime.TypeByExtension(path.Ext(req.URL.Path)))
+// ServeFiles serves files at the specified root with the
+// Cache-Control header set to "no-cache, private, max-age=0".
+func (r *metricsRouter) ServeFiles(pattern string, root fs.FS) {
+ fs := http.FileServerFS(root)
+ r.mux.HandleFunc(pattern, func(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Cache-Control", "no-cache, private, max-age=0")
- s.ServeHTTP(w, req)
+ fs.ServeHTTP(w, req)
})
}

-// Lookup wraps *httprouter.Lookup.
-func (r *metricsRouter) Lookup(method, path string) (httprouter.Handle, httprouter.Params, bool) {
- return r.router.Lookup(method, path)
-}
-
-// ServeHTTP wraps *httprouter.ServeHTTP.
+// ServeHTTP implements http.Handler.
func (r *metricsRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
- r.router.ServeHTTP(w, req)
-}
-
-// Handle calls *httprouter.ServeHTTP with additional metrics reporting.
-func (r *metricsRouter) Handle(method, path string, handle httprouter.Handle) {
- r.router.Handle(method, path, func(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
- ochttp.WithRouteTag(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- handle(w, r, params)
- }), path).ServeHTTP(w, r)
- })
+ r.mux.ServeHTTP(w, req)
}

type MetricsDB struct {
diff --git a/internal/relui/web.go b/internal/relui/web.go
index c97532c..8d999d1 100644
--- a/internal/relui/web.go
+++ b/internal/relui/web.go
@@ -24,7 +24,6 @@

"github.com/google/uuid"
"github.com/jackc/pgx/v4"
- "github.com/julienschmidt/httprouter"
"golang.org/x/build/internal/access"
"golang.org/x/build/internal/criadb"
"golang.org/x/build/internal/metrics"
@@ -71,7 +70,7 @@
func NewServer(p db.PGDBTX, w *Worker, baseURL *url.URL, header SiteHeader, ms *metrics.Service, cria *criadb.AuthDatabase) *Server {
s := &Server{
db: p,
- m: &metricsRouter{router: httprouter.New()},
+ m: &metricsRouter{mux: http.NewServeMux()},
w: w,
scheduler: NewScheduler(p, w),
baseURL: baseURL,
@@ -93,16 +92,16 @@
s.templates = template.Must(template.New("").Funcs(helpers).ParseFS(templates, "templates/*.html"))
s.homeTmpl = s.mustLookup("home.html")
s.newWorkflowTmpl = s.mustLookup("new_workflow.html")
- s.m.GET("/workflows/:id", s.showWorkflowHandler)
- s.m.POST("/workflows/:id/stop", s.stopWorkflowHandler)
- s.m.POST("/workflows/:id/tasks/:name/retry", s.retryTaskHandler)
- s.m.POST("/workflows/:id/tasks/:name/approve", s.approveTaskHandler)
- s.m.POST("/schedules/:id/delete", s.deleteScheduleHandler)
- s.m.Handler(http.MethodGet, "/metrics", ms)
- s.m.Handler(http.MethodGet, "/new_workflow", http.HandlerFunc(s.newWorkflowHandler))
- s.m.Handler(http.MethodPost, "/workflows", http.HandlerFunc(s.createWorkflowHandler))
- s.m.ServeFiles("/static/*filepath", http.FS(static))
- s.m.Handler(http.MethodGet, "/", http.HandlerFunc(s.homeHandler))
+ s.m.HandleFunc("GET /workflows/{id}", s.showWorkflowHandler)
+ s.m.HandleFunc("POST /workflows/{id}/stop", s.stopWorkflowHandler)
+ s.m.HandleFunc("POST /workflows/{id}/tasks/{name}/retry", s.retryTaskHandler)
+ s.m.HandleFunc("POST /workflows/{id}/tasks/{name}/approve", s.approveTaskHandler)
+ s.m.HandleFunc("POST /schedules/{id}/delete", s.deleteScheduleHandler)
+ s.m.Handle("GET /metrics", ms)
+ s.m.HandleFunc("GET /new_workflow", s.newWorkflowHandler)
+ s.m.HandleFunc("POST /workflows", s.createWorkflowHandler)
+ s.m.ServeFiles("GET /static/", static)
+ s.m.HandleFunc("GET /$", s.homeHandler)
if baseURL != nil && baseURL.Path != "/" && baseURL.Path != "" {
nosuffix := strings.TrimSuffix(baseURL.Path, "/")
s.bm = new(http.ServeMux)
@@ -254,10 +253,10 @@
TaskLogs map[string][]db.TaskLog
}

-func (s *Server) showWorkflowHandler(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
- id, err := uuid.Parse(params.ByName("id"))
+func (s *Server) showWorkflowHandler(w http.ResponseWriter, r *http.Request) {
+ id, err := uuid.Parse(r.PathValue("id"))
if err != nil {
- log.Printf("showWorkflowHandler(_, _, %v) uuid.Parse(%v): %v", params, params.ByName("id"), err)
+ log.Printf("showWorkflowHandler: uuid.Parse(%q): %v", r.PathValue("id"), err)
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
}
@@ -444,10 +443,10 @@
http.Redirect(w, r, s.BaseLink("/workflows", id.String()), http.StatusSeeOther)
}

-func (s *Server) retryTaskHandler(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
- id, err := uuid.Parse(params.ByName("id"))
+func (s *Server) retryTaskHandler(w http.ResponseWriter, r *http.Request) {
+ id, err := uuid.Parse(r.PathValue("id"))
if err != nil {
- log.Printf("retryTaskHandler(_, _, %v) uuid.Parse(%v): %v", params, params.ByName("id"), err)
+ log.Printf("retryTaskHandler: uuid.Parse(%q): %v", r.PathValue("id"), err)
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
}
@@ -457,7 +456,7 @@
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return
} else if err != nil {
- log.Printf("retryTaskHandler(_, _, %v): Workflow(%d): %v", params, id, err)
+ log.Printf("retryTaskHandler: Workflow(%v): %v", id, err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
@@ -470,16 +469,16 @@
// authorizedForWorkflow writes errors to w itself.
return
}
- if err := s.w.RetryTask(r.Context(), id, params.ByName("name")); err != nil {
+ if err := s.w.RetryTask(r.Context(), id, r.PathValue("name")); err != nil {
log.Printf("s.w.RetryTask(_, %q): %v", id, err)
}
http.Redirect(w, r, s.BaseLink("/workflows", id.String()), http.StatusSeeOther)
}

-func (s *Server) approveTaskHandler(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
- id, err := uuid.Parse(params.ByName("id"))
+func (s *Server) approveTaskHandler(w http.ResponseWriter, r *http.Request) {
+ id, err := uuid.Parse(r.PathValue("id"))
if err != nil {
- log.Printf("approveTaskHandler(_, _, %v) uuid.Parse(%v): %v", params, params.ByName("id"), err)
+ log.Printf("approveTaskHandler: uuid.Parse(%q): %v", r.PathValue("id"), err)
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
}
@@ -489,7 +488,7 @@
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return
} else if err != nil {
- log.Printf("approveTaskHandler(_, _, %v): Workflow(%d): %v", params, id, err)
+ log.Printf("approveTaskHandler: Workflow(%v): %v", id, err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
@@ -504,7 +503,7 @@
}
t, err := q.ApproveTask(r.Context(), db.ApproveTaskParams{
WorkflowID: id,
- Name: params.ByName("name"),
+ Name: r.PathValue("name"),
ApprovedAt: sql.NullTime{Time: time.Now(), Valid: true},
})
if errors.Is(err, sql.ErrNoRows) || errors.Is(err, pgx.ErrNoRows) {
@@ -519,10 +518,10 @@
http.Redirect(w, r, s.BaseLink("/workflows", id.String()), http.StatusSeeOther)
}

-func (s *Server) stopWorkflowHandler(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
- id, err := uuid.Parse(params.ByName("id"))
+func (s *Server) stopWorkflowHandler(w http.ResponseWriter, r *http.Request) {
+ id, err := uuid.Parse(r.PathValue("id"))
if err != nil {
- log.Printf("stopWorkflowHandler(_, _, %v) uuid.Parse(%v): %v", params, params.ByName("id"), err)
+ log.Printf("stopWorkflowHandler: uuid.Parse(%q): %v", r.PathValue("id"), err)
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
}
@@ -532,7 +531,7 @@
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return
} else if err != nil {
- log.Printf("stopWorkflowHandler(_, _, %v): Workflow(%d): %v", params, id, err)
+ log.Printf("stopWorkflowHandler: Workflow(%v): %v", id, err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
@@ -552,10 +551,10 @@
http.Redirect(w, r, s.BaseLink("/"), http.StatusSeeOther)
}

-func (s *Server) deleteScheduleHandler(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
- id, err := strconv.Atoi(params.ByName("id"))
+func (s *Server) deleteScheduleHandler(w http.ResponseWriter, r *http.Request) {
+ id, err := strconv.Atoi(r.PathValue("id"))
if err != nil {
- log.Printf("deleteScheduleHandler(_, _, %v) strconv.Atoi(%q) = %d, %v", params, params.ByName("id"), id, err)
+ log.Printf("deleteScheduleHandler: strconv.Atoi(%q): %v", r.PathValue("id"), err)
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
}
@@ -590,7 +589,7 @@
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return
} else if err != nil {
- log.Printf("deleteScheduleHandler(_, _, %v) s.scheduler.Delete(_, %d) = %v", params, id, err)
+ log.Printf("deleteScheduleHandler: s.scheduler.Delete(_, %v) = %v", id, err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
diff --git a/internal/relui/web_test.go b/internal/relui/web_test.go
index 293608a..e5bc40d 100644
--- a/internal/relui/web_test.go
+++ b/internal/relui/web_test.go
@@ -28,7 +28,6 @@
"github.com/google/uuid"
"github.com/jackc/pgx/v4"
"github.com/jackc/pgx/v4/pgxpool"
- "github.com/julienschmidt/httprouter"
"golang.org/x/build/internal/access"
"golang.org/x/build/internal/criadb"
"golang.org/x/build/internal/releasetargets"
@@ -76,8 +75,8 @@
req := httptest.NewRequest(http.MethodGet, c.path, nil)
w := httptest.NewRecorder()

- m := &metricsRouter{router: httprouter.New()}
- m.ServeFiles("/*filepath", http.FS(testStatic))
+ m := &metricsRouter{mux: http.NewServeMux()}
+ m.ServeFiles("GET /", testStatic)
m.ServeHTTP(w, req)
resp := w.Result()
defer resp.Body.Close()
@@ -895,13 +894,14 @@
t.Fatalf("FailUnfinishedTasks(_, %v) = _, %v, wanted no error", fail, err)
}

- params := httprouter.Params{{Key: "id", Value: wfID.String()}, {Key: "name", Value: "beep"}}
req := httptest.NewRequest(http.MethodPost, path.Join("/workflows/", wfID.String(), "tasks", "beep", "retry"), nil)
+ req.SetPathValue("id", wfID.String())
+ req.SetPathValue("name", "beep")
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req = req.WithContext(access.ContextWithIAP(req.Context(), iap))
rec := httptest.NewRecorder()

- s.retryTaskHandler(rec, req, params)
+ s.retryTaskHandler(rec, req)
resp := rec.Result()

if resp.StatusCode != expectedStatus {
@@ -936,13 +936,14 @@
t.Fatalf("CreateTask(_, %v) = _, %v, wanted no error", gtg, err)
}

- params := httprouter.Params{{Key: "id", Value: wfID.String()}, {Key: "name", Value: "approve"}}
req := httptest.NewRequest(http.MethodPost, path.Join("/workflows/", wfID.String(), "tasks", "approve", "approve"), nil)
+ req.SetPathValue("id", wfID.String())
+ req.SetPathValue("name", "approve")
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req = req.WithContext(access.ContextWithIAP(req.Context(), iap))
rec := httptest.NewRecorder()

- s.approveTaskHandler(rec, req, params)
+ s.approveTaskHandler(rec, req)
resp := rec.Result()

if resp.StatusCode != expectedStatus {
@@ -968,13 +969,13 @@
}
s.w.markRunning(&workflow.Workflow{ID: wfID}, func() {})

- params := httprouter.Params{{Key: "id", Value: wfID.String()}}
req := httptest.NewRequest(http.MethodPost, path.Join("/workflows/", wfID.String(), "stop"), nil)
+ req.SetPathValue("id", wfID.String())
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req = req.WithContext(access.ContextWithIAP(req.Context(), iap))
rec := httptest.NewRecorder()

- s.stopWorkflowHandler(rec, req, params)
+ s.stopWorkflowHandler(rec, req)
resp := rec.Result()

if resp.StatusCode != expectedStatus {
@@ -992,13 +993,13 @@
t.Fatalf("Scheduler.Create() = _, %v, wanted no error", err)
}

- params := httprouter.Params{{Key: "id", Value: strconv.Itoa(int(sched.ID))}}
req := httptest.NewRequest(http.MethodPost, path.Join("/schedules/", strconv.Itoa(int(sched.ID)), "delete"), nil)
+ req.SetPathValue("id", strconv.Itoa(int(sched.ID)))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req = req.WithContext(access.ContextWithIAP(req.Context(), iap))
rec := httptest.NewRecorder()

- s.deleteScheduleHandler(rec, req, params)
+ s.deleteScheduleHandler(rec, req)
resp := rec.Result()

if resp.StatusCode != expectedStatus {

Change information

Files:
  • M go.mod
  • M internal/relui/metrics.go
  • M internal/relui/web.go
  • M internal/relui/web_test.go
Change size: M
Delta: 4 files changed, 62 insertions(+), 123 deletions(-)
Open in Gerrit

Related details

Attention is currently required from:
  • Michael Knyszek
Submit Requirements:
  • requirement is not satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
  • requirement is not satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: newchange
Gerrit-Project: build
Gerrit-Branch: master
Gerrit-Change-Id: Ib5ac645c5db2c6f2406fcde596f4978a410198f2
Gerrit-Change-Number: 732261
Gerrit-PatchSet: 1
Gerrit-Owner: Dmitri Shuralyov <dmit...@golang.org>
Gerrit-Reviewer: Dmitri Shuralyov <dmit...@golang.org>
Gerrit-Reviewer: Michael Knyszek <mkny...@google.com>
Gerrit-Attention: Michael Knyszek <mkny...@google.com>
unsatisfied_requirement
satisfied_requirement
open
diffy

Dmitri Shuralyov (Gerrit)

unread,
Dec 23, 2025, 5:17:46 PM (2 days ago) Dec 23
to Dmitri Shuralyov, goph...@pubsubhelper.golang.org, Go LUCI, Michael Knyszek, golang-co...@googlegroups.com
Attention needed from Michael Knyszek

Dmitri Shuralyov voted Code-Review+1

Code-Review+1
Open in Gerrit

Related details

Attention is currently required from:
  • Michael Knyszek
Submit Requirements:
  • requirement is not satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
  • requirement is not satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: build
Gerrit-Branch: master
Gerrit-Change-Id: Ib5ac645c5db2c6f2406fcde596f4978a410198f2
Gerrit-Change-Number: 732261
Gerrit-PatchSet: 1
Gerrit-Owner: Dmitri Shuralyov <dmit...@golang.org>
Gerrit-Reviewer: Dmitri Shuralyov <dmit...@golang.org>
Gerrit-Reviewer: Dmitri Shuralyov <dmit...@google.com>
Gerrit-Reviewer: Michael Knyszek <mkny...@google.com>
Gerrit-Attention: Michael Knyszek <mkny...@google.com>
Gerrit-Comment-Date: Tue, 23 Dec 2025 22:17:42 +0000
Gerrit-HasComments: No
Gerrit-Has-Labels: Yes
unsatisfied_requirement
satisfied_requirement
open
diffy

Michael Knyszek (Gerrit)

unread,
Dec 23, 2025, 5:24:49 PM (2 days ago) Dec 23
to Dmitri Shuralyov, goph...@pubsubhelper.golang.org, Dmitri Shuralyov, Go LUCI, golang-co...@googlegroups.com
Attention needed from Dmitri Shuralyov

Michael Knyszek voted Code-Review+2

Code-Review+2
Open in Gerrit

Related details

Attention is currently required from:
  • Dmitri Shuralyov
Submit Requirements:
  • requirement satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement satisfiedReview-Enforcement
  • requirement is not satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: build
Gerrit-Branch: master
Gerrit-Change-Id: Ib5ac645c5db2c6f2406fcde596f4978a410198f2
Gerrit-Change-Number: 732261
Gerrit-PatchSet: 1
Gerrit-Owner: Dmitri Shuralyov <dmit...@golang.org>
Gerrit-Reviewer: Dmitri Shuralyov <dmit...@golang.org>
Gerrit-Reviewer: Dmitri Shuralyov <dmit...@google.com>
Gerrit-Reviewer: Michael Knyszek <mkny...@google.com>
Gerrit-Attention: Dmitri Shuralyov <dmit...@golang.org>
Gerrit-Comment-Date: Tue, 23 Dec 2025 22:24:46 +0000
Gerrit-HasComments: No
Gerrit-Has-Labels: Yes
satisfied_requirement
unsatisfied_requirement
open
diffy

Dmitri Shuralyov (Gerrit)

unread,
Dec 23, 2025, 5:48:11 PM (2 days ago) Dec 23
to Dmitri Shuralyov, goph...@pubsubhelper.golang.org, Go LUCI, Dmitri Shuralyov, golang-co...@googlegroups.com

Dmitri Shuralyov added 1 comment

File internal/relui/web.go
Line 104, Patchset 1: s.m.HandleFunc("GET /$", s.homeHandler)
Dmitri Shuralyov . resolved

This seems to work as is, but I noticed net/http docs suggest putting the $ in {}. Done in PS 2.

Open in Gerrit

Related details

Attention set is empty
Submit Requirements:
  • requirement satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement satisfiedReview-Enforcement
  • requirement is not satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: build
Gerrit-Branch: master
Gerrit-Change-Id: Ib5ac645c5db2c6f2406fcde596f4978a410198f2
Gerrit-Change-Number: 732261
Gerrit-PatchSet: 2
Gerrit-Owner: Dmitri Shuralyov <dmit...@golang.org>
Gerrit-Reviewer: Dmitri Shuralyov <dmit...@golang.org>
Gerrit-Reviewer: Dmitri Shuralyov <dmit...@google.com>
Gerrit-Reviewer: Michael Knyszek <mkny...@google.com>
Gerrit-Comment-Date: Tue, 23 Dec 2025 22:48:04 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
satisfied_requirement
unsatisfied_requirement
open
diffy

Dmitri Shuralyov (Gerrit)

unread,
Dec 23, 2025, 5:48:12 PM (2 days ago) Dec 23
to Dmitri Shuralyov, goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

Dmitri Shuralyov uploaded new patchset

Dmitri Shuralyov uploaded patch set #2 to this change.
Following approvals got outdated and were removed:
  • TryBots-Pass: LUCI-TryBot-Result+1 by Go LUCI
Open in Gerrit

Related details

Attention set is empty
Submit Requirements:
  • requirement satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement satisfiedReview-Enforcement
  • requirement is not satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: newpatchset
satisfied_requirement
unsatisfied_requirement
open
diffy

Dmitri Shuralyov (Gerrit)

unread,
Dec 23, 2025, 5:51:07 PM (2 days ago) Dec 23
to Dmitri Shuralyov, goph...@pubsubhelper.golang.org, Go LUCI, Dmitri Shuralyov, golang-co...@googlegroups.com

Dmitri Shuralyov voted and added 1 comment

Votes added by Dmitri Shuralyov

Hold+1

1 comment

Patchset-level comments
File-level comment, Patchset 2 (Latest):
Dmitri Shuralyov . resolved

Thanks. Hold for now, I'll get this submitted in the new year when I'm around, in case I missed something that needs follow up.

Open in Gerrit

Related details

Attention set is empty
Submit Requirements:
  • requirement satisfiedCode-Review
  • requirement is not satisfiedNo-Holds
  • requirement satisfiedNo-Unresolved-Comments
  • requirement satisfiedReview-Enforcement
  • requirement is not satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: build
Gerrit-Branch: master
Gerrit-Change-Id: Ib5ac645c5db2c6f2406fcde596f4978a410198f2
Gerrit-Change-Number: 732261
Gerrit-PatchSet: 2
Gerrit-Owner: Dmitri Shuralyov <dmit...@golang.org>
Gerrit-Reviewer: Dmitri Shuralyov <dmit...@golang.org>
Gerrit-Reviewer: Dmitri Shuralyov <dmit...@google.com>
Gerrit-Reviewer: Michael Knyszek <mkny...@google.com>
Gerrit-Comment-Date: Tue, 23 Dec 2025 22:51:04 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: Yes
satisfied_requirement
unsatisfied_requirement
open
diffy
Reply all
Reply to author
Forward
0 new messages