|
|
@@ -34,7 +34,7 @@ async function makeHttp11Request(
|
|
|
method: string,
|
|
|
headers: Record<string, string>,
|
|
|
body?: string
|
|
|
-): Promise<{ status: number; statusText: string; headers: Record<string, string>; body: string }> {
|
|
|
+): Promise<{ status: number; statusText: string; headers: Record<string, string>; body: Uint8Array }> {
|
|
|
// Open TLS connection
|
|
|
const conn = await Deno.connectTls({
|
|
|
hostname: hostname,
|
|
|
@@ -74,16 +74,21 @@ async function makeHttp11Request(
|
|
|
const encoder = new TextEncoder()
|
|
|
await conn.write(encoder.encode(request))
|
|
|
|
|
|
- // Read response
|
|
|
- const decoder = new TextDecoder()
|
|
|
- const buffer = new Uint8Array(1024 * 1024) // 1MB buffer
|
|
|
- let responseData = ''
|
|
|
+ // Read response as binary data to preserve gzip/compressed content
|
|
|
+ const chunks: Uint8Array[] = []
|
|
|
+ const buffer = new Uint8Array(64 * 1024) // 64KB buffer per chunk
|
|
|
+ let totalBytes = 0
|
|
|
|
|
|
while (true) {
|
|
|
try {
|
|
|
const n = await conn.read(buffer)
|
|
|
if (n === null) break
|
|
|
- responseData += decoder.decode(buffer.subarray(0, n))
|
|
|
+
|
|
|
+ // Copy the chunk to preserve it
|
|
|
+ const chunk = new Uint8Array(n)
|
|
|
+ chunk.set(buffer.subarray(0, n))
|
|
|
+ chunks.push(chunk)
|
|
|
+ totalBytes += n
|
|
|
} catch (e) {
|
|
|
// Handle UnexpectedEof during read - connection closed by server
|
|
|
// This is expected when server sends Connection: close
|
|
|
@@ -96,26 +101,57 @@ async function makeHttp11Request(
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- console.log('[HTTP/1.0] Received response length:', responseData.length)
|
|
|
- console.log('[HTTP/1.0] First 500 chars:', responseData.substring(0, 500))
|
|
|
+ console.log('[HTTP/1.0] Received response bytes:', totalBytes)
|
|
|
+
|
|
|
+ // Concatenate all chunks into a single Uint8Array
|
|
|
+ const responseData = new Uint8Array(totalBytes)
|
|
|
+ let offset = 0
|
|
|
+ for (const chunk of chunks) {
|
|
|
+ responseData.set(chunk, offset)
|
|
|
+ offset += chunk.length
|
|
|
+ }
|
|
|
+
|
|
|
+ // Parse HTTP response headers - we need to find the header/body separator
|
|
|
+ // Headers are always ASCII text, so we can decode them safely
|
|
|
+ const decoder = new TextDecoder()
|
|
|
|
|
|
- // Parse HTTP response - try both \r\n\r\n and \n\n separators
|
|
|
- let headerEndIndex = responseData.indexOf('\r\n\r\n')
|
|
|
- let headerSeparator = '\r\n\r\n'
|
|
|
+ // Search for \r\n\r\n or \n\n in the binary data
|
|
|
+ let headerEndIndex = -1
|
|
|
+ let headerSeparatorLength = 0
|
|
|
+
|
|
|
+ // Look for \r\n\r\n (most common)
|
|
|
+ for (let i = 0; i < responseData.length - 3; i++) {
|
|
|
+ if (responseData[i] === 13 && responseData[i + 1] === 10 &&
|
|
|
+ responseData[i + 2] === 13 && responseData[i + 3] === 10) {
|
|
|
+ headerEndIndex = i
|
|
|
+ headerSeparatorLength = 4
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
+ // If not found, look for \n\n
|
|
|
if (headerEndIndex === -1) {
|
|
|
- // Try Unix-style line endings
|
|
|
- headerEndIndex = responseData.indexOf('\n\n')
|
|
|
- headerSeparator = '\n\n'
|
|
|
+ for (let i = 0; i < responseData.length - 1; i++) {
|
|
|
+ if (responseData[i] === 10 && responseData[i + 1] === 10) {
|
|
|
+ headerEndIndex = i
|
|
|
+ headerSeparatorLength = 2
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
if (headerEndIndex === -1) {
|
|
|
- console.error('[HTTP/1.0] Could not find header separator. Response data:', responseData.substring(0, 200))
|
|
|
+ const preview = decoder.decode(responseData.subarray(0, Math.min(200, responseData.length)))
|
|
|
+ console.error('[HTTP/1.0] Could not find header separator. Response preview:', preview)
|
|
|
throw new Error('Invalid HTTP response: no header/body separator found')
|
|
|
}
|
|
|
|
|
|
- const headerSection = responseData.substring(0, headerEndIndex)
|
|
|
- const bodySection = responseData.substring(headerEndIndex + headerSeparator.length)
|
|
|
+ // Decode headers only (they're ASCII)
|
|
|
+ const headerBytes = responseData.subarray(0, headerEndIndex)
|
|
|
+ const headerSection = decoder.decode(headerBytes)
|
|
|
+
|
|
|
+ // Body is everything after the separator (keep as binary)
|
|
|
+ const bodyBytes = responseData.subarray(headerEndIndex + headerSeparatorLength)
|
|
|
|
|
|
// Parse status line
|
|
|
const lines = headerSection.split(/\r?\n/)
|
|
|
@@ -142,12 +178,14 @@ async function makeHttp11Request(
|
|
|
}
|
|
|
|
|
|
console.log('[HTTP/1.0] Response received:', status, statusText)
|
|
|
+ console.log('[HTTP/1.0] Content-Encoding:', responseHeaders['Content-Encoding'] || 'none')
|
|
|
+ console.log('[HTTP/1.0] Body size:', bodyBytes.length, 'bytes')
|
|
|
|
|
|
return {
|
|
|
status,
|
|
|
statusText,
|
|
|
headers: responseHeaders,
|
|
|
- body: bodySection,
|
|
|
+ body: bodyBytes,
|
|
|
}
|
|
|
} finally {
|
|
|
try {
|
|
|
@@ -310,7 +348,8 @@ serve(async (req) => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // Return proxied response
|
|
|
+ // Return proxied response with binary body
|
|
|
+ // The body is returned as Uint8Array to preserve gzip/compressed content
|
|
|
return new Response(
|
|
|
shopRenterResponse.body,
|
|
|
{
|