上一篇中我們實(shí)現(xiàn)了接受瀏覽器的請求,并返回本地的網(wǎng)頁給瀏覽器展示,接下來對該簡單的功能進(jìn)行下一步完善
Content-Type
http響應(yīng)頭中非常重要的一個字段是Content-Type,它決定了瀏覽器如何解析返回的響應(yīng)內(nèi)容,如果該字段缺失則默認(rèn)為text/html格式,因此我們上文返回的簡單html網(wǎng)頁并沒有添加該字段,瀏覽器也能正常解析。
但是稍微復(fù)雜的前端頁面都包含了css樣式文件,JavaScript腳本文件等,如果不指定Content-Type字段,則瀏覽器無法正確解析這些文件(有些瀏覽器超強(qiáng)的兼容性可以一定程度上自動判斷內(nèi)容格式)
因此我們在返回本地的文件作為響應(yīng)時,需要手動設(shè)置Content-Type字段,判斷依據(jù)為文件的擴(kuò)展名,這里使用了一個map維護(hù)擴(kuò)展名與Content-Type映射關(guān)系。
void HttpResponse::init_content_type_map(){
content_type_map.insert(pair<string,string>("html","text/html"));
content_type_map.insert(pair<string,string>("htm","text/html"));
content_type_map.insert(pair<string,string>("shtml","text/html"));
content_type_map.insert(pair<string,string>("css","text/css"));
content_type_map.insert(pair<string,string>("js","text/javascript"));
content_type_map.insert(pair<string,string>("txt","text/plain"));
content_type_map.insert(pair<string,string>("js","text/javascript"));
content_type_map.insert(pair<string,string>("xml","text/xml"));
content_type_map.insert(pair<string,string>("ico","image/x-icon"));
content_type_map.insert(pair<string,string>("jpg","image/jpeg"));
content_type_map.insert(pair<string,string>("jpeg","image/jpeg"));
content_type_map.insert(pair<string,string>("jpe","image/jpeg"));
content_type_map.insert(pair<string,string>("gif","image/gif"));
content_type_map.insert(pair<string,string>("png","image/png"));
content_type_map.insert(pair<string,string>("tiff","image/tiff"));
content_type_map.insert(pair<string,string>("tif","image/tiff"));
content_type_map.insert(pair<string,string>("rgb","image/x-rgb"));
content_type_map.insert(pair<string,string>("mpeg","video/mpeg"));
content_type_map.insert(pair<string,string>("mpg","video/mpeg"));
content_type_map.insert(pair<string,string>("mpe","video/mpeg"));
content_type_map.insert(pair<string,string>("qt","video/quicktime"));
content_type_map.insert(pair<string,string>("mov","video/quicktime"));
content_type_map.insert(pair<string,string>("avi","video/x-msvideo"));
content_type_map.insert(pair<string,string>("movie","video/x-sgi-movie"));
content_type_map.insert(pair<string,string>("woff","application/font-woff"));
content_type_map.insert(pair<string,string>("ttf","application/octet-stream"));
}
這里添加了一些常用格式的Content-Type類型,后續(xù)涉及到更復(fù)雜的文件類型時對其進(jìn)一步擴(kuò)展
gzip壓縮
在http響應(yīng)的結(jié)構(gòu)中,我們常??梢钥吹揭粋€名為Content-Encoding的字段,其值大多為gzip,deflate等。該字段決定的是http響應(yīng)體的編碼格式。目前主流瀏覽器均支持gzip等格式的壓縮格式。使用壓縮格式的最大好處就是減少網(wǎng)絡(luò)傳輸?shù)男畔⒘?,提高網(wǎng)頁加載速度,但由于服務(wù)端多了壓縮的步驟,也一定程度增加了服務(wù)器的負(fù)擔(dān)(客戶端單次處理時,解壓的影響可以忽略不計(jì))。
而gzip格式又是應(yīng)用最廣泛的一種壓縮格式,其對文本內(nèi)容的壓縮率常??梢赃_(dá)到40%以上,對于html,css,javascript文件均有著非常好的壓縮效果。
本文實(shí)現(xiàn)的Minihttpd為了增加gzip格式的壓縮功能使用了zlib庫,其代碼均為c編寫,使用方法相對簡單,這里列出部分供參考
//raw_data為原始數(shù)據(jù),buffer為壓縮后數(shù)據(jù)存儲緩沖區(qū),buffer_size為緩沖區(qū)大小,返回值為壓縮后數(shù)據(jù)字節(jié)數(shù)
uLong gzip_compress(string raw_data,Bytef*& buffer,int buffer_size){
size_t raw_data_size = raw_data.size();
z_stream strm;
z_stream d_stream;
d_stream.zalloc = NULL;
d_stream.zfree = NULL;
d_stream.opaque = NULL;
d_stream.next_in = (Bytef*)raw_data.c_str();
d_stream.avail_in = raw_data_size;
d_stream.next_out = buffer;
d_stream.avail_out = buffer_size;
int ret = deflateInit2(&d_stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
MAX_WBITS + 16, 8, Z_DEFAULT_STRATEGY);
if (Z_OK != ret)
{
Log::log("init deflate error",ERROR);
// cout<< ret <<endl;
}
int err = 0;
int flag = 0;
for(;;) {
if((err = deflate(&d_stream, Z_FINISH)) == Z_STREAM_END) break;
if(flag > 3){
stringstream ss;
ss<< "deflate failed,errNo = "<<err;
Log::log(ss.str(),ERROR);
return 0;
}
//輸出緩沖區(qū)不足,嘗試擴(kuò)容,最多三次擴(kuò)容失敗則放棄壓縮
if(err == Z_BUF_ERROR){
flag++;
delete buffer;
buffer_size = buffer_size*1.5;
buffer = new Bytef[buffer_size];
d_stream.next_out = buffer;
d_stream.avail_out = buffer_size;
stringstream ss;
ss<< "deflate buffer error,try larger buffer :"<<flag;
Log::log(ss.str(),WARN);
}
}
if(deflateEnd(&d_stream) != Z_OK){
Log::log("deflate failed when end",ERROR);
return 0;
}
return d_stream.total_out;
}
主要工作流程為:
- deflateInit2() 設(shè)置壓縮格式等信息
- deflate() 進(jìn)行壓縮
- deflateEnd() 壓縮完畢釋放臨時空間等收尾工作
編碼格式判別
接收到http請求后,首先判斷請求頭中是否包含Accept-Encoding字段,如果存在,檢查其后面接受的壓縮格式等,決定是否使用gzip壓縮(注意響應(yīng)頭也需要添加Content-Encoding:gzip字段)
Github
https://github.com/njuwuyuxin/MiniHttpd
歡迎共同學(xué)習(xí)