Skip to content

Latest commit

 

History

History
143 lines (103 loc) · 5.02 KB

File metadata and controls

143 lines (103 loc) · 5.02 KB

If-Match Header Support for Update Operations

This document describes the If-Match header support that has been added to all update endpoints in the AEPC bookstore example.

Overview

The If-Match header provides optimistic concurrency control for update operations. When provided, the server validates that the current resource matches the expected ETag before performing the update. If the ETags don't match, the update is rejected with a 412 Precondition Failed status.

Features

Supported Operations

The If-Match header is supported for all update operations:

  • UpdateBook
  • UpdatePublisher
  • UpdateStore
  • UpdateItem

ETag Generation

ETags are generated by:

  1. Serializing the protobuf message using proto.Marshal
  2. Computing an MD5 hash of the serialized data
  3. Encoding the hash as a hexadecimal string
  4. Wrapping in quotes (e.g., "a1b2c3d4...")

Header Processing

The grpc-gateway is configured to forward the If-Match HTTP header to gRPC metadata:

  • HTTP header: If-Match: "etag-value"
  • gRPC metadata: grpcgateway-if-match: "etag-value"

Usage

HTTP API

# Get a resource to obtain its current state
GET /publishers/1/books/1

# Update with If-Match header
PATCH /publishers/1/books/1
If-Match: "current-etag-value"
Content-Type: application/json

{
  "book": {
    "price": 30,
    "published": true,
    "edition": 2
  }
}

Response Codes

  • 200 OK: Update successful with valid If-Match header
  • 412 Precondition Failed: If-Match header value doesn't match current resource ETag
  • 404 Not Found: Resource doesn't exist
  • No If-Match header: Update proceeds normally (backwards compatible)

gRPC API

The If-Match header is automatically extracted from gRPC metadata by the service methods. No additional client configuration is required when using grpc-gateway.

Implementation Details

Core Components

  1. ETag Generation (types.go):

    • GenerateETag(msg proto.Message): Creates ETag from protobuf message
    • ValidateETag(provided, current string): Compares ETags with quote handling
  2. Header Extraction (service.go):

    • extractIfMatchHeader(ctx context.Context): Extracts If-Match from gRPC metadata
  3. Gateway Configuration (gateway.go):

    • Custom header matcher forwards If-Match header to grpcgateway-if-match metadata
  4. Update Methods: All update methods now:

    • Extract If-Match header from context
    • Fetch current resource if header is present
    • Generate ETag for current resource
    • Validate provided ETag against current ETag
    • Reject with FailedPrecondition if validation fails
    • Proceed with normal update logic if validation passes

Error Handling

  • Missing Resource: Returns NotFound when trying to validate ETag for non-existent resource
  • ETag Mismatch: Returns FailedPrecondition when If-Match header doesn't match current ETag
  • ETag Generation Failure: Returns Internal if ETag generation fails
  • No If-Match Header: Proceeds normally for backwards compatibility

Testing

Unit Tests

The implementation includes comprehensive unit tests:

  • TestUpdateBookWithIfMatchHeader: Tests successful and failed ETag validation
  • TestUpdatePublisherWithIfMatchHeader: Tests publisher-specific ETag handling
  • TestETagGeneration: Tests ETag generation and validation logic

Test Coverage

Tests verify:

  • ✅ Updates succeed with correct If-Match header
  • ✅ Updates fail with incorrect If-Match header (412 status)
  • ✅ Updates succeed without If-Match header (backwards compatibility)
  • ✅ Updates fail for non-existent resources (404 status)
  • ✅ ETag generation produces consistent results for identical content
  • ✅ ETag generation produces different results for different content
  • ✅ ETag validation handles quoted and unquoted ETags correctly

Integration Testing

An integration test script is provided (test_if_match_integration.sh) that demonstrates:

  • End-to-end HTTP API functionality
  • Proper error codes for failed preconditions
  • Complete workflow from resource creation to ETag-validated updates

Security Considerations

  • ETags are deterministic based on resource content
  • ETags do not expose sensitive information (they are content hashes)
  • No additional authentication is required beyond existing API security
  • ETag validation happens before database operations, preventing unnecessary writes

Performance Notes

  • ETag generation requires serializing and hashing the resource
  • Validation requires fetching the current resource before updating
  • Impact is minimal for typical update operations
  • No additional database operations beyond the standard Get/Update pattern
  • ETags are computed on-demand and not stored in the database

Backwards Compatibility

The If-Match header support is fully backwards compatible:

  • Existing clients without If-Match headers continue to work unchanged
  • No changes to existing API contracts or response formats
  • No new required fields in resources themselves
  • All functionality is implemented via HTTP headers and gRPC sidechannel metadata