一行代码让网站停止工作,亲测有效

大家好,我是阿星,最近在开发中发生了一件有意思的事情,那就是我通过一行代码,让网站卡死了,真的是离了大谱,记录一下,长个记性

背景

今天下午,群里突然提示了几个下载简历失败的提示,我看到后,心里一紧,赶紧打开页面看看我能不能下载,结果我这里下载是正常的。于是我就感觉事情不简单,赶紧本地启动项目调试,我本以为本地启动后控制台会有报错,但是实际上并没有,没办法,只能使用绝招:二分法先定位到出问题代码。漫长的缩短问题代码后,最后问题代码找到了

/**
 * 判断是否为链接
 * @param urlString 接受一个字符串
 * @returns 一个 boolean 值,true 表示是链接,false 表示不是链接
 */
export const isLink = (urlString: string) => {
  const regex = /^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/;
  return regex.test(urlString);
};

说实话看到是因为这段代码的我是这样的

15684497691303.jpg

我本来是不会正则的,这个是 AI 帮我写的判断链接的方法。我当时就光测试它是否能判断出一个字符串是否是链接,而忽略了特殊字符串是否会有性能问题。 最后,我给大家复现一下这个bug 最小代码示例:

/^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/.test('https://www.suse.com/nbswebapp/createSummary.jsp?')

只要你在游览器控制台输入这个,这个页面立马卡死。

问题原因分析

那这是为什么呢?听听万能的 AI 这么说的: 在JavaScript中使用正则表达式时,如果正则表达式的设计不当,特别是在处理复杂的字符串或者特定的模式时,可能会导致性能问题,甚至使浏览器卡死。这种现象通常称为“灾难性回溯”(Catastrophic Backtracking)。 在你提供的正则表达式中:

/^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/

这个表达式用于匹配URL,其中包含了几个可选的组件和量词(如*?),这些都可能导致正则表达式引擎进行大量的回溯尝试,尤其是当输入字符串在某些位置上与模式不匹配时。 具体来说,问题可能出现在([\/\w \.-]*)*这部分。这里使用了两个嵌套的量词(*外面还有一个*),这种嵌套的量词是造成灾难性回溯的常见原因。当输入的URL在最后一部分(即[\/\w \.-]*应该匹配的部分)稍有不符时,正则表达式引擎可能会尝试所有可能的分割方式来匹配整个模式,这会导致处理时间急剧增加。 为了避免这种问题,可以尝试优化正则表达式的结构,避免使用嵌套的量词,或者使用更具体的字符类匹配来减少潜在的回溯。例如,可以修改为:

/^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]+)?\/?$/

这里,我将内部的*修改为+,使得这部分至少匹配一次,减少了嵌套量词的使用,这应该可以帮助减轻浏览器的负担。 总之,设计正则表达式时需要注意避免复杂的嵌套和过度的通配符使用,特别是在处理可能非常长或复杂的字符串时。这样不仅可以避免浏览器卡死的问题,还可以提高应用的响应速度和用户体验。 实测,。这个正则是有问题的:

image.png

我现在是用新的检测字符串是否是链接的方法,充足测试下来没有问题:

/**
 * 判断字符串是否为链接
 */
export function isLink(urlString: string): boolean {
  const pattern = new RegExp(
    '^(https?:\\/\\/)?' + // 协议
      '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // 域名
      '((\\d{1,3}\\.){3}\\d{1,3}))' + // 或IP(v4)地址
      '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // 端口和路径
      '(\\?[;&a-z\\d%_.~+=-]*)?' + // 查询参数
      '(\\#[-a-z\\d_]*)?$',
    'i',
  ); // 锚点
  return !!pattern.test(urlString);
}

怎么避免?

这次出现这个问题的原因有两个

  1. 经验不足:如果我知道,不好的正则出导致灾难性回溯的话,我在拿到 AI 给我写的正则,我就会问它给我的正则是否会导致灾难性回溯

  2. 没有充足的测试:如果我的项目有对这种工具方法的充足的测试,应该也不会产生这个 bug 了。

总结

遇到 bug 不要慌,从简单到难的使用排查问题的方法。先定位到问题。例如:我遇到bug,先定位前端问题还是后端问题,再定位问题的大的位置,逐渐缩小范围,最终找到问题的位置。然后解决问题。有没有觉得这其实就是使用二分法的思想来定位问题。找到问题的代码了,那其实就胜利一大半了,剩下的就是写出正确的代码,做充足的测试,最后复盘这次 bug,以后不要再犯同样的错就好了!

关注我的微🌟公众号:爱前端的阿星,每周分享前端知识

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值