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

Side by Side Diff: net/base/ssl_client_socket_win.cc

Issue 87073: Extend the use of IOBuffers to the code underneath... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 11 years, 7 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 | Annotate | Revision Log
« no previous file with comments | « net/base/ssl_client_socket_win.h ('k') | net/base/tcp_client_socket_libevent.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "net/base/ssl_client_socket_win.h" 5 #include "net/base/ssl_client_socket_win.h"
6 6
7 #include <schnlsp.h> 7 #include <schnlsp.h>
8 8
9 #include "base/lock.h" 9 #include "base/lock.h"
10 #include "base/singleton.h" 10 #include "base/singleton.h"
(...skipping 194 matching lines...) Expand 10 before | Expand all | Expand 10 after
205 205
206 SSLClientSocketWin::SSLClientSocketWin(ClientSocket* transport_socket, 206 SSLClientSocketWin::SSLClientSocketWin(ClientSocket* transport_socket,
207 const std::string& hostname, 207 const std::string& hostname,
208 const SSLConfig& ssl_config) 208 const SSLConfig& ssl_config)
209 #pragma warning(suppress: 4355) 209 #pragma warning(suppress: 4355)
210 : io_callback_(this, &SSLClientSocketWin::OnIOComplete), 210 : io_callback_(this, &SSLClientSocketWin::OnIOComplete),
211 transport_(transport_socket), 211 transport_(transport_socket),
212 hostname_(hostname), 212 hostname_(hostname),
213 ssl_config_(ssl_config), 213 ssl_config_(ssl_config),
214 user_callback_(NULL), 214 user_callback_(NULL),
215 user_buf_(NULL),
216 user_buf_len_(0), 215 user_buf_len_(0),
217 next_state_(STATE_NONE), 216 next_state_(STATE_NONE),
218 creds_(NULL), 217 creds_(NULL),
219 isc_status_(SEC_E_OK), 218 isc_status_(SEC_E_OK),
220 payload_send_buffer_len_(0), 219 payload_send_buffer_len_(0),
221 bytes_sent_(0), 220 bytes_sent_(0),
222 decrypted_ptr_(NULL), 221 decrypted_ptr_(NULL),
223 bytes_decrypted_(0), 222 bytes_decrypted_(0),
224 received_ptr_(NULL), 223 received_ptr_(NULL),
225 bytes_received_(0), 224 bytes_received_(0),
(...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after
356 // Unlike IsConnected, this method doesn't return a false positive. 355 // Unlike IsConnected, this method doesn't return a false positive.
357 // 356 //
358 // Strictly speaking, we should check if we have received the close_notify 357 // Strictly speaking, we should check if we have received the close_notify
359 // alert message from the server, and return false in that case. Although 358 // alert message from the server, and return false in that case. Although
360 // the close_notify alert message means EOF in the SSL layer, it is just 359 // the close_notify alert message means EOF in the SSL layer, it is just
361 // bytes to the transport layer below, so transport_->IsConnectedAndIdle() 360 // bytes to the transport layer below, so transport_->IsConnectedAndIdle()
362 // returns the desired false when we receive close_notify. 361 // returns the desired false when we receive close_notify.
363 return completed_handshake_ && transport_->IsConnectedAndIdle(); 362 return completed_handshake_ && transport_->IsConnectedAndIdle();
364 } 363 }
365 364
366 int SSLClientSocketWin::Read(char* buf, int buf_len, 365 int SSLClientSocketWin::Read(IOBuffer* buf, int buf_len,
367 CompletionCallback* callback) { 366 CompletionCallback* callback) {
368 DCHECK(completed_handshake_); 367 DCHECK(completed_handshake_);
369 DCHECK(next_state_ == STATE_NONE); 368 DCHECK(next_state_ == STATE_NONE);
370 DCHECK(!user_callback_); 369 DCHECK(!user_callback_);
371 370
372 // If we have surplus decrypted plaintext, satisfy the Read with it without 371 // If we have surplus decrypted plaintext, satisfy the Read with it without
373 // reading more ciphertext from the transport socket. 372 // reading more ciphertext from the transport socket.
374 if (bytes_decrypted_ != 0) { 373 if (bytes_decrypted_ != 0) {
375 int len = std::min(buf_len, bytes_decrypted_); 374 int len = std::min(buf_len, bytes_decrypted_);
376 memcpy(buf, decrypted_ptr_, len); 375 memcpy(buf->data(), decrypted_ptr_, len);
377 decrypted_ptr_ += len; 376 decrypted_ptr_ += len;
378 bytes_decrypted_ -= len; 377 bytes_decrypted_ -= len;
379 if (bytes_decrypted_ == 0) { 378 if (bytes_decrypted_ == 0) {
380 decrypted_ptr_ = NULL; 379 decrypted_ptr_ = NULL;
381 if (bytes_received_ != 0) { 380 if (bytes_received_ != 0) {
382 memmove(recv_buffer_.get(), received_ptr_, bytes_received_); 381 memmove(recv_buffer_.get(), received_ptr_, bytes_received_);
383 received_ptr_ = recv_buffer_.get(); 382 received_ptr_ = recv_buffer_.get();
384 } 383 }
385 } 384 }
386 return len; 385 return len;
387 } 386 }
388 387
388 DCHECK(!user_buf_);
389 user_buf_ = buf; 389 user_buf_ = buf;
390 user_buf_len_ = buf_len; 390 user_buf_len_ = buf_len;
391 391
392 SetNextStateForRead(); 392 SetNextStateForRead();
393 int rv = DoLoop(OK); 393 int rv = DoLoop(OK);
394 if (rv == ERR_IO_PENDING) 394 if (rv == ERR_IO_PENDING) {
395 user_callback_ = callback; 395 user_callback_ = callback;
396 } else {
397 user_buf_ = NULL;
398 }
396 return rv; 399 return rv;
397 } 400 }
398 401
399 int SSLClientSocketWin::Write(const char* buf, int buf_len, 402 int SSLClientSocketWin::Write(IOBuffer* buf, int buf_len,
400 CompletionCallback* callback) { 403 CompletionCallback* callback) {
401 DCHECK(completed_handshake_); 404 DCHECK(completed_handshake_);
402 DCHECK(next_state_ == STATE_NONE); 405 DCHECK(next_state_ == STATE_NONE);
403 DCHECK(!user_callback_); 406 DCHECK(!user_callback_);
404 407
405 user_buf_ = const_cast<char*>(buf); 408 DCHECK(!user_buf_);
409 user_buf_ = buf;
406 user_buf_len_ = buf_len; 410 user_buf_len_ = buf_len;
407 411
408 next_state_ = STATE_PAYLOAD_ENCRYPT; 412 next_state_ = STATE_PAYLOAD_ENCRYPT;
409 int rv = DoLoop(OK); 413 int rv = DoLoop(OK);
410 if (rv == ERR_IO_PENDING) 414 if (rv == ERR_IO_PENDING) {
411 user_callback_ = callback; 415 user_callback_ = callback;
416 } else {
417 user_buf_ = NULL;
418 }
412 return rv; 419 return rv;
413 } 420 }
414 421
415 void SSLClientSocketWin::DoCallback(int rv) { 422 void SSLClientSocketWin::DoCallback(int rv) {
416 DCHECK(rv != ERR_IO_PENDING); 423 DCHECK(rv != ERR_IO_PENDING);
417 DCHECK(user_callback_); 424 DCHECK(user_callback_);
418 425
419 // since Run may result in Read being called, clear user_callback_ up front. 426 // since Run may result in Read being called, clear user_callback_ up front.
420 CompletionCallback* c = user_callback_; 427 CompletionCallback* c = user_callback_;
421 user_callback_ = NULL; 428 user_callback_ = NULL;
429 user_buf_ = NULL;
422 c->Run(rv); 430 c->Run(rv);
423 } 431 }
424 432
425 void SSLClientSocketWin::OnIOComplete(int result) { 433 void SSLClientSocketWin::OnIOComplete(int result) {
426 int rv = DoLoop(result); 434 int rv = DoLoop(result);
427 if (rv != ERR_IO_PENDING) 435 if (rv != ERR_IO_PENDING)
428 DoCallback(rv); 436 DoCallback(rv);
429 } 437 }
430 438
431 int SSLClientSocketWin::DoLoop(int last_io_result) { 439 int SSLClientSocketWin::DoLoop(int last_io_result) {
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
476 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE); 484 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
477 return rv; 485 return rv;
478 } 486 }
479 487
480 int SSLClientSocketWin::DoHandshakeRead() { 488 int SSLClientSocketWin::DoHandshakeRead() {
481 next_state_ = STATE_HANDSHAKE_READ_COMPLETE; 489 next_state_ = STATE_HANDSHAKE_READ_COMPLETE;
482 490
483 if (!recv_buffer_.get()) 491 if (!recv_buffer_.get())
484 recv_buffer_.reset(new char[kRecvBufferSize]); 492 recv_buffer_.reset(new char[kRecvBufferSize]);
485 493
486 char* buf = recv_buffer_.get() + bytes_received_;
487 int buf_len = kRecvBufferSize - bytes_received_; 494 int buf_len = kRecvBufferSize - bytes_received_;
488 495
489 if (buf_len <= 0) { 496 if (buf_len <= 0) {
490 NOTREACHED() << "Receive buffer is too small!"; 497 NOTREACHED() << "Receive buffer is too small!";
491 return ERR_UNEXPECTED; 498 return ERR_UNEXPECTED;
492 } 499 }
493 500
494 return transport_->Read(buf, buf_len, &io_callback_); 501 DCHECK(!transport_buf_);
502 transport_buf_ = new IOBuffer(buf_len);
503
504 return transport_->Read(transport_buf_, buf_len, &io_callback_);
495 } 505 }
496 506
497 int SSLClientSocketWin::DoHandshakeReadComplete(int result) { 507 int SSLClientSocketWin::DoHandshakeReadComplete(int result) {
498 if (result < 0) 508 DCHECK(transport_buf_);
509 if (result < 0) {
510 transport_buf_ = NULL;
499 return result; 511 return result;
512 }
513 DCHECK_LE(result, kRecvBufferSize - bytes_received_);
514 char* buf = recv_buffer_.get() + bytes_received_;
515 memcpy(buf, transport_buf_->data(), result);
516 transport_buf_ = NULL;
517
500 if (result == 0 && !ignore_ok_result_) 518 if (result == 0 && !ignore_ok_result_)
501 return ERR_SSL_PROTOCOL_ERROR; // Incomplete response :( 519 return ERR_SSL_PROTOCOL_ERROR; // Incomplete response :(
502 520
503 ignore_ok_result_ = false; 521 ignore_ok_result_ = false;
504 522
505 bytes_received_ += result; 523 bytes_received_ += result;
506 524
507 // Process the contents of recv_buffer_. 525 // Process the contents of recv_buffer_.
508 TimeStamp expiry; 526 TimeStamp expiry;
509 DWORD out_flags; 527 DWORD out_flags;
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after
617 next_state_ = STATE_HANDSHAKE_READ; 635 next_state_ = STATE_HANDSHAKE_READ;
618 return OK; 636 return OK;
619 } 637 }
620 638
621 int SSLClientSocketWin::DoHandshakeWrite() { 639 int SSLClientSocketWin::DoHandshakeWrite() {
622 next_state_ = STATE_HANDSHAKE_WRITE_COMPLETE; 640 next_state_ = STATE_HANDSHAKE_WRITE_COMPLETE;
623 641
624 // We should have something to send. 642 // We should have something to send.
625 DCHECK(send_buffer_.pvBuffer); 643 DCHECK(send_buffer_.pvBuffer);
626 DCHECK(send_buffer_.cbBuffer > 0); 644 DCHECK(send_buffer_.cbBuffer > 0);
645 DCHECK(!transport_buf_);
627 646
628 const char* buf = static_cast<char*>(send_buffer_.pvBuffer) + bytes_sent_; 647 const char* buf = static_cast<char*>(send_buffer_.pvBuffer) + bytes_sent_;
629 int buf_len = send_buffer_.cbBuffer - bytes_sent_; 648 int buf_len = send_buffer_.cbBuffer - bytes_sent_;
649 transport_buf_ = new IOBuffer(buf_len);
650 memcpy(transport_buf_->data(), buf, buf_len);
630 651
631 return transport_->Write(buf, buf_len, &io_callback_); 652 return transport_->Write(transport_buf_, buf_len, &io_callback_);
632 } 653 }
633 654
634 int SSLClientSocketWin::DoHandshakeWriteComplete(int result) { 655 int SSLClientSocketWin::DoHandshakeWriteComplete(int result) {
656 DCHECK(transport_buf_);
657 transport_buf_ = NULL;
635 if (result < 0) 658 if (result < 0)
636 return result; 659 return result;
637 660
638 DCHECK(result != 0); 661 DCHECK(result != 0);
639 662
640 bytes_sent_ += result; 663 bytes_sent_ += result;
641 DCHECK(bytes_sent_ <= static_cast<int>(send_buffer_.cbBuffer)); 664 DCHECK(bytes_sent_ <= static_cast<int>(send_buffer_.cbBuffer));
642 665
643 if (bytes_sent_ >= static_cast<int>(send_buffer_.cbBuffer)) { 666 if (bytes_sent_ >= static_cast<int>(send_buffer_.cbBuffer)) {
644 bool overflow = (bytes_sent_ > static_cast<int>(send_buffer_.cbBuffer)); 667 bool overflow = (bytes_sent_ > static_cast<int>(send_buffer_.cbBuffer));
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
696 DCHECK(next_state_ == STATE_NONE); 719 DCHECK(next_state_ == STATE_NONE);
697 } 720 }
698 return result; 721 return result;
699 } 722 }
700 723
701 int SSLClientSocketWin::DoPayloadRead() { 724 int SSLClientSocketWin::DoPayloadRead() {
702 next_state_ = STATE_PAYLOAD_READ_COMPLETE; 725 next_state_ = STATE_PAYLOAD_READ_COMPLETE;
703 726
704 DCHECK(recv_buffer_.get()); 727 DCHECK(recv_buffer_.get());
705 728
706 char* buf = recv_buffer_.get() + bytes_received_;
707 int buf_len = kRecvBufferSize - bytes_received_; 729 int buf_len = kRecvBufferSize - bytes_received_;
708 730
709 if (buf_len <= 0) { 731 if (buf_len <= 0) {
710 NOTREACHED() << "Receive buffer is too small!"; 732 NOTREACHED() << "Receive buffer is too small!";
711 return ERR_FAILED; 733 return ERR_FAILED;
712 } 734 }
713 735
714 return transport_->Read(buf, buf_len, &io_callback_); 736 DCHECK(!transport_buf_);
737 transport_buf_ = new IOBuffer(buf_len);
738
739 return transport_->Read(transport_buf_, buf_len, &io_callback_);
715 } 740 }
716 741
717 int SSLClientSocketWin::DoPayloadReadComplete(int result) { 742 int SSLClientSocketWin::DoPayloadReadComplete(int result) {
718 if (result < 0) 743 if (result < 0) {
744 transport_buf_ = NULL;
719 return result; 745 return result;
746 }
747 if (transport_buf_) {
748 // This method is called after a state transition following DoPayloadRead(),
749 // or if SetNextStateForRead() was called. We have a transport_buf_ only
750 // in the first case, and we have to transfer the data from transport_buf_
751 // to recv_buffer_.
752 DCHECK_LE(result, kRecvBufferSize - bytes_received_);
753 char* buf = recv_buffer_.get() + bytes_received_;
754 memcpy(buf, transport_buf_->data(), result);
755 transport_buf_ = NULL;
756 }
757
720 if (result == 0 && !ignore_ok_result_) { 758 if (result == 0 && !ignore_ok_result_) {
721 // TODO(wtc): Unless we have received the close_notify alert, we need to 759 // TODO(wtc): Unless we have received the close_notify alert, we need to
722 // return an error code indicating that the SSL connection ended 760 // return an error code indicating that the SSL connection ended
723 // uncleanly, a potential truncation attack. 761 // uncleanly, a potential truncation attack.
724 if (bytes_received_ != 0) 762 if (bytes_received_ != 0)
725 return ERR_FAILED; 763 return ERR_FAILED;
726 return OK; 764 return OK;
727 } 765 }
728 766
729 ignore_ok_result_ = false; 767 ignore_ok_result_ = false;
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
778 } 816 }
779 if (!received_ptr_ && buffers[i].BufferType == SECBUFFER_EXTRA) { 817 if (!received_ptr_ && buffers[i].BufferType == SECBUFFER_EXTRA) {
780 received_ptr_ = static_cast<char*>(buffers[i].pvBuffer); 818 received_ptr_ = static_cast<char*>(buffers[i].pvBuffer);
781 bytes_received_ = buffers[i].cbBuffer; 819 bytes_received_ = buffers[i].cbBuffer;
782 } 820 }
783 } 821 }
784 822
785 int len = 0; 823 int len = 0;
786 if (bytes_decrypted_ != 0) { 824 if (bytes_decrypted_ != 0) {
787 len = std::min(user_buf_len_, bytes_decrypted_); 825 len = std::min(user_buf_len_, bytes_decrypted_);
788 memcpy(user_buf_, decrypted_ptr_, len); 826 memcpy(user_buf_->data(), decrypted_ptr_, len);
789 decrypted_ptr_ += len; 827 decrypted_ptr_ += len;
790 bytes_decrypted_ -= len; 828 bytes_decrypted_ -= len;
791 } 829 }
792 if (bytes_decrypted_ == 0) { 830 if (bytes_decrypted_ == 0) {
793 decrypted_ptr_ = NULL; 831 decrypted_ptr_ = NULL;
794 if (bytes_received_ != 0) { 832 if (bytes_received_ != 0) {
795 memmove(recv_buffer_.get(), received_ptr_, bytes_received_); 833 memmove(recv_buffer_.get(), received_ptr_, bytes_received_);
796 received_ptr_ = recv_buffer_.get(); 834 received_ptr_ = recv_buffer_.get();
797 } 835 }
798 } 836 }
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
838 DCHECK(user_buf_len_ > 0); 876 DCHECK(user_buf_len_ > 0);
839 877
840 ULONG message_len = std::min( 878 ULONG message_len = std::min(
841 stream_sizes_.cbMaximumMessage, static_cast<ULONG>(user_buf_len_)); 879 stream_sizes_.cbMaximumMessage, static_cast<ULONG>(user_buf_len_));
842 ULONG alloc_len = 880 ULONG alloc_len =
843 message_len + stream_sizes_.cbHeader + stream_sizes_.cbTrailer; 881 message_len + stream_sizes_.cbHeader + stream_sizes_.cbTrailer;
844 user_buf_len_ = message_len; 882 user_buf_len_ = message_len;
845 883
846 payload_send_buffer_.reset(new char[alloc_len]); 884 payload_send_buffer_.reset(new char[alloc_len]);
847 memcpy(&payload_send_buffer_[stream_sizes_.cbHeader], 885 memcpy(&payload_send_buffer_[stream_sizes_.cbHeader],
848 user_buf_, message_len); 886 user_buf_->data(), message_len);
849 887
850 SecBuffer buffers[4]; 888 SecBuffer buffers[4];
851 buffers[0].pvBuffer = payload_send_buffer_.get(); 889 buffers[0].pvBuffer = payload_send_buffer_.get();
852 buffers[0].cbBuffer = stream_sizes_.cbHeader; 890 buffers[0].cbBuffer = stream_sizes_.cbHeader;
853 buffers[0].BufferType = SECBUFFER_STREAM_HEADER; 891 buffers[0].BufferType = SECBUFFER_STREAM_HEADER;
854 892
855 buffers[1].pvBuffer = &payload_send_buffer_[stream_sizes_.cbHeader]; 893 buffers[1].pvBuffer = &payload_send_buffer_[stream_sizes_.cbHeader];
856 buffers[1].cbBuffer = message_len; 894 buffers[1].cbBuffer = message_len;
857 buffers[1].BufferType = SECBUFFER_DATA; 895 buffers[1].BufferType = SECBUFFER_DATA;
858 896
(...skipping 22 matching lines...) Expand all
881 next_state_ = STATE_PAYLOAD_WRITE; 919 next_state_ = STATE_PAYLOAD_WRITE;
882 return OK; 920 return OK;
883 } 921 }
884 922
885 int SSLClientSocketWin::DoPayloadWrite() { 923 int SSLClientSocketWin::DoPayloadWrite() {
886 next_state_ = STATE_PAYLOAD_WRITE_COMPLETE; 924 next_state_ = STATE_PAYLOAD_WRITE_COMPLETE;
887 925
888 // We should have something to send. 926 // We should have something to send.
889 DCHECK(payload_send_buffer_.get()); 927 DCHECK(payload_send_buffer_.get());
890 DCHECK(payload_send_buffer_len_ > 0); 928 DCHECK(payload_send_buffer_len_ > 0);
929 DCHECK(!transport_buf_);
891 930
892 const char* buf = payload_send_buffer_.get() + bytes_sent_; 931 const char* buf = payload_send_buffer_.get() + bytes_sent_;
893 int buf_len = payload_send_buffer_len_ - bytes_sent_; 932 int buf_len = payload_send_buffer_len_ - bytes_sent_;
933 transport_buf_ = new IOBuffer(buf_len);
934 memcpy(transport_buf_->data(), buf, buf_len);
894 935
895 return transport_->Write(buf, buf_len, &io_callback_); 936 return transport_->Write(transport_buf_, buf_len, &io_callback_);
896 } 937 }
897 938
898 int SSLClientSocketWin::DoPayloadWriteComplete(int result) { 939 int SSLClientSocketWin::DoPayloadWriteComplete(int result) {
940 DCHECK(transport_buf_);
941 transport_buf_ = NULL;
899 if (result < 0) 942 if (result < 0)
900 return result; 943 return result;
901 944
902 DCHECK(result != 0); 945 DCHECK(result != 0);
903 946
904 bytes_sent_ += result; 947 bytes_sent_ += result;
905 DCHECK(bytes_sent_ <= payload_send_buffer_len_); 948 DCHECK(bytes_sent_ <= payload_send_buffer_len_);
906 949
907 if (bytes_sent_ >= payload_send_buffer_len_) { 950 if (bytes_sent_ >= payload_send_buffer_len_) {
908 bool overflow = (bytes_sent_ > payload_send_buffer_len_); 951 bool overflow = (bytes_sent_ > payload_send_buffer_len_);
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
965 } 1008 }
966 } 1009 }
967 1010
968 void SSLClientSocketWin::FreeSendBuffer() { 1011 void SSLClientSocketWin::FreeSendBuffer() {
969 SECURITY_STATUS status = FreeContextBuffer(send_buffer_.pvBuffer); 1012 SECURITY_STATUS status = FreeContextBuffer(send_buffer_.pvBuffer);
970 DCHECK(status == SEC_E_OK); 1013 DCHECK(status == SEC_E_OK);
971 memset(&send_buffer_, 0, sizeof(send_buffer_)); 1014 memset(&send_buffer_, 0, sizeof(send_buffer_));
972 } 1015 }
973 1016
974 } // namespace net 1017 } // namespace net
OLDNEW
« no previous file with comments | « net/base/ssl_client_socket_win.h ('k') | net/base/tcp_client_socket_libevent.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698