void http_got_header(struct connection *c, struct read_buffer *rb) { int cf; int state = c->state != S_PROC ? S_GETH : S_PROC; unsigned char *head; unsigned char *cookie, *ch; int a, h, version; unsigned char *d; struct cache_entry *e; struct http_connection_info *info; unsigned char *host = upcase(c->url[0]) != 'P' ? c->url : get_url_data(c->url); set_timeout(c); info = c->info; if (rb->close == 2) { unsigned char *h; if (!c->tries && (h = get_host_name(host))) { if (info->bl_flags & BL_NO_CHARSET) { del_blacklist_entry(h, BL_NO_CHARSET); } else { add_blacklist_entry(h, BL_NO_CHARSET); c->tries = -1; } mem_free(h); } setcstate(c, S_CANT_READ); retry_connection(c); return; } rb->close = 0; again: if ((a = get_header(rb)) == -1) { setcstate(c, S_HTTP_ERROR); abort_connection(c); return; } if (!a) { read_from_socket(c, c->sock1, rb, http_got_header); setcstate(c, state); return; } if (a != -2) { head = mem_alloc(a + 1); memcpy(head, rb->data, a); head[a] = 0; kill_buffer_data(rb, a); } else { head = stracpy("HTTP/0.9 200 OK\r\nContent-Type: text/html\r\n\r\n"); } if (get_http_code(head, &h, &version) || h == 101) { mem_free(head); setcstate(c, S_HTTP_ERROR); abort_connection(c); return; } if (check_http_server_bugs(host, c->info, head) && is_connection_restartable(c)) { mem_free(head); setcstate(c, S_RESTART); retry_connection(c); return; } ch = head; while ((cookie = parse_http_header(ch, "Set-Cookie", &ch))) { unsigned char *host = upcase(c->url[0]) != 'P' ? c->url : get_url_data(c->url); set_cookie(NULL, host, cookie); mem_free(cookie); } if (h == 100) { mem_free(head); state = S_PROC; goto again; } if (h < 200) { mem_free(head); setcstate(c, S_HTTP_ERROR); abort_connection(c); return; } if (h == 204) { mem_free(head); setcstate(c, S_HTTP_204); http_end_request(c, 0); return; } if (h == 304) { mem_free(head); setcstate(c, S_OK); http_end_request(c, 1); return; } if ((h == 500 || h == 502 || h == 503 || h == 504) && http_bugs.retry_internal_errors && is_connection_restartable(c)) { /* !!! FIXME: wait some time ... */ mem_free(head); setcstate(c, S_RESTART); retry_connection(c); return; } if (!c->cache && get_cache_entry(c->url, &c->cache)) { mem_free(head); setcstate(c, S_OUT_OF_MEM); abort_connection(c); return; } e = c->cache; e->http_code = h; if (e->head) mem_free(e->head); e->head = head; if ((d = parse_http_header(head, "Expires", NULL))) { time_t t = parse_http_date(d); if (t && e->expire_time != 1) e->expire_time = t; mem_free(d); } if ((d = parse_http_header(head, "Pragma", NULL))) { if (!casecmp(d, "no-cache", 8)) e->expire_time = 1; mem_free(d); } if ((d = parse_http_header(head, "Cache-Control", NULL))) { char *f = d; while (1) { while (*f && (*f == ' ' || *f == ',')) f++; if (!*f) break; if (!casecmp(f, "no-cache", 8) || !casecmp(f, "must-revalidate", 15)) { e->expire_time = 1; } if (!casecmp(f, "max-age=", 8)) { if (e->expire_time != 1) e->expire_time = time(NULL) + atoi(f + 8); } while (*f && *f != ',') f++; } mem_free(d); } #ifdef HAVE_SSL if (c->ssl) { int l = 0; if (e->ssl_info) mem_free(e->ssl_info); e->ssl_info = init_str(); add_num_to_str(&e->ssl_info, &l, SSL_get_cipher_bits(c->ssl, NULL)); add_to_str(&e->ssl_info, &l, "-bit "); add_to_str(&e->ssl_info, &l, SSL_get_cipher_version(c->ssl)); add_to_str(&e->ssl_info, &l, " "); add_to_str(&e->ssl_info, &l, (unsigned char *)SSL_get_cipher_name(c->ssl)); } #endif if (e->redirect) mem_free(e->redirect), e->redirect = NULL; if (h == 301 || h == 302 || h == 303 || h == 307) { if ((h == 302 || h == 307) && !e->expire_time) e->expire_time = 1; if ((d = parse_http_header(e->head, "Location", NULL))) { unsigned char *user, *ins; unsigned char *newuser, *newpassword; if (!parse_url(d, NULL, &user, NULL, NULL, NULL, &ins, NULL, NULL, NULL, NULL, NULL, NULL) && !user && ins && (newuser = get_user_name(host))) { if (*newuser) { int ins_off = ins - d; newpassword = get_pass(host); if (!newpassword) newpassword = stracpy(""); add_to_strn(&newuser, ":"); add_to_strn(&newuser, newpassword); add_to_strn(&newuser, "@"); extend_str(&d, strlen(newuser)); ins = d + ins_off; memmove(ins + strlen(newuser), ins, strlen(ins) + 1); memcpy(ins, newuser, strlen(newuser)); mem_free(newpassword); } mem_free(newuser); } if (e->redirect) mem_free(e->redirect); e->redirect = d; e->redirect_get = h == 303; } } if (!e->expire_time && strchr(c->url, POST_CHAR)) e->expire_time = 1; info->close = 0; info->length = -1; info->version = version; if ((d = parse_http_header(e->head, "Connection", NULL)) || (d = parse_http_header(e->head, "Proxy-Connection", NULL))) { if (!strcasecmp(d, "close")) info->close = 1; mem_free(d); } else if (version < 11) info->close = 1; cf = c->from; c->from = 0; if ((d = parse_http_header(e->head, "Content-Range", NULL))) { if (strlen(d) > 6) { d[5] = 0; if (!(strcasecmp(d, "bytes")) && d[6] >= '0' && d[6] <= '9') { #if defined(HAVE_STRTOLL) long long f = strtoll(d + 6, NULL, 10); #elif defined(HAVE_STRTOQ) longlong f = strtoq(d + 6, NULL, 10); #else long f = strtol(d + 6, NULL, 10); if (f == MAXLONG) f = -1; #endif if (f >= 0 && (off_t)f >= 0 && (off_t)f == f) c->from = f; } } mem_free(d); } if (cf && !c->from && !c->unrestartable) c->unrestartable = 1; if (c->from > cf || c->from < 0) { setcstate(c, S_HTTP_ERROR); abort_connection(c); return; } if ((d = parse_http_header(e->head, "Content-Length", NULL))) { unsigned char *ep; #if defined(HAVE_STRTOLL) long long l = strtoll(d, (char **)(void *)&ep, 10); #elif defined(HAVE_STRTOQ) longlong l = strtoq(d, (char **)(void *)&ep, 10); #else long l = strtol(d, (char **)(void *)&ep, 10); if (l == MAXLONG) l = -1; #endif if (!*ep && l >= 0 && (off_t)l >= 0 && (off_t)l == l) { if (!info->close || version >= 11) info->length = l; if (c->from + l >= 0) c->est_length = c->from + l; } mem_free(d); } if ((d = parse_http_header(e->head, "Accept-Ranges", NULL))) { if (!strcasecmp(d, "none") && !c->unrestartable) c->unrestartable = 1; mem_free(d); } else { if (!c->unrestartable && !c->from) c->unrestartable = 1; } if (info->bl_flags & BL_NO_RANGE && !c->unrestartable) c->unrestartable = 1; if ((d = parse_http_header(e->head, "Transfer-Encoding", NULL))) { if (!strcasecmp(d, "chunked")) { info->length = -2; info->chunk_remaining = -1; } mem_free(d); } if (!info->close && info->length == -1) info->close = 1; if ((d = parse_http_header(e->head, "Last-Modified", NULL))) { if (e->last_modified && strcasecmp(e->last_modified, d)) { delete_entry_content(e); if (c->from) { c->from = 0; mem_free(d); setcstate(c, S_MODIFIED); retry_connection(c); return; } } if (!e->last_modified) e->last_modified = d; else mem_free(d); } if (!e->last_modified && (d = parse_http_header(e->head, "Date", NULL))) e->last_modified = d; if (info->length == -1 || (version < 11 && info->close)) rb->close = 1; read_http_data(c, rb); }
void http_server_main_loop_poll(int listenedfd) { // struct pollfd client[MAX_FD_SETSIZE]; int i = 0,maxinx = 1; for(i = 0;i < MAX_FD_SETSIZE;++i) { client[i].fd = -1; } int nready = 0; int connfd = -1; int ret = 0; struct sockaddr_in peeraddr; socklen_t peerlen = sizeof(peeraddr); http_data h_info; client[0].fd = listenedfd; client[0].events = POLLIN; while(1) { nready = poll(client,maxinx,-1); if(-1 == nready) { if(EINTR == errno) { continue; } perror("poll"); break; } if(0 == nready) { continue; } if(client[0].revents & POLLIN) { //表明监听套接字有可读的事件 peerlen = sizeof(peeraddr); connfd = accept(listenedfd, (struct sockaddr *)&peeraddr, &peerlen); if(-1 == connfd) { perror("accept"); break; } for(i = 0;i < MAX_FD_SETSIZE;++i) { if(client[i].fd < 0) { client[i].fd = connfd; if(i > maxinx) { maxinx = i + 2; } else if(1 == maxinx) { maxinx = 2; } break; } } if(MAX_FD_SETSIZE == i) { fprintf(stderr,"too many clients\n"); exit(EXIT_FAILURE); } DEBUG_PRINT("ip = %s port = %d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port)); //设置好这个套接字要关心的事件 client[i].events = POLLIN; if(--nready <= 0) { continue; } } for(i = 1;i < maxinx;++i) { //遍历其它的套接字的事件接收情况 if(-1 == client[i].fd) { continue; } if(client[i].events & POLLIN) { //业务代码处理 bzero(&h_info,sizeof(h_info)); h_info.data_len = sizeof(h_info.buf); //处理接收到的数据 ret = read_http_data(client[i].fd,&h_info); if(ret < 0) { perror("read_http_data"); return; } if(0 == ret) { //client close DEBUG_PRINT("client close\n"); //FD_CLR(client[i],&rfset); close(client[i].fd); client[i].fd = -1; } else { //解析数据填充数据类型结构体 ret = analysis_http_data(&h_info); ret = send_http_data(client[i].fd,&h_info); if(ret < 0) { perror("send_http_data"); return; } } } if(--nready <= 0) { break; } } } }
void http_server_main_loop_epoll_et(int listenedfd) { int i = 0; int nready = 0; int connfd = -1; int ret = 0; struct sockaddr_in peeraddr; socklen_t peerlen = sizeof(peeraddr); http_data h_info; //建立epoll机制 int epollfd = epoll_create1(EPOLL_CLOEXEC); struct epoll_event event; //采用et模式的时候所有加入epoll的套接字都要采用非阻塞的方式 //如果文件描述符是阻塞的,那么读或者写操作会因为没有后续的事 //件而一直处于阻塞状态(饥渴状态) activate_nonblock(listenedfd); event.data.fd = listenedfd; event.events = EPOLLIN | EPOLLET;//采用边沿触发的方式 epoll_ctl(epollfd,EPOLL_CTL_ADD,listenedfd,&event); struct epoll_event events[MAX_FD_SETSIZE]; while(1) { nready = epoll_wait(epollfd,events,MAX_FD_SETSIZE,-1); if(-1 == nready) { if(EINTR == errno) { continue; } perror("epoll_wait"); break; } if(0 == nready) { continue; } //每次返回的事件都会保存到events数组中所以可以直接来进行判断 for(i = 0;i < nready;++i) { if(events[i].data.fd == listenedfd) { while(1) { //accept可能会有多次连接(特别是在进行并发测试的时候) //如果不加,可能会导致write出现EPIPE的错误可能是由于 //epoll的ET模式的机制,如果同时到达的accept不能处理 //完,那么有些就会饿死在epoll中 peerlen = sizeof(peeraddr); connfd = accept(listenedfd, (struct sockaddr *)&peeraddr,&peerlen); if(connfd < 0) { if(EAGAIN == errno || EWOULDBLOCK == errno) { break; } else { perror("accept"); return; } } DEBUG_PRINT("ip = %s port = %d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port)); //设定为非阻塞套接字 activate_nonblock(connfd); event.data.fd = connfd; event.events = EPOLLIN | EPOLLET; epoll_ctl(epollfd,EPOLL_CTL_ADD,connfd,&event); } } else if(events[i].events & EPOLLIN) { connfd = events[i].data.fd; if(connfd < 0) { continue; } //业务逻辑代码 bzero(&h_info,sizeof(h_info)); h_info.data_len = sizeof(h_info.buf); //处理接收到的数据 ret = read_http_data(connfd,&h_info); if(ret < 0) { if(EAGAIN == errno || EWOULDBLOCK == errno) { continue; } else { perror("read_http_data"); return; } } if(0 == ret) { //client close DEBUG_PRINT("client close\n"); //FD_CLR(client[i],&rfset); close(connfd); //从epoll中删除代码 event = events[i]; epoll_ctl(epollfd,EPOLL_CTL_DEL,connfd,&event); continue; } //解析数据填充数据类型结构体 ret = analysis_http_data(&h_info); ret = send_http_data(connfd,&h_info); if(ret < 0) { //这里一般来说会有EPIPE的错误,但是在其它很多的程序中, //对这个错误似乎是无法接受的直接报错退出 perror("send_http_data"); return; } } } } }
//select方式来实现IO复用处理发送 //这个select主要坑的地方在于每次select的文件描述符集合收到事件解除阻塞然后进入到业务处理函数中处理完成后, //必须要重新初始化一边文件描述符集合,然后把要监控的IO重新加入一遍。 //否则文件描述符集合不会再接收新的信号 //上述原因正是导致前期实现的时候总是出现select阻塞导致客户端老是收不到数据的原因。 void http_server_main_loop_select(int listenedfd) { //select方式来实现IO复用处理发送 //首先是设定好一个文件描述符的集合,管理收到数据的文件描述符 fd_set rfset; FD_ZERO(&rfset); FD_SET(listenedfd,&rfset); int nready = 0; int maxfd = listenedfd; int connfd = -1; int client[MAX_FD_SETSIZE] = {[0 ... MAX_FD_SETSIZE - 1] = -1}; int i = 0; int maxinx = 1; int ret = 0; struct sockaddr_in peeraddr; socklen_t peerlen = sizeof(peeraddr); http_data h_info; while(1) { //DEBUG_PRINT("will select!maxfd %d\n",maxfd); nready = select(maxfd + 1,&rfset,NULL,NULL,NULL); //DEBUG_PRINT("after select!\n"); if(nready < 0) { perror("select"); break; } if(nready == 0) { continue; } //否则就是接收到了数据需要进行判断 if(FD_ISSET(listenedfd,&rfset)) { peerlen = sizeof(peeraddr); connfd = accept(listenedfd, (struct sockaddr *)&peeraddr,&peerlen); if(connfd < 0) { perror("accept"); break; } DEBUG_PRINT("ip = %s port = %d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port)); //接收到了新的连接套接字就要将其加入到文件集合中进行监听 FD_SET(connfd,&rfset); // for(i = 0;i < MAX_FD_SETSIZE;++i) { if(-1 == client[i]) { client[i] = connfd; if(i >= maxinx) { maxinx = i + 1; } break; } } if(MAX_FD_SETSIZE == i) { fprintf(stderr,"too many clients\n"); exit(EXIT_FAILURE); } if(connfd > maxfd) { maxfd = connfd; } //说明不需要处理了,因为已经处理完了 if(--nready <= 0) { //重新进入select之前一定要保证文件描述符集合被重新初始化 maxfd = reset_fd_set_and_maxfd(listenedfd,maxinx,client,&rfset); continue; } } //循环判断连接套接字是否有数据收到 for(i = 0;i < maxinx;++i) { if(client[i] < 0) { continue; } if(FD_ISSET(client[i],&rfset)) { bzero(&h_info,sizeof(h_info)); h_info.data_len = sizeof(h_info.buf); //处理接收到的数据 ret = read_http_data(client[i],&h_info); if(ret < 0) { perror("read_http_data"); return; } if(0 == ret) { //client close DEBUG_PRINT("client close\n"); //FD_CLR(client[i],&rfset); close(client[i]); client[i] = -1; } else { //解析数据填充数据类型结构体 ret = analysis_http_data(&h_info); ret = send_http_data(client[i],&h_info); if(ret < 0) { perror("send_http_data"); return; } } if(--nready <= 0) { break; } } } maxfd = reset_fd_set_and_maxfd(listenedfd,maxinx,client,&rfset); } }