隨意抓的一個HTTP包,以做參考
代碼分析:
int read_header(request * req) { int bytes, buf_bytes_left; char *check, *buffer; /* client_stream:是用來裝HTTP數據報文的buffer。 parse_pos:已經解析字符數。 client_stream_pos:從socket中讀取的字符數。 */ check = req->client_stream + req->parse_pos; buffer = req->client_stream; bytes = req->client_stream_pos; #ifdef VERY_FASCIST_LOGGING if (check < (buffer + bytes)) { buffer[bytes] = '\0'; log_error_time(); fprintf(stderr, "%s:%d - Parsing headers (\"%s\")\n", __FILE__, __LINE__, check); } #endif while (check < (buffer + bytes)) { switch (req->status) { case READ_HEADER: if (*check == '\r') { req->status = ONE_CR; req->header_end = check; } else if (*check == '\n') { req->status = ONE_LF; req->header_end = check; } break; /* HTTP數據包,每條信息結尾都以\r\n結尾, 但一些較老的客戶端可能仍然用\n結尾, 所以我們的WEB服務器一般都有一定的容錯能力, 當直接檢測到結尾是\n時,直接跳到ONE_LF狀態, 絲毫不影響對HTTP包頭的解析。*/ case ONE_CR: if (*check == '\n') req->status = ONE_LF; else if (*check != '\r') req->status = READ_HEADER; break; case ONE_LF: /* if here, we've found the end (for sure) of a header */ if (*check == '\r') /* could be end o headers */ req->status = TWO_CR; else if (*check == '\n') req->status = BODY_READ; else req->status = READ_HEADER; break; /*HTTP 以單獨的一行\r\n 結尾所以當連續出現\r\n 時說明HTTP頭解析完成*/ case TWO_CR: if (*check == '\n') req->status = BODY_READ; else if (*check != '\r') req->status = READ_HEADER; break; default: break; } #ifdef VERY_FASCIST_LOGGING log_error_time(); fprintf(stderr, "status, check: %d, %d\n", req->status, *check); #endif req->parse_pos++; /* update parse position */ check++; if (req->status == ONE_LF) { *req->header_end = '\0'; /* terminate string that begins at req->header_line */ if (req->logline) { if (process_option_line(req) == 0) {//解析HTTP頭部信息並放入結構體內 return 0; } } else { if (process_logline(req) == 0)//解析HTTP 頭的第一行,確定訪問方式GET POST HEAD return 0; if (req->simple) return process_header_end(req); } /* set header_line to point to beginning of new header */ req->header_line = check; } else if (req->status == BODY_READ) {//處理HTTP 主體部分 #ifdef VERY_FASCIST_LOGGING int retval; log_error_time(); fprintf(stderr, "%s:%d -- got to body read.\n", __FILE__, __LINE__); retval = process_header_end(req); #else int retval = process_header_end(req);////調用CGI之前做最後的處理 #endif /* process_header_end inits non-POST cgi's */ if (retval && req->method == M_POST) { /* for body_{read,write}, set header_line to start of data, and header_end to end of data */ req->header_line = check; req->header_end = req->client_stream + req->client_stream_pos; req->status = BODY_WRITE; /* so write it */ /* have to write first, or read will be confused * because of the special case where the * filesize is less than we have already read. */ /* As quoted from RFC1945: A valid Content-Length is required on all HTTP/1.0 POST requests. An HTTP/1.0 server should respond with a 400 (bad request) message if it cannot determine the length of the request message's content. */ if (req->content_length) { int content_length; content_length = boa_atoi(req->content_length); /* Is a content-length of 0 legal? */ if (content_length <= 0) { log_error_time(); fprintf(stderr, "Invalid Content-Length [%s] on POST!\n", req->content_length); send_r_bad_request(req); return 0; } if (single_post_limit && content_length > single_post_limit) { log_error_time(); fprintf(stderr, "Content-Length [%d] > SinglePostLimit [%d] on POST!\n", content_length, single_post_limit); send_r_bad_request(req); return 0; } req->filesize = content_length; req->filepos = 0; if (req->header_end - req->header_line > req->filesize) { req->header_end = req->header_line + req->filesize; } } else { log_error_time(); fprintf(stderr, "Unknown Content-Length POST!\n"); send_r_bad_request(req); return 0; } } /* either process_header_end failed or req->method != POST */ return retval; /* 0 - close it done, 1 - keep on ready */ } /* req->status == BODY_READ */ } /* done processing available buffer */ #ifdef VERY_FASCIST_LOGGING log_error_time(); fprintf(stderr, "%s:%d - Done processing buffer. Status: %d\n", __FILE__, __LINE__, req->status); #endif /* 在分析主體部分之前, 也就是在分析HTTP所承載的數據之前不斷的從socket中讀取HTTP頭數據進行解析。*/ if (req->status < BODY_READ) { /* only reached if request is split across more than one packet */ buf_bytes_left = CLIENT_STREAM_SIZE - req->client_stream_pos; if (buf_bytes_left < 1) { log_error_time(); fputs("buffer overrun - read.c, read_header - closing\n", stderr); req->status = DEAD; return 0; } bytes = read(req->fd, buffer + req->client_stream_pos, buf_bytes_left); if (bytes < 0) { if (errno == EINTR) return 1; if (errno == EAGAIN || errno == EWOULDBLOCK) /* request blocked */ return -1; /* else if (errno == EBADF || errno == EPIPE) { req->status = DEAD; return 0; */ log_error_doc(req); perror("header read"); /* don't need to save errno because log_error_doc does */ return 0; } else if (bytes == 0) { /* log_error_time(); fputs("unexpected end of headers\n", stderr); */ return 0; } /* bytes is positive */ req->client_stream_pos += bytes; #ifdef FASCIST_LOGGING1 log_error_time(); req->client_stream[req->client_stream_pos] = '\0'; fprintf(stderr, "%s:%d -- We read %d bytes: \"%s\"\n", __FILE__, __LINE__, bytes, #ifdef VERY_FASCIST_LOGGING2 req->client_stream + req->client_stream_pos - bytes); #else ""); #endif #endif return 1; } return 1; }