geektutu/7days-golang · warning

bad request

Error message

bad request

What it means

geecache's HTTPPool.ServeHTTP only understands URLs of the form /<basePath>/<groupName>/<key>. When the path after basePath splits on '/' into fewer than 2 segments, it responds with HTTP 400 and body 'bad request'. It is the library telling you the request URL does not match the expected cache API shape.

Solutions

  1. Send the full expected path: /<basePath>/<groupName>/<key>, e.g. GET /_geecache/groups/key1.
  2. Exclude this endpoint from health checks, or point health checks at a dedicated route (e.g. /healthz) served by a different handler.
  3. Check any reverse-proxy/ingress rewrite rules so they do not strip the group or key segment.
  4. URL-encode the key if it can contain '/' so SplitN still yields exactly two parts.

Example fix

// before: GET /_geecache/mygroup  -> 400
http.Get("http://localhost:8001/_geecache/mygroup")
// after: include key
http.Get("http://localhost:8001/_geecache/mygroup/key1")
Defensive patterns

Strategy: validation

Validate before calling

// validate the URL before calling the cache node
func validCacheURL(base, group, key string) bool {
    return group != "" && key != "" && !strings.ContainsAny(key, "/?#")
}
// url := base + "/" + group + "/" + url.PathEscape(key)

Prevention

When it happens

Trigger: An HTTP request to the pool whose path after basePath contains fewer than two '/'-separated parts, e.g. GET /_geecache/ or GET /_geecache/mygroup (missing the /<key> segment).

Common situations: Health checks or load balancers hitting the pool's base path directly; a client forgetting to append /<key>; proxy rewriting that strips part of the path; typos like a trailing-slash-only URL.

Related errors


AI-assisted analysis of geektutu/7days-golang@cf36443821 (2026-09-03). Data as JSON: /api/errors/8be1527df24247a6. Report an issue: GitHub.

Appendix: source

Thrown at gee-cache/day3-http-server/geecache/http.go:41

		basePath: defaultBasePath,
	}
}

// Log info with server name
func (p *HTTPPool) Log(format string, v ...interface{}) {
	log.Printf("[Server %s] %s", p.self, fmt.Sprintf(format, v...))
}

// ServeHTTP handle all http requests
func (p *HTTPPool) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if !strings.HasPrefix(r.URL.Path, p.basePath) {
		panic("HTTPPool serving unexpected path: " + r.URL.Path)
	}
	p.Log("%s %s", r.Method, r.URL.Path)
	// /<basepath>/<groupname>/<key> required
	parts := strings.SplitN(r.URL.Path[len(p.basePath):], "/", 2)
	if len(parts) != 2 {
		http.Error(w, "bad request", http.StatusBadRequest)
		return
	}

	groupName := parts[0]
	key := parts[1]

	group := GetGroup(groupName)
	if group == nil {
		http.Error(w, "no such group: "+groupName, http.StatusNotFound)
		return
	}

	view, err := group.Get(key)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

View on GitHub (pinned to cf36443821)