【茶包射手專欄】Cassini's Response Header Encoding
1 |
先前有一篇文章討論ASP.NET如何正確傳回中文下載檔名,網友帆歷經九九八十一難後,終於修成正果,還揪出一隻鬼 --- VS 2005內建的ASP.NET Development Server似乎不支援HeaderEncoding!
為了解開這個謎團,茶包射手再次整裝出發!
這回我們使用的辦案工具是HttpWatch Pro(原因是在我的Vista上Fiddler啟動時的UAC升等視窗都會被埋掉,啟動後會導致IE7 Hang住,最要命的是它抓不到我存取Localhost的記錄,怕明明要射茶包變成修理小提琴,我很有魄力地立刻換了武器),用HttpWatch的錄製封包功能,Dev Server與IIS7的Header差異馬上現形!
ASP.NET Development Server
HTTP/1.1 200 OK
Server: ASP.NET Development Server/9.0.0.0
Date: Sat, 08 Sep 2007 08:07:38 GMT
X-AspNet-Version: 2.0.50727
content-disposition: attachment;filename=銝剜?瑼?.xls
Cache-Control: private
Content-Type: application/octet-stream
Content-Length: 16384
Connection: Close
IIS 7
HTTP/1.1 200 OK
Cache-Control: private
Content-Length: 16384
Content-Type: application/octet-stream
Server: Microsoft-IIS/7.0
X-AspNet-Version: 2.0.50727
content-disposition: attachment;filename=中文檔名.xls
X-Powered-By: ASP.NET
Date: Sat, 08 Sep 2007 08:08:21 GMT
我發現ASP.NET Dev Server傳回的Header中,明明要求用Big5 Encoding,但傳回的卻是亂碼,而依多年來跟中文亂碼打交道的經驗,直覺上像是UTF-8被解成Big5(長度變長,而且有問號)。我寫了兩行Code驗證! Bingo! 就是UTF-8被硬轉成Big5。
由這個現象來推斷,ASP.NET Dev Server並未依我們的要求,在Header區使用Big5 Encoding,而是用UTF-8囉?
要確證這一點,得靠Source Code,但沒有ASP.NET Dev Server的Code怎麼辦? 用Reflector? 可以。但我想到另一招,ASP.NET Dev Server的前身是一個Sample專案Cassini,所以我們可以找它爸爸驗一下DNA吧!
在我的機器上把Cassini Sample Web Server架起來,第一件事先確定是不是有其父必有其子? 下載中文檔名也會失敗?
答案是!!! Cassini傳回的Response Header與ASP.NET Dev Server除了Server Name外,完全相同。
Cassini
HTTP/1.1 200 OK
Server: Microsoft-Cassini/1.0.0.0
Date: Sat, 08 Sep 2007 08:02:56 GMT
X-AspNet-Version: 2.0.50727
content-disposition: attachment;filename=銝剜?瑼?.xls
Cache-Control: private
Content-Type: application/octet-stream
Content-Length: 16384
Connection: Close
追蹤了程式碼,我在Cassini.Connection Class中找到了答案,在Cassini的邏輯中,並不會因為指定Response.HeaderEncoding而變更編碼方式,一律用UTF-8解成byte[]後送至Socket,而在IE端則是以Big5去解析它,符合先前的實驗與分析。Case Closed!
private static String MakeResponseHeaders(
int statusCode, String moreHeaders, int contentLength,
bool keepAlive) {
StringBuilder sb = new StringBuilder();
sb.Append("HTTP/1.1 " + statusCode + " " +
HttpWorkerRequest.GetStatusDescription(statusCode) + "\r\n");
sb.Append("Server: Microsoft-Cassini/"
+Messages.VersionString+"\r\n");
sb.Append("Date: " + DateTime.Now.ToUniversalTime().
ToString("R", DateTimeFormatInfo.InvariantInfo) + "\r\n");
if (contentLength >= 0)
sb.Append("Content-Length: " + contentLength + "\r\n");
if (moreHeaders != null)
sb.Append(moreHeaders);
if (!keepAlive)
sb.Append("Connection: Close\r\n");
sb.Append("\r\n");
return sb.ToString();
}
public void WriteEntireResponseFromString(
int statusCode, String extraHeaders, String body,
bool keepAlive) {
try {
int bodyLength = (body != null) ?
Encoding.UTF8.GetByteCount(body) : 0;
String headers =
MakeResponseHeaders(statusCode, extraHeaders,
bodyLength, keepAlive);
_socket.Send(Encoding.UTF8.GetBytes(headers + body));
}
finally {
if (!keepAlive)
Close();
}
}
Comments
# by eric
黑大哥的文章真的讓小弟佩服的五體投地.不論是文章的深度或隨時可以變出來的怪機司,都真的讓小弟佩服不已.國內的高手應該也是不少,但是像黑大哥這樣願意整理分享的實在是少數.在此致上深深的敬意!!