@@ -154,20 +154,95 @@ enum PackageController {
154154 ) . encodeResponse ( for: req)
155155 }
156156
157+ // Remove cookies from response headers to prevent "upstream sent too big header" errors
158+ var headers = req. headers
159+ headers. remove ( name: . cookie)
160+ headers. replaceOrAdd ( name: . contentType, value: route. contentType)
161+
157162 return try await processor. processedPage. encodeResponse (
158163 status: . ok,
159- headers: req. headers. replacingOrAdding ( name: . contentType,
160- value: route. contentType) ,
164+ headers: headers,
161165 for: req
162166 )
163167 }
164168
165169 static func awsResponse( client: Client, route: DocRoute) async throws -> HTTPClient. Response {
166170 @Dependency ( \. httpClient) var httpClient
171+ @Dependency ( \. environment) var environment
172+ @Dependency ( \. s3) var s3
173+
174+ // Check if direct S3 access is enabled
175+ if environment. awsDirectS3Access ( ) {
176+ guard let bucket = environment. awsDocsBucket ( ) else {
177+ throw AppError . envVariableNotSet ( " AWS_DOCS_BUCKET " )
178+ }
179+
180+ // Construct the S3 key path - try exact path first
181+ let primaryS3Key : String
182+ if route. path. isEmpty {
183+ primaryS3Key = " \( route. baseURL) / \( route. fragment. urlFragment) "
184+ } else {
185+ primaryS3Key = " \( route. baseURL) / \( route. fragment. urlFragment) / \( route. path) "
186+ }
187+ do {
188+ let data = try await s3. fetchDocumentation ( bucket, primaryS3Key)
189+
190+ // Create a mock HTTPClient.Response with the S3 data
191+ let response = HTTPClient . Response (
192+ host: bucket,
193+ status: . ok,
194+ version: . http1_1,
195+ headers: HTTPHeaders ( [
196+ ( " content-type " , route. fragment. contentType) ,
197+ ( " content-length " , " \( data. count) " )
198+ ] ) ,
199+ body: ByteBuffer ( data: data)
200+ )
201+ return response
202+ } catch {
203+ // If the primary key fails and this is a documentation/tutorials request,
204+ // try appending /index.html as a fallback
205+ if ( route. fragment == . documentation || route. fragment == . tutorials) {
206+ let fallbackS3Key = route. path. isEmpty ?
207+ " \( route. baseURL) /index.html " :
208+ " \( route. baseURL) / \( route. fragment. urlFragment) / \( route. path) /index.html "
209+ do {
210+ let data = try await s3. fetchDocumentation ( bucket, fallbackS3Key)
211+
212+ let response = HTTPClient . Response (
213+ host: bucket,
214+ status: . ok,
215+ version: . http1_1,
216+ headers: HTTPHeaders ( [
217+ ( " content-type " , route. fragment. contentType) ,
218+ ( " content-length " , " \( data. count) " )
219+ ] ) ,
220+ body: ByteBuffer ( data: data)
221+ )
222+ return response
223+ } catch let fallbackError {
224+ throw Abort ( . notFound)
225+ }
226+ } else {
227+ throw Abort ( . notFound)
228+ }
229+ }
230+ }
167231
232+ // Fallback to existing HTTP-based approach
168233 let url = try Self . awsDocumentationURL ( route: route)
169- guard let response = try ? await httpClient. fetchDocumentation ( url) else {
170- throw Abort ( . notFound)
234+
235+ let response : HTTPClient . Response
236+ if environment. awsUseIamRole ( ) {
237+ guard let iamResponse = try ? await httpClient. fetchDocumentationWithIAM ( url) else {
238+ throw Abort ( . notFound)
239+ }
240+ response = iamResponse
241+ } else {
242+ guard let standardResponse = try ? await httpClient. fetchDocumentation ( url) else {
243+ throw Abort ( . notFound)
244+ }
245+ response = standardResponse
171246 }
172247 guard ( 200 ..< 399 ) . contains ( response. status. code) else {
173248 // Convert anything that isn't a 2xx or 3xx from AWS into a 404 from us.
@@ -307,7 +382,7 @@ enum PackageController {
307382
308383 do {
309384 @Dependency ( \. s3) var s3
310- let readme = try await s3. fetchReadme ( owner, repository)
385+ let readme = try await s3. fetchReadme ( owner. lowercased ( ) , repository. lowercased ( ) )
311386 guard let branch = pkg. repository? . defaultBranch else {
312387 return PackageReadme . View ( model: . cacheLookupFailed( url: readmeHtmlUrl) ) . document ( )
313388 }
@@ -443,12 +518,17 @@ extension PackageController {
443518extension PackageController {
444519 static func awsDocumentationURL( route: DocRoute ) throws -> URI {
445520 @Dependency ( \. environment) var environment
521+
446522 guard let bucket = environment. awsDocsBucket ( ) else {
447523 throw AppError . envVariableNotSet ( " AWS_DOCS_BUCKET " )
448524 }
449525
450- let baseURLHost = " \( bucket) .s3-website.us-east-2.amazonaws.com "
451- let baseURL = " http:// \( baseURLHost) / \( route. baseURL) "
526+ // Use the correct region for the docs bucket
527+ let region = environment. awsDocsBucketRegion ( ) ?? environment. awsRegion ( ) ?? " us-east-2 "
528+ let baseURLHost = " \( bucket) .s3-website. \( region) .amazonaws.com "
529+ let urlScheme = " http "
530+
531+ let baseURL = " \( urlScheme) :// \( baseURLHost) / \( route. baseURL) "
452532 let path = route. path
453533
454534 switch route. fragment {
0 commit comments