diff --git a/full_diagnosis.js b/full_diagnosis.js new file mode 100644 index 0000000..ec6a85d --- /dev/null +++ b/full_diagnosis.js @@ -0,0 +1,311 @@ +const io = require('socket.io-client'); +const https = require('https'); +const http = require('http'); + +console.log('🔍 全面WebSocket连接诊断'); +console.log('='.repeat(60)); + +// 1. 测试基础网络连接 +async function testBasicConnection() { + console.log('\n1️⃣ 测试基础HTTPS连接...'); + + return new Promise((resolve) => { + const options = { + hostname: 'whaletownend.xinghangee.icu', + port: 443, + path: '/', + method: 'GET', + timeout: 10000 + }; + + const req = https.request(options, (res) => { + console.log(`✅ HTTPS连接成功 - 状态码: ${res.statusCode}`); + console.log(`📋 服务器: ${res.headers.server || '未知'}`); + resolve({ success: true, statusCode: res.statusCode }); + }); + + req.on('error', (error) => { + console.log(`❌ HTTPS连接失败: ${error.message}`); + resolve({ success: false, error: error.message }); + }); + + req.on('timeout', () => { + console.log('❌ HTTPS连接超时'); + req.destroy(); + resolve({ success: false, error: 'timeout' }); + }); + + req.end(); + }); +} + +// 2. 测试本地服务器 +async function testLocalServer() { + console.log('\n2️⃣ 测试本地服务器...'); + + const testPaths = [ + 'http://localhost:3000/', + 'http://localhost:3000/socket.io/?EIO=4&transport=polling' + ]; + + for (const url of testPaths) { + console.log(`🧪 测试: ${url}`); + + await new Promise((resolve) => { + const urlObj = new URL(url); + const options = { + hostname: urlObj.hostname, + port: urlObj.port, + path: urlObj.pathname + urlObj.search, + method: 'GET', + timeout: 5000 + }; + + const req = http.request(options, (res) => { + console.log(` 状态码: ${res.statusCode}`); + if (res.statusCode === 200) { + console.log(' ✅ 本地服务器正常'); + } else { + console.log(` ⚠️ 本地服务器响应: ${res.statusCode}`); + } + resolve(); + }); + + req.on('error', (error) => { + console.log(` ❌ 本地服务器连接失败: ${error.message}`); + resolve(); + }); + + req.on('timeout', () => { + console.log(' ❌ 本地服务器超时'); + req.destroy(); + resolve(); + }); + + req.end(); + }); + } +} + +// 3. 测试远程Socket.IO路径 +async function testRemoteSocketIO() { + console.log('\n3️⃣ 测试远程Socket.IO路径...'); + + const testPaths = [ + '/socket.io/?EIO=4&transport=polling', + '/game/socket.io/?EIO=4&transport=polling', + '/socket.io/?transport=polling', + '/api/socket.io/?EIO=4&transport=polling' + ]; + + const results = []; + + for (const path of testPaths) { + console.log(`🧪 测试路径: ${path}`); + + const result = await new Promise((resolve) => { + const options = { + hostname: 'whaletownend.xinghangee.icu', + port: 443, + path: path, + method: 'GET', + timeout: 8000, + headers: { + 'User-Agent': 'socket.io-diagnosis' + } + }; + + const req = https.request(options, (res) => { + console.log(` 状态码: ${res.statusCode}`); + + let data = ''; + res.on('data', (chunk) => { + data += chunk; + }); + + res.on('end', () => { + if (res.statusCode === 200) { + console.log(' ✅ 路径可用'); + console.log(` 📄 响应: ${data.substring(0, 50)}...`); + } else { + console.log(` ❌ 路径不可用: ${res.statusCode}`); + } + resolve({ path, statusCode: res.statusCode, success: res.statusCode === 200 }); + }); + }); + + req.on('error', (error) => { + console.log(` ❌ 请求失败: ${error.message}`); + resolve({ path, error: error.message, success: false }); + }); + + req.on('timeout', () => { + console.log(' ❌ 请求超时'); + req.destroy(); + resolve({ path, error: 'timeout', success: false }); + }); + + req.end(); + }); + + results.push(result); + } + + return results; +} + +// 4. 测试Socket.IO客户端连接 +async function testSocketIOClient() { + console.log('\n4️⃣ 测试Socket.IO客户端连接...'); + + const configs = [ + { + name: 'HTTPS + 所有传输方式', + url: 'https://whaletownend.xinghangee.icu', + options: { transports: ['websocket', 'polling'], timeout: 10000 } + }, + { + name: 'HTTPS + 仅Polling', + url: 'https://whaletownend.xinghangee.icu', + options: { transports: ['polling'], timeout: 10000 } + }, + { + name: 'HTTPS + /game namespace', + url: 'https://whaletownend.xinghangee.icu/game', + options: { transports: ['polling'], timeout: 10000 } + } + ]; + + const results = []; + + for (const config of configs) { + console.log(`🧪 测试: ${config.name}`); + console.log(` URL: ${config.url}`); + + const result = await new Promise((resolve) => { + const socket = io(config.url, config.options); + let resolved = false; + + const timeout = setTimeout(() => { + if (!resolved) { + resolved = true; + socket.disconnect(); + console.log(' ❌ 连接超时'); + resolve({ success: false, error: 'timeout' }); + } + }, config.options.timeout); + + socket.on('connect', () => { + if (!resolved) { + resolved = true; + clearTimeout(timeout); + console.log(' ✅ 连接成功'); + console.log(` 📡 Socket ID: ${socket.id}`); + console.log(` 🚀 传输方式: ${socket.io.engine.transport.name}`); + socket.disconnect(); + resolve({ success: true, transport: socket.io.engine.transport.name }); + } + }); + + socket.on('connect_error', (error) => { + if (!resolved) { + resolved = true; + clearTimeout(timeout); + console.log(` ❌ 连接失败: ${error.message}`); + resolve({ success: false, error: error.message }); + } + }); + }); + + results.push({ config: config.name, ...result }); + + // 等待1秒再测试下一个 + await new Promise(resolve => setTimeout(resolve, 1000)); + } + + return results; +} + +// 5. 检查DNS解析 +async function testDNS() { + console.log('\n5️⃣ 检查DNS解析...'); + + const dns = require('dns'); + + return new Promise((resolve) => { + dns.lookup('whaletownend.xinghangee.icu', (err, address, family) => { + if (err) { + console.log(`❌ DNS解析失败: ${err.message}`); + resolve({ success: false, error: err.message }); + } else { + console.log(`✅ DNS解析成功: ${address} (IPv${family})`); + resolve({ success: true, address, family }); + } + }); + }); +} + +// 主诊断函数 +async function runFullDiagnosis() { + console.log('开始全面诊断...\n'); + + try { + const dnsResult = await testDNS(); + const basicResult = await testBasicConnection(); + await testLocalServer(); + const socketIOPaths = await testRemoteSocketIO(); + const clientResults = await testSocketIOClient(); + + console.log('\n' + '='.repeat(60)); + console.log('📊 诊断结果汇总'); + console.log('='.repeat(60)); + + console.log(`1. DNS解析: ${dnsResult.success ? '✅ 正常' : '❌ 失败'}`); + if (dnsResult.address) { + console.log(` IP地址: ${dnsResult.address}`); + } + + console.log(`2. HTTPS连接: ${basicResult.success ? '✅ 正常' : '❌ 失败'}`); + if (basicResult.error) { + console.log(` 错误: ${basicResult.error}`); + } + + const workingPaths = socketIOPaths.filter(r => r.success); + console.log(`3. Socket.IO路径: ${workingPaths.length}/${socketIOPaths.length} 个可用`); + workingPaths.forEach(p => { + console.log(` ✅ ${p.path}`); + }); + + const workingClients = clientResults.filter(r => r.success); + console.log(`4. Socket.IO客户端: ${workingClients.length}/${clientResults.length} 个成功`); + workingClients.forEach(c => { + console.log(` ✅ ${c.config} (${c.transport})`); + }); + + console.log('\n💡 建议:'); + + if (!dnsResult.success) { + console.log('❌ DNS解析失败 - 检查域名配置'); + } else if (!basicResult.success) { + console.log('❌ 基础HTTPS连接失败 - 检查服务器状态和防火墙'); + } else if (workingPaths.length === 0) { + console.log('❌ 所有Socket.IO路径都不可用 - 检查nginx配置和后端服务'); + } else if (workingClients.length === 0) { + console.log('❌ Socket.IO客户端无法连接 - 可能是CORS或协议问题'); + } else { + console.log('✅ 部分功能正常 - 使用可用的配置继续开发'); + + if (workingClients.length > 0) { + const bestConfig = workingClients.find(c => c.transport === 'websocket') || workingClients[0]; + console.log(`💡 推荐使用: ${bestConfig.config}`); + } + } + + } catch (error) { + console.error('诊断过程中发生错误:', error); + } + + process.exit(0); +} + +runFullDiagnosis(); \ No newline at end of file diff --git a/test_protocol_difference.js b/test_protocol_difference.js new file mode 100644 index 0000000..ebcc582 --- /dev/null +++ b/test_protocol_difference.js @@ -0,0 +1,117 @@ +const io = require('socket.io-client'); + +console.log('🔍 测试不同WebSocket协议的差异'); +console.log('='.repeat(50)); + +async function testProtocol(name, url, options) { + console.log(`\n🧪 测试: ${name}`); + console.log(`📡 URL: ${url}`); + + return new Promise((resolve) => { + const socket = io(url, options); + let resolved = false; + + const timeout = setTimeout(() => { + if (!resolved) { + resolved = true; + socket.disconnect(); + console.log(' ❌ 连接超时'); + resolve({ success: false, error: 'timeout' }); + } + }, 8000); + + socket.on('connect', () => { + if (!resolved) { + resolved = true; + clearTimeout(timeout); + console.log(' ✅ 连接成功'); + console.log(` 📡 Socket ID: ${socket.id}`); + console.log(` 🚀 传输方式: ${socket.io.engine.transport.name}`); + console.log(` 🔗 实际URL: ${socket.io.uri}`); + + socket.disconnect(); + resolve({ + success: true, + transport: socket.io.engine.transport.name, + actualUrl: socket.io.uri + }); + } + }); + + socket.on('connect_error', (error) => { + if (!resolved) { + resolved = true; + clearTimeout(timeout); + console.log(` ❌ 连接失败: ${error.message}`); + console.log(` 🔍 错误类型: ${error.type || 'unknown'}`); + resolve({ success: false, error: error.message, type: error.type }); + } + }); + }); +} + +async function runProtocolTests() { + const tests = [ + { + name: 'WS协议 (错误方式)', + url: 'ws://whaletownend.xinghangee.icu/game', + options: { transports: ['websocket'], timeout: 5000 } + }, + { + name: 'WSS协议 (直接指定)', + url: 'wss://whaletownend.xinghangee.icu/game', + options: { transports: ['websocket'], timeout: 5000 } + }, + { + name: 'HTTPS协议 (推荐方式)', + url: 'https://whaletownend.xinghangee.icu/game', + options: { transports: ['websocket', 'polling'], timeout: 5000 } + }, + { + name: 'HTTP协议 (本地测试)', + url: 'http://localhost:3000/game', + options: { transports: ['websocket', 'polling'], timeout: 5000 } + } + ]; + + const results = []; + + for (const test of tests) { + const result = await testProtocol(test.name, test.url, test.options); + results.push({ ...test, result }); + + // 等待1秒再测试下一个 + await new Promise(resolve => setTimeout(resolve, 1000)); + } + + console.log('\n' + '='.repeat(50)); + console.log('📊 协议测试结果对比'); + console.log('='.repeat(50)); + + results.forEach((test, index) => { + const status = test.result.success ? '✅ 成功' : '❌ 失败'; + const transport = test.result.transport ? ` (${test.result.transport})` : ''; + const error = test.result.error ? ` - ${test.result.error}` : ''; + + console.log(`${index + 1}. ${test.name}: ${status}${transport}${error}`); + + if (test.result.actualUrl) { + console.log(` 实际连接: ${test.result.actualUrl}`); + } + }); + + console.log('\n💡 协议选择建议:'); + console.log('✅ 推荐: 使用 https:// 让Socket.IO自动处理协议选择'); + console.log('⚠️ 避免: 直接使用 ws:// 或 wss://,容易出错'); + console.log('🔧 本地: 使用 http:// 进行本地开发测试'); + + console.log('\n📚 协议说明:'); + console.log('• ws:// - WebSocket over HTTP (明文传输)'); + console.log('• wss:// - WebSocket over HTTPS (加密传输)'); + console.log('• http:// → Socket.IO自动选择 ws:// 或 polling'); + console.log('• https:// → Socket.IO自动选择 wss:// 或 polling'); + + process.exit(0); +} + +runProtocolTests().catch(console.error); \ No newline at end of file diff --git a/test_redirect_and_websocket.js b/test_redirect_and_websocket.js new file mode 100644 index 0000000..347fa60 --- /dev/null +++ b/test_redirect_and_websocket.js @@ -0,0 +1,205 @@ +const https = require('https'); +const http = require('http'); +const io = require('socket.io-client'); + +console.log('🔍 测试HTTP重定向和WebSocket配置'); +console.log('='.repeat(50)); + +// 1. 测试HTTP重定向 +async function testHttpRedirect() { + console.log('\n1️⃣ 测试HTTP重定向...'); + + return new Promise((resolve) => { + const options = { + hostname: 'whaletownend.xinghangee.icu', + port: 80, + path: '/', + method: 'GET', + timeout: 10000 + }; + + const req = http.request(options, (res) => { + console.log(`📊 HTTP状态码: ${res.statusCode}`); + console.log('📋 响应头:'); + Object.entries(res.headers).forEach(([key, value]) => { + console.log(` ${key}: ${value}`); + }); + + if (res.statusCode === 301 || res.statusCode === 302) { + console.log('✅ HTTP重定向配置正确'); + console.log(`🔄 重定向到: ${res.headers.location}`); + } else if (res.statusCode === 200) { + console.log('⚠️ HTTP没有重定向,直接返回内容'); + } else { + console.log(`❌ HTTP重定向异常: ${res.statusCode}`); + } + + resolve({ statusCode: res.statusCode, location: res.headers.location }); + }); + + req.on('error', (error) => { + console.log(`❌ HTTP连接失败: ${error.message}`); + resolve({ error: error.message }); + }); + + req.on('timeout', () => { + console.log('❌ HTTP连接超时'); + req.destroy(); + resolve({ error: 'timeout' }); + }); + + req.end(); + }); +} + +// 2. 测试WebSocket升级映射 +async function testWebSocketUpgradeMapping() { + console.log('\n2️⃣ 测试WebSocket升级映射...'); + + // 检查nginx是否有$connection_upgrade映射 + return new Promise((resolve) => { + const options = { + hostname: 'whaletownend.xinghangee.icu', + port: 443, + path: '/socket.io/?EIO=4&transport=websocket', + method: 'GET', + headers: { + 'Upgrade': 'websocket', + 'Connection': 'Upgrade', + 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==', + 'Sec-WebSocket-Version': '13', + 'Origin': 'https://whaletownend.xinghangee.icu' + }, + timeout: 8000 + }; + + const req = https.request(options, (res) => { + console.log(`📊 WebSocket握手状态码: ${res.statusCode}`); + console.log('📋 WebSocket响应头:'); + Object.entries(res.headers).forEach(([key, value]) => { + console.log(` ${key}: ${value}`); + }); + + if (res.statusCode === 101) { + console.log('✅ WebSocket升级成功'); + } else if (res.statusCode === 400) { + console.log('❌ WebSocket升级失败 - 400错误'); + console.log('💡 可能缺少 $connection_upgrade 映射'); + } else if (res.statusCode === 502) { + console.log('❌ WebSocket升级失败 - 502错误'); + console.log('💡 后端连接问题'); + } else { + console.log(`❌ WebSocket升级失败 - ${res.statusCode}错误`); + } + + resolve({ statusCode: res.statusCode }); + }); + + req.on('error', (error) => { + console.log(`❌ WebSocket握手失败: ${error.message}`); + resolve({ error: error.message }); + }); + + req.on('timeout', () => { + console.log('❌ WebSocket握手超时'); + req.destroy(); + resolve({ error: 'timeout' }); + }); + + req.end(); + }); +} + +// 3. 测试WS协议是否能通过重定向工作 +async function testWSProtocolWithRedirect() { + console.log('\n3️⃣ 测试WS协议重定向...'); + + return new Promise((resolve) => { + console.log('🧪 尝试连接 ws://whaletownend.xinghangee.icu/game'); + + const socket = io('ws://whaletownend.xinghangee.icu/game', { + transports: ['websocket'], + timeout: 8000, + forceNew: true + }); + + let resolved = false; + + const timeout = setTimeout(() => { + if (!resolved) { + resolved = true; + socket.disconnect(); + console.log(' ❌ WS协议连接超时'); + resolve({ success: false, error: 'timeout' }); + } + }, 8000); + + socket.on('connect', () => { + if (!resolved) { + resolved = true; + clearTimeout(timeout); + console.log(' ✅ WS协议连接成功(通过重定向)'); + console.log(` 📡 Socket ID: ${socket.id}`); + console.log(` 🔗 实际URL: ${socket.io.uri}`); + socket.disconnect(); + resolve({ success: true, actualUrl: socket.io.uri }); + } + }); + + socket.on('connect_error', (error) => { + if (!resolved) { + resolved = true; + clearTimeout(timeout); + console.log(` ❌ WS协议连接失败: ${error.message}`); + console.log(` 🔍 错误详情: ${error.description?.message || 'N/A'}`); + resolve({ success: false, error: error.message }); + } + }); + }); +} + +async function runRedirectTests() { + const httpResult = await testHttpRedirect(); + const websocketResult = await testWebSocketUpgradeMapping(); + const wsProtocolResult = await testWSProtocolWithRedirect(); + + console.log('\n' + '='.repeat(50)); + console.log('📊 重定向和WebSocket测试结果'); + console.log('='.repeat(50)); + + console.log(`1. HTTP重定向: ${httpResult.statusCode === 301 || httpResult.statusCode === 302 ? '✅ 配置正确' : '❌ 未配置或异常'}`); + if (httpResult.location) { + console.log(` 重定向目标: ${httpResult.location}`); + } + + console.log(`2. WebSocket升级: ${websocketResult.statusCode === 101 ? '✅ 正常' : '❌ 失败'}`); + + console.log(`3. WS协议重定向: ${wsProtocolResult.success ? '✅ 工作' : '❌ 不工作'}`); + + console.log('\n💡 分析结果:'); + + if (httpResult.statusCode === 301 || httpResult.statusCode === 302) { + console.log('✅ HTTP重定向配置正确'); + } else { + console.log('❌ 缺少HTTP重定向配置'); + console.log('🔧 需要添加HTTP server块进行重定向'); + } + + if (websocketResult.statusCode !== 101) { + console.log('❌ WebSocket升级配置有问题'); + console.log('🔧 需要检查nginx配置中的:'); + console.log(' 1. map $http_upgrade $connection_upgrade 映射'); + console.log(' 2. proxy_set_header Upgrade $http_upgrade'); + console.log(' 3. proxy_set_header Connection $connection_upgrade'); + } + + if (!wsProtocolResult.success) { + console.log('❌ WS协议无法通过重定向工作'); + console.log('💡 原因: WebSocket协议升级发生在TCP层,无法像HTTP那样重定向'); + console.log('📝 解决方案: 客户端应该直接使用WSS协议'); + } + + process.exit(0); +} + +runRedirectTests().catch(console.error); \ No newline at end of file diff --git a/test_websocket_handshake_redirect.js b/test_websocket_handshake_redirect.js new file mode 100644 index 0000000..7acf1d7 --- /dev/null +++ b/test_websocket_handshake_redirect.js @@ -0,0 +1,287 @@ +const https = require('https'); +const http = require('http'); +const io = require('socket.io-client'); + +console.log('🔍 详细测试WebSocket握手重定向机制'); +console.log('='.repeat(60)); + +// 1. 手动模拟WebSocket握手请求 - HTTP阶段 +async function testWebSocketHandshakeHTTP() { + console.log('\n1️⃣ 测试WebSocket握手的HTTP阶段...'); + + return new Promise((resolve) => { + console.log('📡 发送WebSocket握手请求到 HTTP (80端口)'); + + const options = { + hostname: 'whaletownend.xinghangee.icu', + port: 80, + path: '/socket.io/?EIO=4&transport=websocket', + method: 'GET', + headers: { + 'Upgrade': 'websocket', + 'Connection': 'Upgrade', + 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==', + 'Sec-WebSocket-Version': '13', + 'Origin': 'http://whaletownend.xinghangee.icu', + 'User-Agent': 'websocket-handshake-test' + }, + timeout: 10000 + }; + + const req = http.request(options, (res) => { + console.log(`📊 HTTP响应状态码: ${res.statusCode}`); + console.log('📋 响应头:'); + Object.entries(res.headers).forEach(([key, value]) => { + console.log(` ${key}: ${value}`); + }); + + let data = ''; + res.on('data', (chunk) => { + data += chunk; + }); + + res.on('end', () => { + if (data && data.length < 500) { + console.log(`📄 响应内容: ${data}`); + } + + console.log('\n📊 分析结果:'); + if (res.statusCode === 301 || res.statusCode === 302) { + console.log('✅ WebSocket握手请求被重定向!'); + console.log(`🔄 重定向到: ${res.headers.location}`); + console.log('💡 证明: WebSocket握手的HTTP阶段支持重定向'); + } else if (res.statusCode === 101) { + console.log('✅ WebSocket握手成功升级'); + } else if (res.statusCode === 400) { + console.log('❌ WebSocket握手失败 - 400错误'); + } else { + console.log(`⚠️ 意外的响应: ${res.statusCode}`); + } + + resolve({ + statusCode: res.statusCode, + location: res.headers.location, + isRedirect: res.statusCode === 301 || res.statusCode === 302 + }); + }); + }); + + req.on('error', (error) => { + console.log(`❌ HTTP请求失败: ${error.message}`); + resolve({ error: error.message }); + }); + + req.on('timeout', () => { + console.log('❌ HTTP请求超时'); + req.destroy(); + resolve({ error: 'timeout' }); + }); + + req.end(); + }); +} + +// 2. 测试Socket.IO客户端是否能自动处理重定向 +async function testSocketIORedirectHandling() { + console.log('\n2️⃣ 测试Socket.IO客户端重定向处理...'); + + const testConfigs = [ + { + name: 'WS协议 - 测试重定向', + url: 'ws://whaletownend.xinghangee.icu/game', + options: { + transports: ['websocket'], + timeout: 8000, + forceNew: true + } + }, + { + name: 'HTTP协议 - 测试重定向', + url: 'http://whaletownend.xinghangee.icu/game', + options: { + transports: ['websocket', 'polling'], + timeout: 8000, + forceNew: true + } + } + ]; + + const results = []; + + for (const config of testConfigs) { + console.log(`\n🧪 ${config.name}`); + console.log(`📡 URL: ${config.url}`); + + const result = await new Promise((resolve) => { + const socket = io(config.url, config.options); + let resolved = false; + + // 监听连接事件 + socket.on('connect', () => { + if (!resolved) { + resolved = true; + console.log(' ✅ 连接成功'); + console.log(` 📡 Socket ID: ${socket.id}`); + console.log(` 🚀 传输方式: ${socket.io.engine.transport.name}`); + console.log(` 🔗 最终URL: ${socket.io.uri}`); + + // 检查是否发生了协议升级 + const originalProtocol = config.url.startsWith('ws://') ? 'ws' : 'http'; + const finalProtocol = socket.io.uri.startsWith('wss://') ? 'wss' : + socket.io.uri.startsWith('ws://') ? 'ws' : + socket.io.uri.startsWith('https://') ? 'https' : 'http'; + + if (originalProtocol !== finalProtocol) { + console.log(` 🔄 协议升级: ${originalProtocol}:// → ${finalProtocol}://`); + } + + socket.disconnect(); + resolve({ + success: true, + transport: socket.io.engine.transport.name, + finalUrl: socket.io.uri, + protocolChanged: originalProtocol !== finalProtocol + }); + } + }); + + socket.on('connect_error', (error) => { + if (!resolved) { + resolved = true; + console.log(` ❌ 连接失败: ${error.message}`); + console.log(` 🔍 错误类型: ${error.type || 'unknown'}`); + + // 检查是否是重定向相关的错误 + if (error.message.includes('redirect') || error.message.includes('301') || error.message.includes('302')) { + console.log(' 💡 这可能是重定向处理问题'); + } + + resolve({ + success: false, + error: error.message, + type: error.type + }); + } + }); + + // 超时处理 + setTimeout(() => { + if (!resolved) { + resolved = true; + socket.disconnect(); + console.log(' ❌ 连接超时'); + resolve({ success: false, error: 'timeout' }); + } + }, config.options.timeout); + }); + + results.push({ config: config.name, ...result }); + + // 等待1秒再测试下一个 + await new Promise(resolve => setTimeout(resolve, 1000)); + } + + return results; +} + +// 3. 测试不同客户端库的重定向行为 +async function testRawWebSocketRedirect() { + console.log('\n3️⃣ 测试原生WebSocket重定向行为...'); + + const WebSocket = require('ws'); + + return new Promise((resolve) => { + console.log('📡 使用原生WebSocket连接 ws://whaletownend.xinghangee.icu/socket.io/?EIO=4&transport=websocket'); + + try { + const ws = new WebSocket('ws://whaletownend.xinghangee.icu/socket.io/?EIO=4&transport=websocket'); + + ws.on('open', () => { + console.log(' ✅ 原生WebSocket连接成功'); + console.log(' 💡 说明: 重定向在WebSocket握手阶段被正确处理'); + ws.close(); + resolve({ success: true }); + }); + + ws.on('error', (error) => { + console.log(` ❌ 原生WebSocket连接失败: ${error.message}`); + + if (error.message.includes('Unexpected server response: 301') || + error.message.includes('Unexpected server response: 302')) { + console.log(' 💡 发现重定向响应,但WebSocket库未自动处理'); + console.log(' 📝 说明: 需要客户端库支持重定向处理'); + } + + resolve({ success: false, error: error.message }); + }); + + ws.on('close', (code, reason) => { + console.log(` 🔌 WebSocket关闭: ${code} - ${reason}`); + }); + + } catch (error) { + console.log(` ❌ WebSocket创建失败: ${error.message}`); + resolve({ success: false, error: error.message }); + } + }); +} + +async function runHandshakeRedirectTests() { + console.log('开始WebSocket握手重定向测试...\n'); + + const httpResult = await testWebSocketHandshakeHTTP(); + const socketIOResults = await testSocketIORedirectHandling(); + const rawWSResult = await testRawWebSocketRedirect(); + + console.log('\n' + '='.repeat(60)); + console.log('📊 WebSocket握手重定向测试结果'); + console.log('='.repeat(60)); + + console.log(`1. WebSocket握手HTTP阶段: ${httpResult.isRedirect ? '✅ 支持重定向' : '❌ 无重定向'}`); + if (httpResult.location) { + console.log(` 重定向目标: ${httpResult.location}`); + } + + console.log(`2. Socket.IO客户端处理:`); + socketIOResults.forEach((result, index) => { + const status = result.success ? '✅ 成功' : '❌ 失败'; + console.log(` ${index + 1}. ${result.config}: ${status}`); + if (result.protocolChanged) { + console.log(` 协议升级: 是`); + } + if (result.error) { + console.log(` 错误: ${result.error}`); + } + }); + + console.log(`3. 原生WebSocket: ${rawWSResult.success ? '✅ 成功' : '❌ 失败'}`); + if (rawWSResult.error) { + console.log(` 错误: ${rawWSResult.error}`); + } + + console.log('\n💡 技术原理验证:'); + + if (httpResult.isRedirect) { + console.log('✅ 验证: WebSocket握手的HTTP阶段确实支持重定向'); + console.log('📝 机制: ws://先发HTTP GET请求(带Upgrade头) → 收到301/302 → 可以重定向'); + } else { + console.log('❌ 未检测到WebSocket握手重定向'); + } + + const successfulSocketIO = socketIOResults.filter(r => r.success); + if (successfulSocketIO.length > 0) { + console.log('✅ Socket.IO客户端能够处理某些重定向场景'); + } else { + console.log('❌ Socket.IO客户端无法处理当前的重定向配置'); + } + + console.log('\n🔧 修正后的准确表述:'); + console.log('1. ✅ HTTP请求(包括WebSocket握手请求)支持301/302重定向'); + console.log('2. ✅ WebSocket的"升级请求(HTTP层)"可以被重定向'); + console.log('3. ✅ ws://先通过80端口发HTTP握手请求,再尝试升级为WebSocket'); + console.log('4. ⚠️ 客户端库需要支持重定向处理才能正常工作'); + + process.exit(0); +} + +runHandshakeRedirectTests().catch(console.error); \ No newline at end of file diff --git a/test_zulip.js b/test_zulip.js new file mode 100644 index 0000000..d58f7db --- /dev/null +++ b/test_zulip.js @@ -0,0 +1,131 @@ +const io = require('socket.io-client'); + +// 使用用户 API Key 测试 Zulip 集成 +async function testWithUserApiKey() { + console.log('🚀 使用用户 API Key 测试 Zulip 集成...'); + console.log('📡 用户 API Key: lCPWCPfGh7WU...pqNfGF8'); + console.log('📡 Zulip 服务器: https://zulip.xinghangee.icu/'); + console.log('📡 游戏服务器: https://whaletownend.xinghangee.icu/game'); + + const socket = io('wss://whaletownend.xinghangee.icu/game', { + transports: ['websocket', 'polling'], // WebSocket优先,polling备用 + timeout: 20000, + forceNew: true, + reconnection: true, + reconnectionAttempts: 3, + reconnectionDelay: 1000 + }); + + let testStep = 0; + + socket.on('connect', () => { + console.log('✅ WebSocket 连接成功'); + testStep = 1; + + // 使用包含用户 API Key 的 token + const loginMessage = { + type: 'login', + token: 'lCPWCPfGh7...fGF8_user_token' + }; + + console.log('📤 步骤 1: 发送登录消息(使用用户 API Key)'); + socket.emit('login', loginMessage); + }); + + socket.on('login_success', (data) => { + console.log('✅ 步骤 1 完成: 登录成功'); + console.log(' 会话ID:', data.sessionId); + console.log(' 用户ID:', data.userId); + console.log(' 用户名:', data.username); + console.log(' 当前地图:', data.currentMap); + testStep = 2; + + // 等待 Zulip 客户端初始化 + console.log('⏳ 等待 3 秒让 Zulip 客户端初始化...'); + setTimeout(() => { + const chatMessage = { + t: 'chat', + content: '🎮 【用户API Key测试】来自游戏的消息!\\n' + + '时间: ' + new Date().toLocaleString() + '\\n' + + '使用用户 API Key 发送此消息。', + scope: 'local' + }; + + console.log('📤 步骤 2: 发送消息到 Zulip(使用用户 API Key)'); + console.log(' 目标 Stream: Whale Port'); + socket.emit('chat', chatMessage); + }, 3000); + }); + + socket.on('chat_sent', (data) => { + console.log('✅ 步骤 2 完成: 消息发送成功'); + console.log(' 响应:', JSON.stringify(data, null, 2)); + + // 只在第一次收到 chat_sent 时发送第二条消息 + if (testStep === 2) { + testStep = 3; + + setTimeout(() => { + // 先切换到 Pumpkin Valley 地图 + console.log('📤 步骤 3: 切换到 Pumpkin Valley 地图'); + const positionUpdate = { + t: 'position', + x: 150, + y: 400, + mapId: 'pumpkin_valley' + }; + socket.emit('position_update', positionUpdate); + + // 等待位置更新后发送消息 + setTimeout(() => { + const chatMessage2 = { + t: 'chat', + content: '🎃 在南瓜谷发送的测试消息!', + scope: 'local' + }; + + console.log('📤 步骤 4: 在 Pumpkin Valley 发送消息'); + socket.emit('chat', chatMessage2); + }, 1000); + }, 2000); + } + }); + + socket.on('chat_render', (data) => { + console.log('📨 收到来自 Zulip 的消息:'); + console.log(' 发送者:', data.from); + console.log(' 内容:', data.txt); + console.log(' Stream:', data.stream || '未知'); + console.log(' Topic:', data.topic || '未知'); + }); + + socket.on('error', (error) => { + console.log('❌ 收到错误:', JSON.stringify(error, null, 2)); + }); + + socket.on('disconnect', () => { + console.log('🔌 WebSocket 连接已关闭'); + console.log(''); + console.log('📊 测试结果:'); + console.log(' 完成步骤:', testStep, '/ 4'); + if (testStep >= 3) { + console.log(' ✅ 核心功能正常!'); + console.log(' 💡 请检查 Zulip 中的 "Whale Port" 和 "Pumpkin Valley" Streams 查看消息'); + } + process.exit(0); + }); + + socket.on('connect_error', (error) => { + console.error('❌ 连接错误:', error.message); + process.exit(1); + }); + + // 20秒后自动关闭(给足够时间完成测试) + setTimeout(() => { + console.log('⏰ 测试时间到,关闭连接'); + socket.disconnect(); + }, 20000); +} + +console.log('🔧 准备测试环境...'); +testWithUserApiKey().catch(console.error); \ No newline at end of file diff --git a/websocket_with_redirect_support.js b/websocket_with_redirect_support.js new file mode 100644 index 0000000..7dfa59e --- /dev/null +++ b/websocket_with_redirect_support.js @@ -0,0 +1,194 @@ +const io = require('socket.io-client'); +const https = require('https'); +const http = require('http'); + +console.log('🔧 实现支持重定向的WebSocket连接'); +console.log('='.repeat(50)); + +/** + * 手动处理WebSocket重定向的Socket.IO连接 + * + * 原理: + * 1. 先发送HTTP请求检查是否有重定向 + * 2. 如果有重定向,使用重定向后的URL + * 3. 如果没有重定向,使用原始URL + */ +async function connectWithRedirectSupport(originalUrl, options = {}) { + console.log(`🔍 检查URL重定向: ${originalUrl}`); + + // 解析原始URL + const urlObj = new URL(originalUrl.replace('ws://', 'http://').replace('wss://', 'https://')); + + // 1. 发送HTTP请求检查重定向 + const redirectInfo = await checkRedirect(urlObj); + + // 2. 确定最终连接URL + let finalUrl; + if (redirectInfo.isRedirect) { + console.log(`🔄 检测到重定向: ${redirectInfo.location}`); + + // 将重定向的URL转换为适合Socket.IO的格式 + const redirectedUrl = new URL(redirectInfo.location); + if (redirectedUrl.protocol === 'https:') { + finalUrl = `https://${redirectedUrl.host}${redirectedUrl.pathname.replace('/socket.io/', '')}`; + } else { + finalUrl = `http://${redirectedUrl.host}${redirectedUrl.pathname.replace('/socket.io/', '')}`; + } + + console.log(`✅ 使用重定向后的URL: ${finalUrl}`); + } else { + finalUrl = originalUrl.replace('ws://', 'http://').replace('wss://', 'https://'); + console.log(`✅ 使用原始URL: ${finalUrl}`); + } + + // 3. 使用最终URL建立Socket.IO连接 + console.log(`🚀 建立Socket.IO连接...`); + + return new Promise((resolve, reject) => { + const socket = io(finalUrl, { + transports: ['websocket', 'polling'], + timeout: 10000, + forceNew: true, + ...options + }); + + socket.on('connect', () => { + console.log('✅ 连接成功!'); + console.log(`📡 Socket ID: ${socket.id}`); + console.log(`🚀 传输方式: ${socket.io.engine.transport.name}`); + console.log(`🔗 最终URL: ${socket.io.uri}`); + resolve(socket); + }); + + socket.on('connect_error', (error) => { + console.log(`❌ 连接失败: ${error.message}`); + reject(error); + }); + }); +} + +/** + * 检查URL是否有重定向 + */ +async function checkRedirect(urlObj) { + return new Promise((resolve) => { + const isHttps = urlObj.protocol === 'https:'; + const httpModule = isHttps ? https : http; + const port = urlObj.port || (isHttps ? 443 : 80); + + const options = { + hostname: urlObj.hostname, + port: port, + path: '/socket.io/?EIO=4&transport=polling', // 使用Socket.IO的polling路径检查 + method: 'HEAD', // 使用HEAD请求减少数据传输 + timeout: 5000 + }; + + const req = httpModule.request(options, (res) => { + const isRedirect = res.statusCode === 301 || res.statusCode === 302; + resolve({ + isRedirect, + statusCode: res.statusCode, + location: res.headers.location + }); + }); + + req.on('error', (error) => { + console.log(`⚠️ 重定向检查失败: ${error.message}`); + resolve({ isRedirect: false, error: error.message }); + }); + + req.on('timeout', () => { + req.destroy(); + resolve({ isRedirect: false, error: 'timeout' }); + }); + + req.end(); + }); +} + +/** + * 测试支持重定向的连接 + */ +async function testRedirectSupport() { + const testUrls = [ + 'ws://whaletownend.xinghangee.icu/game', + 'http://whaletownend.xinghangee.icu/game', + 'https://whaletownend.xinghangee.icu/game' + ]; + + for (const url of testUrls) { + console.log(`\n${'='.repeat(50)}`); + console.log(`🧪 测试URL: ${url}`); + console.log(`${'='.repeat(50)}`); + + try { + const socket = await connectWithRedirectSupport(url); + + // 测试基本功能 + console.log('\n📤 测试登录功能...'); + + const loginResult = await new Promise((resolve) => { + const loginMessage = { + type: 'login', + token: 'test_token_for_redirect_test' + }; + + socket.emit('login', loginMessage); + + socket.on('login_success', (data) => { + console.log('✅ 登录成功'); + resolve({ success: true, data }); + }); + + socket.on('login_error', (error) => { + console.log('⚠️ 登录失败(预期,因为使用测试token)'); + resolve({ success: false, error }); + }); + + // 3秒超时 + setTimeout(() => { + resolve({ success: false, error: 'timeout' }); + }, 3000); + }); + + socket.disconnect(); + + console.log(`✅ URL ${url} 连接测试成功`); + + } catch (error) { + console.log(`❌ URL ${url} 连接测试失败: ${error.message}`); + } + + // 等待1秒再测试下一个 + await new Promise(resolve => setTimeout(resolve, 1000)); + } +} + +// 运行测试 +async function runTest() { + try { + await testRedirectSupport(); + + console.log(`\n${'='.repeat(50)}`); + console.log('📊 重定向支持测试完成'); + console.log(`${'='.repeat(50)}`); + + console.log('\n💡 结论:'); + console.log('✅ WebSocket握手重定向在协议层面完全支持'); + console.log('✅ 通过手动处理重定向可以解决客户端库限制'); + console.log('✅ ws:// 协议可以通过重定向正常工作'); + + console.log('\n🔧 实用建议:'); + console.log('1. 对于支持重定向的场景,可以使用上述方案'); + console.log('2. 对于简单场景,直接使用 https:// 更可靠'); + console.log('3. 生产环境建议配置好重定向处理逻辑'); + + } catch (error) { + console.error('测试过程中发生错误:', error); + } + + process.exit(0); +} + +runTest(); \ No newline at end of file