forked from datawhale/whale-town-end
test:添加WebSocket连接诊断和测试工具集
- test_zulip.js: Zulip集成功能的端到端测试脚本 - full_diagnosis.js: 全面的WebSocket连接诊断工具 - test_protocol_difference.js: 不同协议(ws/wss/http/https)的对比测试 - test_redirect_and_websocket.js: HTTP重定向和WebSocket升级测试 - test_websocket_handshake_redirect.js: WebSocket握手重定向机制验证 - websocket_with_redirect_support.js: 支持重定向的WebSocket连接实现 提供完整的WebSocket连接问题诊断和解决方案
This commit is contained in:
311
full_diagnosis.js
Normal file
311
full_diagnosis.js
Normal file
@@ -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();
|
||||||
117
test_protocol_difference.js
Normal file
117
test_protocol_difference.js
Normal file
@@ -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);
|
||||||
205
test_redirect_and_websocket.js
Normal file
205
test_redirect_and_websocket.js
Normal file
@@ -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);
|
||||||
287
test_websocket_handshake_redirect.js
Normal file
287
test_websocket_handshake_redirect.js
Normal file
@@ -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);
|
||||||
131
test_zulip.js
Normal file
131
test_zulip.js
Normal file
@@ -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);
|
||||||
194
websocket_with_redirect_support.js
Normal file
194
websocket_with_redirect_support.js
Normal file
@@ -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();
|
||||||
Reference in New Issue
Block a user