-
Notifications
You must be signed in to change notification settings - Fork 0
Master Sync #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Master Sync #1
Conversation
When optional capture groups don't match, matchIndices contains -1 values. Check indices are valid before slicing to avoid panic.
- Fix Headers map sharing between parent and subcontext using maps.Clone() - Fix Cookies slice sharing between parent and subcontext using slices.Clone() - Previously, when subcontext was freed, it would clear shared Headers map and Cookies slice, affecting parent context - Now each context has its own copy, preventing data corruption
- Implement http.Hijacker interface to support WebSocket upgrades - Pass through Hijack calls to underlying bodyWriter - Required for velaros WebSocket integration
WalkthroughContext was made thread-safe by adding a RWMutex and safe accessors; header/cookie cloning replaced manual copies; response writer gained HTTP Hijack support; pattern matching now guards against invalid slice indices to avoid out-of-bounds behavior. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
context.go (2)
118-147: Guard parent reads and deep-clone headers/cookies to avoid shared mutable state.
NewSubContextWithNodereadsctxfields and copiesHeaders/Cookieswithout locking, and the current cloning is shallow (header value slices and cookie pointers are still shared). That can race or bleed mutations across contexts.Consider taking an
RLockaround the snapshot and deep-cloning nested data.🔒 Suggested fix (snapshot under lock + deep clone)
func NewSubContextWithNode(ctx *Context, firstHandlerNode *HandlerNode) *Context { subContext := contextFromPool() + ctx.mu.RLock() + defer ctx.mu.RUnlock() + subContext.parentContext = ctx subContext.request = ctx.request subContext.method = ctx.method subContext.path = ctx.path for k, v := range ctx.params { subContext.params[k] = v } subContext.Status = ctx.Status - subContext.Headers = maps.Clone(ctx.Headers) - subContext.Cookies = slices.Clone(ctx.Cookies) + subContext.Headers = cloneHeader(ctx.Headers) + subContext.Cookies = cloneCookies(ctx.Cookies) subContext.Body = ctx.Body subContext.bodyWriter = ctx.bodyWriter subContext.hasWrittenHeaders = ctx.hasWrittenHeaders subContext.hasWrittenBody = ctx.hasWrittenBody @@ for k, v := range ctx.associatedValues { subContext.associatedValues[k] = v } subContext.deadline = ctx.deadline subContext.doneChannel = ctx.doneChannelAdditional helpers (outside this range):
func cloneHeader(h http.Header) http.Header { if h == nil { return nil } dst := make(http.Header, len(h)) for k, v := range h { vv := make([]string, len(v)) copy(vv, v) dst[k] = vv } return dst } func cloneCookies(src []*http.Cookie) []*http.Cookie { if src == nil { return nil } dst := make([]*http.Cookie, len(src)) for i, c := range src { if c != nil { cc := *c dst[i] = &cc } } return dst }
223-245: Update parent under lock and avoid shallow copies.
tryUpdateParentmutates the parent without locking and uses shallow copies for headers/cookies. If the parent context is read concurrently, this is a data race and can leak mutations across contexts.Snapshot
cunderRLock, then lockparentContextto apply the update, and reuse the deep-clone helpers.🔒 Suggested fix (snapshot then apply under parent lock)
func (c *Context) tryUpdateParent() { if c.parentContext == nil { return } + + c.mu.RLock() + status := c.Status + headers := cloneHeader(c.Headers) + cookies := cloneCookies(c.Cookies) + body := c.Body + hasWrittenHeaders := c.hasWrittenHeaders + hasWrittenBody := c.hasWrittenBody + maxBodySize := c.MaxRequestBodySize + err := c.Error + errStack := c.ErrorStack + finalErr := c.FinalError + finalErrStack := c.FinalErrorStack + reqUnmarshaller := c.requestBodyUnmarshaller + respMarshaller := c.responseBodyMarshaller + associated := make(map[string]any, len(c.associatedValues)) + for k, v := range c.associatedValues { + associated[k] = v + } + deadline := c.deadline + doneCh := c.doneChannel + c.mu.RUnlock() - c.parentContext.Status = c.Status - c.parentContext.Headers = maps.Clone(c.Headers) - c.parentContext.Cookies = slices.Clone(c.Cookies) - c.parentContext.Body = c.Body - c.parentContext.hasWrittenHeaders = c.hasWrittenHeaders - c.parentContext.hasWrittenBody = c.hasWrittenBody + p := c.parentContext + p.mu.Lock() + defer p.mu.Unlock() + + p.Status = status + p.Headers = headers + p.Cookies = cookies + p.Body = body + p.hasWrittenHeaders = hasWrittenHeaders + p.hasWrittenBody = hasWrittenBody - c.parentContext.MaxRequestBodySize = c.MaxRequestBodySize + p.MaxRequestBodySize = maxBodySize - c.parentContext.Error = c.Error - c.parentContext.ErrorStack = c.ErrorStack - c.parentContext.FinalError = c.FinalError - c.parentContext.FinalErrorStack = c.FinalErrorStack + p.Error = err + p.ErrorStack = errStack + p.FinalError = finalErr + p.FinalErrorStack = finalErrStack - c.parentContext.requestBodyUnmarshaller = c.requestBodyUnmarshaller - c.parentContext.responseBodyMarshaller = c.responseBodyMarshaller + p.requestBodyUnmarshaller = reqUnmarshaller + p.responseBodyMarshaller = respMarshaller - for k, v := range c.associatedValues { - c.parentContext.associatedValues[k] = v - } - c.parentContext.deadline = c.deadline - c.parentContext.doneChannel = c.doneChannel + for k, v := range associated { + p.associatedValues[k] = v + } + p.deadline = deadline + p.doneChannel = doneCh }
🤖 Fix all issues with AI agents
In `@context.go`:
- Around line 268-275: The MustGet method on Context currently returns nil when
a key is absent but should panic per its contract; update Context.MustGet to
check map membership after acquiring the read lock (use the comma-ok pattern on
c.associatedValues[key]) and if the key is missing call panic with a clear
message (e.g., include the missing key and Context type), otherwise return the
value; ensure you still use c.mu.RLock()/RUnlock() around the lookup and only
panic when the key is not found.
| // MustGet retrieves a value attached to the context with Set. It panics | ||
| // if the value does not exist. | ||
| // if the value does not exist. This method is thread-safe. | ||
| func (c *Context) MustGet(key string) any { | ||
| return c.associatedValues[key] | ||
| c.mu.RLock() | ||
| v := c.associatedValues[key] | ||
| c.mu.RUnlock() | ||
| return v | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MustGet doesn’t honor its panic-on-miss contract.
The doc says it panics if the key is missing, but the current implementation returns nil instead.
✅ Suggested fix
func (c *Context) MustGet(key string) any {
c.mu.RLock()
- v := c.associatedValues[key]
+ v, ok := c.associatedValues[key]
c.mu.RUnlock()
+ if !ok {
+ panic("context value not found: " + key)
+ }
return v
}🤖 Prompt for AI Agents
In `@context.go` around lines 268 - 275, The MustGet method on Context currently
returns nil when a key is absent but should panic per its contract; update
Context.MustGet to check map membership after acquiring the read lock (use the
comma-ok pattern on c.associatedValues[key]) and if the key is missing call
panic with a clear message (e.g., include the missing key and Context type),
otherwise return the value; ensure you still use c.mu.RLock()/RUnlock() around
the lookup and only panic when the key is not found.
Master Sync
Summary by CodeRabbit
New Features
Bug Fixes
Chores
✏️ Tip: You can customize this high-level summary in your review settings.