suppress_expired.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. package context
  2. import "context"
  3. // valueOnlyContext provides a utility to preserve only the values of a
  4. // Context. Suppressing any cancellation or deadline on that context being
  5. // propagated downstream of this value.
  6. //
  7. // If preserveExpiredValues is false (default), and the valueCtx is canceled,
  8. // calls to lookup values with the Values method, will always return nil. Setting
  9. // preserveExpiredValues to true, will allow the valueOnlyContext to lookup
  10. // values in valueCtx even if valueCtx is canceled.
  11. //
  12. // Based on the Go standard libraries net/lookup.go onlyValuesCtx utility.
  13. // https://github.com/golang/go/blob/da2773fe3e2f6106634673a38dc3a6eb875fe7d8/src/net/lookup.go
  14. type valueOnlyContext struct {
  15. context.Context
  16. preserveExpiredValues bool
  17. valuesCtx context.Context
  18. }
  19. var _ context.Context = (*valueOnlyContext)(nil)
  20. // Value looks up the key, returning its value. If configured to not preserve
  21. // values of expired context, and the wrapping context is canceled, nil will be
  22. // returned.
  23. func (v *valueOnlyContext) Value(key interface{}) interface{} {
  24. if !v.preserveExpiredValues {
  25. select {
  26. case <-v.valuesCtx.Done():
  27. return nil
  28. default:
  29. }
  30. }
  31. return v.valuesCtx.Value(key)
  32. }
  33. // WithSuppressCancel wraps the Context value, suppressing its deadline and
  34. // cancellation events being propagated downstream to consumer of the returned
  35. // context.
  36. //
  37. // By default the wrapped Context's Values are available downstream until the
  38. // wrapped Context is canceled. Once the wrapped Context is canceled, Values
  39. // method called on the context return will no longer lookup any key. As they
  40. // are now considered expired.
  41. //
  42. // To override this behavior, use WithPreserveExpiredValues on the Context
  43. // before it is wrapped by WithSuppressCancel. This will make the Context
  44. // returned by WithSuppressCancel allow lookup of expired values.
  45. func WithSuppressCancel(ctx context.Context) context.Context {
  46. return &valueOnlyContext{
  47. Context: context.Background(),
  48. valuesCtx: ctx,
  49. preserveExpiredValues: GetPreserveExpiredValues(ctx),
  50. }
  51. }
  52. type preserveExpiredValuesKey struct{}
  53. // WithPreserveExpiredValues adds a Value to the Context if expired values
  54. // should be preserved, and looked up by a Context wrapped by
  55. // WithSuppressCancel.
  56. //
  57. // WithPreserveExpiredValues must be added as a value to a Context, before that
  58. // Context is wrapped by WithSuppressCancel
  59. func WithPreserveExpiredValues(ctx context.Context, enable bool) context.Context {
  60. return context.WithValue(ctx, preserveExpiredValuesKey{}, enable)
  61. }
  62. // GetPreserveExpiredValues looks up, and returns the PreserveExpressValues
  63. // value in the context. Returning true if enabled, false otherwise.
  64. func GetPreserveExpiredValues(ctx context.Context) bool {
  65. v := ctx.Value(preserveExpiredValuesKey{})
  66. if v != nil {
  67. return v.(bool)
  68. }
  69. return false
  70. }