blob: 9ce8033a1c8621ca1e80556a08b499ddfd84322c [file] [log] [blame]
Kristian Monsendc0f95d2011-06-09 11:47:42 +01001// Copyright (c) 2009 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/tools/flip_server/spdy_interface.h"
6
7#include <string>
8
9#include "net/spdy/spdy_framer.h"
10#include "net/spdy/spdy_protocol.h"
11#include "net/tools/dump_cache/url_utilities.h"
12#include "net/tools/flip_server/flip_config.h"
13#include "net/tools/flip_server/http_interface.h"
14#include "net/tools/flip_server/spdy_util.h"
15
16using spdy::kSpdyStreamMaximumWindowSize;
17using spdy::CONTROL_FLAG_NONE;
18using spdy::DATA_FLAG_COMPRESSED;
19using spdy::DATA_FLAG_FIN;
20using spdy::RST_STREAM;
21using spdy::SETTINGS_MAX_CONCURRENT_STREAMS;
22using spdy::SYN_REPLY;
23using spdy::SYN_STREAM;
24using spdy::SettingsFlagsAndId;
25using spdy::SpdyControlFrame;
26using spdy::SpdySettingsControlFrame;
27using spdy::SpdyDataFlags;
28using spdy::SpdyDataFrame;
29using spdy::SpdyRstStreamControlFrame;
30using spdy::SpdyFrame;
31using spdy::SpdyFramer;
32using spdy::SpdyFramerVisitorInterface;
33using spdy::SpdyHeaderBlock;
34using spdy::SpdySetting;
35using spdy::SpdySettings;
36using spdy::SpdyStreamId;
37using spdy::SpdySynReplyControlFrame;
38using spdy::SpdySynStreamControlFrame;
39
40namespace net {
41
42// static
43bool SpdySM::disable_data_compression_ = true;
44// static
45std::string SpdySM::forward_ip_header_;
46
47class SpdyFrameDataFrame : public DataFrame {
48 public:
49 SpdyFrameDataFrame(SpdyFrame* spdy_frame)
50 : frame(spdy_frame) {
51 data = spdy_frame->data();
52 size = spdy_frame->length() + SpdyFrame::size();
53 }
54
55 virtual ~SpdyFrameDataFrame() {
56 delete frame;
57 }
58
59 const SpdyFrame* frame;
60};
61
62SpdySM::SpdySM(SMConnection* connection,
63 SMInterface* sm_http_interface,
64 EpollServer* epoll_server,
65 MemoryCache* memory_cache,
66 FlipAcceptor* acceptor)
67 : seq_num_(0),
68 spdy_framer_(new SpdyFramer),
69 valid_spdy_session_(false),
70 connection_(connection),
71 client_output_list_(connection->output_list()),
72 client_output_ordering_(connection),
73 next_outgoing_stream_id_(2),
74 epoll_server_(epoll_server),
75 acceptor_(acceptor),
76 memory_cache_(memory_cache),
77 close_on_error_(false) {
78 spdy_framer_->set_visitor(this);
79}
80
81SpdySM::~SpdySM() {
82 delete spdy_framer_;
83}
84
85void SpdySM::InitSMConnection(SMConnectionPoolInterface* connection_pool,
86 SMInterface* sm_interface,
87 EpollServer* epoll_server,
88 int fd,
89 std::string server_ip,
90 std::string server_port,
91 std::string remote_ip,
92 bool use_ssl) {
93 VLOG(2) << ACCEPTOR_CLIENT_IDENT
94 << "SpdySM: Initializing server connection.";
95 connection_->InitSMConnection(connection_pool, sm_interface,
96 epoll_server, fd, server_ip, server_port,
97 remote_ip, use_ssl);
98}
99
100SMInterface* SpdySM::NewConnectionInterface() {
101 SMConnection* server_connection =
102 SMConnection::NewSMConnection(epoll_server_,
103 NULL,
104 memory_cache_,
105 acceptor_,
106 "http_conn: ");
107 if (server_connection == NULL) {
108 LOG(ERROR) << "SpdySM: Could not create server connection";
109 return NULL;
110 }
111 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Creating new HTTP interface";
112 SMInterface *sm_http_interface = new HttpSM(server_connection,
113 this,
114 epoll_server_,
115 memory_cache_,
116 acceptor_);
117 return sm_http_interface;
118}
119
120SMInterface* SpdySM::FindOrMakeNewSMConnectionInterface(
121 std::string server_ip, std::string server_port) {
122 SMInterface *sm_http_interface;
123 int32 server_idx;
124 if (unused_server_interface_list.empty()) {
125 sm_http_interface = NewConnectionInterface();
126 server_idx = server_interface_list.size();
127 server_interface_list.push_back(sm_http_interface);
128 VLOG(2) << ACCEPTOR_CLIENT_IDENT
129 << "SpdySM: Making new server connection on index: "
130 << server_idx;
131 } else {
132 server_idx = unused_server_interface_list.back();
133 unused_server_interface_list.pop_back();
134 sm_http_interface = server_interface_list.at(server_idx);
135 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Reusing connection on "
136 << "index: " << server_idx;
137 }
138
139 sm_http_interface->InitSMInterface(this, server_idx);
140 sm_http_interface->InitSMConnection(NULL, sm_http_interface,
141 epoll_server_, -1,
142 server_ip, server_port, "", false);
143
144 return sm_http_interface;
145}
146
147int SpdySM::SpdyHandleNewStream(const SpdyControlFrame* frame,
148 std::string &http_data,
149 bool *is_https_scheme) {
150 bool parsed_headers = false;
151 SpdyHeaderBlock headers;
152 const SpdySynStreamControlFrame* syn_stream =
153 reinterpret_cast<const SpdySynStreamControlFrame*>(frame);
154
155 *is_https_scheme = false;
156 parsed_headers = spdy_framer_->ParseHeaderBlock(frame, &headers);
157 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: OnSyn("
158 << syn_stream->stream_id() << ")";
159 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: headers parsed?: "
160 << (parsed_headers? "yes": "no");
161 if (parsed_headers) {
162 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: # headers: "
163 << headers.size();
164 }
165 SpdyHeaderBlock::iterator url = headers.find("url");
166 SpdyHeaderBlock::iterator method = headers.find("method");
167 if (url == headers.end() || method == headers.end()) {
168 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: didn't find method or url "
169 << "or method. Not creating stream";
170 return 0;
171 }
172
173 SpdyHeaderBlock::iterator scheme = headers.find("scheme");
174 if (scheme->second.compare("https") == 0) {
175 *is_https_scheme = true;
176 }
177
Kristian Monsenddb351d2011-06-28 21:49:31 +0100178 // url->second here only ever seems to contain just the path. When this
179 // path contains a query string with a http:// in one of its values,
180 // UrlUtilities::GetUrlPath will fail and always return a / breaking
181 // the request. GetUrlPath assumes the absolute URL is being passed in.
182 std::string uri;
183 if (url->second.compare(0,4,"http") == 0)
184 uri = UrlUtilities::GetUrlPath(url->second);
185 else
186 uri = std::string(url->second);
Kristian Monsendc0f95d2011-06-09 11:47:42 +0100187 if (acceptor_->flip_handler_type_ == FLIP_HANDLER_SPDY_SERVER) {
188 SpdyHeaderBlock::iterator referer = headers.find("referer");
189 std::string host = UrlUtilities::GetUrlHost(url->second);
190 VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Request: " << method->second
191 << " " << uri;
192 std::string filename = EncodeURL(uri, host, method->second);
193 NewStream(syn_stream->stream_id(),
194 reinterpret_cast<const SpdySynStreamControlFrame*>
195 (frame)->priority(),
196 filename);
197 } else {
198 SpdyHeaderBlock::iterator version = headers.find("version");
199 http_data += method->second + " " + uri + " " + version->second + "\r\n";
200 VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Request: " << method->second << " "
201 << uri << " " << version->second;
202 for (SpdyHeaderBlock::iterator i = headers.begin();
203 i != headers.end(); ++i) {
204 http_data += i->first + ": " + i->second + "\r\n";
205 VLOG(2) << ACCEPTOR_CLIENT_IDENT << i->first.c_str() << ":"
206 << i->second.c_str();
207 }
208 if (forward_ip_header_.length()) {
209 // X-Client-Cluster-IP header
210 http_data += forward_ip_header_ + ": " +
211 connection_->client_ip() + "\r\n";
212 }
213 http_data += "\r\n";
214 }
215
216 VLOG(3) << ACCEPTOR_CLIENT_IDENT << "SpdySM: HTTP Request:\n" << http_data;
217 return 1;
218}
219
220void SpdySM::OnControl(const SpdyControlFrame* frame) {
221 SpdyHeaderBlock headers;
222 bool parsed_headers = false;
223 switch (frame->type()) {
224 case SYN_STREAM:
225 {
226 const SpdySynStreamControlFrame* syn_stream =
227 reinterpret_cast<const SpdySynStreamControlFrame*>(frame);
228
229 std::string http_data;
230 bool is_https_scheme;
231 int ret = SpdyHandleNewStream(frame, http_data, &is_https_scheme);
232 if (!ret) {
233 LOG(ERROR) << "SpdySM: Could not convert spdy into http.";
234 break;
235 }
236 // We've seen a valid looking SYN_STREAM, consider this to have
237 // been a real spdy session.
238 valid_spdy_session_ = true;
239
240 if (acceptor_->flip_handler_type_ == FLIP_HANDLER_PROXY) {
241 std::string server_ip;
242 std::string server_port;
243 if (is_https_scheme) {
244 server_ip = acceptor_->https_server_ip_;
245 server_port = acceptor_->https_server_port_;
246 } else {
247 server_ip = acceptor_->http_server_ip_;
248 server_port = acceptor_->http_server_port_;
249 }
250 SMInterface *sm_http_interface =
251 FindOrMakeNewSMConnectionInterface(server_ip, server_port);
252 stream_to_smif_[syn_stream->stream_id()] = sm_http_interface;
253 sm_http_interface->SetStreamID(syn_stream->stream_id());
254 sm_http_interface->ProcessWriteInput(http_data.c_str(),
255 http_data.size());
256 }
257 }
258 break;
259
260 case SYN_REPLY:
261 parsed_headers = spdy_framer_->ParseHeaderBlock(frame, &headers);
262 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: OnSynReply(" <<
263 reinterpret_cast<const SpdySynReplyControlFrame*>(frame)->stream_id()
264 << ")";
265 break;
266 case RST_STREAM:
267 {
268 const SpdyRstStreamControlFrame* rst_stream =
269 reinterpret_cast<const SpdyRstStreamControlFrame*>(frame);
270 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: OnRst("
271 << rst_stream->stream_id() << ")";
272 client_output_ordering_.RemoveStreamId(rst_stream ->stream_id());
273 }
274 break;
275
276 default:
277 LOG(ERROR) << "SpdySM: Unknown control frame type";
278 }
279}
280
Selim Gurun2c4085b2011-11-10 18:38:04 -0800281bool SpdySM::OnControlFrameHeaderData(spdy::SpdyStreamId stream_id,
282 const char* header_data,
283 size_t len) {
284 DCHECK(false);
285 return false;
286}
287
288void SpdySM::OnDataFrameHeader(const spdy::SpdyDataFrame* frame) {
289 DCHECK(false);
290}
291
Kristian Monsendc0f95d2011-06-09 11:47:42 +0100292void SpdySM::OnStreamFrameData(SpdyStreamId stream_id,
293 const char* data, size_t len) {
294 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: StreamData(" << stream_id
295 << ", [" << len << "])";
296 StreamToSmif::iterator it = stream_to_smif_.find(stream_id);
297 if (it == stream_to_smif_.end()) {
298 VLOG(2) << "Dropping frame from unknown stream " << stream_id;
299 if (!valid_spdy_session_)
300 close_on_error_ = true;
301 return;
302 }
303
304 SMInterface* interface = it->second;
305 if (acceptor_->flip_handler_type_ == FLIP_HANDLER_PROXY)
306 interface->ProcessWriteInput(data, len);
307}
308
309size_t SpdySM::ProcessReadInput(const char* data, size_t len) {
310 return spdy_framer_->ProcessInput(data, len);
311}
312
313size_t SpdySM::ProcessWriteInput(const char* data, size_t len) {
314 return 0;
315}
316
317bool SpdySM::MessageFullyRead() const {
318 return spdy_framer_->MessageFullyRead();
319}
320
321bool SpdySM::Error() const {
322 return close_on_error_ || spdy_framer_->HasError();
323}
324
325const char* SpdySM::ErrorAsString() const {
326 DCHECK(Error());
327 return SpdyFramer::ErrorCodeToString(spdy_framer_->error_code());
328}
329
330void SpdySM::ResetForNewInterface(int32 server_idx) {
331 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Reset for new interface: "
332 << "server_idx: " << server_idx;
333 unused_server_interface_list.push_back(server_idx);
334}
335
336void SpdySM::ResetForNewConnection() {
337 // seq_num is not cleared, intentionally.
338 delete spdy_framer_;
339 spdy_framer_ = new SpdyFramer;
340 spdy_framer_->set_visitor(this);
341 valid_spdy_session_ = false;
342 client_output_ordering_.Reset();
343 next_outgoing_stream_id_ = 2;
344}
345
346// Send a settings frame
347int SpdySM::PostAcceptHook() {
348 SpdySettings settings;
349 SettingsFlagsAndId settings_id(SETTINGS_MAX_CONCURRENT_STREAMS);
350 settings.push_back(SpdySetting(settings_id, 100));
351 SpdySettingsControlFrame* settings_frame =
352 spdy_framer_->CreateSettings(settings);
353
354 VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Sending Settings Frame";
355 EnqueueDataFrame(new SpdyFrameDataFrame(settings_frame));
356 return 1;
357}
358
359void SpdySM::NewStream(uint32 stream_id,
360 uint32 priority,
361 const std::string& filename) {
362 MemCacheIter mci;
363 mci.stream_id = stream_id;
364 mci.priority = priority;
365 if (acceptor_->flip_handler_type_ == FLIP_HANDLER_SPDY_SERVER) {
366 if (!memory_cache_->AssignFileData(filename, &mci)) {
367 // error creating new stream.
368 VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Sending ErrorNotFound";
369 SendErrorNotFound(stream_id);
370 } else {
371 AddToOutputOrder(mci);
372 }
373 } else {
374 AddToOutputOrder(mci);
375 }
376}
377
378void SpdySM::AddToOutputOrder(const MemCacheIter& mci) {
379 client_output_ordering_.AddToOutputOrder(mci);
380}
381
382void SpdySM::SendEOF(uint32 stream_id) {
383 SendEOFImpl(stream_id);
384}
385
386void SpdySM::SendErrorNotFound(uint32 stream_id) {
387 SendErrorNotFoundImpl(stream_id);
388}
389
390void SpdySM::SendOKResponse(uint32 stream_id, std::string* output) {
391 SendOKResponseImpl(stream_id, output);
392}
393
394size_t SpdySM::SendSynStream(uint32 stream_id, const BalsaHeaders& headers) {
395 return SendSynStreamImpl(stream_id, headers);
396}
397
398size_t SpdySM::SendSynReply(uint32 stream_id, const BalsaHeaders& headers) {
399 return SendSynReplyImpl(stream_id, headers);
400}
401
402void SpdySM::SendDataFrame(uint32 stream_id, const char* data, int64 len,
403 uint32 flags, bool compress) {
404 SpdyDataFlags spdy_flags = static_cast<SpdyDataFlags>(flags);
405 SendDataFrameImpl(stream_id, data, len, spdy_flags, compress);
406}
407
408void SpdySM::SendEOFImpl(uint32 stream_id) {
409 SendDataFrame(stream_id, NULL, 0, DATA_FLAG_FIN, false);
410 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Sending EOF: " << stream_id;
411 KillStream(stream_id);
412 stream_to_smif_.erase(stream_id);
413}
414
415void SpdySM::SendErrorNotFoundImpl(uint32 stream_id) {
416 BalsaHeaders my_headers;
417 my_headers.SetFirstlineFromStringPieces("HTTP/1.1", "404", "Not Found");
418 SendSynReplyImpl(stream_id, my_headers);
419 SendDataFrame(stream_id, "wtf?", 4, DATA_FLAG_FIN, false);
420 client_output_ordering_.RemoveStreamId(stream_id);
421}
422
423void SpdySM::SendOKResponseImpl(uint32 stream_id, std::string* output) {
424 BalsaHeaders my_headers;
425 my_headers.SetFirstlineFromStringPieces("HTTP/1.1", "200", "OK");
426 SendSynReplyImpl(stream_id, my_headers);
427 SendDataFrame(
428 stream_id, output->c_str(), output->size(), DATA_FLAG_FIN, false);
429 client_output_ordering_.RemoveStreamId(stream_id);
430}
431
432void SpdySM::KillStream(uint32 stream_id) {
433 client_output_ordering_.RemoveStreamId(stream_id);
434}
435
436void SpdySM::CopyHeaders(SpdyHeaderBlock& dest, const BalsaHeaders& headers) {
437 for (BalsaHeaders::const_header_lines_iterator hi =
438 headers.header_lines_begin();
439 hi != headers.header_lines_end();
440 ++hi) {
441 // It is illegal to send SPDY headers with empty value or header
442 // names.
443 if (!hi->first.length() || !hi->second.length())
444 continue;
445
446 SpdyHeaderBlock::iterator fhi = dest.find(hi->first.as_string());
447 if (fhi == dest.end()) {
448 dest[hi->first.as_string()] = hi->second.as_string();
449 } else {
450 dest[hi->first.as_string()] = (
Kristian Monsenddb351d2011-06-28 21:49:31 +0100451 std::string(fhi->second.data(), fhi->second.size()) + "\0" +
Kristian Monsendc0f95d2011-06-09 11:47:42 +0100452 std::string(hi->second.data(), hi->second.size()));
453 }
454 }
455
456 // These headers have no value
457 dest.erase("X-Associated-Content"); // TODO(mbelshe): case-sensitive
458 dest.erase("X-Original-Url"); // TODO(mbelshe): case-sensitive
459}
460
461size_t SpdySM::SendSynStreamImpl(uint32 stream_id,
462 const BalsaHeaders& headers) {
463 SpdyHeaderBlock block;
464 block["method"] = headers.request_method().as_string();
465 if (!headers.HasHeader("status"))
466 block["status"] = headers.response_code().as_string();
467 if (!headers.HasHeader("version"))
468 block["version"] =headers.response_version().as_string();
469 if (headers.HasHeader("X-Original-Url")) {
470 std::string original_url = headers.GetHeader("X-Original-Url").as_string();
471 block["path"] = UrlUtilities::GetUrlPath(original_url);
472 } else {
473 block["path"] = headers.request_uri().as_string();
474 }
475 CopyHeaders(block, headers);
476
477 SpdySynStreamControlFrame* fsrcf =
478 spdy_framer_->CreateSynStream(stream_id, 0, 0, CONTROL_FLAG_NONE, true,
479 &block);
480 size_t df_size = fsrcf->length() + SpdyFrame::size();
481 EnqueueDataFrame(new SpdyFrameDataFrame(fsrcf));
482
483 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Sending SynStreamheader "
484 << stream_id;
485 return df_size;
486}
487
488size_t SpdySM::SendSynReplyImpl(uint32 stream_id, const BalsaHeaders& headers) {
489 SpdyHeaderBlock block;
490 CopyHeaders(block, headers);
491 block["status"] = headers.response_code().as_string() + " " +
492 headers.response_reason_phrase().as_string();
493 block["version"] = headers.response_version().as_string();
494
495 SpdySynReplyControlFrame* fsrcf =
496 spdy_framer_->CreateSynReply(stream_id, CONTROL_FLAG_NONE, true, &block);
497 size_t df_size = fsrcf->length() + SpdyFrame::size();
498 EnqueueDataFrame(new SpdyFrameDataFrame(fsrcf));
499
500 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Sending SynReplyheader "
501 << stream_id;
502 return df_size;
503}
504
505void SpdySM::SendDataFrameImpl(uint32 stream_id, const char* data, int64 len,
506 SpdyDataFlags flags, bool compress) {
507 // Force compression off if disabled via command line.
508 if (disable_data_compression())
509 flags = static_cast<SpdyDataFlags>(flags & ~DATA_FLAG_COMPRESSED);
510
511 // TODO(mbelshe): We can't compress here - before going into the
512 // priority queue. Compression needs to be done
513 // with late binding.
514 if (len == 0) {
515 SpdyDataFrame* fdf = spdy_framer_->CreateDataFrame(stream_id, data, len,
516 flags);
517 EnqueueDataFrame(new SpdyFrameDataFrame(fdf));
518 return;
519 }
520
521 // Chop data frames into chunks so that one stream can't monopolize the
522 // output channel.
523 while (len > 0) {
524 int64 size = std::min(len, static_cast<int64>(kSpdySegmentSize));
525 SpdyDataFlags chunk_flags = flags;
526
527 // If we chunked this block, and the FIN flag was set, there is more
528 // data coming. So, remove the flag.
529 if ((size < len) && (flags & DATA_FLAG_FIN))
530 chunk_flags = static_cast<SpdyDataFlags>(chunk_flags & ~DATA_FLAG_FIN);
531
532 SpdyDataFrame* fdf = spdy_framer_->CreateDataFrame(stream_id, data, size,
533 chunk_flags);
534 EnqueueDataFrame(new SpdyFrameDataFrame(fdf));
535
536 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Sending data frame "
537 << stream_id << " [" << size << "] shrunk to " << fdf->length()
538 << ", flags=" << flags;
539
540 data += size;
541 len -= size;
542 }
543}
544
545void SpdySM::EnqueueDataFrame(DataFrame* df) {
546 connection_->EnqueueDataFrame(df);
547}
548
549void SpdySM::GetOutput() {
550 while (client_output_list_->size() < 2) {
551 MemCacheIter* mci = client_output_ordering_.GetIter();
552 if (mci == NULL) {
553 VLOG(2) << ACCEPTOR_CLIENT_IDENT
554 << "SpdySM: GetOutput: nothing to output!?";
555 return;
556 }
557 if (!mci->transformed_header) {
558 mci->transformed_header = true;
559 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: GetOutput transformed "
560 << "header stream_id: [" << mci->stream_id << "]";
561 if ((mci->stream_id % 2) == 0) {
562 // this is a server initiated stream.
563 // Ideally, we'd do a 'syn-push' here, instead of a syn-reply.
564 BalsaHeaders headers;
565 headers.CopyFrom(*(mci->file_data->headers));
566 headers.ReplaceOrAppendHeader("status", "200");
567 headers.ReplaceOrAppendHeader("version", "http/1.1");
568 headers.SetRequestFirstlineFromStringPieces("PUSH",
569 mci->file_data->filename,
570 "");
571 mci->bytes_sent = SendSynStream(mci->stream_id, headers);
572 } else {
573 BalsaHeaders headers;
574 headers.CopyFrom(*(mci->file_data->headers));
575 mci->bytes_sent = SendSynReply(mci->stream_id, headers);
576 }
577 return;
578 }
579 if (mci->body_bytes_consumed >= mci->file_data->body.size()) {
580 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: GetOutput "
581 << "remove_stream_id: [" << mci->stream_id << "]";
582 SendEOF(mci->stream_id);
583 return;
584 }
585 size_t num_to_write =
586 mci->file_data->body.size() - mci->body_bytes_consumed;
587 if (num_to_write > mci->max_segment_size)
588 num_to_write = mci->max_segment_size;
589
590 bool should_compress = false;
591 if (!mci->file_data->headers->HasHeader("content-encoding")) {
592 if (mci->file_data->headers->HasHeader("content-type")) {
593 std::string content_type =
594 mci->file_data->headers->GetHeader("content-type").as_string();
595 if (content_type.find("image") == content_type.npos)
596 should_compress = true;
597 }
598 }
599
600 SendDataFrame(mci->stream_id,
601 mci->file_data->body.data() + mci->body_bytes_consumed,
602 num_to_write, 0, should_compress);
603 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: GetOutput SendDataFrame["
604 << mci->stream_id << "]: " << num_to_write;
605 mci->body_bytes_consumed += num_to_write;
606 mci->bytes_sent += num_to_write;
607 }
608}
609
610} // namespace net
611