Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(937)

Side by Side Diff: net/spdy/spdy_deframer_visitor.cc

Issue 2248343004: Add SpdyDeframerVisitor. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix MSVC compile error. Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « net/spdy/spdy_deframer_visitor.h ('k') | net/spdy/spdy_deframer_visitor_test.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2016 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 "net/spdy/spdy_deframer_visitor.h"
6
7 #include <stdlib.h>
8 #include <string.h>
9
10 #include <algorithm>
11 #include <limits>
12
13 #include "base/logging.h"
14 #include "base/memory/ptr_util.h"
15 #include "net/spdy/hpack/hpack_constants.h"
16 #include "net/spdy/mock_spdy_framer_visitor.h"
17 #include "net/spdy/spdy_frame_builder.h"
18 #include "net/spdy/spdy_frame_reader.h"
19 #include "net/spdy/spdy_protocol.h"
20 #include "net/spdy/spdy_test_utils.h"
21
22 using ::base::MakeUnique;
23 using ::base::StringPiece;
24 using ::std::string;
25 using ::testing::AssertionFailure;
26 using ::testing::AssertionResult;
27 using ::testing::AssertionSuccess;
28
29 namespace net {
30 namespace test {
31
32 // Specify whether to process headers as request or response in visitor-related
33 // params.
34 enum class HeaderDirection { REQUEST, RESPONSE };
35
36 // Types of HTTP/2 frames, per RFC 7540.
37 // TODO(jamessynge): Switch to using //net/http2/http2_constants.h when ready.
38 enum Http2FrameType {
39 DATA = 0,
40 HEADERS = 1,
41 PRIORITY = 2,
42 RST_STREAM = 3,
43 SETTINGS = 4,
44 PUSH_PROMISE = 5,
45 PING = 6,
46 GOAWAY = 7,
47 WINDOW_UPDATE = 8,
48 CONTINUATION = 9,
49 ALTSVC = 10,
50
51 // Not a frame type.
52 UNSET = -1,
53 UNKNOWN = -2,
54 };
55
56 // TODO(jamessynge): Switch to using //net/http2/http2_constants.h when ready.
57 const char* Http2FrameTypeToString(Http2FrameType v) {
58 switch (v) {
59 case DATA:
60 return "DATA";
61 case HEADERS:
62 return "HEADERS";
63 case PRIORITY:
64 return "PRIORITY";
65 case RST_STREAM:
66 return "RST_STREAM";
67 case SETTINGS:
68 return "SETTINGS";
69 case PUSH_PROMISE:
70 return "PUSH_PROMISE";
71 case PING:
72 return "PING";
73 case GOAWAY:
74 return "GOAWAY";
75 case WINDOW_UPDATE:
76 return "WINDOW_UPDATE";
77 case CONTINUATION:
78 return "CONTINUATION";
79 case ALTSVC:
80 return "ALTSVC";
81 case UNSET:
82 return "UNSET";
83 case UNKNOWN:
84 return "UNKNOWN";
85 default:
86 return "Invalid Http2FrameType";
87 }
88 }
89
90 // TODO(jamessynge): Switch to using //net/http2/http2_constants.h when ready.
91 inline std::ostream& operator<<(std::ostream& out, Http2FrameType v) {
92 return out << Http2FrameTypeToString(v);
93 }
94
95 // Flag bits in the flag field of the common header of HTTP/2 frames
96 // (see https://httpwg.github.io/specs/rfc7540.html#FrameHeader for details on
97 // the fixed 9-octet header structure shared by all frames).
98 // Flag bits are only valid for specified frame types.
99 // TODO(jamessynge): Switch to using //net/http2/http2_constants.h when ready.
100 enum Http2HeaderFlag {
101 NO_FLAGS = 0,
102
103 END_STREAM_FLAG = 0x1,
104 ACK_FLAG = 0x1,
105 END_HEADERS_FLAG = 0x4,
106 PADDED_FLAG = 0x8,
107 PRIORITY_FLAG = 0x20,
108 };
109
110 // Returns name of frame type.
111 // TODO(jamessynge): Switch to using //net/http2/http2_constants.h when ready.
112 const char* Http2FrameTypeToString(Http2FrameType v);
113
114 void SpdyDeframerVisitorInterface::OnPingAck(
115 std::unique_ptr<SpdyPingIR> frame) {
116 OnPing(std::move(frame));
117 }
118
119 void SpdyDeframerVisitorInterface::OnSettingsAck(
120 std::unique_ptr<SpdySettingsIR> frame) {
121 OnSettings(std::move(frame), nullptr);
122 }
123
124 class SpdyTestDeframerImpl : public SpdyTestDeframer,
125 public SpdyHeadersHandlerInterface {
126 public:
127 explicit SpdyTestDeframerImpl(
128 std::unique_ptr<SpdyDeframerVisitorInterface> listener)
129 : listener_(std::move(listener)) {
130 CHECK(listener_);
131 }
132 ~SpdyTestDeframerImpl() override {}
133
134 bool AtFrameEnd() override;
135
136 // Callbacks defined in SpdyFramerVisitorInterface. These are in the
137 // alphabetical order for ease of navigation, and are not in same order
138 // as in SpdyFramerVisitorInterface.
139 void OnAltSvc(SpdyStreamId stream_id,
140 StringPiece origin,
141 const SpdyAltSvcWireFormat::AlternativeServiceVector&
142 altsvc_vector) override;
143 void OnBlocked(SpdyStreamId stream_id) override;
144 void OnContinuation(SpdyStreamId stream_id, bool end) override;
145 SpdyHeadersHandlerInterface* OnHeaderFrameStart(
146 SpdyStreamId stream_id) override;
147 void OnHeaderFrameEnd(SpdyStreamId stream_id, bool end_headers) override;
148 bool OnControlFrameHeaderData(SpdyStreamId stream_id,
149 const char* header_data,
150 size_t header_data_len) override;
151 void OnDataFrameHeader(SpdyStreamId stream_id,
152 size_t length,
153 bool fin) override;
154 void OnError(SpdyFramer* framer) override;
155 void OnGoAway(SpdyStreamId last_accepted_stream_id,
156 SpdyGoAwayStatus status) override;
157 bool OnGoAwayFrameData(const char* goaway_data, size_t len) override;
158 void OnHeaders(SpdyStreamId stream_id,
159 bool has_priority,
160 int weight,
161 SpdyStreamId parent_stream_id,
162 bool exclusive,
163 bool fin,
164 bool end) override;
165 void OnPing(SpdyPingId unique_id, bool is_ack) override;
166 void OnPriority(SpdyStreamId stream_id,
167 SpdyStreamId parent_stream_id,
168 int weight,
169 bool exclusive) override;
170 void OnPushPromise(SpdyStreamId stream_id,
171 SpdyStreamId promised_stream_id,
172 bool end) override;
173 void OnRstStream(SpdyStreamId stream_id, SpdyRstStreamStatus status) override;
174 bool OnRstStreamFrameData(const char* rst_stream_data, size_t len) override;
175 void OnSetting(SpdySettingsIds id, uint8_t flags, uint32_t value) override;
176 void OnSettings(bool clear_persisted) override;
177 void OnSettingsAck() override;
178 void OnSettingsEnd() override;
179 void OnStreamFrameData(SpdyStreamId stream_id,
180 const char* data,
181 size_t len) override;
182 void OnStreamEnd(SpdyStreamId stream_id) override;
183 void OnStreamPadding(SpdyStreamId stream_id, size_t len) override;
184 void OnSynReply(SpdyStreamId stream_id, bool fin) override;
185 void OnSynStream(SpdyStreamId stream_id,
186 SpdyStreamId associated_stream_id,
187 SpdyPriority priority,
188 bool fin,
189 bool unidirectional) override;
190 bool OnUnknownFrame(SpdyStreamId stream_id, int frame_type) override;
191 void OnWindowUpdate(SpdyStreamId stream_id, int delta_window_size) override;
192
193 // Callbacks defined in SpdyHeadersHandlerInterface.
194
195 void OnHeaderBlockStart() override;
196 void OnHeader(StringPiece key, StringPiece value) override;
197 void OnHeaderBlockEnd(size_t header_bytes_parsed) override;
198
199 protected:
200 void AtDataEnd();
201 void AtGoAwayEnd();
202 void AtHeadersEnd();
203 void AtPushPromiseEnd();
204
205 // Per-physical frame state.
206 // Frame type of the frame currently being processed.
207 Http2FrameType frame_type_ = UNSET;
208 // Stream id of the frame currently being processed.
209 SpdyStreamId stream_id_;
210 // Did the most recent frame header include the END_HEADERS flag?
211 bool end_ = false;
212 // Did the most recent frame header include the ack flag?
213 bool ack_ = false;
214
215 // Per-HPACK block state. Only valid while processing a HEADERS or
216 // PUSH_PROMISE frame, and its CONTINUATION frames.
217 // Did the most recent HEADERS or PUSH_PROMISE include the END_STREAM flag?
218 // Note that this does not necessarily indicate that the current frame is
219 // the last frame for the stream (may be followed by CONTINUATION frames,
220 // may only half close).
221 bool fin_ = false;
222 bool got_hpack_end_ = false;
223
224 std::unique_ptr<string> data_;
225
226 // Total length of the data frame.
227 size_t data_len_ = 0;
228
229 // Amount of skipped padding (i.e. total length of padding, including Pad
230 // Length field).
231 size_t padding_len_ = 0;
232
233 std::unique_ptr<string> goaway_description_;
234 std::unique_ptr<StringPairVector> headers_;
235 std::unique_ptr<SettingVector> settings_;
236 std::unique_ptr<TestHeadersHandler> headers_handler_;
237
238 std::unique_ptr<SpdyGoAwayIR> goaway_ir_;
239 std::unique_ptr<SpdyHeadersIR> headers_ir_;
240 std::unique_ptr<SpdyPushPromiseIR> push_promise_ir_;
241 std::unique_ptr<SpdySettingsIR> settings_ir_;
242
243 private:
244 std::unique_ptr<SpdyDeframerVisitorInterface> listener_;
245
246 DISALLOW_COPY_AND_ASSIGN(SpdyTestDeframerImpl);
247 };
248
249 // static
250 std::unique_ptr<SpdyTestDeframer> SpdyTestDeframer::CreateConverter(
251 std::unique_ptr<SpdyDeframerVisitorInterface> listener) {
252 return MakeUnique<SpdyTestDeframerImpl>(std::move(listener));
253 }
254
255 void SpdyTestDeframerImpl::AtDataEnd() {
256 DVLOG(1) << "AtDataEnd";
257 CHECK_EQ(data_len_, padding_len_ + data_->size());
258 auto ptr = MakeUnique<SpdyDataIR>(stream_id_, std::move(*data_));
259 CHECK_EQ(0u, data_->size());
260 data_.reset();
261
262 CHECK_LE(0u, padding_len_);
263 CHECK_LE(padding_len_, 256u);
264 if (padding_len_ > 0) {
265 ptr->set_padding_len(padding_len_);
266 }
267 padding_len_ = 0;
268
269 ptr->set_fin(fin_);
270 listener_->OnData(std::move(ptr));
271 frame_type_ = UNSET;
272 fin_ = false;
273 data_len_ = 0;
274 }
275
276 void SpdyTestDeframerImpl::AtGoAwayEnd() {
277 DVLOG(1) << "AtDataEnd";
278 CHECK_EQ(frame_type_, GOAWAY);
279 CHECK(goaway_description_);
280 if (goaway_description_->empty()) {
281 listener_->OnGoAway(std::move(goaway_ir_));
282 } else {
283 listener_->OnGoAway(MakeUnique<SpdyGoAwayIR>(
284 goaway_ir_->last_good_stream_id(), goaway_ir_->status(),
285 std::move(*goaway_description_)));
286 CHECK_EQ(0u, goaway_description_->size());
287 }
288 goaway_description_.reset();
289 goaway_ir_.reset();
290 frame_type_ = UNSET;
291 }
292
293 void SpdyTestDeframerImpl::AtHeadersEnd() {
294 DVLOG(1) << "AtDataEnd";
295 CHECK(frame_type_ == HEADERS || frame_type_ == CONTINUATION)
296 << " frame_type_=" << Http2FrameTypeToString(frame_type_);
297 CHECK(end_) << " frame_type_=" << Http2FrameTypeToString(frame_type_);
298 CHECK(got_hpack_end_);
299
300 CHECK(headers_ir_);
301 CHECK(headers_);
302 CHECK(headers_handler_);
303
304 CHECK_LE(0u, padding_len_);
305 CHECK_LE(padding_len_, 256u);
306 if (padding_len_ > 0) {
307 headers_ir_->set_padding_len(padding_len_);
308 }
309 padding_len_ = 0;
310
311 headers_ir_->set_header_block(headers_handler_->decoded_block().Clone());
312 headers_handler_.reset();
313 listener_->OnHeaders(std::move(headers_ir_), std::move(headers_));
314
315 frame_type_ = UNSET;
316 fin_ = false;
317 end_ = false;
318 got_hpack_end_ = false;
319 }
320
321 void SpdyTestDeframerImpl::AtPushPromiseEnd() {
322 DVLOG(1) << "AtDataEnd";
323 CHECK(frame_type_ == PUSH_PROMISE || frame_type_ == CONTINUATION)
324 << " frame_type_=" << Http2FrameTypeToString(frame_type_);
325 CHECK(end_) << " frame_type_=" << Http2FrameTypeToString(frame_type_);
326
327 CHECK(push_promise_ir_);
328 CHECK(headers_);
329 CHECK(headers_handler_);
330
331 CHECK_EQ(headers_ir_.get(), nullptr);
332
333 CHECK_LE(0u, padding_len_);
334 CHECK_LE(padding_len_, 256u);
335 if (padding_len_ > 0) {
336 push_promise_ir_->set_padding_len(padding_len_);
337 }
338 padding_len_ = 0;
339
340 push_promise_ir_->set_header_block(headers_handler_->decoded_block().Clone());
341 headers_handler_.reset();
342 listener_->OnPushPromise(std::move(push_promise_ir_), std::move(headers_));
343
344 frame_type_ = UNSET;
345 end_ = false;
346 }
347
348 bool SpdyTestDeframerImpl::AtFrameEnd() {
349 bool incomplete_logical_header = false;
350 // The caller says that the SpdyFrame has reached the end of the frame,
351 // so if we have any accumulated data, flush it.
352 switch (frame_type_) {
353 case DATA:
354 AtDataEnd();
355 break;
356
357 case GOAWAY:
358 AtGoAwayEnd();
359 break;
360
361 case HEADERS:
362 if (end_) {
363 AtHeadersEnd();
364 } else {
365 incomplete_logical_header = true;
366 }
367 break;
368
369 case PUSH_PROMISE:
370 if (end_) {
371 AtPushPromiseEnd();
372 } else {
373 incomplete_logical_header = true;
374 }
375 break;
376
377 case CONTINUATION:
378 if (end_) {
379 if (headers_ir_) {
380 AtHeadersEnd();
381 } else if (push_promise_ir_) {
382 AtPushPromiseEnd();
383 } else {
384 LOG(FATAL) << "Where is the SpdyFrameIR for the headers!";
385 }
386 } else {
387 incomplete_logical_header = true;
388 }
389 break;
390
391 case UNSET:
392 // Except for the frame types above, the others don't leave any record
393 // in the state of this object. Make sure nothing got left by accident.
394 CHECK_EQ(data_.get(), nullptr);
395 CHECK_EQ(goaway_description_.get(), nullptr);
396 CHECK_EQ(goaway_ir_.get(), nullptr);
397 CHECK_EQ(headers_.get(), nullptr);
398 CHECK_EQ(headers_handler_.get(), nullptr);
399 CHECK_EQ(headers_ir_.get(), nullptr);
400 CHECK_EQ(push_promise_ir_.get(), nullptr);
401 CHECK_EQ(settings_.get(), nullptr);
402 CHECK_EQ(settings_ir_.get(), nullptr);
403 break;
404
405 default:
406 SPDY_BUG << "Expected UNSET, instead frame_type_==" << frame_type_;
407 return false;
408 }
409 frame_type_ = UNSET;
410 stream_id_ = 0;
411 end_ = false;
412 ack_ = false;
413 if (!incomplete_logical_header) {
414 fin_ = false;
415 }
416 return true;
417 }
418
419 // Overridden methods from SpdyFramerVisitorInterface in alpha order...
420
421 void SpdyTestDeframerImpl::OnAltSvc(
422 SpdyStreamId stream_id,
423 StringPiece origin,
424 const SpdyAltSvcWireFormat::AlternativeServiceVector& altsvc_vector) {
425 DVLOG(1) << "OnAltSvc stream_id: " << stream_id;
426 CHECK_EQ(frame_type_, UNSET) << " frame_type_="
427 << Http2FrameTypeToString(frame_type_);
428 CHECK_GT(stream_id, 0u);
429 auto ptr = MakeUnique<SpdyAltSvcIR>(stream_id);
430 ptr->set_origin(origin.as_string());
431 for (auto& altsvc : altsvc_vector) {
432 ptr->add_altsvc(altsvc);
433 }
434 listener_->OnAltSvc(std::move(ptr));
435 }
436
437 // Frame type BLOCKED was removed in draft 12 of HTTP/2. The intent appears to
438 // have been to support debugging; it is not expected to be seen except if the
439 // peer "thinks" that a bug exists in the flow control such that the peer can't
440 // send because the receiver hasn't sent WINDOW_UPDATE frames. Since we might
441 // be talking to multiple backends, it is quite plausible that one backend
442 // is unable to take more input from the client (hence no WINDOW_UPDATE), yet
443 // other backends can take more input.
444 void SpdyTestDeframerImpl::OnBlocked(SpdyStreamId stream_id) {
445 LOG(FATAL) << "OnBlocked stream_id: " << stream_id;
446 CHECK_EQ(frame_type_, UNSET) << " frame_type_="
447 << Http2FrameTypeToString(frame_type_);
448 CHECK_GT(stream_id, 0u);
449 frame_type_ = UNSET;
450 stream_id_ = stream_id;
451 }
452
453 // A CONTINUATION frame contains a Header Block Fragment, and immediately
454 // follows another frame that contains a Header Block Fragment (HEADERS,
455 // PUSH_PROMISE or CONTINUATION). The last such frame has the END flag set.
456 // SpdyFramer ensures that the behavior is correct before calling the visitor.
457 void SpdyTestDeframerImpl::OnContinuation(SpdyStreamId stream_id, bool end) {
458 DVLOG(1) << "OnContinuation stream_id: " << stream_id;
459 CHECK_EQ(frame_type_, UNSET) << " frame_type_="
460 << Http2FrameTypeToString(frame_type_);
461 CHECK_GT(stream_id, 0u);
462 CHECK_NE(nullptr, headers_.get());
463 frame_type_ = CONTINUATION;
464
465 stream_id_ = stream_id;
466 end_ = end;
467 }
468
469 // Called with the decompressed contents of headers, in zero or more pieces
470 // (i.e. an empty headers frame doesn't have any non-zero length data).
471 // Note that the end of the headers is indicated by header_data==nullptr
472 // AND header_data_len==0, even if there were no non-zero pieces.
473 // SpdyHeadersBlockParser decodes these.
474 // Returning false kills the connection (SpdyFramer enters state SPDY_ERROR).
475 bool SpdyTestDeframerImpl::OnControlFrameHeaderData(SpdyStreamId stream_id,
476 const char* header_data,
477 size_t header_data_len) {
478 DVLOG(1) << "OnControlFrameHeaderData stream_id: " << stream_id
479 << " len: " << header_data_len;
480 CHECK(frame_type_ == HEADERS || frame_type_ == CONTINUATION ||
481 frame_type_ == PUSH_PROMISE)
482 << " frame_type_=" << Http2FrameTypeToString(frame_type_);
483 CHECK_EQ(stream_id_, stream_id);
484 LOG(FATAL) << "Must call SpdyFramer::set_use_new_methods_for_test(true)";
485 return true;
486 }
487
488 // Note that length includes the padding length (0 to 256, when the optional
489 // padding length field is counted). Padding comes after the payload, both
490 // for DATA frames and for control frames.
491 void SpdyTestDeframerImpl::OnDataFrameHeader(SpdyStreamId stream_id,
492 size_t length,
493 bool fin) {
494 DVLOG(1) << "OnDataFrameHeader stream_id: " << stream_id;
495 CHECK_EQ(frame_type_, UNSET) << " frame_type_="
496 << Http2FrameTypeToString(frame_type_);
497 CHECK_GT(stream_id, 0u);
498 CHECK_EQ(data_.get(), nullptr);
499 frame_type_ = DATA;
500
501 stream_id_ = stream_id;
502 fin_ = fin;
503 data_len_ = length;
504 data_.reset(new string());
505 }
506
507 // The SpdyFramer will not process any more data at this point.
508 void SpdyTestDeframerImpl::OnError(SpdyFramer* framer) {
509 DVLOG(1) << "SpdyFramer detected an error in the stream: "
510 << SpdyFramer::ErrorCodeToString(framer->error_code())
511 << " frame_type_: " << Http2FrameTypeToString(frame_type_);
512 listener_->OnError(framer, this);
513 }
514
515 // Received a GOAWAY frame from the peer. The last stream id it accepted from us
516 // is |last_accepted_stream_id|. |status| is a protocol defined error code.
517 // The frame may also contain data. After this OnGoAwayFrameData will be called
518 // for any non-zero amount of data, and after that it will be called with len==0
519 // to indicate the end of the GOAWAY frame.
520 void SpdyTestDeframerImpl::OnGoAway(SpdyStreamId last_good_stream_id,
521 SpdyGoAwayStatus status) {
522 DVLOG(1) << "OnGoAway last_good_stream_id: " << last_good_stream_id
523 << " status: " << status;
524 CHECK_EQ(frame_type_, UNSET) << " frame_type_="
525 << Http2FrameTypeToString(frame_type_);
526 frame_type_ = GOAWAY;
527 goaway_ir_ = MakeUnique<SpdyGoAwayIR>(last_good_stream_id, status, "");
528 goaway_description_.reset(new string());
529 }
530
531 // If len==0 then we've reached the end of the GOAWAY frame.
532 bool SpdyTestDeframerImpl::OnGoAwayFrameData(const char* goaway_data,
533 size_t len) {
534 DVLOG(1) << "OnGoAwayFrameData";
535 CHECK_EQ(frame_type_, GOAWAY) << " frame_type_="
536 << Http2FrameTypeToString(frame_type_);
537 CHECK(goaway_description_);
538 StringPiece(goaway_data, len).AppendToString(goaway_description_.get());
539 return true;
540 }
541
542 SpdyHeadersHandlerInterface* SpdyTestDeframerImpl::OnHeaderFrameStart(
543 SpdyStreamId stream_id) {
544 return this;
545 }
546
547 void SpdyTestDeframerImpl::OnHeaderFrameEnd(SpdyStreamId stream_id,
548 bool end_headers) {
549 DVLOG(1) << "OnHeaderFrameEnd stream_id: " << stream_id
550 << " end_headers: " << (end_headers ? "true" : "false");
551 }
552
553 // Received the fixed portion of a HEADERS frame. Called before the variable
554 // length (including zero length) Header Block Fragment is processed. If fin
555 // is true then there will be no DATA or trailing HEADERS after this HEADERS
556 // frame.
557 // If end is true, then there will be no CONTINUATION frame(s) following this
558 // frame; else if true then there will be CONTINATION frames(s) immediately
559 // following this frame, terminated by a CONTINUATION frame with end==true.
560 void SpdyTestDeframerImpl::OnHeaders(SpdyStreamId stream_id,
561 bool has_priority,
562 int weight,
563 SpdyStreamId parent_stream_id,
564 bool exclusive,
565 bool fin,
566 bool end) {
567 DVLOG(1) << "OnHeaders stream_id: " << stream_id;
568 CHECK_EQ(frame_type_, UNSET) << " frame_type_="
569 << Http2FrameTypeToString(frame_type_);
570 CHECK_GT(stream_id, 0u);
571 frame_type_ = HEADERS;
572
573 stream_id_ = stream_id;
574 fin_ = fin;
575 end_ = end;
576
577 headers_.reset(new StringPairVector());
578 headers_handler_.reset(new TestHeadersHandler());
579 headers_ir_ = MakeUnique<SpdyHeadersIR>(stream_id);
580 headers_ir_->set_fin(fin);
581 if (has_priority) {
582 headers_ir_->set_has_priority(true);
583 headers_ir_->set_weight(weight);
584 headers_ir_->set_parent_stream_id(parent_stream_id);
585 headers_ir_->set_exclusive(exclusive);
586 }
587 }
588
589 // The HTTP/2 protocol refers to the payload, |unique_id| here, as 8 octets of
590 // opaque data that is to be echoed back to the sender, with the ACK bit added.
591 // It isn't defined as a counter,
592 // or frame id, as the SpdyPingId naming might imply.
593 // Responding to a PING is supposed to be at the highest priority.
594 void SpdyTestDeframerImpl::OnPing(uint64_t unique_id, bool is_ack) {
595 DVLOG(1) << "OnPing unique_id: " << unique_id
596 << " is_ack: " << (is_ack ? "true" : "false");
597 CHECK_EQ(frame_type_, UNSET) << " frame_type_="
598 << Http2FrameTypeToString(frame_type_);
599 auto ptr = MakeUnique<SpdyPingIR>(unique_id);
600 if (is_ack) {
601 ptr->set_is_ack(is_ack);
602 listener_->OnPingAck(std::move(ptr));
603 } else {
604 listener_->OnPing(std::move(ptr));
605 }
606 }
607
608 void SpdyTestDeframerImpl::OnPriority(SpdyStreamId stream_id,
609 SpdyStreamId parent_stream_id,
610 int weight,
611 bool exclusive) {
612 DVLOG(1) << "OnPriority stream_id: " << stream_id;
613 CHECK_EQ(frame_type_, UNSET) << " frame_type_="
614 << Http2FrameTypeToString(frame_type_);
615 CHECK_GT(stream_id, 0u);
616
617 listener_->OnPriority(MakeUnique<SpdyPriorityIR>(stream_id, parent_stream_id,
618 weight, exclusive));
619 }
620
621 void SpdyTestDeframerImpl::OnPushPromise(SpdyStreamId stream_id,
622 SpdyStreamId promised_stream_id,
623 bool end) {
624 DVLOG(1) << "OnPushPromise stream_id: " << stream_id;
625 CHECK_EQ(frame_type_, UNSET) << " frame_type_="
626 << Http2FrameTypeToString(frame_type_);
627 CHECK_GT(stream_id, 0u);
628
629 frame_type_ = PUSH_PROMISE;
630 stream_id_ = stream_id;
631 end_ = end;
632
633 headers_.reset(new StringPairVector());
634 headers_handler_.reset(new TestHeadersHandler());
635 push_promise_ir_ =
636 MakeUnique<SpdyPushPromiseIR>(stream_id, promised_stream_id);
637 }
638
639 // Closes the specified stream. After this the sender may still send PRIORITY
640 // frames for this stream, which we can ignore.
641 void SpdyTestDeframerImpl::OnRstStream(SpdyStreamId stream_id,
642 SpdyRstStreamStatus status) {
643 DVLOG(1) << "OnRstStream stream_id: " << stream_id
644 << " status: " << status;
645 CHECK_EQ(frame_type_, UNSET) << " frame_type_="
646 << Http2FrameTypeToString(frame_type_);
647 CHECK_GT(stream_id, 0u);
648
649 listener_->OnRstStream(MakeUnique<SpdyRstStreamIR>(stream_id, status));
650 }
651
652 // The HTTP/2 spec states that there is no data in the frame other that the
653 // SpdyRstStreamStatus passed to OnRstStream, so it appears that SpdyFramer's
654 // comments w.r.t. this method are obsolete. We still receive a zero length
655 // invocation when RST_STREAM frame processing completes.
656 bool SpdyTestDeframerImpl::OnRstStreamFrameData(const char* rst_stream_data,
657 size_t len) {
658 DVLOG(1) << "OnRstStreamFrameData";
659 CHECK_EQ(0u, len);
660 return true;
661 }
662
663 // Called for an individual setting. There is no negotiation, the sender is
664 // stating the value that the sender is using.
665 void SpdyTestDeframerImpl::OnSetting(SpdySettingsIds id,
666 uint8_t flags,
667 uint32_t value) {
668 DVLOG(1) << "OnSetting id: " << id << std::hex << " flags: " << flags
669 << " value: " << value;
670 CHECK_EQ(frame_type_, SETTINGS) << " frame_type_="
671 << Http2FrameTypeToString(frame_type_);
672 CHECK(settings_);
673 settings_->push_back(std::make_pair(id, value));
674 settings_ir_->AddSetting(id, true, true, value);
675 }
676
677 // Called at the start of a SETTINGS frame with setting entries, but not the
678 // (required) ACK of a SETTINGS frame. There is no stream_id because
679 // the settings apply to the entire connection, not to an individual stream.
680 // The |clear_persisted| flag is a pre-HTTP/2 remnant.
681 void SpdyTestDeframerImpl::OnSettings(bool /*clear_persisted*/) {
682 DVLOG(1) << "OnSettings";
683 CHECK_EQ(frame_type_, UNSET) << " frame_type_="
684 << Http2FrameTypeToString(frame_type_);
685 CHECK_EQ(nullptr, settings_ir_.get());
686 CHECK_EQ(nullptr, settings_.get());
687 frame_type_ = SETTINGS;
688 ack_ = false;
689
690 settings_.reset(new SettingVector());
691 settings_ir_.reset(new SpdySettingsIR());
692 }
693
694 void SpdyTestDeframerImpl::OnSettingsAck() {
695 DVLOG(1) << "OnSettingsAck";
696 CHECK_EQ(frame_type_, UNSET) << " frame_type_="
697 << Http2FrameTypeToString(frame_type_);
698 auto ptr = MakeUnique<SpdySettingsIR>();
699 ptr->set_is_ack(true);
700 listener_->OnSettingsAck(std::move(ptr));
701 }
702
703 void SpdyTestDeframerImpl::OnSettingsEnd() {
704 DVLOG(1) << "OnSettingsEnd";
705 CHECK_EQ(frame_type_, SETTINGS) << " frame_type_="
706 << Http2FrameTypeToString(frame_type_);
707 CHECK(!ack_);
708 CHECK_NE(nullptr, settings_ir_.get());
709 CHECK_NE(nullptr, settings_.get());
710 listener_->OnSettings(std::move(settings_ir_), std::move(settings_));
711 frame_type_ = UNSET;
712 }
713
714 // Called for a zero length DATA frame with the END_STREAM flag set, or at the
715 // end a complete HPACK block (and its padding) that started with a HEADERS
716 // frame with the END_STREAM flag set. Doesn't apply to PUSH_PROMISE frames
717 // because they don't have END_STREAM flags.
718 void SpdyTestDeframerImpl::OnStreamEnd(SpdyStreamId stream_id) {
719 DVLOG(1) << "OnStreamEnd stream_id: " << stream_id;
720 CHECK_EQ(stream_id_, stream_id);
721 CHECK(frame_type_ == DATA || frame_type_ == HEADERS ||
722 frame_type_ == CONTINUATION)
723 << " frame_type_=" << Http2FrameTypeToString(frame_type_);
724 CHECK(fin_);
725 }
726
727 // The data arg points into the non-padding payload of a DATA frame.
728 // This must be a DATA frame (i.e. this method will not be
729 // called for HEADERS or CONTINUATION frames).
730 // This method may be called multiple times for a single DATA frame, depending
731 // upon buffer boundaries.
732 void SpdyTestDeframerImpl::OnStreamFrameData(SpdyStreamId stream_id,
733 const char* data,
734 size_t len) {
735 DVLOG(1) << "OnStreamFrameData stream_id: " << stream_id
736 << " len: " << len;
737 CHECK_EQ(stream_id_, stream_id);
738 CHECK_EQ(frame_type_, DATA);
739 StringPiece(data, len).AppendToString(data_.get());
740 }
741
742 // Called when padding is skipped over, including the padding length field at
743 // the start of the frame payload, and the actual padding at the end. len will
744 // be in the range 1 to 255.
745 void SpdyTestDeframerImpl::OnStreamPadding(SpdyStreamId stream_id, size_t len) {
746 DVLOG(1) << "OnStreamPadding stream_id: " << stream_id << " len: " << len;
747 CHECK(frame_type_ == DATA || frame_type_ == HEADERS ||
748 frame_type_ == PUSH_PROMISE)
749 << " frame_type_=" << Http2FrameTypeToString(frame_type_);
750 CHECK_EQ(stream_id_, stream_id);
751 CHECK_LE(1u, len);
752 CHECK_GE(255u, len);
753 padding_len_ += len;
754 CHECK_LE(padding_len_, 256u) << "len=" << len;
755 }
756
757 // Obsolete.
758 void SpdyTestDeframerImpl::OnSynStream(SpdyStreamId stream_id,
759 SpdyStreamId associated_stream_id,
760 SpdyPriority priority,
761 bool fin,
762 bool unidirectional) {
763 DVLOG(1) << "OnSynStream stream_id: " << stream_id;
764 CHECK_EQ(frame_type_, UNSET) << " frame_type_="
765 << Http2FrameTypeToString(frame_type_);
766 frame_type_ = UNKNOWN;
767 stream_id_ = stream_id;
768 fin_ = fin;
769 LOG(DFATAL) << "SYN_STREAM is not a valid HTTP/2 frame type.";
770 }
771
772 // Obsolete.
773 void SpdyTestDeframerImpl::OnSynReply(SpdyStreamId stream_id, bool fin) {
774 DVLOG(1) << "OnSynReply stream_id: " << stream_id;
775 CHECK_EQ(frame_type_, UNSET) << " frame_type_="
776 << Http2FrameTypeToString(frame_type_);
777 frame_type_ = UNKNOWN;
778 stream_id_ = stream_id;
779 fin_ = fin;
780 LOG(DFATAL) << "SYN_REPLY is not a valid HTTP/2 frame type.";
781 }
782
783 // WINDOW_UPDATE is supposed to be hop-by-hop, according to the spec.
784 // stream_id is 0 if the update applies to the connection, else stream_id
785 // will be the id of a stream previously seen, which maybe half or fully
786 // closed.
787 void SpdyTestDeframerImpl::OnWindowUpdate(SpdyStreamId stream_id,
788 int delta_window_size) {
789 DVLOG(1) << "OnWindowUpdate stream_id: " << stream_id
790 << " delta_window_size: " << delta_window_size;
791 CHECK_EQ(frame_type_, UNSET) << " frame_type_="
792 << Http2FrameTypeToString(frame_type_);
793 CHECK_NE(0, delta_window_size);
794
795 listener_->OnWindowUpdate(
796 MakeUnique<SpdyWindowUpdateIR>(stream_id, delta_window_size));
797 }
798
799 // Return true to indicate that the stream_id is valid; if not valid then
800 // SpdyFramer considers the connection corrupted. Requires keeping track
801 // of the set of currently open streams. For now we'll assume that unknown
802 // frame types are unsupported.
803 bool SpdyTestDeframerImpl::OnUnknownFrame(SpdyStreamId stream_id,
804 int frame_type) {
805 DVLOG(1) << "OnAltSvc stream_id: " << stream_id;
806 CHECK_EQ(frame_type_, UNSET) << " frame_type_="
807 << Http2FrameTypeToString(frame_type_);
808 frame_type_ = UNKNOWN;
809
810 stream_id_ = stream_id;
811 return false;
812 }
813
814 // Callbacks defined in SpdyHeadersHandlerInterface.
815
816 void SpdyTestDeframerImpl::OnHeaderBlockStart() {
817 CHECK(frame_type_ == HEADERS || frame_type_ == PUSH_PROMISE)
818 << " frame_type_=" << Http2FrameTypeToString(frame_type_);
819 CHECK(headers_);
820 CHECK_EQ(0u, headers_->size());
821 got_hpack_end_ = false;
822 }
823
824 void SpdyTestDeframerImpl::OnHeader(StringPiece key, StringPiece value) {
825 CHECK(frame_type_ == HEADERS || frame_type_ == CONTINUATION ||
826 frame_type_ == PUSH_PROMISE)
827 << " frame_type_=" << Http2FrameTypeToString(frame_type_);
828 CHECK(!got_hpack_end_);
829 CHECK(headers_);
830 headers_->emplace_back(key.as_string(), value.as_string());
831 CHECK(headers_handler_);
832 headers_handler_->OnHeader(key, value);
833 }
834
835 void SpdyTestDeframerImpl::OnHeaderBlockEnd(size_t header_bytes_parsed) {
836 CHECK(headers_);
837 CHECK(frame_type_ == HEADERS || frame_type_ == CONTINUATION ||
838 frame_type_ == PUSH_PROMISE)
839 << " frame_type_=" << Http2FrameTypeToString(frame_type_);
840 CHECK(end_);
841 CHECK(!got_hpack_end_);
842 got_hpack_end_ = true;
843 }
844
845 class LoggingSpdyDeframerDelegate : public SpdyDeframerVisitorInterface {
846 public:
847 explicit LoggingSpdyDeframerDelegate(
848 std::unique_ptr<SpdyDeframerVisitorInterface> wrapped)
849 : wrapped_(std::move(wrapped)) {
850 if (!wrapped_) {
851 wrapped_ = MakeUnique<SpdyDeframerVisitorInterface>();
852 }
853 }
854 ~LoggingSpdyDeframerDelegate() override {}
855
856 void OnAltSvc(std::unique_ptr<SpdyAltSvcIR> frame) override {
857 DVLOG(1) << "LoggingSpdyDeframerDelegate::OnAltSvc";
858 wrapped_->OnAltSvc(std::move(frame));
859 }
860 void OnData(std::unique_ptr<SpdyDataIR> frame) override {
861 DVLOG(1) << "LoggingSpdyDeframerDelegate::OnData";
862 wrapped_->OnData(std::move(frame));
863 }
864 void OnGoAway(std::unique_ptr<SpdyGoAwayIR> frame) override {
865 DVLOG(1) << "LoggingSpdyDeframerDelegate::OnGoAway";
866 wrapped_->OnGoAway(std::move(frame));
867 }
868
869 // SpdyHeadersIR and SpdyPushPromiseIR each has a SpdyHeaderBlock which
870 // significantly modifies the headers, so the actual header entries (name
871 // and value strings) are provided in a vector.
872 void OnHeaders(std::unique_ptr<SpdyHeadersIR> frame,
873 std::unique_ptr<StringPairVector> headers) override {
874 DVLOG(1) << "LoggingSpdyDeframerDelegate::OnHeaders";
875 wrapped_->OnHeaders(std::move(frame), std::move(headers));
876 }
877
878 void OnPing(std::unique_ptr<SpdyPingIR> frame) override {
879 DVLOG(1) << "LoggingSpdyDeframerDelegate::OnPing";
880 wrapped_->OnPing(std::move(frame));
881 }
882 void OnPingAck(std::unique_ptr<SpdyPingIR> frame) override {
883 DVLOG(1) << "LoggingSpdyDeframerDelegate::OnPingAck";
884 wrapped_->OnPingAck(std::move(frame));
885 }
886
887 void OnPriority(std::unique_ptr<SpdyPriorityIR> frame) override {
888 DVLOG(1) << "LoggingSpdyDeframerDelegate::OnPriority";
889 wrapped_->OnPriority(std::move(frame));
890 }
891
892 // SpdyHeadersIR and SpdyPushPromiseIR each has a SpdyHeaderBlock which
893 // significantly modifies the headers, so the actual header entries (name
894 // and value strings) are provided in a vector.
895 void OnPushPromise(std::unique_ptr<SpdyPushPromiseIR> frame,
896 std::unique_ptr<StringPairVector> headers) override {
897 DVLOG(1) << "LoggingSpdyDeframerDelegate::OnPushPromise";
898 wrapped_->OnPushPromise(std::move(frame), std::move(headers));
899 }
900
901 void OnRstStream(std::unique_ptr<SpdyRstStreamIR> frame) override {
902 DVLOG(1) << "LoggingSpdyDeframerDelegate::OnRstStream";
903 wrapped_->OnRstStream(std::move(frame));
904 }
905
906 // SpdySettingsIR has a map for settings, so loses info about the order of
907 // settings, and whether the same setting appeared more than once, so the
908 // the actual settings (parameter and value) are provided in a vector.
909 void OnSettings(std::unique_ptr<SpdySettingsIR> frame,
910 std::unique_ptr<SettingVector> settings) override {
911 DVLOG(1) << "LoggingSpdyDeframerDelegate::OnSettings";
912 wrapped_->OnSettings(std::move(frame), std::move(settings));
913 }
914
915 // A settings frame with an ACK has no content, but for uniformity passing
916 // a frame with the ACK flag set.
917 void OnSettingsAck(std::unique_ptr<SpdySettingsIR> frame) override {
918 DVLOG(1) << "LoggingSpdyDeframerDelegate::OnSettingsAck";
919 wrapped_->OnSettingsAck(std::move(frame));
920 }
921
922 void OnWindowUpdate(std::unique_ptr<SpdyWindowUpdateIR> frame) override {
923 DVLOG(1) << "LoggingSpdyDeframerDelegate::OnWindowUpdate";
924 wrapped_->OnWindowUpdate(std::move(frame));
925 }
926
927 // The SpdyFramer will not process any more data at this point.
928 void OnError(SpdyFramer* framer, SpdyTestDeframer* deframer) override {
929 DVLOG(1) << "LoggingSpdyDeframerDelegate::OnError";
930 wrapped_->OnError(framer, deframer);
931 }
932
933 private:
934 std::unique_ptr<SpdyDeframerVisitorInterface> wrapped_;
935 };
936
937 // static
938 std::unique_ptr<SpdyDeframerVisitorInterface>
939 SpdyDeframerVisitorInterface::LogBeforeVisiting(
940 std::unique_ptr<SpdyDeframerVisitorInterface> wrapped_listener) {
941 return MakeUnique<LoggingSpdyDeframerDelegate>(std::move(wrapped_listener));
942 }
943
944 CollectedFrame::CollectedFrame() {}
945
946 CollectedFrame::CollectedFrame(CollectedFrame&& other)
947 : frame_ir(std::move(other.frame_ir)),
948 headers(std::move(other.headers)),
949 settings(std::move(other.settings)),
950 error_reported(other.error_reported) {}
951
952 CollectedFrame::~CollectedFrame() {}
953
954 CollectedFrame& CollectedFrame::operator=(CollectedFrame&& other) {
955 frame_ir = std::move(other.frame_ir);
956 headers = std::move(other.headers);
957 settings = std::move(other.settings);
958 error_reported = other.error_reported;
959 return *this;
960 }
961
962 AssertionResult CollectedFrame::VerifyHasHeaders(
963 const StringPairVector& expected_headers) const {
964 if (headers.get() == nullptr)
965 return AssertionFailure();
966 if (*headers != expected_headers)
967 return AssertionFailure();
968
969 return AssertionSuccess();
970 }
971
972 AssertionResult CollectedFrame::VerifyHasSettings(
973 const SettingVector& expected_settings) const {
974 if (settings.get() == nullptr)
975 return AssertionFailure();
976 if (*settings != expected_settings)
977 return AssertionFailure();
978
979 return AssertionSuccess();
980 }
981
982 DeframerCallbackCollector::DeframerCallbackCollector(
983 std::vector<CollectedFrame>* collected_frames)
984 : collected_frames_(collected_frames) {
985 CHECK(collected_frames);
986 }
987
988 void DeframerCallbackCollector::OnAltSvc(
989 std::unique_ptr<SpdyAltSvcIR> frame_ir) {
990 CollectedFrame cf;
991 cf.frame_ir = std::move(frame_ir);
992 collected_frames_->push_back(std::move(cf));
993 }
994 void DeframerCallbackCollector::OnData(std::unique_ptr<SpdyDataIR> frame_ir) {
995 CollectedFrame cf;
996 cf.frame_ir = std::move(frame_ir);
997 collected_frames_->push_back(std::move(cf));
998 }
999 void DeframerCallbackCollector::OnGoAway(
1000 std::unique_ptr<SpdyGoAwayIR> frame_ir) {
1001 CollectedFrame cf;
1002 cf.frame_ir = std::move(frame_ir);
1003 collected_frames_->push_back(std::move(cf));
1004 }
1005
1006 // SpdyHeadersIR and SpdyPushPromiseIR each has a SpdyHeaderBlock which
1007 // significantly modifies the headers, so the actual header entries (name
1008 // and value strings) are provided in a vector.
1009 void DeframerCallbackCollector::OnHeaders(
1010 std::unique_ptr<SpdyHeadersIR> frame_ir,
1011 std::unique_ptr<StringPairVector> headers) {
1012 CollectedFrame cf;
1013 cf.frame_ir = std::move(frame_ir);
1014 cf.headers = std::move(headers);
1015 collected_frames_->push_back(std::move(cf));
1016 }
1017
1018 void DeframerCallbackCollector::OnPing(std::unique_ptr<SpdyPingIR> frame_ir) {
1019 EXPECT_TRUE(frame_ir && !frame_ir->is_ack());
1020 CollectedFrame cf;
1021 cf.frame_ir = std::move(frame_ir);
1022 collected_frames_->push_back(std::move(cf));
1023 }
1024
1025 void DeframerCallbackCollector::OnPingAck(
1026 std::unique_ptr<SpdyPingIR> frame_ir) {
1027 EXPECT_TRUE(frame_ir && frame_ir->is_ack());
1028 CollectedFrame cf;
1029 cf.frame_ir = std::move(frame_ir);
1030 collected_frames_->push_back(std::move(cf));
1031 }
1032
1033 void DeframerCallbackCollector::OnPriority(
1034 std::unique_ptr<SpdyPriorityIR> frame_ir) {
1035 CollectedFrame cf;
1036 cf.frame_ir = std::move(frame_ir);
1037 collected_frames_->push_back(std::move(cf));
1038 }
1039
1040 // SpdyHeadersIR and SpdyPushPromiseIR each has a SpdyHeaderBlock which
1041 // significantly modifies the headers, so the actual header entries (name
1042 // and value strings) are provided in a vector.
1043 void DeframerCallbackCollector::OnPushPromise(
1044 std::unique_ptr<SpdyPushPromiseIR> frame_ir,
1045 std::unique_ptr<StringPairVector> headers) {
1046 CollectedFrame cf;
1047 cf.frame_ir = std::move(frame_ir);
1048 cf.headers = std::move(headers);
1049 collected_frames_->push_back(std::move(cf));
1050 }
1051
1052 void DeframerCallbackCollector::OnRstStream(
1053 std::unique_ptr<SpdyRstStreamIR> frame_ir) {
1054 CollectedFrame cf;
1055 cf.frame_ir = std::move(frame_ir);
1056 collected_frames_->push_back(std::move(cf));
1057 }
1058
1059 // SpdySettingsIR has a map for settings, so loses info about the order of
1060 // settings, and whether the same setting appeared more than once, so the
1061 // the actual settings (parameter and value) are provided in a vector.
1062 void DeframerCallbackCollector::OnSettings(
1063 std::unique_ptr<SpdySettingsIR> frame_ir,
1064 std::unique_ptr<SettingVector> settings) {
1065 EXPECT_TRUE(frame_ir && !frame_ir->is_ack());
1066 CollectedFrame cf;
1067 cf.frame_ir = std::move(frame_ir);
1068 cf.settings = std::move(settings);
1069 collected_frames_->push_back(std::move(cf));
1070 }
1071
1072 // A settings frame_ir with an ACK has no content, but for uniformity passing
1073 // a frame_ir with the ACK flag set.
1074 void DeframerCallbackCollector::OnSettingsAck(
1075 std::unique_ptr<SpdySettingsIR> frame_ir) {
1076 EXPECT_TRUE(frame_ir && frame_ir->is_ack());
1077 CollectedFrame cf;
1078 cf.frame_ir = std::move(frame_ir);
1079 collected_frames_->push_back(std::move(cf));
1080 }
1081
1082 void DeframerCallbackCollector::OnWindowUpdate(
1083 std::unique_ptr<SpdyWindowUpdateIR> frame_ir) {
1084 CollectedFrame cf;
1085 cf.frame_ir = std::move(frame_ir);
1086 collected_frames_->push_back(std::move(cf));
1087 }
1088
1089 // The SpdyFramer will not process any more data at this point.
1090 void DeframerCallbackCollector::OnError(SpdyFramer* framer,
1091 SpdyTestDeframer* deframer) {
1092 CollectedFrame cf;
1093 cf.error_reported = true;
1094 collected_frames_->push_back(std::move(cf));
1095 }
1096
1097 } // namespace test
1098 } // namespace net
OLDNEW
« no previous file with comments | « net/spdy/spdy_deframer_visitor.h ('k') | net/spdy/spdy_deframer_visitor_test.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698