|
17 | 17 | import httpx |
18 | 18 | import json |
19 | 19 | import logging |
20 | | -from botocore.auth import SigV4Auth |
21 | | -from botocore.awsrequest import AWSRequest |
22 | 20 | from httpx._content import ByteStream |
23 | 21 | from typing import Any, Dict, Optional |
24 | 22 |
|
@@ -76,73 +74,50 @@ async def _handle_error_response(response: httpx.Response) -> None: |
76 | 74 | ) |
77 | 75 |
|
78 | 76 |
|
79 | | -def _resign_request_with_sigv4( |
80 | | - request: httpx.Request, |
| 77 | +async def _sign_request_hook( |
81 | 78 | region: str, |
82 | 79 | service: str, |
83 | | - profile: Optional[str] = None, |
| 80 | + profile: Optional[str], |
| 81 | + request: httpx.Request, |
84 | 82 | ) -> None: |
85 | | - """Re-sign an HTTP request with AWS SigV4 after content modification. |
| 83 | + """Request hook to sign HTTP requests with AWS SigV4. |
86 | 84 |
|
87 | | - This function removes old signature headers, creates a new signature based on |
88 | | - the current request content, and updates the request headers with the new signature. |
| 85 | + This hook signs the request with AWS SigV4 credentials and adds signature headers. |
| 86 | +
|
| 87 | + This should be the last hook called to ensure the signature includes any modifications. |
89 | 88 |
|
90 | 89 | Args: |
91 | | - request: The HTTP request object to re-sign (modified in-place) |
92 | 90 | region: AWS region for SigV4 signing |
93 | 91 | service: AWS service name for SigV4 signing |
94 | 92 | profile: AWS profile to use (optional) |
| 93 | + request: The HTTP request object to sign (modified in-place) |
95 | 94 | """ |
96 | | - # Import here to avoid circular dependency |
97 | | - from mcp_proxy_for_aws.sigv4_helper import create_aws_session |
98 | | - |
99 | | - # Remove old signature headers before re-signing |
100 | | - headers_to_remove = ['Content-Length', 'x-amz-date', 'x-amz-security-token', 'authorization'] |
101 | | - for header in headers_to_remove: |
102 | | - request.headers.pop(header, None) |
| 95 | + # Import here to avoid circular dependency and for compatibility |
| 96 | + from mcp_proxy_for_aws.sigv4_helper import SigV4HTTPXAuth, create_aws_session |
103 | 97 |
|
104 | | - # Set the new Content-Length |
| 98 | + # Set Content-Length for signing |
105 | 99 | request.headers['Content-Length'] = str(len(request.content)) |
106 | 100 |
|
107 | | - logger.info('Headers after cleanup: %s', request.headers) |
108 | | - |
109 | 101 | # Get AWS credentials |
110 | 102 | session = create_aws_session(profile) |
111 | 103 | credentials = session.get_credentials() |
112 | | - logger.info('Re-signing request with credentials for access key: %s', credentials.access_key) |
113 | | - |
114 | | - # Create headers dict for signing, removing connection header like in auth_flow |
115 | | - headers_for_signing = dict(request.headers) |
116 | | - headers_for_signing.pop('connection', None) # Remove connection header for signing |
| 104 | + logger.info('Signing request with credentials for access key: %s', credentials.access_key) |
117 | 105 |
|
118 | | - # Create SigV4 signer and AWS request |
119 | | - signer = SigV4Auth(credentials, service, region) |
120 | | - aws_request = AWSRequest( |
121 | | - method=request.method, |
122 | | - url=str(request.url), |
123 | | - data=request.content, |
124 | | - headers=headers_for_signing, |
125 | | - ) |
| 106 | + # Create SigV4 auth and use its signing logic |
| 107 | + auth = SigV4HTTPXAuth(credentials, service, region) |
126 | 108 |
|
127 | | - # Sign the request |
128 | | - logger.info('AWS request before signing: %s', aws_request.headers) |
129 | | - signer.add_auth(aws_request) |
130 | | - logger.info('AWS request after signing: %s', aws_request.headers) |
| 109 | + # Call auth_flow to sign the request (it modifies request in-place) |
| 110 | + auth_flow = auth.auth_flow(request) |
| 111 | + next(auth_flow) # Execute the generator to perform signing |
131 | 112 |
|
132 | | - # Update request headers with signed headers |
133 | | - request.headers.update(dict(aws_request.headers)) |
134 | | - logger.info('Request headers after re-signing: %s', request.headers) |
| 113 | + logger.debug('Request headers after signing: %s', request.headers) |
135 | 114 |
|
136 | 115 |
|
137 | | -async def _inject_metadata_hook( |
138 | | - metadata: Dict[str, Any], region: str, service: str, request: httpx.Request |
139 | | -) -> None: |
| 116 | +async def _inject_metadata_hook(metadata: Dict[str, Any], request: httpx.Request) -> None: |
140 | 117 | """Request hook to inject metadata into MCP calls. |
141 | 118 |
|
142 | 119 | Args: |
143 | 120 | metadata: Dictionary of metadata to inject into _meta field |
144 | | - region: AWS region for SigV4 re-signing after metadata injection |
145 | | - service: AWS service name for SigV4 re-signing after metadata injection |
146 | 121 | request: The HTTP request object |
147 | 122 | """ |
148 | 123 | logger.info('=== Outgoing Request ===') |
@@ -189,9 +164,6 @@ async def _inject_metadata_hook( |
189 | 164 | request.stream = ByteStream(new_content) |
190 | 165 | request._content = new_content |
191 | 166 |
|
192 | | - # Re-sign the request with the new content |
193 | | - _resign_request_with_sigv4(request, region, service) |
194 | | - |
195 | 167 | logger.info('Injected metadata into _meta: %s', body['params']['_meta']) |
196 | 168 |
|
197 | 169 | except (json.JSONDecodeError, KeyError, TypeError) as e: |
|
0 commit comments