Linux企业级项目实践之网络爬虫(12)——处理HTTP应答头

Web服务器的HTTP应答一般由以下几项构成:一个状态行,一个或多个应答头,一个空行,内容文档。设置HTTP应答头往往和设置状态行中的状态代码结合起来。例如,有好几个表示“文档位置已经改变”的状态代码都伴随着一个Location头,而401(Unauthorized)状态代码则必须伴随一个WWW-Authenticate头。
然而,即使在没有设置特殊含义的状态代码时,指定应答头也是很有用的。应答头可以用来完成:设置Cookie,指定修改日期,指示浏览器按照指定的间隔刷新页面,声明文档的长度以便利用持久HTTP连接,……等等许多其他任务。
设置应答头最常用的方法是HttpServletResponse的setHeader,该方法有两个参数,分别表示应答头的名字和值。和设置状态代码相似,设置应答头应该在发送任何文档内容之前进行。

HTTP应答头 说明:
Allow 服务器支持哪些请求方法(如GET、POST等)。
Content-Encoding 文档的编码(Encode)方法。只有在解码之后才可以得到Content-Type头指定的内容类型。利用gzip压缩文档能够显著地减少HTML文档的下载时间。Java的GZIPOutputStream可以很方便地进行gzip压缩,但只有Unix上的Netscape和Windows上的IE 4、IE 5才支持它。因此,Servlet应该通过查看Accept-Encoding头(即request.getHeader("Accept-Encoding"))检查浏览器是否支持gzip,为支持gzip的浏览器返回经gzip压缩的HTML页面,为其他浏览器返回普通页面。
Content-Length 表示内容长度。只有当浏览器使用持久HTTP连接时才需要这个数据。如果你想要利用持久连接的优势,可以把输出文档写入ByteArrayOutputStram,完成后查看其大小,然后把该值放入Content-Length头,最后通过byteArrayStream.writeTo(response.getOutputStream()发送内容。
Content-Type 表示后面的文档属于什么MIME类型。Servlet默认为text/plain,但通常需要显式地指定为text/html。由于经常要设置Content-Type,因此HttpServletResponse提供了一个专用的方法setContentType。
Date 当前的GMT时间。你可以用setDateHeader来设置这个头以避免转换时间格式的麻烦。
Expires 应该在什么时候认为文档已经过期,从而不再缓存它?



void * recv_response(void * arg)
{
    begin_thread();


    int i, n, trunc_head = 0, len = 0;
    char * body_ptr = NULL;
    evso_arg * narg = (evso_arg *)arg;
    Response *resp = (Response *)malloc(sizeof(Response));
    resp->header = NULL;
    resp->body = (char *)malloc(HTML_MAXLEN);
    resp->body_len = 0;
    resp->url = narg->url;


    regex_t re;
    if (regcomp(&re, HREF_PATTERN, 0) != 0) {/* compile error */
        SPIDER_LOG(SPIDER_LEVEL_ERROR, "compile regex error");
    }


    SPIDER_LOG(SPIDER_LEVEL_INFO, "Crawling url: %s/%s", narg->url->domain, narg->url->path);


    while(1) {
        /* what if content-length exceeds HTML_MAXLEN? */
        n = read(narg->fd, resp->body + len, 1024);
        if (n < 0) {
            if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) { 
                /**
                 * TODO: Why always recv EAGAIN?
                 * should we deal EINTR
                 */
                //SPIDER_LOG(SPIDER_LEVEL_WARN, "thread %lu meet EAGAIN or EWOULDBLOCK, sleep", pthread_self());
                usleep(100000);
                continue;
            } 
            SPIDER_LOG(SPIDER_LEVEL_WARN, "Read socket fail: %s", strerror(errno));
            break;


        } else if (n == 0) {
            /* finish reading */
            resp->body_len = len;
            if (resp->body_len > 0) {
                extract_url(&re, resp->body, narg->url);
            }
            /* deal resp->body */
            for (i = 0; i < (int)modules_post_html.size(); i++) {
                modules_post_html[i]->handle(resp);
            }


            break;


        } else {
            //SPIDER_LOG(SPIDER_LEVEL_WARN, "read socket ok! len=%d", n);
            len += n;
            resp->body[len] = '\0';


            if (!trunc_head) {
                if ((body_ptr = strstr(resp->body, "\r\n\r\n")) != NULL) {
                    *(body_ptr+2) = '\0';
                    resp->header = parse_header(resp->body);
                    if (!header_postcheck(resp->header)) {
                        goto leave; /* modulues filter fail */
                    }
                    trunc_head = 1;


                    /* cover header */
                    body_ptr += 4;
                    for (i = 0; *body_ptr; i++) {
                        resp->body[i] = *body_ptr;
                        body_ptr++;
                    }
                    resp->body[i] = '\0';
                    len = i;
                } 
                continue;
            }
        }
    }


leave:
    close(narg->fd); /* close socket */
    free_url(narg->url); /* free Url object */
    regfree(&re); /* free regex object */
    /* free resp */
    free(resp->header->content_type);
    free(resp->header);
    free(resp->body);
    free(resp);


    end_thread();
    return NULL;
}


posted on 2014-08-31 22:35  三少爷的剑123  阅读(200)  评论(0编辑  收藏  举报

导航