1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/certificate_transparency/log_proof_fetcher.h"
9 #include "base/logging.h"
10 #include "base/memory/ref_counted.h"
11 #include "base/stl_util.h"
12 #include "base/values.h"
13 #include "components/safe_json/safe_json_parser.h"
14 #include "net/base/io_buffer.h"
15 #include "net/base/load_flags.h"
16 #include "net/base/net_errors.h"
17 #include "net/base/request_priority.h"
18 #include "net/cert/ct_log_response_parser.h"
19 #include "net/cert/signed_tree_head.h"
20 #include "net/http/http_status_code.h"
21 #include "net/url_request/url_request_context.h"
24 namespace certificate_transparency
{
28 // Shamelessly copied from domain_reliability/util.cc
29 int GetNetErrorFromURLRequestStatus(const net::URLRequestStatus
& status
) {
30 switch (status
.status()) {
31 case net::URLRequestStatus::SUCCESS
:
33 case net::URLRequestStatus::CANCELED
:
34 return net::ERR_ABORTED
;
35 case net::URLRequestStatus::FAILED
:
36 return status
.error();
39 return net::ERR_FAILED
;
45 struct LogProofFetcher::FetchState
{
46 FetchState(const std::string
& log_id
,
47 const SignedTreeHeadFetchedCallback
& fetched_callback
,
48 const FetchFailedCallback
& failed_callback
);
52 SignedTreeHeadFetchedCallback fetched_callback
;
53 FetchFailedCallback failed_callback
;
54 scoped_refptr
<net::IOBufferWithSize
> response_buffer
;
55 std::string assembled_response
;
58 LogProofFetcher::FetchState::FetchState(
59 const std::string
& log_id
,
60 const SignedTreeHeadFetchedCallback
& fetched_callback
,
61 const FetchFailedCallback
& failed_callback
)
63 fetched_callback(fetched_callback
),
64 failed_callback(failed_callback
),
65 response_buffer(new net::IOBufferWithSize(kMaxLogResponseSizeInBytes
)) {}
67 LogProofFetcher::FetchState::~FetchState() {}
69 LogProofFetcher::LogProofFetcher(net::URLRequestContext
* request_context
)
70 : request_context_(request_context
), weak_factory_(this) {
71 DCHECK(request_context
);
74 LogProofFetcher::~LogProofFetcher() {
75 STLDeleteContainerPairPointers(inflight_requests_
.begin(),
76 inflight_requests_
.end());
79 void LogProofFetcher::FetchSignedTreeHead(
80 const GURL
& base_log_url
,
81 const std::string
& log_id
,
82 const SignedTreeHeadFetchedCallback
& fetched_callback
,
83 const FetchFailedCallback
& failed_callback
) {
84 DCHECK(base_log_url
.SchemeIsHTTPOrHTTPS());
85 GURL
fetch_url(base_log_url
.Resolve("ct/v1/get-sth"));
86 scoped_ptr
<net::URLRequest
> request
=
87 request_context_
->CreateRequest(fetch_url
, net::DEFAULT_PRIORITY
, this);
88 request
->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES
|
89 net::LOAD_DO_NOT_SAVE_COOKIES
|
90 net::LOAD_DO_NOT_SEND_AUTH_DATA
);
92 FetchState
* fetch_state
=
93 new FetchState(log_id
, fetched_callback
, failed_callback
);
95 inflight_requests_
.insert(std::make_pair(request
.release(), fetch_state
));
98 void LogProofFetcher::OnResponseStarted(net::URLRequest
* request
) {
99 net::URLRequestStatus
status(request
->status());
100 DCHECK(inflight_requests_
.count(request
));
101 FetchState
* fetch_state
= inflight_requests_
.find(request
)->second
;
103 if (!status
.is_success() || request
->GetResponseCode() != net::HTTP_OK
) {
104 int net_error
= net::OK
;
105 int http_response_code
= request
->GetResponseCode();
107 DVLOG(1) << "Fetching STH from " << request
->original_url()
108 << " failed. status:" << status
.status()
109 << " error:" << status
.error()
110 << " http response code: " << http_response_code
;
111 if (!status
.is_success())
112 net_error
= GetNetErrorFromURLRequestStatus(status
);
114 InvokeFailureCallback(request
, net_error
, http_response_code
);
118 StartNextRead(request
, fetch_state
);
121 void LogProofFetcher::OnReadCompleted(net::URLRequest
* request
,
123 DCHECK(inflight_requests_
.count(request
));
124 FetchState
* fetch_state
= inflight_requests_
.find(request
)->second
;
126 if (HandleReadResult(request
, fetch_state
, bytes_read
))
127 StartNextRead(request
, fetch_state
);
130 bool LogProofFetcher::HandleReadResult(net::URLRequest
* request
,
131 FetchState
* fetch_state
,
133 // Start by checking for an error condition.
134 // If there are errors, invoke the failure callback and clean up the
136 if (bytes_read
== -1 || !request
->status().is_success()) {
137 net::URLRequestStatus
status(request
->status());
138 DVLOG(1) << "Read error: " << status
.status() << " " << status
.error();
139 InvokeFailureCallback(request
, GetNetErrorFromURLRequestStatus(status
),
145 // Not an error, but no data available, so wait for OnReadCompleted
147 if (request
->status().is_io_pending())
150 // Nothing more to read from the stream - finish handling the response.
151 if (bytes_read
== 0) {
152 RequestComplete(request
);
156 // We have data, collect it and indicate another read is needed.
157 DVLOG(1) << "Have " << bytes_read
<< " bytes to assemble.";
158 DCHECK_GE(bytes_read
, 0);
159 fetch_state
->assembled_response
.append(fetch_state
->response_buffer
->data(),
161 if (fetch_state
->assembled_response
.size() > kMaxLogResponseSizeInBytes
) {
162 // Log response is too big, invoke the failure callback.
163 InvokeFailureCallback(request
, net::ERR_FILE_TOO_BIG
, net::HTTP_OK
);
170 void LogProofFetcher::StartNextRead(net::URLRequest
* request
,
171 FetchState
* fetch_state
) {
172 bool continue_reading
= true;
173 while (continue_reading
) {
175 request
->Read(fetch_state
->response_buffer
.get(),
176 fetch_state
->response_buffer
->size(), &read_bytes
);
177 continue_reading
= HandleReadResult(request
, fetch_state
, read_bytes
);
181 void LogProofFetcher::RequestComplete(net::URLRequest
* request
) {
182 DCHECK(inflight_requests_
.count(request
));
184 FetchState
* fetch_state
= inflight_requests_
.find(request
)->second
;
186 // Get rid of the buffer as it really isn't necessary.
187 fetch_state
->response_buffer
= nullptr;
188 safe_json::SafeJsonParser::Parse(
189 fetch_state
->assembled_response
,
190 base::Bind(&LogProofFetcher::OnSTHJsonParseSuccess
,
191 weak_factory_
.GetWeakPtr(), request
),
192 base::Bind(&LogProofFetcher::OnSTHJsonParseError
,
193 weak_factory_
.GetWeakPtr(), request
));
196 void LogProofFetcher::CleanupRequest(net::URLRequest
* request
) {
197 DVLOG(1) << "Cleaning up request to " << request
->original_url();
198 auto it
= inflight_requests_
.find(request
);
199 DCHECK(it
!= inflight_requests_
.end());
201 std::advance(next_it
, 1);
203 // Delete FetchState and URLRequest, then the entry from inflight_requests_.
204 STLDeleteContainerPairPointers(it
, next_it
);
205 inflight_requests_
.erase(it
);
208 void LogProofFetcher::InvokeFailureCallback(net::URLRequest
* request
,
210 int http_response_code
) {
211 DCHECK(inflight_requests_
.count(request
));
212 auto it
= inflight_requests_
.find(request
);
213 FetchState
* fetch_state
= it
->second
;
215 fetch_state
->failed_callback
.Run(fetch_state
->log_id
, net_error
,
217 CleanupRequest(request
);
220 void LogProofFetcher::OnSTHJsonParseSuccess(
221 net::URLRequest
* request
,
222 scoped_ptr
<base::Value
> parsed_json
) {
223 DCHECK(inflight_requests_
.count(request
));
225 FetchState
* fetch_state
= inflight_requests_
.find(request
)->second
;
226 net::ct::SignedTreeHead signed_tree_head
;
227 if (net::ct::FillSignedTreeHead(*parsed_json
.get(), &signed_tree_head
)) {
228 fetch_state
->fetched_callback
.Run(fetch_state
->log_id
, signed_tree_head
);
230 fetch_state
->failed_callback
.Run(fetch_state
->log_id
,
231 net::ERR_CT_STH_INCOMPLETE
, net::HTTP_OK
);
234 CleanupRequest(request
);
237 void LogProofFetcher::OnSTHJsonParseError(net::URLRequest
* request
,
238 const std::string
& error
) {
239 InvokeFailureCallback(request
, net::ERR_CT_STH_PARSING_FAILED
, net::HTTP_OK
);
242 } // namespace certificate_transparency