| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "net/tools/quic/quic_spdy_client_stream.h" |
| |
| #include "base/logging.h" |
| #include "base/stl_util.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "net/quic/spdy_utils.h" |
| #include "net/spdy/spdy_protocol.h" |
| #include "net/tools/quic/quic_client_session.h" |
| #include "net/tools/quic/spdy_balsa_utils.h" |
| |
| using base::StringPiece; |
| using std::string; |
| using base::StringToInt; |
| |
| namespace net { |
| namespace tools { |
| |
| QuicSpdyClientStream::QuicSpdyClientStream(QuicStreamId id, |
| QuicClientSession* session) |
| : QuicDataStream(id, session), |
| content_length_(-1), |
| response_code_(0), |
| header_bytes_read_(0), |
| header_bytes_written_(0) { |
| } |
| |
| QuicSpdyClientStream::~QuicSpdyClientStream() { |
| } |
| |
| void QuicSpdyClientStream::OnStreamFrame(const QuicStreamFrame& frame) { |
| if (!write_side_closed()) { |
| DVLOG(1) << "Got a response before the request was complete. " |
| << "Aborting request."; |
| CloseWriteSide(); |
| } |
| QuicDataStream::OnStreamFrame(frame); |
| } |
| |
| void QuicSpdyClientStream::OnStreamHeadersComplete(bool fin, |
| size_t frame_len) { |
| header_bytes_read_ = frame_len; |
| QuicDataStream::OnStreamHeadersComplete(fin, frame_len); |
| } |
| |
| uint32 QuicSpdyClientStream::ProcessData(const char* data, uint32 data_len) { |
| if (!headers_decompressed()) { |
| // Let the headers data accumulate in the underlying QuicDataStream. |
| return 0; |
| } |
| if (response_headers_.empty()) { |
| if (!ParseResponseHeaders(data, data_len)) { |
| // Headers were invalid. |
| Reset(QUIC_BAD_APPLICATION_PAYLOAD); |
| return 0; |
| } |
| } else { |
| data_.append(data, data_len); |
| } |
| DCHECK(!response_headers_.empty()); |
| if (content_length_ >= 0 && |
| static_cast<int>(data_.size()) > content_length_) { |
| Reset(QUIC_BAD_APPLICATION_PAYLOAD); |
| return 0; |
| } |
| DVLOG(1) << "Client processed " << data_len << " bytes for stream " << id(); |
| return data_len; |
| } |
| |
| bool QuicSpdyClientStream::ParseResponseHeaders(const char* data, |
| uint32 data_len) { |
| DCHECK(headers_decompressed()); |
| SpdyFramer framer(SPDY3); |
| size_t len = framer.ParseHeaderBlockInBuffer(data, |
| data_len, |
| &response_headers_); |
| DCHECK_LE(len, data_len); |
| if (len == 0 || response_headers_.empty()) { |
| return false; // Headers were invalid. |
| } |
| |
| if (data_len > len) { |
| data_.append(data + len, data_len - len); |
| } |
| if (ContainsKey(response_headers_, "content-length") && |
| !StringToInt(response_headers_["content-length"], &content_length_)) { |
| return false; // Invalid content-length. |
| } |
| string status = response_headers_[":status"]; |
| size_t end = status.find(" "); |
| if (end != string::npos) { |
| status.erase(end); |
| } |
| if (!StringToInt(status, &response_code_)) { |
| return false; // Invalid response code. |
| } |
| return true; |
| } |
| |
| size_t QuicSpdyClientStream::SendRequest(const SpdyHeaderBlock& headers, |
| StringPiece body, |
| bool fin) { |
| bool send_fin_with_headers = fin && body.empty(); |
| size_t bytes_sent = body.size(); |
| header_bytes_written_ = |
| WriteHeaders(headers, send_fin_with_headers, nullptr); |
| bytes_sent += header_bytes_written_; |
| |
| if (!body.empty()) { |
| WriteOrBufferData(body, fin, nullptr); |
| } |
| |
| return bytes_sent; |
| } |
| |
| void QuicSpdyClientStream::SendBody(const string& data, bool fin) { |
| SendBody(data, fin, nullptr); |
| } |
| |
| void QuicSpdyClientStream::SendBody( |
| const string& data, bool fin, |
| QuicAckNotifier::DelegateInterface* delegate) { |
| WriteOrBufferData(data, fin, delegate); |
| } |
| |
| } // namespace tools |
| } // namespace net |