phase 4: connectrpc on the gateway authenticated edge
Replace the native-gRPC server bootstrap with a single `connectrpc.com/connect` HTTP/h2c listener. Connect-Go natively serves Connect, gRPC, and gRPC-Web on the same port, so browsers can now reach the authenticated surface without giving up the gRPC framing native and desktop clients may use later. The decorator stack (envelope → session → payload-hash → signature → freshness/replay → rate-limit → routing/push) is reused unchanged behind a small Connect → gRPC adapter and a `grpc.ServerStream` shim around `*connect.ServerStream`. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -3,8 +3,6 @@ package grpcapi
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"galaxy/gateway/internal/config"
|
||||
"galaxy/gateway/internal/ratelimit"
|
||||
@@ -13,7 +11,6 @@ import (
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/peer"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
@@ -41,7 +38,7 @@ var (
|
||||
ErrAuthenticatedPolicyUnavailable = errors.New("authenticated request policy is unavailable")
|
||||
)
|
||||
|
||||
// AuthenticatedRequestLimiter applies authenticated gRPC rate-limit policy to
|
||||
// AuthenticatedRequestLimiter applies authenticated edge rate-limit policy to
|
||||
// one concrete bucket key.
|
||||
type AuthenticatedRequestLimiter interface {
|
||||
// Reserve evaluates key under policy and reports whether the request may
|
||||
@@ -52,10 +49,11 @@ type AuthenticatedRequestLimiter interface {
|
||||
// AuthenticatedRequest describes the authenticated request metadata exposed to
|
||||
// the edge-policy hook.
|
||||
type AuthenticatedRequest struct {
|
||||
// RPCMethod identifies the public gRPC method being processed.
|
||||
// RPCMethod identifies the public RPC method being processed.
|
||||
RPCMethod string
|
||||
|
||||
// PeerIP is the transport peer IP derived from the gRPC connection.
|
||||
// PeerIP is the transport peer IP host part derived from the
|
||||
// authenticated edge HTTP listener peer address.
|
||||
PeerIP string
|
||||
|
||||
// MessageClass is the stable rate-limit and policy class. The gateway uses
|
||||
@@ -258,23 +256,21 @@ func authenticatedMessageClass(messageType string) string {
|
||||
return messageType
|
||||
}
|
||||
|
||||
type peerIPContextKey struct{}
|
||||
|
||||
// contextWithPeerIP attaches the authenticated edge transport peer IP to ctx.
|
||||
// It is set by the transport interceptor before the service decorator stack
|
||||
// runs, and read back via peerIPFromContext.
|
||||
func contextWithPeerIP(ctx context.Context, ip string) context.Context {
|
||||
return context.WithValue(ctx, peerIPContextKey{}, ip)
|
||||
}
|
||||
|
||||
func peerIPFromContext(ctx context.Context) string {
|
||||
peerInfo, ok := peer.FromContext(ctx)
|
||||
if !ok || peerInfo.Addr == nil {
|
||||
return unknownAuthenticatedPeerIP
|
||||
if ip, ok := ctx.Value(peerIPContextKey{}).(string); ok && ip != "" {
|
||||
return ip
|
||||
}
|
||||
|
||||
value := strings.TrimSpace(peerInfo.Addr.String())
|
||||
if value == "" {
|
||||
return unknownAuthenticatedPeerIP
|
||||
}
|
||||
|
||||
host, _, err := net.SplitHostPort(value)
|
||||
if err == nil && host != "" {
|
||||
return host
|
||||
}
|
||||
|
||||
return value
|
||||
return unknownAuthenticatedPeerIP
|
||||
}
|
||||
|
||||
type noopAuthenticatedRequestPolicy struct{}
|
||||
|
||||
Reference in New Issue
Block a user