|
|
@@ -23,123 +23,12 @@ const corsHeaders = {
|
|
|
* Request and response bodies are proxied as-is.
|
|
|
*/
|
|
|
|
|
|
-/**
|
|
|
- * Makes an HTTP/1.0 request to ShopRenter API
|
|
|
- * This is required because ShopRenter has a bug where it only accepts HTTP/1.0
|
|
|
- */
|
|
|
-async function makeHttp10Request(
|
|
|
- urlString: string,
|
|
|
- method: string,
|
|
|
- headers: Headers,
|
|
|
- body: string | null
|
|
|
-): Promise<{ status: number; statusText: string; headers: Record<string, string>; body: string }> {
|
|
|
- const url = new URL(urlString)
|
|
|
- const hostname = url.hostname
|
|
|
- const port = url.port || '443'
|
|
|
- const path = url.pathname + url.search
|
|
|
-
|
|
|
- // Build HTTP/1.0 request
|
|
|
- let request = `${method} ${path} HTTP/1.0\r\n`
|
|
|
- request += `Host: ${hostname}\r\n`
|
|
|
-
|
|
|
- // Add all headers
|
|
|
- for (const [key, value] of headers.entries()) {
|
|
|
- request += `${key}: ${value}\r\n`
|
|
|
- }
|
|
|
-
|
|
|
- // Add Connection: close for HTTP/1.0
|
|
|
- request += `Connection: close\r\n`
|
|
|
- request += `\r\n`
|
|
|
-
|
|
|
- // Add body if present
|
|
|
- if (body) {
|
|
|
- request += body
|
|
|
- }
|
|
|
-
|
|
|
- console.log(`[HTTP/1.0] Connecting to ${hostname}:${port}`)
|
|
|
-
|
|
|
- // Connect to server (TLS for HTTPS)
|
|
|
- const conn = await Deno.connectTls({
|
|
|
- hostname: hostname,
|
|
|
- port: parseInt(port),
|
|
|
- })
|
|
|
-
|
|
|
- try {
|
|
|
- // Send request
|
|
|
- const encoder = new TextEncoder()
|
|
|
- await conn.write(encoder.encode(request))
|
|
|
-
|
|
|
- console.log('[HTTP/1.0] Request sent, reading response...')
|
|
|
-
|
|
|
- // Read response
|
|
|
- const decoder = new TextDecoder()
|
|
|
- const chunks: Uint8Array[] = []
|
|
|
- const buffer = new Uint8Array(4096)
|
|
|
-
|
|
|
- while (true) {
|
|
|
- const n = await conn.read(buffer)
|
|
|
- if (n === null) break // EOF
|
|
|
- chunks.push(buffer.slice(0, n))
|
|
|
- }
|
|
|
-
|
|
|
- // Combine all chunks
|
|
|
- const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0)
|
|
|
- const responseData = new Uint8Array(totalLength)
|
|
|
- let offset = 0
|
|
|
- for (const chunk of chunks) {
|
|
|
- responseData.set(chunk, offset)
|
|
|
- offset += chunk.length
|
|
|
- }
|
|
|
-
|
|
|
- const responseText = decoder.decode(responseData)
|
|
|
-
|
|
|
- // Parse HTTP response
|
|
|
- const lines = responseText.split('\r\n')
|
|
|
- const statusLine = lines[0]
|
|
|
- const statusMatch = statusLine.match(/HTTP\/1\.[01] (\d+) (.*)/)
|
|
|
-
|
|
|
- if (!statusMatch) {
|
|
|
- throw new Error(`Invalid HTTP response: ${statusLine}`)
|
|
|
- }
|
|
|
-
|
|
|
- const status = parseInt(statusMatch[1])
|
|
|
- const statusText = statusMatch[2]
|
|
|
-
|
|
|
- // Parse headers
|
|
|
- const responseHeaders: Record<string, string> = {}
|
|
|
- let i = 1
|
|
|
- for (; i < lines.length; i++) {
|
|
|
- const line = lines[i]
|
|
|
- if (line === '') break // End of headers
|
|
|
-
|
|
|
- const colonIndex = line.indexOf(':')
|
|
|
- if (colonIndex > 0) {
|
|
|
- const key = line.substring(0, colonIndex).trim()
|
|
|
- const value = line.substring(colonIndex + 1).trim()
|
|
|
- responseHeaders[key] = value
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // Get body (everything after empty line)
|
|
|
- const bodyStartIndex = responseText.indexOf('\r\n\r\n')
|
|
|
- const responseBody = bodyStartIndex >= 0 ? responseText.substring(bodyStartIndex + 4) : ''
|
|
|
-
|
|
|
- console.log(`[HTTP/1.0] Response received: ${status} ${statusText}`)
|
|
|
-
|
|
|
- return {
|
|
|
- status,
|
|
|
- statusText,
|
|
|
- headers: responseHeaders,
|
|
|
- body: responseBody,
|
|
|
- }
|
|
|
- } finally {
|
|
|
- try {
|
|
|
- conn.close()
|
|
|
- } catch (e) {
|
|
|
- console.error('[HTTP/1.0] Error closing connection:', e)
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
+// Create HTTP client that forces HTTP/1.1 only (no HTTP/2)
|
|
|
+// This is required because ShopRenter has issues with HTTP/2
|
|
|
+const httpClient = Deno.createHttpClient({
|
|
|
+ http1: true, // Enable HTTP/1.1
|
|
|
+ http2: false, // Disable HTTP/2
|
|
|
+})
|
|
|
|
|
|
serve(async (req) => {
|
|
|
// Handle CORS preflight
|
|
|
@@ -242,14 +131,17 @@ serve(async (req) => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // Make request to ShopRenter API using HTTP/1.0
|
|
|
- // ShopRenter API requires HTTP/1.0 specifically - higher versions cause auth errors
|
|
|
- const shopRenterResponse = await makeHttp10Request(
|
|
|
- shopRenterUrl,
|
|
|
- req.method,
|
|
|
- shopRenterHeaders,
|
|
|
- body
|
|
|
- )
|
|
|
+ // Make request to ShopRenter API using HTTP/1.1 only (HTTP/2 disabled)
|
|
|
+ // ShopRenter API requires HTTP/1.x - higher versions cause auth errors
|
|
|
+ console.log(`[ShopRenter Proxy] Making request with HTTP/1.1 to: ${shopRenterUrl}`)
|
|
|
+ console.log(`[ShopRenter Proxy] Headers:`, Object.fromEntries(shopRenterHeaders.entries()))
|
|
|
+
|
|
|
+ const shopRenterResponse = await fetch(shopRenterUrl, {
|
|
|
+ method: req.method,
|
|
|
+ headers: shopRenterHeaders,
|
|
|
+ body: body,
|
|
|
+ client: httpClient, // Use HTTP/1.1-only client
|
|
|
+ })
|
|
|
|
|
|
console.log(`[ShopRenter Proxy] ShopRenter API responded with status: ${shopRenterResponse.status}`)
|
|
|
|
|
|
@@ -257,7 +149,7 @@ serve(async (req) => {
|
|
|
const responseHeaders = new Headers(corsHeaders)
|
|
|
|
|
|
// Copy relevant headers from ShopRenter response
|
|
|
- for (const [key, value] of Object.entries(shopRenterResponse.headers)) {
|
|
|
+ for (const [key, value] of shopRenterResponse.headers.entries()) {
|
|
|
const lowerKey = key.toLowerCase()
|
|
|
// Copy headers but skip transfer-encoding and connection as they're handled by the Edge Function
|
|
|
if (lowerKey !== 'transfer-encoding' && lowerKey !== 'connection') {
|
|
|
@@ -265,9 +157,12 @@ serve(async (req) => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // Get response body
|
|
|
+ const responseBody = await shopRenterResponse.text()
|
|
|
+
|
|
|
// Return proxied response
|
|
|
return new Response(
|
|
|
- shopRenterResponse.body,
|
|
|
+ responseBody,
|
|
|
{
|
|
|
status: shopRenterResponse.status,
|
|
|
statusText: shopRenterResponse.statusText,
|