📄 httpclient.cc
字号:
return HE_CACHE; } // Write all unknown and end-to-end headers to a cache file for (HttpData::const_iterator it = response_.begin(); it != response_.end(); ++it) { HttpHeader header; if (FromString(header, it->first) && !HttpHeaderIsEndToEnd(header)) continue; std::string formatted_header(it->first); formatted_header.append(": "); formatted_header.append(it->second); formatted_header.append("\r\n"); StreamResult result = stream->WriteAll(formatted_header.data(), formatted_header.length(), NULL, NULL); if (SR_SUCCESS != result) { LOG_F(LS_ERROR) << "Couldn't write header cache"; return HE_CACHE; } } return HE_NONE;}void HttpClient::CompleteCacheFile() { // Restore previous response document StreamTap* tap = static_cast<StreamTap*>(response_.document.release()); response_.document.reset(tap->Detach()); int error; StreamResult result = tap->GetTapResult(&error); // Delete the tap and cache stream (which completes cache unlock) delete tap; if (SR_SUCCESS != result) { LOG(LS_ERROR) << "Cache file error: " << error; cache_->DeleteResource(GetCacheID(server_, request_)); }}bool HttpClient::CheckCache() { ASSERT(NULL != cache_); ASSERT(CS_READY == cache_state_); std::string id = GetCacheID(server_, request_); if (!cache_->HasResource(id)) { // No cache file available return false; } HttpError error = ReadCacheHeaders(id, true); if (HE_NONE == error) { switch (HttpGetCacheState(request_, response_)) { case HCS_FRESH: // Cache content is good, read from cache break; case HCS_STALE: // Cache content may be acceptable. Issue a validation request. if (PrepareValidate()) { return false; } // Couldn't validate, fall through. case HCS_NONE: // Cache content is not useable. Issue a regular request. response_.clear(false); return false; } } if (HE_NONE == error) { error = ReadCacheBody(id); cache_state_ = CS_READY; } if (HE_CACHE == error) { LOG_F(LS_WARNING) << "Cache failure, continuing with normal request"; response_.clear(false); return false; } SignalHttpClientComplete(this, error); return true;}HttpError HttpClient::ReadCacheHeaders(const std::string& id, bool override) { scoped_ptr<StreamInterface> stream(cache_->ReadResource(id, kCacheHeader)); if (!stream.get()) { return HE_CACHE; } HttpData::HeaderCombine combine = override ? HttpData::HC_REPLACE : HttpData::HC_AUTO; while (true) { std::string formatted_header; StreamResult result = stream->ReadLine(&formatted_header); if (SR_EOS == result) break; if (SR_SUCCESS != result) { LOG_F(LS_ERROR) << "ReadLine error in cache headers"; return HE_CACHE; } size_t end_of_name = formatted_header.find(':'); if (std::string::npos == end_of_name) { LOG_F(LS_WARNING) << "Malformed cache header"; continue; } size_t start_of_value = end_of_name + 1; size_t end_of_value = formatted_header.length(); while ((start_of_value < end_of_value) && isspace(formatted_header[start_of_value])) ++start_of_value; while ((start_of_value < end_of_value) && isspace(formatted_header[end_of_value-1])) --end_of_value; size_t value_length = end_of_value - start_of_value; std::string name(formatted_header.substr(0, end_of_name)); std::string value(formatted_header.substr(start_of_value, value_length)); response_.changeHeader(name, value, combine); } response_.scode = HC_OK; return HE_NONE;}HttpError HttpClient::ReadCacheBody(const std::string& id) { cache_state_ = CS_READING; HttpError error = HE_NONE; size_t data_size; scoped_ptr<StreamInterface> stream(cache_->ReadResource(id, kCacheBody)); if (!stream.get() || !stream->GetSize(&data_size)) { LOG_F(LS_ERROR) << "Unavailable cache body"; error = HE_CACHE; } else { error = OnHeaderAvailable(false, false, data_size); } if ((HE_NONE == error) && (HV_HEAD != request_.verb) && (NULL != response_.document.get())) { char buffer[1024 * 64]; StreamResult result = Flow(stream.get(), buffer, ARRAY_SIZE(buffer), response_.document.get()); if (SR_SUCCESS != result) { error = HE_STREAM; } } return error;}bool HttpClient::PrepareValidate() { ASSERT(CS_READY == cache_state_); // At this point, request_ contains the pending request, and response_ // contains the cached response headers. Reformat the request to validate // the cached content. HttpValidatorStrength vs_required = HttpRequestValidatorLevel(request_); HttpValidatorStrength vs_available = HttpResponseValidatorLevel(response_); if (vs_available < vs_required) { return false; } std::string value; if (response_.hasHeader(HH_ETAG, &value)) { request_.addHeader(HH_IF_NONE_MATCH, value); } if (response_.hasHeader(HH_LAST_MODIFIED, &value)) { request_.addHeader(HH_IF_MODIFIED_SINCE, value); } response_.clear(false); cache_state_ = CS_VALIDATING; return true;}HttpError HttpClient::CompleteValidate() { ASSERT(CS_VALIDATING == cache_state_); std::string id = GetCacheID(server_, request_); // Merge cached headers with new headers HttpError error = ReadCacheHeaders(id, false); if (HE_NONE != error) { // Rewrite merged headers to cache CacheLock lock(cache_, id); error = WriteCacheHeaders(id); } if (HE_NONE != error) { error = ReadCacheBody(id); } return error;}HttpError HttpClient::OnHeaderAvailable(bool ignore_data, bool chunked, size_t data_size) { if (!ignore_data && !chunked && response_.document.get()) { // Attempt to pre-allocate space for the downloaded data. if (!response_.document->ReserveSize(data_size)) { return HE_OVERFLOW; } } SignalHeaderAvailable(this, chunked, data_size); return HE_NONE;}//// HttpBase Implementation//HttpError HttpClient::onHttpHeaderComplete(bool chunked, size_t& data_size) { if (CS_VALIDATING == cache_state_) { if (HC_NOT_MODIFIED == response_.scode) { return CompleteValidate(); } // Should we remove conditional headers from request? cache_state_ = CS_READY; cache_->DeleteResource(GetCacheID(server_, request_)); // Continue processing response as normal } ASSERT(!IsCacheActive()); if ((request_.verb == HV_HEAD) || !HttpCodeHasBody(response_.scode)) { // HEAD requests and certain response codes contain no body data_size = 0; } if ((HttpCodeIsRedirection(response_.scode) && !fail_redirect_) || ((HC_PROXY_AUTHENTICATION_REQUIRED == response_.scode) && (PROXY_HTTPS == proxy_.type))) { // We're going to issue another request, so ignore the incoming data. base_.set_ignore_data(true); } HttpError error = OnHeaderAvailable(base_.ignore_data(), chunked, data_size); if (HE_NONE != error) { return error; } if ((NULL != cache_) && !base_.ignore_data() && HttpShouldCache(request_, response_)) { if (BeginCacheFile()) { cache_state_ = CS_WRITING; } } return HE_NONE;}void HttpClient::onHttpComplete(HttpMode mode, HttpError err) { if (err != HE_NONE) { // fall through } else if (mode == HM_CONNECT) { base_.send(&request_); return; } else if ((mode == HM_SEND) || HttpCodeIsInformational(response_.scode)) { // If you're interested in informational headers, catch // SignalHeaderAvailable. base_.recv(&response_); return; } else { if (!HttpShouldKeepAlive(response_)) { LOG(INFO) << "HttpClient: closing socket"; base_.stream()->Close(); } if (HttpCodeIsRedirection(response_.scode) && !fail_redirect_) { std::string value; if (!response_.hasHeader(HH_LOCATION, &value)) { err = HE_PROTOCOL; } else { Url<char> purl(value); set_server(SocketAddress(purl.server(), purl.port(), false)); request_.path = purl.full_path(); if (response_.scode == HC_SEE_OTHER) { request_.verb = HV_GET; request_.clearHeader(HH_CONTENT_TYPE); request_.clearHeader(HH_CONTENT_LENGTH); request_.document.reset(); } else if (request_.document.get() && !request_.document->Rewind()) { // Unable to replay the request document. err = HE_STREAM; } } if (err == HE_NONE) { context_.reset(); response_.clear(false); release(); start(); return; } } else if ((HC_PROXY_AUTHENTICATION_REQUIRED == response_.scode) && (PROXY_HTTPS == proxy_.type)) { std::string response, auth_method; HttpData::const_iterator begin = response_.begin(HH_PROXY_AUTHENTICATE); HttpData::const_iterator end = response_.end(HH_PROXY_AUTHENTICATE); for (HttpData::const_iterator it = begin; it != end; ++it) { HttpAuthResult res = HttpAuthenticate( it->second.data(), it->second.size(), proxy_.address, ToString(request_.verb), request_.path, proxy_.username, proxy_.password, *context_.use(), response, auth_method); if (res == HAR_RESPONSE) { request_.setHeader(HH_PROXY_AUTHORIZATION, response); if (request_.document.get() && !request_.document->Rewind()) { err = HE_STREAM; } else { // Explicitly do not reset the HttpAuthContext response_.clear(false); // TODO: Reuse socket when authenticating? release(); start(); return; } } else if (res == HAR_IGNORE) { LOG(INFO) << "Ignoring Proxy-Authenticate: " << auth_method; continue; } else { break; } } } } if (CS_WRITING == cache_state_) { CompleteCacheFile(); cache_state_ = CS_READY; } else if (CS_READING == cache_state_) { cache_state_ = CS_READY; } release(); SignalHttpClientComplete(this, err);}void HttpClient::onHttpClosed(HttpError err) { SignalHttpClientClosed(this, err);}//////////////////////////////////////////////////////////////////////} // namespace talk_base
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -