从零实现HTTP服务器——Minihttpd(二)

上一篇中我们实现了接受浏览器的请求,并返回本地的网页给浏览器展示,接下来对该简单的功能进行下一步完善

Content-Type

http响应头中非常重要的一个字段是Content-Type,它决定了浏览器如何解析返回的响应内容,如果该字段缺失则默认为text/html格式,因此我们上文返回的简单html网页并没有添加该字段,浏览器也能正常解析。
但是稍微复杂的前端页面都包含了css样式文件,JavaScript脚本文件等,如果不指定Content-Type字段,则浏览器无法正确解析这些文件(有些浏览器超强的兼容性可以一定程度上自动判断内容格式)
因此我们在返回本地的文件作为响应时,需要手动设置Content-Type字段,判断依据为文件的扩展名,这里使用了一个map维护扩展名与Content-Type映射关系。

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类型,后续涉及到更复杂的文件类型时对其进一步扩展

gzip压缩

在http响应的结构中,我们常常可以看到一个名为Content-Encoding的字段,其值大多为gzip,deflate等。该字段决定的是http响应体的编码格式。目前主流浏览器均支持gzip等格式的压缩格式。使用压缩格式的最大好处就是减少网络传输的信息量,提高网页加载速度,但由于服务端多了压缩的步骤,也一定程度增加了服务器的负担(客户端单次处理时,解压的影响可以忽略不计)。
而gzip格式又是应用最广泛的一种压缩格式,其对文本内容的压缩率常常可以达到40%以上,对于html,css,javascript文件均有着非常好的压缩效果。

本文实现的Minihttpd为了增加gzip格式的压缩功能使用了zlib库,其代码均为c编写,使用方法相对简单,这里列出部分供参考

//raw_data为原始数据,buffer为压缩后数据存储缓冲区,buffer_size为缓冲区大小,返回值为压缩后数据字节数
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;
        }

        //输出缓冲区不足,尝试扩容,最多三次扩容失败则放弃压缩
        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;
}

主要工作流程为:

  1. deflateInit2() 设置压缩格式等信息
  2. deflate() 进行压缩
  3. deflateEnd() 压缩完毕释放临时空间等收尾工作

编码格式判别

接收到http请求后,首先判断请求头中是否包含Accept-Encoding字段,如果存在,检查其后面接受的压缩格式等,决定是否使用gzip压缩(注意响应头也需要添加Content-Encoding:gzip字段)

Github

https://github.com/njuwuyuxin/MiniHttpd
欢迎共同学习

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341