7 Bit Encoding and Email

最近工作上比較常接觸到 email 的東西,然後比較認真的看了 HTML email 信件的內容,以前我以為都要用 base64 編碼來處理,可是用 base64 來處理 HTML email 我一直覺得很不合理,一來大小會變 1.33 倍,二來整個 HTML 原始碼傳送時會變的幾乎無法辨識,收信軟體還要先解碼一次才可以 parse HTML,感覺完全不需要多此一舉,總之就是覺得為什麼要做這麼愚蠢的事情,明明看起來MIME就沒這樣限制,所以我應該可以這樣寫:

Content-Type: text/html; charset=utf-8

然後內文直接放 HTML 原始碼,可是不知道為什麼沒人這樣做,事實上也不 work;最近多看了一些郵件原始碼才發現其實還有個 Quoted-Printable encoding 也很常用,看起來比 Base64 的結果還要接近原始碼許多了,所以就研究了一下它到底是什麼格式。

Quoted-Printable encoding 的基本原理就是用=作為 escape 字元,然後可以把要轉換的字元轉成=字碼的形式,例如 Big5 中文的就要轉成=A7=DA,規範上要轉換的是除了可見(printable)ASCII字元以外的字元都要轉,而 ASCII 是個 7bit 編碼,字碼只有從 0 到 127 而已,而 email 要用 Quoted-Printable encoding 的主要原因其實就是為了讓文件內的每個字元編碼都維持在 7bit 編碼範圍內,現在大家常用的編碼像是 UTF-8 和以前常用的 Big5 等都是 8bit 編碼,兩者差別就在於每個傳輸的 byte 中有沒有使用到第 8 個 bit,轉成二進位的時候,7bit 系統編碼不會用到最左(higher-order)邊的那個 bit。

為什麼需要用 7bit 的文字編碼呢?主因是計算機和電信網路早期很多系統是只支援 7bit 編碼的,SMTP 的規範就直接要求 TCP 傳輸時,每個 byte 最左邊的 higher-order bit 要填 0:

The TCP connection supports the transmission of 8-bit bytes. The SMTP data is 7-bit ASCII characters. Each character is transmitted as an 8-bit byte with the high-order bit cleared to zero.

當然這規範很落後時代,所以在MIME(Multipurpose Internet Mail Extensions)規範其實也有Content-Transfer-Encoding可以指定傳輸用的是什麼編碼:

Content-Transfer-Encoding: 8bit

不過為了相容舊系統,還是很少真的這樣使用的信件在傳遞,因為要是傳到了 7bit 系統,小則亂碼、大則程式當機。不過這就帶出另外一個問題了,難道 7bit 系統只能傳輸 ASCII 字集嗎?因為我還蠻常看到日文的純文字郵件,就去找了一些來看看,結果發現到有的是用ISO-2022-JP,而且是使用 7bit 的傳輸:

Content-Type: text/plain; charset=ISO-2022-JP
Content-Transfer-Encoding: 7bit

信件內容的文字也都很正確,沒有亂碼:

iso-2022-jp

於是就看一下ISO-2022的介紹,發現原來是個很早就有的 7bit 編碼方法,後來根據這方法有訂出了 CN、JP、KR 等語言的編碼,不過比較通行的看來只有 ISO-2022-JP,然後我也找到 HTML email 用 ISO-2022-JP 的:

ISO-2022-JP

看起來就像是我理想中的 HTML email 原始碼啊,所以問題的癥結其實是,大家為了相容於舊系統,所以都用 7bit 傳輸,要 7bit safe 的 encoding 選擇有限,除了比較通行的 ISO-2022-JP 可以給日文用、字元太少只能給英文用的 ASCII 之外,其它語言就只能用 Base64 encoding 和 Quoted-Printable encoding 了,所以事實上其它 7bit 編碼的內容,也是可以直接透過 SMTP 協定來傳輸的,只是要看收信端的軟體能不能支援解碼,像是已經不太有人用的UTF-7就是 7bit 的 unicode 編碼。

最後,就是假設我們已經不用擔心老舊系統的時候,其實只要這樣寫在 MIME header 裡就可以直接傳 UTF-8 的 HTML source,不用再經過任何編碼處理了:

Content-Transfer-Encoding: 8bit
Content-Type: text/html; charset=utf-8

不過距離這一步不知道還有多遠就是了。


HKOSCON 2016

HKOSCON 2016

今年有幸可以參與香港的 Open Source Conference,這趟還承蒙 Mozilla 贊助交通費用,另外因為是講者的關係,住宿的部分則是 HKOSCON 幫忙。其實至少去年就想去了,不過去年因為事情比較多就放棄了,今年還乾脆的投稿分享,不過因為工作的關係,只有參加週六的議程,週五的部分就沒參加到了,聽的議程如下:

  • 回顧台灣自由開源軟體發展 by Jim Huang
  • Interactive Dashboard Development with Plotly by Mart van de Ven
  • Fluentd: Unified Logging Layer by Masahiro Nakagawa by Andy Shu
  • Optimizing Chinese Webfont & Typography by Andy Shu
  • Mozilla Eir - Bring medical devices into the world of webby Kevin Hu
  • From Open Source to Open Media by Hsin-Chan Chien
  • My Mozilla Contributing Journey by Irvin Chen

第一場就是 Jserv 介紹台灣以前的自由開源軟體的發展,我到會場的時候已經開始講的,介紹的比較是早期的發展,是我有參與到這個圈子前的時期,對我來說大部分都是,照我印象比在台灣之前說到的還要詳細些,不過我的聽幾次 Jserv 的分享感覺下來,就是大概進入 COSCUP 開始辦之後,參與發展在地化的自由開源軟體的人卻越來越少,當然我自己也是沒參與啦,其實曾經我本來要幫忙新酷音的網站(目前是WM維護),不過因為一些奇妙的原因我就沒幫上了。

HKOSCON 2016

Plotly則是一個 plot library,我是沒很注意聽,有興趣可以自己研究一下優缺點,目前這市場似乎有點飽和,可能等真的有需求時再來研究吧。

Fluentd是最近蠻多人會用的 log 收集中間人,我目前工作上雖然沒有直接接觸,到是有間接使用到,不過我到來聽這場的這時候才知道,原來作者是日本人,不得不說有點意外,來講的是 Masahiro Nakagawa,是目前專案上排第二的開發者。

Optimizing Chinese Webfont & Typography 這場的重點其實就是動態組字形技術,以前台灣這邊也有人在弄,有商業化的就是justfont了吧,不過沒想到這技術現在也有開源專案提供了,這場 Andy 介紹的是font-spider這個專案,有興趣的可以自己玩玩看。PS: 我覺得 justfont 不斷拓展方向的策略還蠻正確的,不然單靠 Webfont 就蠻危險的。

Mozilla Eir則是第一場 Mozilla 相關的主題,主要是介紹 Mozilla 參與醫療器材平台發展的專案,目前還在蠻初期的,個人感覺還在規劃和討論的階段,所以還沒有實際的成果或是可以 demo 的東西。

HKOSCON 2016

From Open Source to Open Media 則是 HC 從 OSDC 到現在報導者之間的歷程,聽完之後覺得經濟動能推升方案那支廣告,當時看覺得是什麼鬼,現在看覺得效用真的很大啊(?),然後就讓我想到 g0v 有一個推人入坑的方法,就是先弄一個糟糕的版本推上線,然後就會有人受不了出來修改。

HKOSCON 2016

HKOSCON 2016

My Mozilla Contributing Journey 這場則是Irvin自己從開始參與 Moztw 社群到現在一路的歷程,因為參與 Moztw 而有了什麼收穫等等,我印象中有這樣明確整理出因為參與開源社群而獲得的收穫的分享,好像是沒有,有興趣的話,他在 SITCON 夏令營還會再講一次,有興趣的或許...報名好像結束了就是。

除了上面聽的兩場和 Mozilla 相關的題目之外,還有一場我沒聽的是 Gary 的演講,題目是之前講過的 Fuzzy Test,不過是 2016 年版,Fuzzy Test 我覺得真的是自動化整合測試最後一關了,基本上就是把所有正常的操作都測試完之後,就來測個不正常的操作吧,那怎樣產生不正常的操作流程呢,就是可以用 Fuzzy 的方法來產生,不過他自己有說今年 Mozilla 在這邊的資源變少了;其實我覺得 Mozilla 最近方向實在是不太明確,我印象中的整個轉捩點大概是 Brendan Eich 下台開始,然後 Firefox OS 的市佔率也起不來,Firefox 的發展也越來越跟不上 Chrome,像是 e10s 就一直都還不太行,真的是蠻讓人擔心的,好在是短時間還不需要擔心資金問題的樣子。

然後還有一場間接相關的就是我的分享了,這個題目本來是有考慮投 COSCUP 的,後來還是投到 HKOSCON 這邊,也是我第一次嘗試用英文演講,可惜我練習不夠,講的實在不好,很多地方都覺得好像沒正確的講清楚,下次還有機會用英文演講的話應該還是會再試一次,希望下次練習更充分,表現正常些,不然就要上場前喝罐啤酒試試看了,根據我之前的試驗結果,好像喝過酒的情況下,講的會比較流暢XD。

除了演講外,還有一區有廠商攤位,還有一個 Job Board 版面可以給人貼徵才資訊,攤位的部分,IBM、VLC、Google、HKLUG﹑自由香港字型等是我比較有印象的,IBM 是介紹他們的 AI as Service 的服務,紀念品當中還有一些 for Dummies 系列叢書:

HKOSCON 2016

Google 不知道怎麼會願意來擺攤,沒跟他們聊聊,台灣 Google 好像還沒在社群擺過攤的樣子,如果有錯還請幫忙更正,不過總之我拿到 Chrome 貼紙了,這樣曾經的五大瀏覽器,Chrome、IE、Firefox、Opera 的紀念品我都有拿到過了,只差一個台灣實在很難碰到的 Apple 的 Safari 而已。

自由香港字型則是一群人要造屬於香港字的自由字型而聚集起來的社群,主因是因為香港漢字其實有一些字的寫法、筆畫和台灣日本的不一樣,例如下圖中間的「周」:

HKOSCON 2016

然後過去又沒有可以自由使用的字型可以用,所以開始了這個計畫,相信大家也都知道要造一套字型有多大難度,所以這社群有些成果出來真的是很厲害。

整個 HKOSCON 規模比台灣常參加的研討會還要小一些,不過參與的外國人比例感覺比較高,英文議程也多一些,相較來說是對外國會眾友善不少,當然我想英文在香港比較流通也是主因之一,在台灣要做到國際化的研討會實在不太容易,目前比較常見的問題包括:翻譯問題,雖然可以靠口譯解決,不過成本瞬間提升,光靠贊助很難,所以幾乎是要全商業化的形式才比較有可能,票價也會因此和國際接軌;入境問題,中國人要到台灣困難度很高(好像還有幾個國家要來也很困難,一時忘了),這也造成一些國際研討會亞洲場不考慮台灣的原因,這問題很難靠主辦解決就是。

最後還是要感謝 Mozilla 的贊助,還有 HKOSCON 大會和 Sammy Fung 的協助讓這趟行程可以很順利的完成,Sammy 也是 COSCUP 的常客,前陣子還開始了opensource.asia這個網站,收集亞洲相關的 Open Source 活動,大家有興趣可以 COSCUP 活動時找他聊聊。

HKOSCON 2016


Web Push

前兩天要研究一下 Chrome 接 GCM 的實做,發現到 Google 又出一個新的服務叫 Firebase,然後新的 cloud messaging 服務就叫Firebase Cloud Messaging(FCM),隨便看了一下 Google 官方的文件,結果發現有提到另外一個正在制訂中的Web Push Protocol,照文件的說法,FCM 也只是個過渡時期的方案,最終目標還是用這個 Web Push Protocol,於是我就研究了一下他的設計,發現設計的還蠻漂亮的。

整個 Web Push Protocol 的基本架構如下圖:

Web Push Protocol

User Agent(UA) 通常是行動端的應用程式、Application 則是自家服務的後台;整個流程首先是 UA 透過 HTTP/1.1 POST 去跟 Push Service 訂閱(第一條橫線 Subscribe),然後會拿到一個 subscription resource,可能長成:

https://push.example.net/subscription/LBhhw0OohO-Wl4Oi971UG

另外還會拿到一個發訊息用的 push resource:

https://push.example.net/push/JzLQ3raZJfFBR0aqvOMsLrt54w4rJUsV

可以注意到兩個 resource 後面的 token 是不一樣的,兩者之間的 mapping 就是 Push Service 來負責;然後 UA 拿到這兩個網址後,發訊息用的 push resource 要交給自家服務的後台,也就是圖上第三條橫線 Distribute Push Resource,另外一個 subscription resource 則是要自己使用,UA 用 HTTP/2 打 GET 到 subscription resource,然後 push service 會把連線保持住不關掉,這就是圖上的第二條橫線 Monitor;自家服務後台的要送訊息的時候,就打 POST 去 push resource,也就是第四條橫線,從 Application 到 Push Service 間的 Push Message,push service 收到這個訊息時,就利用 HTTP/2 的 Server Push 機制主動傳送訊息,最後這個動作就是第五條橫線的 Push Message 了。

就這樣很漂亮的用 HTTP/1.1 + HTTP/2 把一個基本的 Cloud Message Service 的協定建構起來,而除了這最基本的訊息傳遞外,這份文件還有定義像是訊息重要度、訊息回條、群組訊息等等的方法,設計都還蠻漂亮的,安全性的部分則是限制走 HTTPS over TLS,還有 operation 相關的說明,像是實際上要跑起這個服務,需要大量的 TCP connection 等等(因為都走 HTTP 了),有興趣的可以加減看一下。

補充:Firefox 目前實做的似乎就是這個協定更早版本的草案


YAJS.vim and Vim Syntax Highlight

上個週末在香港 Open Source Conference分享的主題,第一次使用英文分享,結果表現不太好,不知道上場前喝點啤酒會不會比較好就是...

這個主題本來是想要投 COSCUP 的,主要是想介紹一下之前在做yajs.vim時遇到的比較有印象的問題,在這之前先介紹一下 Vim Syntax Highlight 的機制,因為這些問題很多都和 Vim 的 Syntax Highlight 機制的設計關係很大,然後最後就是有一個還沒辦法解的問題,這個問題就是 yajs.vim 目前還沒辦法完美的 highlight 有 default parameter 的 arrow function。


nginx & fcgiwrap

菲貓,

雖然很久以前就想換到 nginx 試試看,不過直到最近這次更新才換成功,最早單純只是想要輕量一點的 HTTP server,後來則是因為和 Apache 相比,nginx 明顯開發更新比較快,最近有很多想要嘗試各種新功能都是 nginx 先做,像是 HTTP/2,還有最近這次的 brotli 支援,而以前沒辦法換過去的最主要原因,其實是 nginx 沒有 CGI 的支援,跑 MovableType 會有困難,雖然 MovableType 可以用 FastCGI,不過很難設定,我安裝過 n 次大概也只有成功過一兩次,所以其實一直都不太考慮這個選項。

不過認真研究了一下,終於發現其實可以透過FCGI Wrap這個工具來達成 nginx 對 CGI script 的支援,它的作法其實就是一個中間人,把 FCGI 介面轉到 CGI 介面過去,我大概設定了一下跑 MovableType 的 nginx conf 如下:

location ~ ^/path/to/mt/mt.*\.cgi {
    gzip off;
    fastcgi_index index.cgi;
    fastcgi_split_path_info ^(.+?\.cgi)(/.*)$;
    if (!-e $document_root$fastcgi_script_name) {
        return 404;
    }
    include fastcgi.conf;

    ## MT-related
    fastcgi_param PERL5LIB $document_root/mt/lib;
    fastcgi_param MT_HOME $document_root/mt/;
    fastcgi_param MT_CONFIG $document_root/mt/mt-config.cgi;

    fastcgi_param PATH_INFO $fastcgi_path_info;
    fastcgi_param PATH_TRANSLATED $fastcgi_path_info;
    fastcgi_pass unix:/var/run/fcgiwrap.sock;
}

然後主機上要開好 FCGI Wrap 的服務,我是用 ArchLinux 的 pacman 直接裝套件,然後參考官方文件,有寫說設定檔位置/usr/lib/systemd/system/fcgiwrap.socket,cat 出來就可以看到 UNIX Socket 檔案位置ListenStream=/run/fcgiwrap.sock,這個路徑的位置其實就指到上面設定最後一行的/var/run/fcgiwrap.sock/run/var/run兩邊其實有 Symbolic Link 起來,所以兩個 sock 檔案其實是同一個。

最後要說一下 conf 裡的這行:

include fastcgi.conf;

這個fastcgi.conf檔案其實是 nginx 內建好方便大家使用的,內容如下:

fastcgi_param  SCRIPT_FILENAME    $document_root$fastcgi_script_name;
fastcgi_param  QUERY_STRING       $query_string;
fastcgi_param  REQUEST_METHOD     $request_method;
fastcgi_param  CONTENT_TYPE       $content_type;
fastcgi_param  CONTENT_LENGTH     $content_length;

fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
fastcgi_param  REQUEST_URI        $request_uri;
fastcgi_param  DOCUMENT_URI       $document_uri;
fastcgi_param  DOCUMENT_ROOT      $document_root;
fastcgi_param  SERVER_PROTOCOL    $server_protocol;
fastcgi_param  REQUEST_SCHEME     $scheme;
fastcgi_param  HTTPS              $https if_not_empty;

fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
fastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;

fastcgi_param  REMOTE_ADDR        $remote_addr;
fastcgi_param  REMOTE_PORT        $remote_port;
fastcgi_param  SERVER_ADDR        $server_addr;
fastcgi_param  SERVER_PORT        $server_port;
fastcgi_param  SERVER_NAME        $server_name;

# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param  REDIRECT_STATUS    200;

可以看到其實這個檔案就是把直接走 FCGI 時會遺失的環境變數補回去用的,nginx 還提供很多這類檔案,以前都不太清楚怎麼剛裝好的 nginx 會附上一堆沒有用到的 conf 檔,直到這次才瞭解它們其實都很有用啊。


➡ 看看其它文章