OLD | NEW |
---|---|
(Empty) | |
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. | |
4 | |
5 #include "components/certificate_transparency/log_proof_fetcher.h" | |
6 | |
7 #include "base/logging.h" | |
8 #include "base/stl_util.h" | |
9 #include "base/strings/string_piece.h" | |
10 #include "base/strings/stringprintf.h" | |
11 #include "base/values.h" | |
12 #include "components/safe_json/safe_json_parser.h" | |
13 #include "net/base/load_flags.h" | |
14 #include "net/base/request_priority.h" | |
15 #include "net/cert/ct_log_response_parser.h" | |
16 #include "net/cert/ct_log_verifier.h" | |
17 #include "net/cert/signed_tree_head.h" | |
18 #include "net/url_request/url_request_context.h" | |
19 | |
20 const int kMaxLogResponseSizeInBytes = 600; | |
21 | |
22 namespace certificate_transparency { | |
23 | |
24 namespace { | |
25 | |
26 // Shamelessly copied from domain_reliability/util.cc | |
27 int GetNetErrorFromURLRequestStatus(const net::URLRequestStatus& status) { | |
28 switch (status.status()) { | |
29 case net::URLRequestStatus::SUCCESS: | |
30 return net::OK; | |
31 case net::URLRequestStatus::CANCELED: | |
32 return net::ERR_ABORTED; | |
33 case net::URLRequestStatus::FAILED: | |
34 return status.error(); | |
35 default: | |
36 NOTREACHED(); | |
37 return net::ERR_FAILED; | |
38 } | |
39 } | |
40 | |
41 } // namespace | |
42 | |
43 struct LogProofFetcher::FetchState { | |
44 FetchState(const std::string& id, | |
45 FetchSTHCallback fetched_callback, | |
46 FetchFailedCallback failed_callback); | |
47 ~FetchState(); | |
48 | |
49 std::string log_id; | |
50 FetchSTHCallback fetched_callback; | |
51 FetchFailedCallback failed_callback; | |
52 scoped_refptr<net::IOBufferWithSize> response_buffer; | |
53 int read_bytes; | |
54 std::string assembled_response; | |
55 int total_response_size; | |
56 }; | |
57 | |
58 LogProofFetcher::LogProofFetcher(net::URLRequestContext* request_context) | |
59 : request_context_(request_context), weak_factory_(this) { | |
60 DCHECK(request_context); | |
61 } | |
62 | |
63 LogProofFetcher::~LogProofFetcher() { | |
64 for (auto it = inflight_requests_.begin(); it != inflight_requests_.end(); | |
65 ++it) | |
mmenke
2015/07/17 17:57:47
Can just delete these - no need to cancel first.
Eran Messeri
2015/07/29 15:16:58
Done.
| |
66 it->first->Cancel(); | |
67 STLDeleteContainerPairPointers(inflight_requests_.begin(), | |
68 inflight_requests_.end()); | |
69 } | |
70 | |
71 void LogProofFetcher::FetchSTH(const GURL& log_url, | |
72 const std::string& log_id, | |
73 FetchSTHCallback fetched_cb, | |
74 FetchFailedCallback failed_cb) { | |
75 GURL fetch_url(log_url.Resolve("ct/v1/get-sth")); | |
mmenke
2015/07/17 17:57:47
log_url -> base_log_url, maybe? I assume we reall
Eran Messeri
2015/07/29 15:16:59
Correct, we do want to take in a full URL, not jus
| |
76 scoped_ptr<net::URLRequest> request = CreateURLRequest(fetch_url); | |
77 | |
78 FetchState* fetch_state = new FetchState(log_id, fetched_cb, failed_cb); | |
79 auto it = inflight_requests_.insert(std::make_pair(request.release(), | |
80 fetch_state)).first; | |
81 it->first->Start(); | |
mmenke
2015/07/17 17:57:46
I think this is sufficiently ugly that you may jus
Eran Messeri
2015/07/29 15:16:58
Done.
| |
82 } | |
83 | |
84 void LogProofFetcher::OnResponseStarted(net::URLRequest* request) { | |
85 const net::URLRequestStatus& status(request->status()); | |
mmenke
2015/07/17 17:57:46
Given that this is just a pair of ints, suggest ju
Eran Messeri
2015/07/29 15:16:58
Done - copied the value.
| |
86 auto it = inflight_requests_.find(request); | |
mmenke
2015/07/17 17:57:47
optional: Suggest avoiding the use of auto unless
Eran Messeri
2015/07/29 15:16:58
Done - adopted your suggestion.
| |
87 FetchState* fetch_state = it->second; | |
88 | |
89 DCHECK(it != inflight_requests_.end()); | |
mmenke
2015/07/17 17:57:47
This DCHECK should go before you potentially deref
Eran Messeri
2015/07/29 15:16:58
Done.
| |
90 if (!status.is_success() || request->GetResponseCode() != 200) { | |
91 int error_value = 0; | |
92 if (!status.is_success()) { | |
93 DVLOG(1) << "Fetching STH from " << request->original_url() | |
94 << " failed. status:" << status.status() | |
95 << " error:" << status.error(); | |
96 error_value = GetNetErrorFromURLRequestStatus(status); | |
97 } else { // request->GetResponseCode != 200 | |
98 DVLOG(1) << "Fetch STH HTTP status: " << request->GetResponseCode(); | |
99 error_value = request->GetResponseCode(); | |
mmenke
2015/07/17 17:57:47
Packing in a net error and an HTTP response code i
Eran Messeri
2015/07/29 15:16:58
You're right - I thought I saw this pattern somewh
| |
100 } | |
101 | |
102 scoped_ptr<FetchState> params = CleanupRequest(request); | |
103 params->failed_callback.Run(params->log_id, error_value); | |
mmenke
2015/07/17 17:57:47
Can we just invoke the callback before calling Cle
Eran Messeri
2015/07/29 15:16:58
Yes, done.
| |
104 return; | |
105 } | |
106 | |
107 bool continue_reading = true; | |
108 while (continue_reading) { | |
109 KickOffARead(request, fetch_state); | |
110 continue_reading = CheckReadStatus(request, fetch_state); | |
111 } | |
112 } | |
113 | |
114 void LogProofFetcher::OnReadCompleted(net::URLRequest* request, | |
115 int bytes_read) { | |
116 auto it = inflight_requests_.find(request); | |
117 DCHECK(it != inflight_requests_.end()); | |
118 FetchState* params = it->second; | |
119 | |
120 params->read_bytes = bytes_read; | |
mmenke
2015/07/17 17:57:47
I don't think you need the read_bytes field. Can
Eran Messeri
2015/07/29 15:16:58
Changed to pass the bytes_read into HandleReadResu
| |
121 | |
122 bool another_read = CheckReadStatus(request, params); | |
123 | |
124 while (another_read) { | |
125 KickOffARead(request, params); | |
126 another_read = CheckReadStatus(request, params); | |
127 } | |
mmenke
2015/07/17 17:57:47
This exactly matches the end of LogProofFetcher::O
Eran Messeri
2015/07/29 15:16:59
Done.
| |
128 } | |
129 | |
130 bool LogProofFetcher::CheckReadStatus(net::URLRequest* request, | |
mmenke
2015/07/17 17:57:46
HandleReadResult maybe?
Eran Messeri
2015/07/29 15:16:59
Done.
| |
131 FetchState* params) { | |
132 const int bytes_read = params->read_bytes; | |
133 // Start by checking for an error condition. | |
134 if (bytes_read == -1 || !request->status().is_success()) { | |
135 auto status = request->status(); | |
mmenke
2015/07/17 17:57:47
Writing out net::URLRequestStatus isn't a huge tas
Eran Messeri
2015/07/29 15:16:58
Done.
| |
136 DVLOG(1) << "Read error: " << status.status() << " " << status.error(); | |
137 scoped_ptr<FetchState> params(CleanupRequest(request)); | |
138 int error_value = GetNetErrorFromURLRequestStatus(status); | |
139 params->failed_callback.Run(params->log_id, error_value); | |
140 return false; | |
141 } | |
142 | |
143 // Not an error, but no data available, so wait for OnReadComplete | |
144 // callback. | |
145 if (request->status().is_io_pending()) { | |
146 DVLOG(1) << "IO Pending: Will wait."; | |
147 return false; | |
148 } | |
149 | |
150 // Nothing more to read from the stream - finish handling the response. | |
151 if (bytes_read == 0) { | |
152 DVLOG(1) << "No more bytes to read, finishing."; | |
mmenke
2015/07/17 17:57:46
Do you plan on keeping all these log statements ar
Eran Messeri
2015/07/29 15:16:58
I've removed some, what would you recommend leavin
mmenke
2015/07/29 18:51:50
I mostly use breakpoint debugging, so not a big fa
| |
153 RequestComplete(request); | |
154 return false; | |
155 } | |
156 | |
157 // We have data, collect it and indicate another read is needed. | |
158 DVLOG(1) << "Have " << bytes_read << " bytes to assemble."; | |
159 DCHECK(bytes_read > 0); | |
mmenke
2015/07/17 17:57:47
DCHECK_GE
Eran Messeri
2015/07/29 15:16:58
Done.
| |
160 base::StringPiece sth_fragment(params->response_buffer->data(), bytes_read); | |
161 sth_fragment.AppendToString(¶ms->assembled_response); | |
mmenke
2015/07/17 17:57:47
Why not just use:
params->assumbed_response.appen
Eran Messeri
2015/07/29 15:16:58
Done.
| |
162 params->total_response_size += bytes_read; | |
mmenke
2015/07/17 17:57:47
Why is this needed? Doesn't params->assumbed_resp
Eran Messeri
2015/07/29 15:16:58
Yes, removed.
| |
163 // Indicate this fragment was read. | |
164 params->read_bytes = 0; | |
165 return true; | |
166 } | |
167 | |
168 void LogProofFetcher::KickOffARead(net::URLRequest* request, | |
169 FetchState* params) { | |
170 params->read_bytes = 0; | |
171 request->Read(params->response_buffer.get(), params->response_buffer->size(), | |
172 ¶ms->read_bytes); | |
173 } | |
174 | |
175 void LogProofFetcher::RequestComplete(net::URLRequest* request) { | |
176 auto it(inflight_requests_.find(request)); | |
177 DCHECK(it != inflight_requests_.end()); | |
178 if (it->first != request) { | |
mmenke
2015/07/17 17:57:47
Get rid of this branch - shouldn't handle cases th
Eran Messeri
2015/07/29 15:16:58
My bad, this is a leftover check from an older ver
| |
179 NOTREACHED(); | |
180 return; | |
181 } | |
182 | |
183 FetchState* params = it->second; | |
184 | |
185 // Extract STH json an parse it. | |
186 DCHECK(params->total_response_size < kMaxLogResponseSizeInBytes); | |
mmenke
2015/07/17 17:57:46
DCHECK_LT will produce more useful output on trybo
Eran Messeri
2015/07/29 15:16:58
Done.
| |
187 | |
188 // Explicitly copy the request params as it'll be cleaned up by | |
189 // CleanupRequest later on. | |
190 FetchState request_params = *params; | |
191 // Get rid of the buffer as it really isn't necessary. | |
192 request_params.response_buffer = nullptr; | |
193 safe_json::SafeJsonParser::Parse( | |
194 params->assembled_response, | |
195 base::Bind(&LogProofFetcher::OnSTHJsonParseSuccess, | |
196 weak_factory_.GetWeakPtr(), request_params), | |
197 base::Bind(&LogProofFetcher::OnSTHJsonParseError, | |
198 weak_factory_.GetWeakPtr(), request_params)); | |
199 | |
200 CleanupRequest(request); | |
201 } | |
202 | |
203 scoped_ptr<LogProofFetcher::FetchState> LogProofFetcher::CleanupRequest( | |
204 net::URLRequest* request) { | |
205 DVLOG(1) << "Cleaning up request to " << request->original_url(); | |
206 auto it = inflight_requests_.find(request); | |
207 DCHECK(it != inflight_requests_.end()); | |
208 | |
209 scoped_ptr<FetchState> params(it->second); | |
210 // Delete the request, remove from map. | |
211 scoped_ptr<net::URLRequest> url_request(it->first); | |
212 // Both pointers will be deleted when exiting the method. | |
213 inflight_requests_.erase(it); | |
214 return params.Pass(); | |
215 } | |
216 | |
217 scoped_ptr<net::URLRequest> LogProofFetcher::CreateURLRequest( | |
218 const GURL& fetch_sth_url) { | |
219 DCHECK(request_context_); | |
220 | |
221 scoped_ptr<net::URLRequest> request = request_context_->CreateRequest( | |
222 fetch_sth_url, net::DEFAULT_PRIORITY, this); | |
223 request->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES | | |
224 net::LOAD_DO_NOT_SAVE_COOKIES); | |
225 request->set_method("GET"); | |
226 return request.Pass(); | |
227 } | |
228 | |
229 void LogProofFetcher::OnSTHJsonParseSuccess( | |
230 FetchState params, | |
231 scoped_ptr<base::Value> parsed_json) { | |
232 net::ct::SignedTreeHead sth; | |
mmenke
2015/07/17 17:57:46
Again, suggest writing this out.
Eran Messeri
2015/07/29 15:16:58
Done.
| |
233 if (!net::ct::FillSignedTreeHead(*parsed_json.get(), &sth)) { | |
234 params.failed_callback.Run(params.log_id, net::ERR_CT_STH_INCOMPLETE); | |
235 return; | |
236 } | |
237 | |
238 params.fetched_callback.Run(params.log_id, sth); | |
239 } | |
240 | |
241 void LogProofFetcher::OnSTHJsonParseError(FetchState params, | |
242 const std::string& error) { | |
243 params.failed_callback.Run(params.log_id, net::ERR_CT_STH_PARSING_FAILED); | |
244 } | |
245 | |
246 LogProofFetcher::FetchState::FetchState(const std::string& id, | |
247 FetchSTHCallback fetched_callback, | |
248 FetchFailedCallback failed_callback) | |
249 : log_id(id), | |
250 fetched_callback(fetched_callback), | |
251 failed_callback(failed_callback), | |
252 response_buffer(new net::IOBufferWithSize(kMaxLogResponseSizeInBytes)), | |
253 total_response_size(0) { | |
254 } | |
255 | |
256 LogProofFetcher::FetchState::~FetchState() { | |
257 } | |
mmenke
2015/07/17 17:57:47
These should be up with the declaration of FetchSt
Eran Messeri
2015/07/29 15:16:58
Done.
| |
258 | |
259 } // namespace certificate_transparency | |
OLD | NEW |