警惕 Chrome 的查看源代码 (View Page Source) 功能

前阵子解决一个问题的时候,差点以为是我们自己在 HTML 代码中输出的一段信息有问题,结果发现,Chrome 的 View Source Code 竟然会重新发送一个请求!

有史以来,所有的浏览器从来没有过这样天才的设计。大家都是老老实实,既然你让我显示源代码,那我就直接给你把正在看的这个页面的源代码显示出来。没有人想过竟然可以重新发起一个请求,去拿“纯洁的”源代码。这是革命性的!Chrome 你做到了!

早在2008年,就已经有人提出这个 bug - View source forces page reload. 中间有人将之标记过 Fixed,但是世界末日快来了,Chrome 的稳定版本已经飚到18了,市场份额已经远超 Firefox 了,实际上这个 bug 仍然存在。我的天啊,究竟是什么样的设计,导致解决这样一个问题这么难?

甚至还曾有开发者认为 View Source 就应该是这样的行为 (链接):

Yes, when you "view source", you're really opening a new tab that opens the page again and displays the source rather than renders the page. Many web pages are dynamic and modify their HTML content (eg. using JavaScript/XMLHttpRequest) so we cannot display the current HTML - this is why a new request is made.

If you want to view the current state of a dynamic page, you should use the inspector. "File icon -> Developer -> JavaScript console"

I don't see how this is a security bug or a bug at all - the behavior is by design - closing as such.

注意里面加粗倾斜的那段话——因为很多网页是动态的,所以无法显示当前的 HTML,所以发送一个新的请求。这逻辑真够奇怪的!你是想“显示当前的 HTML”吗?你发送一个新的请求解决了这个问题吗?没有解决,那你何必要发一个新的请求?这就相当于脱了裤子,屁还是没有放出来。

运算符优先级

一段计时的代码,把时间长度用“2小时37分钟28秒”这样的形式输出,但是偶然注意到结果很有问题。盯着代码看了半天,觉得逻辑判断都是正确的,后来用一个数字 debug 才找到真相。

比如 4000 秒,程序先判断如果大于一小时,就输出小时数,然后算余数。就是算余数这一步除了问题,代码写成 secs % 60*60。写代码的人为了清晰,还故意在乘号两边去掉了空格,可是这更加容易地造成了错觉,让人觉得 60*60 是先计算的。可是 "%" 的优先级和乘除是同等的!

运算符优先级是挺难记的。我觉得,迷惑的时候,加括号就行了,可读性也绝对好。

不过这次通过 Oracle 这个文档我是记住了,"%" 和乘除都是 multiplicative operators, 所以是同等优先级。想想处理器原理,确实是这样,除法的结果不就顺便出来余数了吗。

Java SimpleDateFormat 与 locale (以及 Mac OS X 更改语言)

遇到一个非常怪异的问题,Tomcat 里面有个 servlet 用 SimpleDateFormat 解析日期的,类似这样:

DateFormat formatter = new SimpleDateFormat("dd-MMM-yyyy");
formatter.parse("05-Jan-2012")

但是会抛出 ParseException - Unparseable date "05-Jan-2012".

我仔细看了看,似乎一切都是对的,不应该出错。于是写一个最简单的测试类,main 函数就这么两行,同一台机器上运行完全正常。更纳闷了。

最后在 servlet 代码里打出 formatter.format(new Date()) 的结果,发现是 "29-二月-2012"!

这才想起我最早拿到这台 MacBook Pro 时系统是中文,我改成英文但是登录界面等少数地方还是中文。不知道 Tomcat 是怎么设置的 locale,不过这里有人在 Windows 上遇到同样的问题,可以通过指定 java 参数解决。

但是苹果恰好有一个文档:Mac OS X: How to change the language displayed in the login window!看起来,我在 System Preferences 里修改的只是我当前用户的 locale,而我启动 tomcat 的时候,使用了 sudo——root 的 locale 仍然是中文?

不管怎么样,既然有了这么专业的文档,照做就是了。不过苹果给的第一种解决办法太让人无语了:

Reinstall Mac OS X and select the desired language during installation

正常做法是在 Terminal 里执行 sudo languagesetup,挑选想要的语言即可。

P.S. SimpleDateFormat 是有个 constructor 可以指定 locale 的。

无限递归导致 Segmentation fault

某服务器上一个 cron job 是 shell 脚本调用 Java 程序,最近老是报 Segmentation fault, 每次看见此信息

/bin/sh: line 1: 4285 Segmentation fault java ...

总觉得无处下手,bash 的问题?Java 版本不对?信息量太少了。其实遇到这种事情不能谎,表面上没有信息一定要挖出信息来。今天仔细一看,这个脚本把标准输出重定向到一个日志文件去了,于是去看日志。这个程序的主体是对一个信息列表做循环,恰好在每个循环的关键部分开始前、结束后都会写一条日志。其目的是为了计时,不过正是这两条日志让我很快找到了错误缘由,因为发现日志文件的末尾只有一个开始前的,说面在这个循环的处理过程里面发生了 Segmentation fault.

找出程序的源代码,发现循环里面调用的方法有一个是递归的,情况就开始明朗了,猜测就是递归无法终止导致 stack overflow,segmentation fault. 果然,根据日志里最后一行日志中记录的真实数据,发现这条数据是有问题的,会导致此方法无限递归。

Wikipedia 的 Segmentation fault 词条里有一节是 "Common causes", 我这次遇到的就是最后一条。

wget 自动发送用户名密码

有个 Server 需要 Basic Auth 认证,但是我发现在它自己上面有一个任务会通过 wget 访问一个自己的 URL,调用的过程并没有提供用户名和密码,竟然可以成功访问!

一开始我以为是 Apache 里面配置的访问规则是对本地访问不需要认证,但是并非如此。bash alias? 也不是。加上 --debug 参数调用 wget,发现它确实在访问本机的这个域名时会加上 Authorization 这个 header, 而访问其它域名的时候则不加。

最终通过 strace 发现它会打开 $HOME/.netrc 文件,原来秘密就在里面。中间看了半天 manual,只看到它会读取 /etc/wgetrc, $HOME/.wgetrc, 没注意到还会读这个文件。我不太喜欢这种做法——谈不上安全,又不容易维护。

参考:

小打小闹

在我的网站日志目录里先找到 Sogou spider 的 IP:

# grep -h -F "Sogou web spider" * | awk '{print $1}' | sort | uniq -c | sort -nr | head -n 5
 109766 220.181.94.231
  26244 220.181.125.69
     93 220.181.94.235
     90 220.181.125.107
     83 220.181.94.236

然后看看从访问最多的那个 IP 来的都是什么 user agent:

# grep -h -F "220.181.94.231" * | grep -v -F "robots.txt" | awk '{ for (i=12; i<=NF; i++) printf("%s ", $i); printf("\n"); }' | sort | uniq -c | sort -nr
 109497 "Sogou web spider/4.0(+http://www.sogou.com/docs/help/webmasters.htm#07)" 
    187 "Sogou-Test-Spider/4.0 (compatible; MSIE 5.5; Windows 98)" 
    109 "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Avant Browser; InfoPath.1; .NET CLR 2.0.50727; .NET CLR1.1.4322)" 
     70 "Tsinghua AI Lab Robot 2.0" 
     55 "Tsinghua AI Lab Robot" 
     35 "-" 
     21 "Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.7) Gecko/2009031915 Gentoo Firefox/3.0.7" 
     18 "Sogou Pic Spider/3.0(+http://www.sogou.com/docs/help/webmasters.htm#07)" 
      1 "Sogou Mobile Spider1.0 (http://wap.sogou.com)"

真有意思。

grep: writing output: Broken pipe in iTerm2

前天用 iTerm2,在执行一个 grep "xxx" filename | head (filename 这个文件应该相当大,grep 到的内容也应该有很多)这样的命令时,遇到大量如下错误输出:

grep: writing output: Broken pipe

而在 Mac 自带的 Terminal.app 里面执行完全一样的命令,不会有任何错误。用 which 查看,使用的确实是同一个 grep 命令,同一个 head 命令。再仔细观察,发现其实 iTerm 里面,命令也输出了正确的结果,那后面的这些错误信息应该是输出到 stderr 的。把命令改成

grep "xxx" filename 2>errors | head

确实,错误信息都跑到 errors 文件里了。这时又发现,在 Terminal.app 里命令在输出10行之后立即结束执行,而在 iTerm 里则取决于这个文件有多大,grep 出来的内容有多少行,它就会执行相应长的时间。网上看到有人说这是 head 取得10行后立即退出,这个 pipe 的读端就没了,grep 继续往 pipe 写,于是 - broken pipe, 只要将错误定向到 /dev/null, 忽略即可。可是我经常用 grep 来在特别大的日志文件里找东西,进行下一步分析前,就先用 head 看看 grep 的正则表达式写的对不对。每次 grep 都不管 head 只读少数行这个现实,一直执行到读完整个文件,太浪费时间和资源了。

... Read full article: grep: writing output: Broken pipe in iTerm2

李笑来《把时间当作朋友》

把时间当作朋友:运用心智获得解放 - 李笑来. 国内很少有人像这样认真写好书了!

在2012来临前把这本书读完了,收获比我想象中大得多(这本书是我买一本数学书时顺便一起买下的),值得多看几遍。这本书大致是关于时间,心智,思考,学习,坚持的探讨和思考,也有关于成功的定义,以及介绍达到成功目的的一些必要途径,不过当然不是市面上流行的成功学书籍。

关于时间管理,David Allen 那本著名的 Getting Things Done 我也看了,看到一半就觉得好像作者在反复重复本来很简单的一些事,就好像当年学政治一样,看不下去了,也许是我不够有耐心吧。书中介绍的方法基本了解了,但是对我的工作学习似乎没有什么帮助,该拖延的事我还是拖延。我觉得方法这东西,每个人都应该有自己的一套,别人的好方法对自己并不一定有用。李笑来这本书虽然名字“把时间当作朋友”听起来就是一本时间管理的书,实际上它并不是教你时间管理的方法,而是在整个过程中告诉读者思考、反省、心智的重要性。

... Read full article: 李笑来《把时间当作朋友》

JVM 的 Client Mode 与 Server Mode

目前正在读“深入理解 Java 虚拟机 - JVM 高级特性与最佳实践”,这本书确实是国内少见的好书之一,虽然个人感觉也有一些不恰当的地方。例如第3章介绍 GC 策略时,一直没有简单解释一下"新生代 (Young generation)" 和"老年代 (Tenured generation)" 这两个概念。我也是在这书中才发现 HotSpot VM 有两个模式 - Server Mode 与 Client Mode,作者也没有介绍。也许是因为这些话题比较初级吧。

关于这两个模式的区别,这个 FAQ 里有介绍。简单地说,Client Mode 启动快,Server Mode 整体性能好。它们使用了不同的 JIT 编译器,Server VM 使用的那一个对代码做了更加多的优化。一般来说,像 Tomcat 这样的 Servlet container 需要长时间运行,启动速度相对来说不是那么的敏感,适合运行在 Server VM 里。而 GUI 程序对启动速度要求比较高,所以使用 Client VM 较好。

... Read full article: JVM 的 Client Mode 与 Server Mode

让 Greasemonkey 脚本同时支持 Firefox 和 Chrome

Greasemonkey 最初只是 Firefox 的一个扩展,不过流行之后,很快被其它的浏览器以不同形式采纳。IE 我不喜欢,此文就不关心它了。剩下的市场份额较高的是 Chrome 和 Firefox, 本文就简单讨论一下如何写出同时支持这两种浏览器的 user script (所谓“跨浏览器”).

最早的时候也是热心用户给 Chrome 写了插件让它支持 user scripts, 但是现在 Chrome 不再另外需要插件就可以支持了。它的处理方式是每次安装 user script 的时候,自动把它转换成一个扩展!刚开始在 Chrome 上调试脚本的时候,我还尝试在磁盘上找到它存储脚本的目录——在 Firefox 里我都是直接编辑脚本,保存,刷新页面马上看到效果,简单粗暴。肯定有更简单的调试方式,只是我不知道。

看看 Chrome 的官方文档怎么介绍的:

... Read full article: 让 Greasemonkey 脚本同时支持 Firefox 和 Chrome

Latest Comments
  • 警惕 Chrome 的查看源代码 (View Page Source) 功能 5
    "我记得ff也是这样的,而且还可以在源码界面reload. 应该是没保存服务..." - by netwjx
  • Flickr 被封,图片不显示的解决办法 12
    "目前West的farm 3 5 6 7 8都是紅叉……" - by Rabookie
  • Flickr 被封,图片不显示的解决办法 12
    "改hosts就是直接把 76.13.18.78 farm3.static.fl..." - by Rabookie
  • 警惕 Chrome 的查看源代码 (View Page Source) 功能 5
    "@fanzeyi 我经常用此功能,但是如果这个功能足够的话,以简洁作为设计理念的..." - by qingbo