Roll src/third_party/WebKit d9c6159:8139f33 (svn 201974:201975)
[chromium-blink-merge.git] / net / tools / flip_server / spdy_interface.cc
blobb5701aa9bff180fe143c57f4d2f5e248b82e71ce
1 // Copyright (c) 2012 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.
5 #include "net/tools/flip_server/spdy_interface.h"
7 #include <algorithm>
8 #include <string>
10 #include "net/spdy/spdy_framer.h"
11 #include "net/spdy/spdy_protocol.h"
12 #include "net/tools/flip_server/constants.h"
13 #include "net/tools/flip_server/flip_config.h"
14 #include "net/tools/flip_server/http_interface.h"
15 #include "net/tools/flip_server/spdy_util.h"
16 #include "net/tools/flip_server/url_utilities.h"
18 namespace net {
20 // static
21 std::string SpdySM::forward_ip_header_;
23 class SpdyFrameDataFrame : public DataFrame {
24 public:
25 explicit SpdyFrameDataFrame(SpdyFrame* spdy_frame) : frame(spdy_frame) {
26 data = spdy_frame->data();
27 size = spdy_frame->size();
30 ~SpdyFrameDataFrame() override { delete frame; }
32 const SpdyFrame* frame;
35 SpdySM::SpdySM(SMConnection* connection,
36 SMInterface* sm_http_interface,
37 EpollServer* epoll_server,
38 MemoryCache* memory_cache,
39 FlipAcceptor* acceptor,
40 SpdyMajorVersion spdy_version)
41 : buffered_spdy_framer_(new BufferedSpdyFramer(spdy_version, true)),
42 valid_spdy_session_(false),
43 connection_(connection),
44 client_output_list_(connection->output_list()),
45 client_output_ordering_(connection),
46 next_outgoing_stream_id_(2),
47 epoll_server_(epoll_server),
48 acceptor_(acceptor),
49 memory_cache_(memory_cache),
50 close_on_error_(false) {
51 buffered_spdy_framer_->set_visitor(this);
54 SpdySM::~SpdySM() { }
56 void SpdySM::InitSMConnection(SMConnectionPoolInterface* connection_pool,
57 SMInterface* sm_interface,
58 EpollServer* epoll_server,
59 int fd,
60 std::string server_ip,
61 std::string server_port,
62 std::string remote_ip,
63 bool use_ssl) {
64 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Initializing server connection.";
65 connection_->InitSMConnection(connection_pool,
66 sm_interface,
67 epoll_server,
68 fd,
69 server_ip,
70 server_port,
71 remote_ip,
72 use_ssl);
75 SMInterface* SpdySM::NewConnectionInterface() {
76 SMConnection* server_connection =
77 SMConnection::NewSMConnection(epoll_server_,
78 NULL,
79 memory_cache_,
80 acceptor_,
81 "http_conn: ");
82 if (server_connection == NULL) {
83 LOG(ERROR) << "SpdySM: Could not create server connection";
84 return NULL;
86 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Creating new HTTP interface";
87 SMInterface* sm_http_interface =
88 new HttpSM(server_connection, this, memory_cache_, acceptor_);
89 return sm_http_interface;
92 SMInterface* SpdySM::FindOrMakeNewSMConnectionInterface(
93 const std::string& server_ip,
94 const std::string& server_port) {
95 SMInterface* sm_http_interface;
96 int32 server_idx;
97 if (unused_server_interface_list.empty()) {
98 sm_http_interface = NewConnectionInterface();
99 server_idx = server_interface_list.size();
100 server_interface_list.push_back(sm_http_interface);
101 VLOG(2) << ACCEPTOR_CLIENT_IDENT
102 << "SpdySM: Making new server connection on index: " << server_idx;
103 } else {
104 server_idx = unused_server_interface_list.back();
105 unused_server_interface_list.pop_back();
106 sm_http_interface = server_interface_list.at(server_idx);
107 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Reusing connection on "
108 << "index: " << server_idx;
111 sm_http_interface->InitSMInterface(this, server_idx);
112 sm_http_interface->InitSMConnection(NULL,
113 sm_http_interface,
114 epoll_server_,
116 server_ip,
117 server_port,
118 std::string(),
119 false);
121 return sm_http_interface;
124 int SpdySM::SpdyHandleNewStream(SpdyStreamId stream_id,
125 SpdyPriority priority,
126 const SpdyHeaderBlock& headers,
127 std::string& http_data,
128 bool* is_https_scheme) {
129 *is_https_scheme = false;
130 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: OnSyn(" << stream_id << ")";
131 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: # headers: " << headers.size();
133 SpdyHeaderBlock::const_iterator method = headers.end();
134 SpdyHeaderBlock::const_iterator host = headers.end();
135 SpdyHeaderBlock::const_iterator path = headers.end();
136 SpdyHeaderBlock::const_iterator scheme = headers.end();
137 SpdyHeaderBlock::const_iterator version = headers.end();
138 SpdyHeaderBlock::const_iterator url = headers.end();
140 std::string path_string, host_string, version_string;
142 if (spdy_version() == SPDY2) {
143 url = headers.find("url");
144 method = headers.find("method");
145 version = headers.find("version");
146 scheme = headers.find("scheme");
147 if (url == headers.end() || method == headers.end() ||
148 version == headers.end() || scheme == headers.end()) {
149 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: A mandatory header is "
150 << "missing. Not creating stream";
151 return 0;
153 // url->second here only ever seems to contain just the path. When this
154 // path contains a query string with a http:// in one of its values,
155 // UrlUtilities::GetUrlPath will fail and always return a / breaking
156 // the request. GetUrlPath assumes the absolute URL is being passed in.
157 path_string = UrlUtilities::GetUrlPath(url->second);
158 host_string = UrlUtilities::GetUrlHost(url->second);
159 version_string = version->second;
160 } else {
161 method = headers.find(":method");
162 host = headers.find(":host");
163 path = headers.find(":path");
164 scheme = headers.find(":scheme");
165 if (method == headers.end() || host == headers.end() ||
166 path == headers.end() || scheme == headers.end()) {
167 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: A mandatory header is "
168 << "missing. Not creating stream";
169 return 0;
171 host_string = host->second;
172 path_string = path->second;
173 version_string = "HTTP/1.1";
176 if (scheme->second.compare("https") == 0) {
177 *is_https_scheme = true;
180 if (acceptor_->flip_handler_type_ == FLIP_HANDLER_SPDY_SERVER) {
181 VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Request: " << method->second
182 << " " << path_string;
183 std::string filename = EncodeURL(path_string,
184 host_string,
185 method->second);
186 NewStream(stream_id, priority, filename);
187 } else {
188 http_data +=
189 method->second + " " + path_string + " " + version_string + "\r\n";
190 VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Request: " << method->second << " "
191 << path_string << " " << version_string;
192 http_data += "Host: " + (*is_https_scheme ?
193 acceptor_->https_server_ip_ :
194 acceptor_->http_server_ip_) + "\r\n";
195 for (SpdyHeaderBlock::const_iterator i = headers.begin();
196 i != headers.end(); ++i) {
197 if ((i->first.size() > 0 && i->first[0] == ':') ||
198 i->first == "host" ||
199 i == method ||
200 i == host ||
201 i == path ||
202 i == scheme ||
203 i == version ||
204 i == url) {
205 // Ignore the entry.
206 } else {
207 http_data += i->first + ": " + i->second + "\r\n";
208 VLOG(2) << ACCEPTOR_CLIENT_IDENT << i->first.c_str() << ":"
209 << i->second.c_str();
212 if (forward_ip_header_.length()) {
213 // X-Client-Cluster-IP header
214 http_data += forward_ip_header_ + ": " +
215 connection_->client_ip() + "\r\n";
217 http_data += "\r\n";
220 VLOG(3) << ACCEPTOR_CLIENT_IDENT << "SpdySM: HTTP Request:\n" << http_data;
221 return 1;
224 void SpdySM::OnStreamFrameData(SpdyStreamId stream_id,
225 const char* data,
226 size_t len,
227 bool fin) {
228 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: StreamData(" << stream_id
229 << ", [" << len << "])";
230 StreamToSmif::iterator it = stream_to_smif_.find(stream_id);
231 if (it == stream_to_smif_.end()) {
232 VLOG(2) << "Dropping frame from unknown stream " << stream_id;
233 if (!valid_spdy_session_)
234 close_on_error_ = true;
235 return;
238 SMInterface* interface = it->second;
239 if (acceptor_->flip_handler_type_ == FLIP_HANDLER_PROXY)
240 interface->ProcessWriteInput(data, len);
243 void SpdySM::OnStreamPadding(SpdyStreamId stream_id, size_t len) {
244 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: StreamPadding(" << stream_id
245 << ", [" << len << "])";
248 void SpdySM::OnSynStream(SpdyStreamId stream_id,
249 SpdyStreamId associated_stream_id,
250 SpdyPriority priority,
251 bool fin,
252 bool unidirectional,
253 const SpdyHeaderBlock& headers) {
254 std::string http_data;
255 bool is_https_scheme;
256 int ret = SpdyHandleNewStream(
257 stream_id, priority, headers, http_data, &is_https_scheme);
258 if (!ret) {
259 LOG(ERROR) << "SpdySM: Could not convert spdy into http.";
260 return;
262 // We've seen a valid looking SYN_STREAM, consider this to have
263 // been a real spdy session.
264 valid_spdy_session_ = true;
266 if (acceptor_->flip_handler_type_ == FLIP_HANDLER_PROXY) {
267 std::string server_ip;
268 std::string server_port;
269 if (is_https_scheme) {
270 server_ip = acceptor_->https_server_ip_;
271 server_port = acceptor_->https_server_port_;
272 } else {
273 server_ip = acceptor_->http_server_ip_;
274 server_port = acceptor_->http_server_port_;
276 SMInterface* sm_http_interface =
277 FindOrMakeNewSMConnectionInterface(server_ip, server_port);
278 stream_to_smif_[stream_id] = sm_http_interface;
279 sm_http_interface->SetStreamID(stream_id);
280 sm_http_interface->ProcessWriteInput(http_data.c_str(), http_data.size());
284 void SpdySM::OnSynReply(SpdyStreamId stream_id,
285 bool fin,
286 const SpdyHeaderBlock& headers) {
287 // TODO(willchan): if there is an error parsing headers, we
288 // should send a RST_STREAM.
289 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: OnSynReply(" << stream_id << ")";
292 void SpdySM::OnHeaders(SpdyStreamId stream_id,
293 bool has_priority,
294 SpdyPriority priority,
295 SpdyStreamId parent_stream_id,
296 bool exclusive,
297 bool fin,
298 const SpdyHeaderBlock& headers) {
299 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: OnHeaders(" << stream_id << ")";
302 void SpdySM::OnRstStream(SpdyStreamId stream_id, SpdyRstStreamStatus status) {
303 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: OnRstStream(" << stream_id
304 << ")";
305 client_output_ordering_.RemoveStreamId(stream_id);
308 bool SpdySM::OnUnknownFrame(SpdyStreamId stream_id, int frame_type) {
309 return false;
312 size_t SpdySM::ProcessReadInput(const char* data, size_t len) {
313 DCHECK(buffered_spdy_framer_);
314 return buffered_spdy_framer_->ProcessInput(data, len);
317 size_t SpdySM::ProcessWriteInput(const char* data, size_t len) { return 0; }
319 bool SpdySM::MessageFullyRead() const {
320 DCHECK(buffered_spdy_framer_);
321 return buffered_spdy_framer_->MessageFullyRead();
324 bool SpdySM::Error() const {
325 DCHECK(buffered_spdy_framer_);
326 return close_on_error_ || buffered_spdy_framer_->HasError();
329 const char* SpdySM::ErrorAsString() const {
330 DCHECK(Error());
331 DCHECK(buffered_spdy_framer_);
332 return SpdyFramer::ErrorCodeToString(buffered_spdy_framer_->error_code());
335 void SpdySM::ResetForNewInterface(int32 server_idx) {
336 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Reset for new interface: "
337 << "server_idx: " << server_idx;
338 unused_server_interface_list.push_back(server_idx);
341 void SpdySM::ResetForNewConnection() {
342 // seq_num is not cleared, intentionally.
343 buffered_spdy_framer_.reset();
344 valid_spdy_session_ = false;
345 client_output_ordering_.Reset();
346 next_outgoing_stream_id_ = 2;
349 // Send a settings frame
350 int SpdySM::PostAcceptHook() {
351 // We should have buffered_spdy_framer_ set after reuse
352 DCHECK(buffered_spdy_framer_);
353 SettingsMap settings;
354 settings[SETTINGS_MAX_CONCURRENT_STREAMS] =
355 SettingsFlagsAndValue(SETTINGS_FLAG_NONE, 100);
356 SpdyFrame* settings_frame = buffered_spdy_framer_->CreateSettings(settings);
358 VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Sending Settings Frame";
359 EnqueueDataFrame(new SpdyFrameDataFrame(settings_frame));
360 return 1;
363 void SpdySM::NewStream(uint32 stream_id,
364 uint32 priority,
365 const std::string& filename) {
366 MemCacheIter mci;
367 mci.stream_id = stream_id;
368 mci.priority = priority;
369 // TODO(yhirano): The program will crash when
370 // acceptor_->flip_handler_type_ != FLIP_HANDLER_SPDY_SERVER.
371 // It should be fixed or an assertion should be placed.
372 if (acceptor_->flip_handler_type_ == FLIP_HANDLER_SPDY_SERVER) {
373 if (!memory_cache_->AssignFileData(filename, &mci)) {
374 // error creating new stream.
375 VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Sending ErrorNotFound";
376 SendErrorNotFound(stream_id);
377 } else {
378 AddToOutputOrder(mci);
380 } else {
381 AddToOutputOrder(mci);
385 void SpdySM::AddToOutputOrder(const MemCacheIter& mci) {
386 client_output_ordering_.AddToOutputOrder(mci);
389 void SpdySM::SendEOF(uint32 stream_id) { SendEOFImpl(stream_id); }
391 void SpdySM::SendErrorNotFound(uint32 stream_id) {
392 SendErrorNotFoundImpl(stream_id);
395 size_t SpdySM::SendSynStream(uint32 stream_id, const BalsaHeaders& headers) {
396 return SendSynStreamImpl(stream_id, headers);
399 size_t SpdySM::SendSynReply(uint32 stream_id, const BalsaHeaders& headers) {
400 return SendSynReplyImpl(stream_id, headers);
403 void SpdySM::SendDataFrame(uint32 stream_id,
404 const char* data,
405 int64 len,
406 uint32 flags,
407 bool compress) {
408 SpdyDataFlags spdy_flags = static_cast<SpdyDataFlags>(flags);
409 SendDataFrameImpl(stream_id, data, len, spdy_flags, compress);
412 void SpdySM::SendEOFImpl(uint32 stream_id) {
413 SendDataFrame(stream_id, NULL, 0, DATA_FLAG_FIN, false);
414 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Sending EOF: " << stream_id;
415 KillStream(stream_id);
416 stream_to_smif_.erase(stream_id);
419 void SpdySM::SendErrorNotFoundImpl(uint32 stream_id) {
420 BalsaHeaders my_headers;
421 my_headers.SetFirstlineFromStringPieces("HTTP/1.1", "404", "Not Found");
422 SendSynReplyImpl(stream_id, my_headers);
423 SendDataFrame(stream_id, "wtf?", 4, DATA_FLAG_FIN, false);
424 client_output_ordering_.RemoveStreamId(stream_id);
427 void SpdySM::KillStream(uint32 stream_id) {
428 client_output_ordering_.RemoveStreamId(stream_id);
431 void SpdySM::CopyHeaders(SpdyHeaderBlock& dest, const BalsaHeaders& headers) {
432 for (BalsaHeaders::const_header_lines_iterator hi =
433 headers.header_lines_begin();
434 hi != headers.header_lines_end();
435 ++hi) {
436 // It is illegal to send SPDY headers with empty value or header
437 // names.
438 if (!hi->first.length() || !hi->second.length())
439 continue;
441 // Key must be all lower case in SPDY headers.
442 std::string key = hi->first.as_string();
443 std::transform(key.begin(), key.end(), key.begin(), ::tolower);
444 SpdyHeaderBlock::iterator fhi = dest.find(key);
445 if (fhi == dest.end()) {
446 dest[key] = hi->second.as_string();
447 } else {
448 dest[key] = (std::string(fhi->second.data(), fhi->second.size()) + "\0" +
449 std::string(hi->second.data(), hi->second.size()));
453 // These headers have no value
454 dest.erase("X-Associated-Content"); // TODO(mbelshe): case-sensitive
455 dest.erase("X-Original-Url"); // TODO(mbelshe): case-sensitive
458 size_t SpdySM::SendSynStreamImpl(uint32 stream_id,
459 const BalsaHeaders& headers) {
460 SpdyHeaderBlock block;
461 CopyHeaders(block, headers);
462 if (spdy_version() == SPDY2) {
463 block["method"] = headers.request_method().as_string();
464 if (!headers.HasHeader("version"))
465 block["version"] = headers.request_version().as_string();
466 if (headers.HasHeader("X-Original-Url")) {
467 std::string original_url =
468 headers.GetHeader("X-Original-Url").as_string();
469 block["url"] = UrlUtilities::GetUrlPath(original_url);
470 } else {
471 block["url"] = headers.request_uri().as_string();
473 } else {
474 block[":method"] = headers.request_method().as_string();
475 block[":version"] = headers.request_version().as_string();
476 if (headers.HasHeader("X-Original-Url")) {
477 std::string original_url =
478 headers.GetHeader("X-Original-Url").as_string();
479 block[":path"] = UrlUtilities::GetUrlPath(original_url);
480 block[":host"] = UrlUtilities::GetUrlPath(original_url);
481 } else {
482 block[":path"] = headers.request_uri().as_string();
483 if (block.find("host") != block.end()) {
484 block[":host"] = headers.GetHeader("Host").as_string();
485 block.erase("host");
490 DCHECK(buffered_spdy_framer_);
491 SpdyFrame* fsrcf = buffered_spdy_framer_->CreateSynStream(
492 stream_id, 0, 0, CONTROL_FLAG_NONE, &block);
493 size_t df_size = fsrcf->size();
494 EnqueueDataFrame(new SpdyFrameDataFrame(fsrcf));
496 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Sending SynStreamheader "
497 << stream_id;
498 return df_size;
501 size_t SpdySM::SendSynReplyImpl(uint32 stream_id, const BalsaHeaders& headers) {
502 SpdyHeaderBlock block;
503 CopyHeaders(block, headers);
504 if (spdy_version() == SPDY2) {
505 block["status"] = headers.response_code().as_string() + " " +
506 headers.response_reason_phrase().as_string();
507 block["version"] = headers.response_version().as_string();
508 } else {
509 block[":status"] = headers.response_code().as_string() + " " +
510 headers.response_reason_phrase().as_string();
511 block[":version"] = headers.response_version().as_string();
514 DCHECK(buffered_spdy_framer_);
515 SpdyFrame* fsrcf = buffered_spdy_framer_->CreateSynReply(
516 stream_id, CONTROL_FLAG_NONE, &block);
517 size_t df_size = fsrcf->size();
518 EnqueueDataFrame(new SpdyFrameDataFrame(fsrcf));
520 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Sending SynReplyheader "
521 << stream_id;
522 return df_size;
525 void SpdySM::SendDataFrameImpl(uint32 stream_id,
526 const char* data,
527 int64 len,
528 SpdyDataFlags flags,
529 bool compress) {
530 DCHECK(buffered_spdy_framer_);
531 // TODO(mbelshe): We can't compress here - before going into the
532 // priority queue. Compression needs to be done
533 // with late binding.
534 if (len == 0) {
535 SpdyFrame* fdf =
536 buffered_spdy_framer_->CreateDataFrame(stream_id, data, len, flags);
537 EnqueueDataFrame(new SpdyFrameDataFrame(fdf));
538 return;
541 // Chop data frames into chunks so that one stream can't monopolize the
542 // output channel.
543 while (len > 0) {
544 int64 size = std::min(len, static_cast<int64>(kSpdySegmentSize));
545 SpdyDataFlags chunk_flags = flags;
547 // If we chunked this block, and the FIN flag was set, there is more
548 // data coming. So, remove the flag.
549 if ((size < len) && (flags & DATA_FLAG_FIN))
550 chunk_flags = static_cast<SpdyDataFlags>(chunk_flags & ~DATA_FLAG_FIN);
552 SpdyFrame* fdf = buffered_spdy_framer_->CreateDataFrame(
553 stream_id, data, size, chunk_flags);
554 EnqueueDataFrame(new SpdyFrameDataFrame(fdf));
556 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Sending data frame "
557 << stream_id << " [" << size << "] shrunk to "
558 << (fdf->size() - kSpdyOverhead) << ", flags=" << flags;
560 data += size;
561 len -= size;
565 void SpdySM::EnqueueDataFrame(DataFrame* df) {
566 connection_->EnqueueDataFrame(df);
569 void SpdySM::GetOutput() {
570 while (client_output_list_->size() < 2) {
571 MemCacheIter* mci = client_output_ordering_.GetIter();
572 if (mci == NULL) {
573 VLOG(2) << ACCEPTOR_CLIENT_IDENT
574 << "SpdySM: GetOutput: nothing to output!?";
575 return;
577 if (!mci->transformed_header) {
578 mci->transformed_header = true;
579 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: GetOutput transformed "
580 << "header stream_id: [" << mci->stream_id << "]";
581 if ((mci->stream_id % 2) == 0) {
582 // this is a server initiated stream.
583 // Ideally, we'd do a 'syn-push' here, instead of a syn-reply.
584 BalsaHeaders headers;
585 headers.CopyFrom(*(mci->file_data->headers()));
586 headers.ReplaceOrAppendHeader("status", "200");
587 headers.ReplaceOrAppendHeader("version", "http/1.1");
588 headers.SetRequestFirstlineFromStringPieces(
589 "PUSH", mci->file_data->filename(), "");
590 mci->bytes_sent = SendSynStream(mci->stream_id, headers);
591 } else {
592 BalsaHeaders headers;
593 headers.CopyFrom(*(mci->file_data->headers()));
594 mci->bytes_sent = SendSynReply(mci->stream_id, headers);
596 return;
598 if (mci->body_bytes_consumed >= mci->file_data->body().size()) {
599 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: GetOutput "
600 << "remove_stream_id: [" << mci->stream_id << "]";
601 SendEOF(mci->stream_id);
602 return;
604 size_t num_to_write =
605 mci->file_data->body().size() - mci->body_bytes_consumed;
606 if (num_to_write > mci->max_segment_size)
607 num_to_write = mci->max_segment_size;
609 bool should_compress = false;
610 if (!mci->file_data->headers()->HasHeader("content-encoding")) {
611 if (mci->file_data->headers()->HasHeader("content-type")) {
612 std::string content_type =
613 mci->file_data->headers()->GetHeader("content-type").as_string();
614 if (content_type.find("image") == content_type.npos)
615 should_compress = true;
619 SendDataFrame(mci->stream_id,
620 mci->file_data->body().data() + mci->body_bytes_consumed,
621 num_to_write,
623 should_compress);
624 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: GetOutput SendDataFrame["
625 << mci->stream_id << "]: " << num_to_write;
626 mci->body_bytes_consumed += num_to_write;
627 mci->bytes_sent += num_to_write;
631 void SpdySM::CreateFramer(SpdyMajorVersion spdy_version) {
632 DCHECK(!buffered_spdy_framer_);
633 buffered_spdy_framer_.reset(new BufferedSpdyFramer(spdy_version, true));
634 buffered_spdy_framer_->set_visitor(this);
637 } // namespace net