JavaWeb开发中的字符编码问题的直截了当解决方案

2017-01-11 • 技术文章

解决方案

从容器字符编码配置、响应头设置、Meta字符集设置全部统一为UTF-8,不要通过自己写代码做层层转码。

容器:URI_ENCODING=UTF-8,USE_BODY_ENCODING_FOR_QUERY_STRING=true

ContentType:text/html; charset=UTF-8

Meta:charset=UTF-8

当然,对于用户可以手动拼参数的场景,比如定制百度搜索,那么应该具备自动探测编码的能力,而不是仅固定支持,百度可以自动识别queryString的编码是个很好的范例。

备注

HttpServletRequest.setCharacterEncoding方法仅仅只适用于设置POST提交的RequestBody部分的数据的编码而不是设置GET提交的queryString的编码。

HttpServletRequest.getPathInfo和getParameter返回的结果是由Servlet服务器解码过的。

HttpServletRequest.getRequestURI返回的字符串没有被Servlet服务器解码过。

编码随谈:加号与空格

URI把允许出现的字符分为保留未保留。百分号编码一个保留字符,其实是在这个字符的16进制ASCII值前面加上转义字符%,未保留字符不需要被百分号编码。对于那些不在保留字符和未保留字符范围内的字符,先转换为UTF-8字节序列,然后对其字节值使用百分号编码。在正常的编码解码流程中,编码的时候先把加号替换为%2B,然后把空格替换为加号;解码的时候先把加号替换为空格,再把%2B替换为加号,就是正常的。但是在已经urlencoded的内容中如果使用这二者不慎,就很有可能造成编解码不正确的问题了。

SSH会话保持基本设置

2016-03-27 • 技术文章

配置文件

1
2
/etc/ssh/ssh_config(Linux)
~/.ssh/config(MacOS)

新窗口连接同一服务器免重登

1
2
3
host *
ControlMaster auto
ControlPath ~/.ssh/master-%r@%h:%p

最后一行表示在指定目录下生成sock文件,保存会话记录。

避免SSH连接断开

为避免packet_write_wait: Connection to [IP]: Broken pipe连接断开的情况,可通过以下配置。

1. 客户端实现(其中55代表定时向SSH Server发送心跳的间隔秒数,下同):

1
2
host *
ServerAliveInterval 55

2. 命令实现:

1
ssh -o ServerAliveInterval=55 user@sshserver

3. 也可以在服务端sshd_config中配置,避免客户端单独配置:

1
ClientAliveInterval 55

操作系统虚拟化与Linux命名空间

2015-11-22 • 技术文章

虚拟化的本质需求来源于任务的隔离,也就是资源的隔离。所以,很多时候我们不是要在OS上虚拟出一个独立的OS,而是希望让虚拟机共享操作系统内核而又保持隔离性。

chroot

诞生于1979年的chroot是资源隔离的一种方案,chroot使一个进程把某个特定目录作为根目录,所有文件系统操作都被限定在该目录里进行。

但是chroot仅仅做到了文件系统层面的隔离是远远不够的,我们希望对进程、网络等各种资源进行更深层次的隔离而且希望能够实现对这些资源的审计。

Linux命名空间

Linux的命名空间机制为实现基于容器的虚拟化技术提供了很好的基础。

命名空间建立了系统的不同视图,只使用一个内核在一台物理机上运作,将全局资源都通过命名空间抽象起来,每个命名空间中的资源对其它命名空间是透明的。LXC就是利用这一特性实现了资源的隔离,由于没有多余的一层操作系统内核,容器比虚拟机更加轻量,启动更快,内存开销、调度开销也更小,更重要的是访问磁盘等IO设备不需要经过虚拟化层,没有性能损失。

当我们用fork或clone系统调用创建新进程时,有特定的标志位可以控制是与父进程共享命名空间,还是建立新的命名空间。命名空间的实现需要两个部分:每个子系统的命名空间结构,将此前所有的全局组件包装到命名空间中;将给定进程关联到所属各个命名空间的机制。子系统此前的全局属性现在封装到命名空间中,每个进程关联到一个选定的命名空间。每个可以感知命名空间的内核子系统都必须提供一个数据结构,将所有通过命名空间形式提供的对象集中起来。nsproxy结构体用于汇集指向特定于子系统的命名空间包装器的指针。

1
2
3
4
5
6
7
8
9
10
struct nsproxy {
    // 注释仅为匹配该行对应命名空间的标志位注解,不是本处引用代码的说明
    atomic_t count; 
    struct uts_namespace *uts_ns;   // 标志位CLONE_NEWUTS
    struct ipc_namespace *ipc_ns;   // 标志位CLONE_NEWIPC
    struct mnt_namespace *mnt_ns;   // 标志位CLONE_NEWNS
    struct pid_namespace *pid_ns;   // 标志位CLONE_NEWPID
    struct user_namespace *user_ns;
    struct net *net_ns; 
};

uts_namespace包含了运行内核的名称、版本、底层体系结构类型等信息(UTS即UNIX Timesharing System);ipc_namespace包含所有与进程间通信(IPC)有关的信息;mnt_namespace包含了文件系统的视图信息;pid_namespace报行了进程ID相关的信息;user_namespace包含了用于限制每个用户资源使用的信息;net_ns包含所有网络相关的命名空间参数;count是引用计数。

clone(child_exec, child_stack + STACKSIZE, clone_flags, &args)其中的clone_flags即为设置不同命名空间的标志位的或运算结果。如同时设定CLONE_NEWPID和CLONE_NEWIPC,那么不同命名空间的进程彼此不可见,也不能互相通信,这样就实现了进程间的隔离;CLONE_NEWUTS和CLONE_NEWNET一起使用,可以虚拟出一个有独立主机名和网络空间的环境,就跟网络上一台独立的主机一样。

以上所有标志位都可以一起使用,为进程提供了一个独立的运行环境。LXC就是通过clone时设定这些flag,为进程创建一个有独立PID、IPC、FS、Network、UTS空间的容器。一个容器就是一个虚拟的运行环境,对容器内的进程是透明的,它会以为自己是直接在一个系统上运行的。

几种其它虚拟化方案

OpenVZ也是Linux内核里实现操作系统级虚拟化的解决方案之一,但相对LXC,可以分辨子系统中内存和磁盘的配额分配情况,可进行独立审计,且支持检查点、热迁移等更多特性,缺点是基于非常老的Linux内核设计,对新特性支持差。

Docker如今大红大紫,它将基础运行环境和依赖都打包在镜像中,且镜像是层次化的,基于增量修改(基于AUFS实现,基础目录A和增量目录B合并为目录C,冲突时以B为准,修改C写回B),低开销实现了版本控制和分发,软件运行在独立容器中避免冲突。Docker很好地解决了依赖与资源冲突的问题,大大降低了运维成本。

Chromium插件内容脚本与背景脚本消息传递实现

2015-06-01 • 技术文章

在Chromium浏览器插件的开发过程中,内容脚本(content script)和背景脚本(background script)的信息交互是非常常用的。然而由于Chromium的安全隔离机制,这种交互变得成本很高。笔者最近为了省去很多重复无意义的工作步骤,计划开发一款浏览器插件,经过对WebsiteIP插件源代码的研究以及数个小时的试错,终于在自己的插件中实现了这种交互。

1
2
3
4
5
6
7
8
9
10
11
// background: start listening
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
     sendResponse({key: request.action == "guessMyLocation" ? sender.tab.url : "Mars"});
});
 
// content script: send messages
docReady(function() {
    chrome.runtime.sendMessage({action: "guessMyLocation"}, function(response) {
        console.log("You're from " + response.key);
    }
});

其实实现并不困难,之所以耗费了大量的时间是因为一个小的语法错误在文本编辑中没有看出来,一个好的IDE对于代码繁冗的项目还是很必要的。想起之前做php开发的时候,一个bug一直没分析出来,后来试用了PhpStorm,打开项目后直接就自动分析出错误所在了。

说完了这个再说说WebsiteIP的实现机制,其实现中用到了LocalStorage来存放IP信息,而实际上是否使用LocalStorage关系不大,使用普通的对象存储也是可以的。关键就在于chrome.webRequest.onCompleted.addListener(function(details)),details包含了本次请求的各种信息,比如主机IP地址、状态码等等,对于诊断页面加载情况有一定帮助。

Aaron Swartz的理想世界

2015-04-22 • 业界评论

2013年1月11日,Aaron Swartz结束了自己年轻的生命。

你可能之前并没有听说过Aaron这个名字,但是我们耳熟能详的Reddit、web.py、RSS、Markdown、Creative Commons都和他有脱不开的联系。受其父亲影响,他很小的时候就接触了计算机和互联网,14岁时就加入了RSS工作组。他早年退学并成功创业,对多项互联网信息技术的贡献都让人称道不已。在此,我更想想谈谈我对于Aaron思想的一点认识。

首先是Aaron Swartz下载并在互联网上免费发布PACER(公共使用法院电子记录数据库)的部分美国联邦法院文件这个事件。PACER中的文件是政府应当对外公开的,但是PACER对其中的文件实行按页收费来支持数据库的技术投入并产生了资金上的盈余。Aaron Swartz受到FBI调查,但他在案件中并未受到起诉,案件也不了了之了。

另一件标志性事件便是反对SOPA(禁止网络盗版法案),SOPA对政府监控民众网络活动给予了一定的授权。大财团将在SOPA中受益,代表其利益的参议员们高度一致地赞同这个议案,但是民众对这项法案并不满意。在民众的反对压力下,部分支持SOPA的企业转而反对SOPA,白宫也表示并不支持SOPA,之后大批议员也改变了态度, 最终SOPA被阻止实施。

之后是他通过MIT的网络下载了数百万篇JSTOR的学术期刊文章,下载过程还导致了JSTOR的服务器奔溃,而JSTOR仅对学生和研究人员有限开放。检方控告其13项罪名,各方面压力下,Aaron以自杀结束了自己的生命。

以上几个值得注意的事件关键都在于Aaron看不惯阻止信息或者说是阻止知识自由流动与共享的行为。知识或信息的某一个点是由部分人制造的,经过传播原作者和其他人可以利用已有知识直接创造价值,或者在已有知识基础上进行扩展再创造新的价值。但是在目前的社会条件下,也就是货币没有在全世界范围内彻底消亡的社会中,知识真正的自由传播是不可能的。一些科技界的进展被发表出来,但是阅读这些内容还是收费的,而且收取的不仅仅是成本费那么简单。知识与信息的封锁隔绝,在很大程度上阻碍了科技与社会的进步。人生在这个社会上,却不能完全享用前辈所留下来的成果,细想想也是挺可悲的。

互联网的核心精神是共享与信息传播,但在当前的社会条件下,这种精神无法被完整地实现。我觉得,在一定程度上来说,互联网已经领先于目前的社会形态,造成了目前互联网与社会制度在一定程度上​存在冲突。​应该说,Aaron的思想有一定的先进性,但是更多基础性的社会形态改革还没有完成甚至还看不到变革的苗头就追求更先进的顶层设计是没太多意义的。另外,Aaron还试图自己参政来做点什么,但是恐怕当前美国的意识形态根本满足不了其要求,所以他的思想还是没能超越既有的思想框架,是具有一定的局限性的。

Aaron的理想世界是美好的,他的名字应该被我们所铭记,向着这个理想的世界进发注定要举步维艰,在有生之年大概也还看不到什么。我认为,在现阶段,Aaron的期望过高了,还是寄希望于每个人先能看清并认同先进的前进方向比较切实也比较有实际意义。

扩展阅读

http://www.aaronsw.com/

http://zh.wikipedia.org/wiki/亚伦·斯沃茨

The Internet's Own Boy: The Story of Aaron Swartz