summaryrefslogtreecommitdiffstats
path: root/net/tools/flip_server/spdy_interface.cc
diff options
context:
space:
mode:
Diffstat (limited to 'net/tools/flip_server/spdy_interface.cc')
-rw-r--r--net/tools/flip_server/spdy_interface.cc591
1 files changed, 591 insertions, 0 deletions
diff --git a/net/tools/flip_server/spdy_interface.cc b/net/tools/flip_server/spdy_interface.cc
new file mode 100644
index 0000000..c6149b8
--- /dev/null
+++ b/net/tools/flip_server/spdy_interface.cc
@@ -0,0 +1,591 @@
+// Copyright (c) 2009 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/flip_server/spdy_interface.h"
+
+#include <string>
+
+#include "net/spdy/spdy_framer.h"
+#include "net/spdy/spdy_protocol.h"
+#include "net/tools/dump_cache/url_utilities.h"
+#include "net/tools/flip_server/flip_config.h"
+#include "net/tools/flip_server/http_interface.h"
+#include "net/tools/flip_server/spdy_util.h"
+
+using spdy::kSpdyStreamMaximumWindowSize;
+using spdy::CONTROL_FLAG_NONE;
+using spdy::DATA_FLAG_COMPRESSED;
+using spdy::DATA_FLAG_FIN;
+using spdy::RST_STREAM;
+using spdy::SETTINGS_MAX_CONCURRENT_STREAMS;
+using spdy::SYN_REPLY;
+using spdy::SYN_STREAM;
+using spdy::SettingsFlagsAndId;
+using spdy::SpdyControlFrame;
+using spdy::SpdySettingsControlFrame;
+using spdy::SpdyDataFlags;
+using spdy::SpdyDataFrame;
+using spdy::SpdyRstStreamControlFrame;
+using spdy::SpdyFrame;
+using spdy::SpdyFramer;
+using spdy::SpdyFramerVisitorInterface;
+using spdy::SpdyHeaderBlock;
+using spdy::SpdySetting;
+using spdy::SpdySettings;
+using spdy::SpdyStreamId;
+using spdy::SpdySynReplyControlFrame;
+using spdy::SpdySynStreamControlFrame;
+
+namespace net {
+
+// static
+bool SpdySM::disable_data_compression_ = true;
+// static
+std::string SpdySM::forward_ip_header_;
+
+class SpdyFrameDataFrame : public DataFrame {
+ public:
+ SpdyFrameDataFrame(SpdyFrame* spdy_frame)
+ : frame(spdy_frame) {
+ data = spdy_frame->data();
+ size = spdy_frame->length() + SpdyFrame::size();
+ }
+
+ virtual ~SpdyFrameDataFrame() {
+ delete frame;
+ }
+
+ const SpdyFrame* frame;
+};
+
+SpdySM::SpdySM(SMConnection* connection,
+ SMInterface* sm_http_interface,
+ EpollServer* epoll_server,
+ MemoryCache* memory_cache,
+ FlipAcceptor* acceptor)
+ : seq_num_(0),
+ spdy_framer_(new SpdyFramer),
+ valid_spdy_session_(false),
+ connection_(connection),
+ client_output_list_(connection->output_list()),
+ client_output_ordering_(connection),
+ next_outgoing_stream_id_(2),
+ epoll_server_(epoll_server),
+ acceptor_(acceptor),
+ memory_cache_(memory_cache),
+ close_on_error_(false) {
+ spdy_framer_->set_visitor(this);
+}
+
+SpdySM::~SpdySM() {
+ delete spdy_framer_;
+}
+
+void SpdySM::InitSMConnection(SMConnectionPoolInterface* connection_pool,
+ SMInterface* sm_interface,
+ EpollServer* epoll_server,
+ int fd,
+ std::string server_ip,
+ std::string server_port,
+ std::string remote_ip,
+ bool use_ssl) {
+ VLOG(2) << ACCEPTOR_CLIENT_IDENT
+ << "SpdySM: Initializing server connection.";
+ connection_->InitSMConnection(connection_pool, sm_interface,
+ epoll_server, fd, server_ip, server_port,
+ remote_ip, use_ssl);
+}
+
+SMInterface* SpdySM::NewConnectionInterface() {
+ SMConnection* server_connection =
+ SMConnection::NewSMConnection(epoll_server_,
+ NULL,
+ memory_cache_,
+ acceptor_,
+ "http_conn: ");
+ if (server_connection == NULL) {
+ LOG(ERROR) << "SpdySM: Could not create server connection";
+ return NULL;
+ }
+ VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Creating new HTTP interface";
+ SMInterface *sm_http_interface = new HttpSM(server_connection,
+ this,
+ epoll_server_,
+ memory_cache_,
+ acceptor_);
+ return sm_http_interface;
+}
+
+SMInterface* SpdySM::FindOrMakeNewSMConnectionInterface(
+ std::string server_ip, std::string server_port) {
+ SMInterface *sm_http_interface;
+ int32 server_idx;
+ if (unused_server_interface_list.empty()) {
+ sm_http_interface = NewConnectionInterface();
+ server_idx = server_interface_list.size();
+ server_interface_list.push_back(sm_http_interface);
+ VLOG(2) << ACCEPTOR_CLIENT_IDENT
+ << "SpdySM: Making new server connection on index: "
+ << server_idx;
+ } else {
+ server_idx = unused_server_interface_list.back();
+ unused_server_interface_list.pop_back();
+ sm_http_interface = server_interface_list.at(server_idx);
+ VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Reusing connection on "
+ << "index: " << server_idx;
+ }
+
+ sm_http_interface->InitSMInterface(this, server_idx);
+ sm_http_interface->InitSMConnection(NULL, sm_http_interface,
+ epoll_server_, -1,
+ server_ip, server_port, "", false);
+
+ return sm_http_interface;
+}
+
+int SpdySM::SpdyHandleNewStream(const SpdyControlFrame* frame,
+ std::string &http_data,
+ bool *is_https_scheme) {
+ bool parsed_headers = false;
+ SpdyHeaderBlock headers;
+ const SpdySynStreamControlFrame* syn_stream =
+ reinterpret_cast<const SpdySynStreamControlFrame*>(frame);
+
+ *is_https_scheme = false;
+ parsed_headers = spdy_framer_->ParseHeaderBlock(frame, &headers);
+ VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: OnSyn("
+ << syn_stream->stream_id() << ")";
+ VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: headers parsed?: "
+ << (parsed_headers? "yes": "no");
+ if (parsed_headers) {
+ VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: # headers: "
+ << headers.size();
+ }
+ SpdyHeaderBlock::iterator url = headers.find("url");
+ SpdyHeaderBlock::iterator method = headers.find("method");
+ if (url == headers.end() || method == headers.end()) {
+ VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: didn't find method or url "
+ << "or method. Not creating stream";
+ return 0;
+ }
+
+ SpdyHeaderBlock::iterator scheme = headers.find("scheme");
+ if (scheme->second.compare("https") == 0) {
+ *is_https_scheme = true;
+ }
+
+ std::string uri = UrlUtilities::GetUrlPath(url->second);
+ if (acceptor_->flip_handler_type_ == FLIP_HANDLER_SPDY_SERVER) {
+ SpdyHeaderBlock::iterator referer = headers.find("referer");
+ std::string host = UrlUtilities::GetUrlHost(url->second);
+ VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Request: " << method->second
+ << " " << uri;
+ std::string filename = EncodeURL(uri, host, method->second);
+ NewStream(syn_stream->stream_id(),
+ reinterpret_cast<const SpdySynStreamControlFrame*>
+ (frame)->priority(),
+ filename);
+ } else {
+ SpdyHeaderBlock::iterator version = headers.find("version");
+ http_data += method->second + " " + uri + " " + version->second + "\r\n";
+ VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Request: " << method->second << " "
+ << uri << " " << version->second;
+ for (SpdyHeaderBlock::iterator i = headers.begin();
+ i != headers.end(); ++i) {
+ http_data += i->first + ": " + i->second + "\r\n";
+ VLOG(2) << ACCEPTOR_CLIENT_IDENT << i->first.c_str() << ":"
+ << i->second.c_str();
+ }
+ if (forward_ip_header_.length()) {
+ // X-Client-Cluster-IP header
+ http_data += forward_ip_header_ + ": " +
+ connection_->client_ip() + "\r\n";
+ }
+ http_data += "\r\n";
+ }
+
+ VLOG(3) << ACCEPTOR_CLIENT_IDENT << "SpdySM: HTTP Request:\n" << http_data;
+ return 1;
+}
+
+void SpdySM::OnControl(const SpdyControlFrame* frame) {
+ SpdyHeaderBlock headers;
+ bool parsed_headers = false;
+ switch (frame->type()) {
+ case SYN_STREAM:
+ {
+ const SpdySynStreamControlFrame* syn_stream =
+ reinterpret_cast<const SpdySynStreamControlFrame*>(frame);
+
+ std::string http_data;
+ bool is_https_scheme;
+ int ret = SpdyHandleNewStream(frame, http_data, &is_https_scheme);
+ if (!ret) {
+ LOG(ERROR) << "SpdySM: Could not convert spdy into http.";
+ break;
+ }
+ // We've seen a valid looking SYN_STREAM, consider this to have
+ // been a real spdy session.
+ valid_spdy_session_ = true;
+
+ if (acceptor_->flip_handler_type_ == FLIP_HANDLER_PROXY) {
+ std::string server_ip;
+ std::string server_port;
+ if (is_https_scheme) {
+ server_ip = acceptor_->https_server_ip_;
+ server_port = acceptor_->https_server_port_;
+ } else {
+ server_ip = acceptor_->http_server_ip_;
+ server_port = acceptor_->http_server_port_;
+ }
+ SMInterface *sm_http_interface =
+ FindOrMakeNewSMConnectionInterface(server_ip, server_port);
+ stream_to_smif_[syn_stream->stream_id()] = sm_http_interface;
+ sm_http_interface->SetStreamID(syn_stream->stream_id());
+ sm_http_interface->ProcessWriteInput(http_data.c_str(),
+ http_data.size());
+ }
+ }
+ break;
+
+ case SYN_REPLY:
+ parsed_headers = spdy_framer_->ParseHeaderBlock(frame, &headers);
+ VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: OnSynReply(" <<
+ reinterpret_cast<const SpdySynReplyControlFrame*>(frame)->stream_id()
+ << ")";
+ break;
+ case RST_STREAM:
+ {
+ const SpdyRstStreamControlFrame* rst_stream =
+ reinterpret_cast<const SpdyRstStreamControlFrame*>(frame);
+ VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: OnRst("
+ << rst_stream->stream_id() << ")";
+ client_output_ordering_.RemoveStreamId(rst_stream ->stream_id());
+ }
+ break;
+
+ default:
+ LOG(ERROR) << "SpdySM: Unknown control frame type";
+ }
+}
+
+void SpdySM::OnStreamFrameData(SpdyStreamId stream_id,
+ const char* data, size_t len) {
+ VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: StreamData(" << stream_id
+ << ", [" << len << "])";
+ StreamToSmif::iterator it = stream_to_smif_.find(stream_id);
+ if (it == stream_to_smif_.end()) {
+ VLOG(2) << "Dropping frame from unknown stream " << stream_id;
+ if (!valid_spdy_session_)
+ close_on_error_ = true;
+ return;
+ }
+
+ SMInterface* interface = it->second;
+ if (acceptor_->flip_handler_type_ == FLIP_HANDLER_PROXY)
+ interface->ProcessWriteInput(data, len);
+}
+
+size_t SpdySM::ProcessReadInput(const char* data, size_t len) {
+ return spdy_framer_->ProcessInput(data, len);
+}
+
+size_t SpdySM::ProcessWriteInput(const char* data, size_t len) {
+ return 0;
+}
+
+bool SpdySM::MessageFullyRead() const {
+ return spdy_framer_->MessageFullyRead();
+}
+
+bool SpdySM::Error() const {
+ return close_on_error_ || spdy_framer_->HasError();
+}
+
+const char* SpdySM::ErrorAsString() const {
+ DCHECK(Error());
+ return SpdyFramer::ErrorCodeToString(spdy_framer_->error_code());
+}
+
+void SpdySM::ResetForNewInterface(int32 server_idx) {
+ VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Reset for new interface: "
+ << "server_idx: " << server_idx;
+ unused_server_interface_list.push_back(server_idx);
+}
+
+void SpdySM::ResetForNewConnection() {
+ // seq_num is not cleared, intentionally.
+ delete spdy_framer_;
+ spdy_framer_ = new SpdyFramer;
+ spdy_framer_->set_visitor(this);
+ valid_spdy_session_ = false;
+ client_output_ordering_.Reset();
+ next_outgoing_stream_id_ = 2;
+}
+
+// Send a settings frame
+int SpdySM::PostAcceptHook() {
+ SpdySettings settings;
+ SettingsFlagsAndId settings_id(SETTINGS_MAX_CONCURRENT_STREAMS);
+ settings.push_back(SpdySetting(settings_id, 100));
+ SpdySettingsControlFrame* settings_frame =
+ spdy_framer_->CreateSettings(settings);
+
+ VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Sending Settings Frame";
+ EnqueueDataFrame(new SpdyFrameDataFrame(settings_frame));
+ return 1;
+}
+
+void SpdySM::NewStream(uint32 stream_id,
+ uint32 priority,
+ const std::string& filename) {
+ MemCacheIter mci;
+ mci.stream_id = stream_id;
+ mci.priority = priority;
+ if (acceptor_->flip_handler_type_ == FLIP_HANDLER_SPDY_SERVER) {
+ if (!memory_cache_->AssignFileData(filename, &mci)) {
+ // error creating new stream.
+ VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Sending ErrorNotFound";
+ SendErrorNotFound(stream_id);
+ } else {
+ AddToOutputOrder(mci);
+ }
+ } else {
+ AddToOutputOrder(mci);
+ }
+}
+
+void SpdySM::AddToOutputOrder(const MemCacheIter& mci) {
+ client_output_ordering_.AddToOutputOrder(mci);
+}
+
+void SpdySM::SendEOF(uint32 stream_id) {
+ SendEOFImpl(stream_id);
+}
+
+void SpdySM::SendErrorNotFound(uint32 stream_id) {
+ SendErrorNotFoundImpl(stream_id);
+}
+
+void SpdySM::SendOKResponse(uint32 stream_id, std::string* output) {
+ SendOKResponseImpl(stream_id, output);
+}
+
+size_t SpdySM::SendSynStream(uint32 stream_id, const BalsaHeaders& headers) {
+ return SendSynStreamImpl(stream_id, headers);
+}
+
+size_t SpdySM::SendSynReply(uint32 stream_id, const BalsaHeaders& headers) {
+ return SendSynReplyImpl(stream_id, headers);
+}
+
+void SpdySM::SendDataFrame(uint32 stream_id, const char* data, int64 len,
+ uint32 flags, bool compress) {
+ SpdyDataFlags spdy_flags = static_cast<SpdyDataFlags>(flags);
+ SendDataFrameImpl(stream_id, data, len, spdy_flags, compress);
+}
+
+void SpdySM::SendEOFImpl(uint32 stream_id) {
+ SendDataFrame(stream_id, NULL, 0, DATA_FLAG_FIN, false);
+ VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Sending EOF: " << stream_id;
+ KillStream(stream_id);
+}
+
+void SpdySM::SendErrorNotFoundImpl(uint32 stream_id) {
+ BalsaHeaders my_headers;
+ my_headers.SetFirstlineFromStringPieces("HTTP/1.1", "404", "Not Found");
+ SendSynReplyImpl(stream_id, my_headers);
+ SendDataFrame(stream_id, "wtf?", 4, DATA_FLAG_FIN, false);
+ client_output_ordering_.RemoveStreamId(stream_id);
+}
+
+void SpdySM::SendOKResponseImpl(uint32 stream_id, std::string* output) {
+ BalsaHeaders my_headers;
+ my_headers.SetFirstlineFromStringPieces("HTTP/1.1", "200", "OK");
+ SendSynReplyImpl(stream_id, my_headers);
+ SendDataFrame(
+ stream_id, output->c_str(), output->size(), DATA_FLAG_FIN, false);
+ client_output_ordering_.RemoveStreamId(stream_id);
+}
+
+void SpdySM::KillStream(uint32 stream_id) {
+ client_output_ordering_.RemoveStreamId(stream_id);
+}
+
+void SpdySM::CopyHeaders(SpdyHeaderBlock& dest, const BalsaHeaders& headers) {
+ for (BalsaHeaders::const_header_lines_iterator hi =
+ headers.header_lines_begin();
+ hi != headers.header_lines_end();
+ ++hi) {
+ // It is illegal to send SPDY headers with empty value or header
+ // names.
+ if (!hi->first.length() || !hi->second.length())
+ continue;
+
+ SpdyHeaderBlock::iterator fhi = dest.find(hi->first.as_string());
+ if (fhi == dest.end()) {
+ dest[hi->first.as_string()] = hi->second.as_string();
+ } else {
+ dest[hi->first.as_string()] = (
+ std::string(fhi->second.data(), fhi->second.size()) + "," +
+ std::string(hi->second.data(), hi->second.size()));
+ }
+ }
+
+ // These headers have no value
+ dest.erase("X-Associated-Content"); // TODO(mbelshe): case-sensitive
+ dest.erase("X-Original-Url"); // TODO(mbelshe): case-sensitive
+}
+
+size_t SpdySM::SendSynStreamImpl(uint32 stream_id,
+ const BalsaHeaders& headers) {
+ SpdyHeaderBlock block;
+ block["method"] = headers.request_method().as_string();
+ if (!headers.HasHeader("status"))
+ block["status"] = headers.response_code().as_string();
+ if (!headers.HasHeader("version"))
+ block["version"] =headers.response_version().as_string();
+ if (headers.HasHeader("X-Original-Url")) {
+ std::string original_url = headers.GetHeader("X-Original-Url").as_string();
+ block["path"] = UrlUtilities::GetUrlPath(original_url);
+ } else {
+ block["path"] = headers.request_uri().as_string();
+ }
+ CopyHeaders(block, headers);
+
+ SpdySynStreamControlFrame* fsrcf =
+ spdy_framer_->CreateSynStream(stream_id, 0, 0, CONTROL_FLAG_NONE, true,
+ &block);
+ size_t df_size = fsrcf->length() + SpdyFrame::size();
+ EnqueueDataFrame(new SpdyFrameDataFrame(fsrcf));
+
+ VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Sending SynStreamheader "
+ << stream_id;
+ return df_size;
+}
+
+size_t SpdySM::SendSynReplyImpl(uint32 stream_id, const BalsaHeaders& headers) {
+ SpdyHeaderBlock block;
+ CopyHeaders(block, headers);
+ block["status"] = headers.response_code().as_string() + " " +
+ headers.response_reason_phrase().as_string();
+ block["version"] = headers.response_version().as_string();
+
+ SpdySynReplyControlFrame* fsrcf =
+ spdy_framer_->CreateSynReply(stream_id, CONTROL_FLAG_NONE, true, &block);
+ size_t df_size = fsrcf->length() + SpdyFrame::size();
+ EnqueueDataFrame(new SpdyFrameDataFrame(fsrcf));
+
+ VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Sending SynReplyheader "
+ << stream_id;
+ return df_size;
+}
+
+void SpdySM::SendDataFrameImpl(uint32 stream_id, const char* data, int64 len,
+ SpdyDataFlags flags, bool compress) {
+ // Force compression off if disabled via command line.
+ if (disable_data_compression())
+ flags = static_cast<SpdyDataFlags>(flags & ~DATA_FLAG_COMPRESSED);
+
+ // TODO(mbelshe): We can't compress here - before going into the
+ // priority queue. Compression needs to be done
+ // with late binding.
+ if (len == 0) {
+ SpdyDataFrame* fdf = spdy_framer_->CreateDataFrame(stream_id, data, len,
+ flags);
+ EnqueueDataFrame(new SpdyFrameDataFrame(fdf));
+ return;
+ }
+
+ // Chop data frames into chunks so that one stream can't monopolize the
+ // output channel.
+ while (len > 0) {
+ int64 size = std::min(len, static_cast<int64>(kSpdySegmentSize));
+ SpdyDataFlags chunk_flags = flags;
+
+ // If we chunked this block, and the FIN flag was set, there is more
+ // data coming. So, remove the flag.
+ if ((size < len) && (flags & DATA_FLAG_FIN))
+ chunk_flags = static_cast<SpdyDataFlags>(chunk_flags & ~DATA_FLAG_FIN);
+
+ SpdyDataFrame* fdf = spdy_framer_->CreateDataFrame(stream_id, data, size,
+ chunk_flags);
+ EnqueueDataFrame(new SpdyFrameDataFrame(fdf));
+
+ VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Sending data frame "
+ << stream_id << " [" << size << "] shrunk to " << fdf->length()
+ << ", flags=" << flags;
+
+ data += size;
+ len -= size;
+ }
+}
+
+void SpdySM::EnqueueDataFrame(DataFrame* df) {
+ connection_->EnqueueDataFrame(df);
+}
+
+void SpdySM::GetOutput() {
+ while (client_output_list_->size() < 2) {
+ MemCacheIter* mci = client_output_ordering_.GetIter();
+ if (mci == NULL) {
+ VLOG(2) << ACCEPTOR_CLIENT_IDENT
+ << "SpdySM: GetOutput: nothing to output!?";
+ return;
+ }
+ if (!mci->transformed_header) {
+ mci->transformed_header = true;
+ VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: GetOutput transformed "
+ << "header stream_id: [" << mci->stream_id << "]";
+ if ((mci->stream_id % 2) == 0) {
+ // this is a server initiated stream.
+ // Ideally, we'd do a 'syn-push' here, instead of a syn-reply.
+ BalsaHeaders headers;
+ headers.CopyFrom(*(mci->file_data->headers));
+ headers.ReplaceOrAppendHeader("status", "200");
+ headers.ReplaceOrAppendHeader("version", "http/1.1");
+ headers.SetRequestFirstlineFromStringPieces("PUSH",
+ mci->file_data->filename,
+ "");
+ mci->bytes_sent = SendSynStream(mci->stream_id, headers);
+ } else {
+ BalsaHeaders headers;
+ headers.CopyFrom(*(mci->file_data->headers));
+ mci->bytes_sent = SendSynReply(mci->stream_id, headers);
+ }
+ return;
+ }
+ if (mci->body_bytes_consumed >= mci->file_data->body.size()) {
+ VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: GetOutput "
+ << "remove_stream_id: [" << mci->stream_id << "]";
+ SendEOF(mci->stream_id);
+ return;
+ }
+ size_t num_to_write =
+ mci->file_data->body.size() - mci->body_bytes_consumed;
+ if (num_to_write > mci->max_segment_size)
+ num_to_write = mci->max_segment_size;
+
+ bool should_compress = false;
+ if (!mci->file_data->headers->HasHeader("content-encoding")) {
+ if (mci->file_data->headers->HasHeader("content-type")) {
+ std::string content_type =
+ mci->file_data->headers->GetHeader("content-type").as_string();
+ if (content_type.find("image") == content_type.npos)
+ should_compress = true;
+ }
+ }
+
+ SendDataFrame(mci->stream_id,
+ mci->file_data->body.data() + mci->body_bytes_consumed,
+ num_to_write, 0, should_compress);
+ VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: GetOutput SendDataFrame["
+ << mci->stream_id << "]: " << num_to_write;
+ mci->body_bytes_consumed += num_to_write;
+ mci->bytes_sent += num_to_write;
+ }
+}
+
+} // namespace net
+