intHttpNetworkTransaction::DoBuildRequest(){next_state_=STATE_BUILD_REQUEST_COMPLETE;headers_valid_=false;// This is constructed lazily (instead of within our Start method), so that
// we have proxy info available.
if(request_headers_.IsEmpty()){boolusing_http_proxy_without_tunnel=UsingHttpProxyWithoutTunnel();returnBuildRequestHeaders(using_http_proxy_without_tunnel);}returnOK;}intHttpNetworkTransaction::BuildRequestHeaders(boolusing_http_proxy_without_tunnel){request_headers_.SetHeader(HttpRequestHeaders::kHost,GetHostAndOptionalPort(request_->url));// For compat with HTTP/1.0 servers and proxies:
if(using_http_proxy_without_tunnel){request_headers_.SetHeader(HttpRequestHeaders::kProxyConnection,"keep-alive");}else{request_headers_.SetHeader(HttpRequestHeaders::kConnection,"keep-alive");}// Add a content length header?
if(request_->upload_data_stream){if(request_->upload_data_stream->is_chunked()){request_headers_.SetHeader(HttpRequestHeaders::kTransferEncoding,"chunked");}else{request_headers_.SetHeader(HttpRequestHeaders::kContentLength,base::NumberToString(request_->upload_data_stream->size()));}}elseif(request_->method=="POST"||request_->method=="PUT"){// An empty POST/PUT request still needs a content length. As for HEAD,
// IE and Safari also add a content length header. Presumably it is to
// support sending a HEAD request to an URL that only expects to be sent a
// POST or some other method that normally would have a message body.
// Firefox (40.0) does not send the header, and RFC 7230 & 7231
// specify that it should not be sent due to undefined behavior.
request_headers_.SetHeader(HttpRequestHeaders::kContentLength,"0");}// Honor load flags that impact proxy caches.
if(request_->load_flags&LOAD_BYPASS_CACHE){request_headers_.SetHeader(HttpRequestHeaders::kPragma,"no-cache");request_headers_.SetHeader(HttpRequestHeaders::kCacheControl,"no-cache");}elseif(request_->load_flags&LOAD_VALIDATE_CACHE){request_headers_.SetHeader(HttpRequestHeaders::kCacheControl,"max-age=0");}if(ShouldApplyProxyAuth()&&HaveAuth(HttpAuth::AUTH_PROXY))auth_controllers_[HttpAuth::AUTH_PROXY]->AddAuthorizationHeader(&request_headers_);if(ShouldApplyServerAuth()&&HaveAuth(HttpAuth::AUTH_SERVER))auth_controllers_[HttpAuth::AUTH_SERVER]->AddAuthorizationHeader(&request_headers_);if(net::features::kIpPrivacyAddHeaderToProxiedRequests.Get()&&proxy_info_.is_for_ip_protection()){CHECK(!proxy_info_.is_direct()||net::features::kIpPrivacyDirectOnly.Get());if(!proxy_info_.is_direct()){request_headers_.SetHeader("IP-Protection","1");}}request_headers_.MergeFrom(request_->extra_headers);if(modify_headers_callbacks_){modify_headers_callbacks_.Run(&request_headers_);}response_.did_use_http_auth=request_headers_.HasHeader(HttpRequestHeaders::kAuthorization)||request_headers_.HasHeader(HttpRequestHeaders::kProxyAuthorization);returnOK;}
intHttpNetworkTransaction::Start(constHttpRequestInfo*request_info,CompletionOnceCallbackcallback,constNetLogWithSource&net_log){if(request_info->load_flags&LOAD_ONLY_FROM_CACHE)returnERR_CACHE_MISS;DCHECK(request_info->traffic_annotation.is_valid());DCHECK(request_info->IsConsistent());net_log_=net_log;request_=request_info;url_=request_->url;network_anonymization_key_=request_->network_anonymization_key;#if BUILDFLAG(ENABLE_REPORTING)
// Store values for later use in NEL report generation.
request_method_=request_->method;request_->extra_headers.GetHeader(HttpRequestHeaders::kReferer,&request_referrer_);request_->extra_headers.GetHeader(HttpRequestHeaders::kUserAgent,&request_user_agent_);request_reporting_upload_depth_=request_->reporting_upload_depth;start_timeticks_=base::TimeTicks::Now();#endif // BUILDFLAG(ENABLE_REPORTING)
if(request_->idempotency==IDEMPOTENT||(request_->idempotency==DEFAULT_IDEMPOTENCY&&HttpUtil::IsMethodSafe(request_info->method))){can_send_early_data_=true;}if(request_->load_flags&LOAD_PREFETCH){response_.unused_since_prefetch=true;}if(request_->load_flags&LOAD_RESTRICTED_PREFETCH){DCHECK(response_.unused_since_prefetch);response_.restricted_prefetch=true;}next_state_=STATE_NOTIFY_BEFORE_CREATE_STREAM;intrv=DoLoop(OK);if(rv==ERR_IO_PENDING)callback_=std::move(callback);// This always returns ERR_IO_PENDING because DoCreateStream() does, but
// GenerateNetworkErrorLoggingReportIfError() should be called here if any
// other net::Error can be returned.
DCHECK_EQ(rv,ERR_IO_PENDING);returnrv;}
HttpResponseHeaders::HttpResponseHeaders(BuilderPassKey,HttpVersionversion,std::string_viewstatus,base::span<conststd::pair<std::string_view,std::string_view>>headers):http_version_(version){// This must match the behaviour of Parse(). We don't use Parse() because
// avoiding the overhead of parsing is the point of this constructor.
std::stringformatted_status;formatted_status.reserve(status.size()+1);// ParseStatus() may add a space
response_code_=ParseStatus(status,formatted_status);// First calculate how big the output will be so that we can allocate the
// right amount of memory.
size_texpected_size=8;// "HTTP/x.x"
expected_size+=formatted_status.size();expected_size+=1;// "\\0"
size_texpected_parsed_size=0;// Track which headers (by index) have a comma in the value. Since bools are
// only 1 byte, we can afford to put 100 of them on the stack and avoid
// allocating more memory 99.9% of the time.
absl::InlinedVector<bool,100>header_contains_comma;for(constauto&[key,value]:headers){expected_size+=key.size();expected_size+=1;// ":"
expected_size+=value.size();expected_size+=1;// "\\0"
// It's okay if we over-estimate the size of `parsed_`, so treat all ','
// characters as if they might split the value to avoid parsing the value
// carefully here.
constsize_tcomma_count=base::ranges::count(value,',')+1;expected_parsed_size+=comma_count;header_contains_comma.push_back(comma_count);}expected_size+=1;// "\\0"
raw_headers_.reserve(expected_size);parsed_.reserve(expected_parsed_size);// Now fill in the output.
constuint16_tmajor=version.major_value();constuint16_tminor=version.minor_value();CHECK_LE(major,9);CHECK_LE(minor,9);raw_headers_.append("HTTP/");raw_headers_.push_back('0'+major);raw_headers_.push_back('.');raw_headers_.push_back('0'+minor);raw_headers_.append(formatted_status);raw_headers_.push_back('\\0');// It is vital that `raw_headers_` iterators are not invalidated after this
// point.
constchar*constdata_at_start=raw_headers_.data();size_tindex=0;for(constauto&[key,value]:headers){CheckDoesNotHaveEmbeddedNulls(key);CheckDoesNotHaveEmbeddedNulls(value);// Because std::string iterators are random-access, end() has to point to
// the position where the next character will be appended.
constautoname_begin=raw_headers_.cend();raw_headers_.append(key);constautoname_end=raw_headers_.cend();raw_headers_.push_back(':');autovalues_begin=raw_headers_.cend();raw_headers_.append(value);autovalues_end=raw_headers_.cend();raw_headers_.push_back('\\0');// The HTTP/2 standard disallows header values starting or ending with
// whitespace (RFC 9113 8.2.1). Hopefully the same is also true of HTTP/3.
// TODO(crbug.com/40282642): Validate that our implementations
// actually enforce this constraint and change this TrimLWS() to a DCHECK.
HttpUtil::TrimLWS(&values_begin,&values_end);AddHeader(name_begin,name_end,values_begin,values_end,header_contains_comma[index]?ContainsCommas::kYes:ContainsCommas::kNo);++index;}raw_headers_.push_back('\\0');CHECK_EQ(expected_size,raw_headers_.size());CHECK_EQ(data_at_start,raw_headers_.data());DCHECK_LE(parsed_.size(),expected_parsed_size);DCHECK_EQ('\\0',raw_headers_[raw_headers_.size()-2]);DCHECK_EQ('\\0',raw_headers_[raw_headers_.size()-1]);}
intHttpNetworkTransaction::DoReadBody(){DCHECK(read_buf_.get());DCHECK_GT(read_buf_len_,0);DCHECK(stream_!=nullptr);next_state_=STATE_READ_BODY_COMPLETE;returnstream_->ReadResponseBody(read_buf_.get(),read_buf_len_,io_callback_);}intHttpNetworkTransaction::DoReadBodyComplete(intresult){// We are done with the Read call.
booldone=false;if(result<=0){DCHECK_NE(ERR_IO_PENDING,result);done=true;}// Clean up connection if we are done.
if(done){// Note: Just because IsResponseBodyComplete is true, we're not
// necessarily "done". We're only "done" when it is the last
// read on this HttpNetworkTransaction, which will be signified
// by a zero-length read.
// TODO(mbelshe): The keep-alive property is really a property of
// the stream. No need to compute it here just to pass back
// to the stream's Close function.
boolkeep_alive=stream_->IsResponseBodyComplete()&&stream_->CanReuseConnection();stream_->Close(!keep_alive);// Note: we don't reset the stream here. We've closed it, but we still
// need it around so that callers can call methods such as
// GetUploadProgress() and have them be meaningful.
// TODO(mbelshe): This means we closed the stream here, and we close it
// again in ~HttpNetworkTransaction. Clean that up.
// The next Read call will return 0 (EOF).
// This transaction was successful. If it had been retried because of an
// error with an alternative service, mark that alternative service broken.
if(!enable_alternative_services_&&retried_alternative_service_.protocol!=kProtoUnknown){HistogramBrokenAlternateProtocolLocation(BROKEN_ALTERNATE_PROTOCOL_LOCATION_HTTP_NETWORK_TRANSACTION);session_->http_server_properties()->MarkAlternativeServiceBroken(retried_alternative_service_,network_anonymization_key_);}#if BUILDFLAG(ENABLE_REPORTING)
GenerateNetworkErrorLoggingReport(result);#endif // BUILDFLAG(ENABLE_REPORTING)
}// Clear these to avoid leaving around old state.
read_buf_=nullptr;read_buf_len_=0;returnresult;}