OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "net/tools/flip_server/spdy_interface.h" | |
6 | |
7 #include <algorithm> | |
8 #include <string> | |
9 | |
10 #include "net/spdy/spdy_framer.h" | |
11 #include "net/spdy/spdy_protocol.h" | |
12 #include "net/tools/dump_cache/url_utilities.h" | |
13 #include "net/tools/flip_server/constants.h" | |
14 #include "net/tools/flip_server/flip_config.h" | |
15 #include "net/tools/flip_server/http_interface.h" | |
16 #include "net/tools/flip_server/spdy_util.h" | |
17 | |
18 namespace net { | |
19 | |
20 // static | |
21 std::string SpdySM::forward_ip_header_; | |
22 | |
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(); | |
28 } | |
29 | |
30 ~SpdyFrameDataFrame() override { delete frame; } | |
31 | |
32 const SpdyFrame* frame; | |
33 }; | |
34 | |
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); | |
52 } | |
53 | |
54 SpdySM::~SpdySM() { } | |
55 | |
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); | |
73 } | |
74 | |
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; | |
85 } | |
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; | |
90 } | |
91 | |
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; | |
109 } | |
110 | |
111 sm_http_interface->InitSMInterface(this, server_idx); | |
112 sm_http_interface->InitSMConnection(NULL, | |
113 sm_http_interface, | |
114 epoll_server_, | |
115 -1, | |
116 server_ip, | |
117 server_port, | |
118 std::string(), | |
119 false); | |
120 | |
121 return sm_http_interface; | |
122 } | |
123 | |
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(); | |
132 | |
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(); | |
139 | |
140 std::string path_string, host_string, version_string; | |
141 | |
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; | |
152 } | |
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; | |
170 } | |
171 host_string = host->second; | |
172 path_string = path->second; | |
173 version_string = "HTTP/1.1"; | |
174 } | |
175 | |
176 if (scheme->second.compare("https") == 0) { | |
177 *is_https_scheme = true; | |
178 } | |
179 | |
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(); | |
210 } | |
211 } | |
212 if (forward_ip_header_.length()) { | |
213 // X-Client-Cluster-IP header | |
214 http_data += forward_ip_header_ + ": " + | |
215 connection_->client_ip() + "\r\n"; | |
216 } | |
217 http_data += "\r\n"; | |
218 } | |
219 | |
220 VLOG(3) << ACCEPTOR_CLIENT_IDENT << "SpdySM: HTTP Request:\n" << http_data; | |
221 return 1; | |
222 } | |
223 | |
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; | |
236 } | |
237 | |
238 SMInterface* interface = it->second; | |
239 if (acceptor_->flip_handler_type_ == FLIP_HANDLER_PROXY) | |
240 interface->ProcessWriteInput(data, len); | |
241 } | |
242 | |
243 void SpdySM::OnSynStream(SpdyStreamId stream_id, | |
244 SpdyStreamId associated_stream_id, | |
245 SpdyPriority priority, | |
246 bool fin, | |
247 bool unidirectional, | |
248 const SpdyHeaderBlock& headers) { | |
249 std::string http_data; | |
250 bool is_https_scheme; | |
251 int ret = SpdyHandleNewStream( | |
252 stream_id, priority, headers, http_data, &is_https_scheme); | |
253 if (!ret) { | |
254 LOG(ERROR) << "SpdySM: Could not convert spdy into http."; | |
255 return; | |
256 } | |
257 // We've seen a valid looking SYN_STREAM, consider this to have | |
258 // been a real spdy session. | |
259 valid_spdy_session_ = true; | |
260 | |
261 if (acceptor_->flip_handler_type_ == FLIP_HANDLER_PROXY) { | |
262 std::string server_ip; | |
263 std::string server_port; | |
264 if (is_https_scheme) { | |
265 server_ip = acceptor_->https_server_ip_; | |
266 server_port = acceptor_->https_server_port_; | |
267 } else { | |
268 server_ip = acceptor_->http_server_ip_; | |
269 server_port = acceptor_->http_server_port_; | |
270 } | |
271 SMInterface* sm_http_interface = | |
272 FindOrMakeNewSMConnectionInterface(server_ip, server_port); | |
273 stream_to_smif_[stream_id] = sm_http_interface; | |
274 sm_http_interface->SetStreamID(stream_id); | |
275 sm_http_interface->ProcessWriteInput(http_data.c_str(), http_data.size()); | |
276 } | |
277 } | |
278 | |
279 void SpdySM::OnSynReply(SpdyStreamId stream_id, | |
280 bool fin, | |
281 const SpdyHeaderBlock& headers) { | |
282 // TODO(willchan): if there is an error parsing headers, we | |
283 // should send a RST_STREAM. | |
284 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: OnSynReply(" << stream_id << ")"; | |
285 } | |
286 | |
287 void SpdySM::OnHeaders(SpdyStreamId stream_id, | |
288 bool has_priority, | |
289 SpdyPriority priority, | |
290 bool fin, | |
291 const SpdyHeaderBlock& headers) { | |
292 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: OnHeaders(" << stream_id << ")"; | |
293 } | |
294 | |
295 void SpdySM::OnRstStream(SpdyStreamId stream_id, SpdyRstStreamStatus status) { | |
296 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: OnRstStream(" << stream_id | |
297 << ")"; | |
298 client_output_ordering_.RemoveStreamId(stream_id); | |
299 } | |
300 | |
301 bool SpdySM::OnUnknownFrame(SpdyStreamId stream_id, int frame_type) { | |
302 return false; | |
303 } | |
304 | |
305 size_t SpdySM::ProcessReadInput(const char* data, size_t len) { | |
306 DCHECK(buffered_spdy_framer_); | |
307 return buffered_spdy_framer_->ProcessInput(data, len); | |
308 } | |
309 | |
310 size_t SpdySM::ProcessWriteInput(const char* data, size_t len) { return 0; } | |
311 | |
312 bool SpdySM::MessageFullyRead() const { | |
313 DCHECK(buffered_spdy_framer_); | |
314 return buffered_spdy_framer_->MessageFullyRead(); | |
315 } | |
316 | |
317 bool SpdySM::Error() const { | |
318 DCHECK(buffered_spdy_framer_); | |
319 return close_on_error_ || buffered_spdy_framer_->HasError(); | |
320 } | |
321 | |
322 const char* SpdySM::ErrorAsString() const { | |
323 DCHECK(Error()); | |
324 DCHECK(buffered_spdy_framer_); | |
325 return SpdyFramer::ErrorCodeToString(buffered_spdy_framer_->error_code()); | |
326 } | |
327 | |
328 void SpdySM::ResetForNewInterface(int32 server_idx) { | |
329 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Reset for new interface: " | |
330 << "server_idx: " << server_idx; | |
331 unused_server_interface_list.push_back(server_idx); | |
332 } | |
333 | |
334 void SpdySM::ResetForNewConnection() { | |
335 // seq_num is not cleared, intentionally. | |
336 buffered_spdy_framer_.reset(); | |
337 valid_spdy_session_ = false; | |
338 client_output_ordering_.Reset(); | |
339 next_outgoing_stream_id_ = 2; | |
340 } | |
341 | |
342 // Send a settings frame | |
343 int SpdySM::PostAcceptHook() { | |
344 // We should have buffered_spdy_framer_ set after reuse | |
345 DCHECK(buffered_spdy_framer_); | |
346 SettingsMap settings; | |
347 settings[SETTINGS_MAX_CONCURRENT_STREAMS] = | |
348 SettingsFlagsAndValue(SETTINGS_FLAG_NONE, 100); | |
349 SpdyFrame* settings_frame = buffered_spdy_framer_->CreateSettings(settings); | |
350 | |
351 VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Sending Settings Frame"; | |
352 EnqueueDataFrame(new SpdyFrameDataFrame(settings_frame)); | |
353 return 1; | |
354 } | |
355 | |
356 void SpdySM::NewStream(uint32 stream_id, | |
357 uint32 priority, | |
358 const std::string& filename) { | |
359 MemCacheIter mci; | |
360 mci.stream_id = stream_id; | |
361 mci.priority = priority; | |
362 // TODO(yhirano): The program will crash when | |
363 // acceptor_->flip_handler_type_ != FLIP_HANDLER_SPDY_SERVER. | |
364 // It should be fixed or an assertion should be placed. | |
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 | |
378 void SpdySM::AddToOutputOrder(const MemCacheIter& mci) { | |
379 client_output_ordering_.AddToOutputOrder(mci); | |
380 } | |
381 | |
382 void SpdySM::SendEOF(uint32 stream_id) { SendEOFImpl(stream_id); } | |
383 | |
384 void SpdySM::SendErrorNotFound(uint32 stream_id) { | |
385 SendErrorNotFoundImpl(stream_id); | |
386 } | |
387 | |
388 size_t SpdySM::SendSynStream(uint32 stream_id, const BalsaHeaders& headers) { | |
389 return SendSynStreamImpl(stream_id, headers); | |
390 } | |
391 | |
392 size_t SpdySM::SendSynReply(uint32 stream_id, const BalsaHeaders& headers) { | |
393 return SendSynReplyImpl(stream_id, headers); | |
394 } | |
395 | |
396 void SpdySM::SendDataFrame(uint32 stream_id, | |
397 const char* data, | |
398 int64 len, | |
399 uint32 flags, | |
400 bool compress) { | |
401 SpdyDataFlags spdy_flags = static_cast<SpdyDataFlags>(flags); | |
402 SendDataFrameImpl(stream_id, data, len, spdy_flags, compress); | |
403 } | |
404 | |
405 void SpdySM::SendEOFImpl(uint32 stream_id) { | |
406 SendDataFrame(stream_id, NULL, 0, DATA_FLAG_FIN, false); | |
407 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Sending EOF: " << stream_id; | |
408 KillStream(stream_id); | |
409 stream_to_smif_.erase(stream_id); | |
410 } | |
411 | |
412 void SpdySM::SendErrorNotFoundImpl(uint32 stream_id) { | |
413 BalsaHeaders my_headers; | |
414 my_headers.SetFirstlineFromStringPieces("HTTP/1.1", "404", "Not Found"); | |
415 SendSynReplyImpl(stream_id, my_headers); | |
416 SendDataFrame(stream_id, "wtf?", 4, DATA_FLAG_FIN, false); | |
417 client_output_ordering_.RemoveStreamId(stream_id); | |
418 } | |
419 | |
420 void SpdySM::KillStream(uint32 stream_id) { | |
421 client_output_ordering_.RemoveStreamId(stream_id); | |
422 } | |
423 | |
424 void SpdySM::CopyHeaders(SpdyHeaderBlock& dest, const BalsaHeaders& headers) { | |
425 for (BalsaHeaders::const_header_lines_iterator hi = | |
426 headers.header_lines_begin(); | |
427 hi != headers.header_lines_end(); | |
428 ++hi) { | |
429 // It is illegal to send SPDY headers with empty value or header | |
430 // names. | |
431 if (!hi->first.length() || !hi->second.length()) | |
432 continue; | |
433 | |
434 // Key must be all lower case in SPDY headers. | |
435 std::string key = hi->first.as_string(); | |
436 std::transform(key.begin(), key.end(), key.begin(), ::tolower); | |
437 SpdyHeaderBlock::iterator fhi = dest.find(key); | |
438 if (fhi == dest.end()) { | |
439 dest[key] = hi->second.as_string(); | |
440 } else { | |
441 dest[key] = (std::string(fhi->second.data(), fhi->second.size()) + "\0" + | |
442 std::string(hi->second.data(), hi->second.size())); | |
443 } | |
444 } | |
445 | |
446 // These headers have no value | |
447 dest.erase("X-Associated-Content"); // TODO(mbelshe): case-sensitive | |
448 dest.erase("X-Original-Url"); // TODO(mbelshe): case-sensitive | |
449 } | |
450 | |
451 size_t SpdySM::SendSynStreamImpl(uint32 stream_id, | |
452 const BalsaHeaders& headers) { | |
453 SpdyHeaderBlock block; | |
454 CopyHeaders(block, headers); | |
455 if (spdy_version() == SPDY2) { | |
456 block["method"] = headers.request_method().as_string(); | |
457 if (!headers.HasHeader("version")) | |
458 block["version"] = headers.request_version().as_string(); | |
459 if (headers.HasHeader("X-Original-Url")) { | |
460 std::string original_url = | |
461 headers.GetHeader("X-Original-Url").as_string(); | |
462 block["url"] = UrlUtilities::GetUrlPath(original_url); | |
463 } else { | |
464 block["url"] = headers.request_uri().as_string(); | |
465 } | |
466 } else { | |
467 block[":method"] = headers.request_method().as_string(); | |
468 block[":version"] = headers.request_version().as_string(); | |
469 if (headers.HasHeader("X-Original-Url")) { | |
470 std::string original_url = | |
471 headers.GetHeader("X-Original-Url").as_string(); | |
472 block[":path"] = UrlUtilities::GetUrlPath(original_url); | |
473 block[":host"] = UrlUtilities::GetUrlPath(original_url); | |
474 } else { | |
475 block[":path"] = headers.request_uri().as_string(); | |
476 if (block.find("host") != block.end()) { | |
477 block[":host"] = headers.GetHeader("Host").as_string(); | |
478 block.erase("host"); | |
479 } | |
480 } | |
481 } | |
482 | |
483 DCHECK(buffered_spdy_framer_); | |
484 SpdyFrame* fsrcf = buffered_spdy_framer_->CreateSynStream( | |
485 stream_id, 0, 0, CONTROL_FLAG_NONE, &block); | |
486 size_t df_size = fsrcf->size(); | |
487 EnqueueDataFrame(new SpdyFrameDataFrame(fsrcf)); | |
488 | |
489 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Sending SynStreamheader " | |
490 << stream_id; | |
491 return df_size; | |
492 } | |
493 | |
494 size_t SpdySM::SendSynReplyImpl(uint32 stream_id, const BalsaHeaders& headers) { | |
495 SpdyHeaderBlock block; | |
496 CopyHeaders(block, headers); | |
497 if (spdy_version() == SPDY2) { | |
498 block["status"] = headers.response_code().as_string() + " " + | |
499 headers.response_reason_phrase().as_string(); | |
500 block["version"] = headers.response_version().as_string(); | |
501 } else { | |
502 block[":status"] = headers.response_code().as_string() + " " + | |
503 headers.response_reason_phrase().as_string(); | |
504 block[":version"] = headers.response_version().as_string(); | |
505 } | |
506 | |
507 DCHECK(buffered_spdy_framer_); | |
508 SpdyFrame* fsrcf = buffered_spdy_framer_->CreateSynReply( | |
509 stream_id, CONTROL_FLAG_NONE, &block); | |
510 size_t df_size = fsrcf->size(); | |
511 EnqueueDataFrame(new SpdyFrameDataFrame(fsrcf)); | |
512 | |
513 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Sending SynReplyheader " | |
514 << stream_id; | |
515 return df_size; | |
516 } | |
517 | |
518 void SpdySM::SendDataFrameImpl(uint32 stream_id, | |
519 const char* data, | |
520 int64 len, | |
521 SpdyDataFlags flags, | |
522 bool compress) { | |
523 DCHECK(buffered_spdy_framer_); | |
524 // TODO(mbelshe): We can't compress here - before going into the | |
525 // priority queue. Compression needs to be done | |
526 // with late binding. | |
527 if (len == 0) { | |
528 SpdyFrame* fdf = | |
529 buffered_spdy_framer_->CreateDataFrame(stream_id, data, len, flags); | |
530 EnqueueDataFrame(new SpdyFrameDataFrame(fdf)); | |
531 return; | |
532 } | |
533 | |
534 // Chop data frames into chunks so that one stream can't monopolize the | |
535 // output channel. | |
536 while (len > 0) { | |
537 int64 size = std::min(len, static_cast<int64>(kSpdySegmentSize)); | |
538 SpdyDataFlags chunk_flags = flags; | |
539 | |
540 // If we chunked this block, and the FIN flag was set, there is more | |
541 // data coming. So, remove the flag. | |
542 if ((size < len) && (flags & DATA_FLAG_FIN)) | |
543 chunk_flags = static_cast<SpdyDataFlags>(chunk_flags & ~DATA_FLAG_FIN); | |
544 | |
545 SpdyFrame* fdf = buffered_spdy_framer_->CreateDataFrame( | |
546 stream_id, data, size, chunk_flags); | |
547 EnqueueDataFrame(new SpdyFrameDataFrame(fdf)); | |
548 | |
549 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Sending data frame " | |
550 << stream_id << " [" << size << "] shrunk to " | |
551 << (fdf->size() - kSpdyOverhead) << ", flags=" << flags; | |
552 | |
553 data += size; | |
554 len -= size; | |
555 } | |
556 } | |
557 | |
558 void SpdySM::EnqueueDataFrame(DataFrame* df) { | |
559 connection_->EnqueueDataFrame(df); | |
560 } | |
561 | |
562 void SpdySM::GetOutput() { | |
563 while (client_output_list_->size() < 2) { | |
564 MemCacheIter* mci = client_output_ordering_.GetIter(); | |
565 if (mci == NULL) { | |
566 VLOG(2) << ACCEPTOR_CLIENT_IDENT | |
567 << "SpdySM: GetOutput: nothing to output!?"; | |
568 return; | |
569 } | |
570 if (!mci->transformed_header) { | |
571 mci->transformed_header = true; | |
572 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: GetOutput transformed " | |
573 << "header stream_id: [" << mci->stream_id << "]"; | |
574 if ((mci->stream_id % 2) == 0) { | |
575 // this is a server initiated stream. | |
576 // Ideally, we'd do a 'syn-push' here, instead of a syn-reply. | |
577 BalsaHeaders headers; | |
578 headers.CopyFrom(*(mci->file_data->headers())); | |
579 headers.ReplaceOrAppendHeader("status", "200"); | |
580 headers.ReplaceOrAppendHeader("version", "http/1.1"); | |
581 headers.SetRequestFirstlineFromStringPieces( | |
582 "PUSH", mci->file_data->filename(), ""); | |
583 mci->bytes_sent = SendSynStream(mci->stream_id, headers); | |
584 } else { | |
585 BalsaHeaders headers; | |
586 headers.CopyFrom(*(mci->file_data->headers())); | |
587 mci->bytes_sent = SendSynReply(mci->stream_id, headers); | |
588 } | |
589 return; | |
590 } | |
591 if (mci->body_bytes_consumed >= mci->file_data->body().size()) { | |
592 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: GetOutput " | |
593 << "remove_stream_id: [" << mci->stream_id << "]"; | |
594 SendEOF(mci->stream_id); | |
595 return; | |
596 } | |
597 size_t num_to_write = | |
598 mci->file_data->body().size() - mci->body_bytes_consumed; | |
599 if (num_to_write > mci->max_segment_size) | |
600 num_to_write = mci->max_segment_size; | |
601 | |
602 bool should_compress = false; | |
603 if (!mci->file_data->headers()->HasHeader("content-encoding")) { | |
604 if (mci->file_data->headers()->HasHeader("content-type")) { | |
605 std::string content_type = | |
606 mci->file_data->headers()->GetHeader("content-type").as_string(); | |
607 if (content_type.find("image") == content_type.npos) | |
608 should_compress = true; | |
609 } | |
610 } | |
611 | |
612 SendDataFrame(mci->stream_id, | |
613 mci->file_data->body().data() + mci->body_bytes_consumed, | |
614 num_to_write, | |
615 0, | |
616 should_compress); | |
617 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: GetOutput SendDataFrame[" | |
618 << mci->stream_id << "]: " << num_to_write; | |
619 mci->body_bytes_consumed += num_to_write; | |
620 mci->bytes_sent += num_to_write; | |
621 } | |
622 } | |
623 | |
624 void SpdySM::CreateFramer(SpdyMajorVersion spdy_version) { | |
625 DCHECK(!buffered_spdy_framer_); | |
626 buffered_spdy_framer_.reset(new BufferedSpdyFramer(spdy_version, true)); | |
627 buffered_spdy_framer_->set_visitor(this); | |
628 } | |
629 | |
630 } // namespace net | |
OLD | NEW |