📄 http.c
字号:
if (rb->close == READ_BUFFER_END) { if (!conn->tries && uri->host) { if (info->bl_flags & SERVER_BLACKLIST_NO_CHARSET) { del_blacklist_entry(uri, SERVER_BLACKLIST_NO_CHARSET); } else { add_blacklist_entry(uri, SERVER_BLACKLIST_NO_CHARSET); conn->tries = -1; } } retry_conn_with_state(conn, S_CANT_READ); return; } rb->close = READ_BUFFER_RETRY_ONCLOSE;again: a = get_header(rb); if (a == -1) { abort_conn_with_state(conn, S_HTTP_ERROR); return; } if (!a) { read_from_socket(conn, &conn->socket, rb, http_got_header); set_connection_state(conn, state); return; } if (a == -2) a = 0; if ((a && get_http_code(rb->data, &h, &version)) || h == 101) { abort_conn_with_state(conn, S_HTTP_ERROR); return; } /* When no header, HTTP/0.9 document. That's always text/html, * according to * http://www.w3.org/Protocols/HTTP/AsImplemented.html. */ /* FIXME: This usage of fake protocol headers for setting up the * content type has been obsoleted by the @content_type member of * {struct cache_entry}. */ head = (a ? memacpy(rb->data, a) : stracpy("\r\nContent-Type: text/html\r\n")); if (!head) { abort_conn_with_state(conn, S_OUT_OF_MEM); return; } if (check_http_server_bugs(uri, info, head)) { mem_free(head); retry_conn_with_state(conn, S_RESTART); return; }#ifdef CONFIG_CGI if (uri->protocol == PROTOCOL_FILE) { /* ``Status'' is not a standard HTTP header field although some * HTTP servers like www.php.net uses it for some reason. It should * only be used for CGI scripts so that it does not interfere * with status code depended handling for ``normal'' HTTP like * redirects. */ d = parse_header(head, "Status", NULL); if (d) { int h2 = atoi(d); mem_free(d); if (h2 >= 100 && h2 < 600) h = h2; if (h == 101) { mem_free(head); abort_conn_with_state(conn, S_HTTP_ERROR); return; } } }#endif#ifdef CONFIG_COOKIES ch = head; while ((cookie = parse_header(ch, "Set-Cookie", &ch))) { set_cookie(uri, cookie); mem_free(cookie); }#endif info->http_code = h; if (h == 100) { mem_free(head); state = S_PROC; kill_buffer_data(rb, a); goto again; } if (h < 200) { mem_free(head); abort_conn_with_state(conn, S_HTTP_ERROR); return; } if (h == 304) { mem_free(head); http_end_request(conn, S_OK, 1); return; } if (h == 204) { mem_free(head); http_end_request(conn, S_HTTP_204, 0); return; } if (h == 200 && connection_is_https_proxy(conn) && !conn->socket.ssl) {#ifdef CONFIG_SSL mem_free(head); conn->conn_info = init_connection_info(uri, &conn->socket, http_send_header); if (!conn->conn_info) { abort_conn_with_state(conn, S_OUT_OF_MEM); return; } if (ssl_connect(conn, &conn->socket) == -1) return;#else abort_conn_with_state(conn, S_SSL_ERROR);#endif return; } conn->cached = get_cache_entry(conn->uri); if (!conn->cached) { mem_free(head); abort_conn_with_state(conn, S_OUT_OF_MEM); return; } mem_free_set(&conn->cached->head, head); if (!get_opt_bool("document.cache.ignore_cache_control")) { struct cache_entry *cached = conn->cached; /* I am not entirely sure in what order we should process these * headers and if we should still process Cache-Control max-age * if we already set max age to date mentioned in Expires. * --jonas */ /* Ensure that when ever cached->max_age is set, cached->expired * is also set, so the cache management knows max_age contains a * valid time. If on the other hand no caching is requested * cached->expire should be set to zero. */ if ((d = parse_header(cached->head, "Expires", NULL))) { /* Convert date to seconds. */ time_t expires = parse_date(&d, NULL, 0, 1); mem_free(d); if (expires && cached->cache_mode != CACHE_MODE_NEVER) { cached->max_age = expires; cached->expire = 1; } } if ((d = parse_header(cached->head, "Pragma", NULL))) { if (strstr(d, "no-cache")) { cached->cache_mode = CACHE_MODE_NEVER; cached->expire = 0; } mem_free(d); } if (cached->cache_mode != CACHE_MODE_NEVER && (d = parse_header(cached->head, "Cache-Control", NULL))) { if (strstr(d, "no-cache") || strstr(d, "must-revalidate")) { cached->cache_mode = CACHE_MODE_NEVER; cached->expire = 0; } else { unsigned char *pos = strstr(d, "max-age="); assert(cached->cache_mode != CACHE_MODE_NEVER); if (pos) { /* Grab the number of seconds. */ time_t max_age = str_to_ttime(pos + 8); cached->max_age = max_age + time(NULL); cached->expire = 1; } } mem_free(d); } }#ifdef CONFIG_SSL /* TODO: Move this to some more generic place like lowlevel/connect.c * or sched/connection.c when other protocols will need it. --jonas */ if (conn->socket.ssl) mem_free_set(&conn->cached->ssl_info, get_ssl_connection_cipher(conn));#endif /* XXX: Is there some reason why NOT to follow the Location header * for any status? If the server didn't mean it, it wouldn't send * it, after all...? --pasky */ if (h == 201 || h == 301 || h == 302 || h == 303 || h == 307) { d = parse_header(conn->cached->head, "Location", NULL); if (d) { int use_get_method = (h == 303); /* A note from RFC 2616 section 10.3.3: * RFC 1945 and RFC 2068 specify that the client is not * allowed to change the method on the redirected * request. However, most existing user agent * implementations treat 302 as if it were a 303 * response, performing a GET on the Location * field-value regardless of the original request * method. */ /* So POST must not be redirected to GET, but some * BUGGY message boards rely on it :-( */ if (h == 302 && get_opt_bool("protocol.http.bugs.broken_302_redirect")) use_get_method = 1; redirect_cache(conn->cached, d, use_get_method, -1); mem_free(d); } } if (h == 401) { unsigned char *head = conn->cached->head; check_http_authentication(uri, head, "WWW-Authenticate"); } if (h == 407) { unsigned char *str; d = parse_header(conn->cached->head, "Proxy-Authenticate", &str); while (d) { if (!strncasecmp(d, "Basic", 5)) { unsigned char *realm = get_header_param(d, "realm"); if (realm) { mem_free_set(&proxy_auth.realm, realm); proxy_auth.digest = 0; mem_free(d); break; } } else if (!strncasecmp(d, "Digest", 6)) { unsigned char *realm = get_header_param(d, "realm"); unsigned char *nonce = get_header_param(d, "nonce"); unsigned char *opaque = get_header_param(d, "opaque"); mem_free_set(&proxy_auth.realm, realm); mem_free_set(&proxy_auth.nonce, nonce); mem_free_set(&proxy_auth.opaque, opaque); proxy_auth.digest = 1; mem_free(d); break; } mem_free(d); d = parse_header(str, "Proxy-Authenticate", &str); } } kill_buffer_data(rb, a); info->close = 0; info->length = -1; info->recv_version = version; if ((d = parse_header(conn->cached->head, "Connection", NULL)) || (d = parse_header(conn->cached->head, "Proxy-Connection", NULL))) { if (!strcasecmp(d, "close")) info->close = 1; mem_free(d); } else if (PRE_HTTP_1_1(version)) { info->close = 1; } cf = conn->from; conn->from = 0; d = parse_header(conn->cached->head, "Content-Range", NULL); if (d) { if (strlen(d) > 6) { d[5] = 0; if (isdigit(d[6]) && !strcasecmp(d, "bytes")) { int f; errno = 0; f = strtol(d + 6, NULL, 10); if (!errno && f >= 0) conn->from = f; } } mem_free(d); } if (cf && !conn->from && !conn->unrestartable) conn->unrestartable = 1; if ((conn->progress.start <= 0 && conn->from > cf) || conn->from < 0) { /* We don't want this if conn->progress.start because then conn->from will * be probably value of conn->progress.start, while cf is 0. */ abort_conn_with_state(conn, S_HTTP_ERROR); return; }#if 0 { struct status *s; foreach (s, conn->downloads) { fprintf(stderr, "conn %p status %p pri %d st %d er %d :: ce %s", conn, s, s->pri, s->state, s->prev_error, s->cached ? s->cached->url : (unsigned char *) "N-U-L-L"); } }#endif if (conn->progress.start >= 0) { /* Update to the real value which we've got from Content-Range. */ conn->progress.seek = conn->from; } conn->progress.start = conn->from; d = parse_header(conn->cached->head, "Content-Length", NULL); if (d) { unsigned char *ep; int l; errno = 0; l = strtol(d, (char **) &ep, 10); if (!errno && !*ep && l >= 0) { if (!info->close || POST_HTTP_1_0(version)) info->length = l; conn->est_length = conn->from + l; } mem_free(d); } if (!conn->unrestartable) { d = parse_header(conn->cached->head, "Accept-Ranges", NULL); if (d) { if (!strcasecmp(d, "none")) conn->unrestartable = 1; mem_free(d); } else { if (!conn->from) conn->unrestartable = 1; } } d = parse_header(conn->cached->head, "Transfer-Encoding", NULL); if (d) { if (!strcasecmp(d, "chunked")) { info->length = LEN_CHUNKED; info->chunk_remaining = CHUNK_SIZE; } mem_free(d); } if (!info->close && info->length == -1) info->close = 1; d = parse_header(conn->cached->head, "Last-Modified", NULL); if (d) { if (conn->cached->last_modified && strcasecmp(conn->cached->last_modified, d)) { delete_entry_content(conn->cached); if (conn->from) { conn->from = 0; mem_free(d); retry_conn_with_state(conn, S_MODIFIED); return; } } if (!conn->cached->last_modified) conn->cached->last_modified = d; else mem_free(d); } if (!conn->cached->last_modified) { d = parse_header(conn->cached->head, "Date", NULL); if (d) conn->cached->last_modified = d; } /* FIXME: Parse only if HTTP/1.1 or later? --Zas */ d = parse_header(conn->cached->head, "ETag", NULL); if (d) { if (conn->cached->etag) { unsigned char *old_tag = conn->cached->etag; unsigned char *new_tag = d; /* http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.19 */ if (new_tag[0] == 'W' && new_tag[1] == '/') new_tag += 2; if (old_tag[0] == 'W' && old_tag[1] == '/') old_tag += 2; if (strcmp(new_tag, old_tag)) { delete_entry_content(conn->cached); if (conn->from) { conn->from = 0; mem_free(d); retry_conn_with_state(conn, S_MODIFIED); return; } } } if (!conn->cached->etag) conn->cached->etag = d; else mem_free(d); } d = parse_header(conn->cached->head, "Content-Encoding", NULL); if (d) { unsigned char *extension = get_extension_from_uri(uri); enum stream_encoding file_encoding; file_encoding = extension ? guess_encoding(extension) : ENCODING_NONE; mem_free_if(extension); /* If the content is encoded, we want to preserve the encoding * if it is implied by the extension, so that saving the URI * will leave the saved file with the correct encoding. */#ifdef CONFIG_GZIP if (file_encoding != ENCODING_GZIP && (!strcasecmp(d, "gzip") || !strcasecmp(d, "x-gzip"))) conn->content_encoding = ENCODING_GZIP;#endif#ifdef BUG_517#ifdef CONFIG_BZIP2 if (file_encoding != ENCODING_BZIP2 && (!strcasecmp(d, "bzip2") || !strcasecmp(d, "x-bzip2"))) conn->content_encoding = ENCODING_BZIP2;#endif#endif mem_free(d); } if (conn->content_encoding != ENCODING_NONE) { mem_free_if(conn->cached->encoding_info); conn->cached->encoding_info = stracpy(get_encoding_name(conn->content_encoding)); } if (info->length == -1 || (PRE_HTTP_1_1(info->recv_version) && info->close)) rb->close = READ_BUFFER_END_ONCLOSE; read_http_data(conn, rb);}static voidhttp_get_header(struct connection *conn){ struct read_buffer *rb = alloc_read_buffer(conn); if (!rb) return; set_connection_timeout(conn); rb->close = READ_BUFFER_END_ONCLOSE; read_from_socket(conn, &conn->socket, rb, http_got_header);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -