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"
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/flip_config.h"
14 #include "net/tools/flip_server/http_interface.h"
15 #include "net/tools/flip_server/spdy_util.h"
20 std::string
SpdySM::forward_ip_header_
;
22 class SpdyFrameDataFrame
: public DataFrame
{
24 SpdyFrameDataFrame(SpdyFrame
* spdy_frame
)
26 data
= spdy_frame
->data();
27 size
= spdy_frame
->length() + SpdyFrame::kHeaderSize
;
30 virtual ~SpdyFrameDataFrame() {
34 const SpdyFrame
* frame
;
37 SpdySM::SpdySM(SMConnection
* connection
,
38 SMInterface
* sm_http_interface
,
39 EpollServer
* epoll_server
,
40 MemoryCache
* memory_cache
,
41 FlipAcceptor
* acceptor
)
42 : buffered_spdy_framer_(new BufferedSpdyFramer(2, true)),
43 valid_spdy_session_(false),
44 connection_(connection
),
45 client_output_list_(connection
->output_list()),
46 client_output_ordering_(connection
),
47 next_outgoing_stream_id_(2),
48 epoll_server_(epoll_server
),
50 memory_cache_(memory_cache
),
51 close_on_error_(false) {
52 buffered_spdy_framer_
->set_visitor(this);
56 delete buffered_spdy_framer_
;
59 void SpdySM::InitSMConnection(SMConnectionPoolInterface
* connection_pool
,
60 SMInterface
* sm_interface
,
61 EpollServer
* epoll_server
,
63 std::string server_ip
,
64 std::string server_port
,
65 std::string remote_ip
,
67 VLOG(2) << ACCEPTOR_CLIENT_IDENT
68 << "SpdySM: Initializing server connection.";
69 connection_
->InitSMConnection(connection_pool
, sm_interface
,
70 epoll_server
, fd
, server_ip
, server_port
,
74 SMInterface
* SpdySM::NewConnectionInterface() {
75 SMConnection
* server_connection
=
76 SMConnection::NewSMConnection(epoll_server_
,
81 if (server_connection
== NULL
) {
82 LOG(ERROR
) << "SpdySM: Could not create server connection";
85 VLOG(2) << ACCEPTOR_CLIENT_IDENT
<< "SpdySM: Creating new HTTP interface";
86 SMInterface
*sm_http_interface
= new HttpSM(server_connection
,
91 return sm_http_interface
;
94 SMInterface
* SpdySM::FindOrMakeNewSMConnectionInterface(
95 std::string server_ip
, std::string server_port
) {
96 SMInterface
*sm_http_interface
;
98 if (unused_server_interface_list
.empty()) {
99 sm_http_interface
= NewConnectionInterface();
100 server_idx
= server_interface_list
.size();
101 server_interface_list
.push_back(sm_http_interface
);
102 VLOG(2) << ACCEPTOR_CLIENT_IDENT
103 << "SpdySM: Making new server connection on index: "
106 server_idx
= unused_server_interface_list
.back();
107 unused_server_interface_list
.pop_back();
108 sm_http_interface
= server_interface_list
.at(server_idx
);
109 VLOG(2) << ACCEPTOR_CLIENT_IDENT
<< "SpdySM: Reusing connection on "
110 << "index: " << server_idx
;
113 sm_http_interface
->InitSMInterface(this, server_idx
);
114 sm_http_interface
->InitSMConnection(NULL
, sm_http_interface
,
116 server_ip
, server_port
, "", false);
118 return sm_http_interface
;
121 int SpdySM::SpdyHandleNewStream(
122 SpdyStreamId stream_id
,
123 SpdyPriority priority
,
124 const SpdyHeaderBlock
& headers
,
125 std::string
&http_data
,
126 bool* is_https_scheme
) {
127 *is_https_scheme
= false;
128 VLOG(2) << ACCEPTOR_CLIENT_IDENT
<< "SpdySM: OnSyn("
130 VLOG(2) << ACCEPTOR_CLIENT_IDENT
<< "SpdySM: # headers: "
133 SpdyHeaderBlock::const_iterator url
= headers
.find("url");
134 SpdyHeaderBlock::const_iterator method
= headers
.find("method");
135 if (url
== headers
.end() || method
== headers
.end()) {
136 VLOG(2) << ACCEPTOR_CLIENT_IDENT
<< "SpdySM: didn't find method or url "
137 << "or method. Not creating stream";
141 SpdyHeaderBlock::const_iterator scheme
= headers
.find("scheme");
142 if (scheme
->second
.compare("https") == 0) {
143 *is_https_scheme
= true;
146 // url->second here only ever seems to contain just the path. When this
147 // path contains a query string with a http:// in one of its values,
148 // UrlUtilities::GetUrlPath will fail and always return a / breaking
149 // the request. GetUrlPath assumes the absolute URL is being passed in.
151 if (url
->second
.compare(0,4,"http") == 0)
152 uri
= UrlUtilities::GetUrlPath(url
->second
);
154 uri
= std::string(url
->second
);
155 if (acceptor_
->flip_handler_type_
== FLIP_HANDLER_SPDY_SERVER
) {
156 std::string host
= UrlUtilities::GetUrlHost(url
->second
);
157 VLOG(1) << ACCEPTOR_CLIENT_IDENT
<< "Request: " << method
->second
159 std::string filename
= EncodeURL(uri
, host
, method
->second
);
160 NewStream(stream_id
, priority
, filename
);
162 SpdyHeaderBlock::const_iterator version
= headers
.find("version");
163 http_data
+= method
->second
+ " " + uri
+ " " + version
->second
+ "\r\n";
164 VLOG(1) << ACCEPTOR_CLIENT_IDENT
<< "Request: " << method
->second
<< " "
165 << uri
<< " " << version
->second
;
166 for (SpdyHeaderBlock::const_iterator i
= headers
.begin();
167 i
!= headers
.end(); ++i
) {
168 http_data
+= i
->first
+ ": " + i
->second
+ "\r\n";
169 VLOG(2) << ACCEPTOR_CLIENT_IDENT
<< i
->first
.c_str() << ":"
170 << i
->second
.c_str();
172 if (forward_ip_header_
.length()) {
173 // X-Client-Cluster-IP header
174 http_data
+= forward_ip_header_
+ ": " +
175 connection_
->client_ip() + "\r\n";
180 VLOG(3) << ACCEPTOR_CLIENT_IDENT
<< "SpdySM: HTTP Request:\n" << http_data
;
184 void SpdySM::OnStreamFrameData(SpdyStreamId stream_id
,
187 SpdyDataFlags flags
) {
188 VLOG(2) << ACCEPTOR_CLIENT_IDENT
<< "SpdySM: StreamData(" << stream_id
189 << ", [" << len
<< "])";
190 StreamToSmif::iterator it
= stream_to_smif_
.find(stream_id
);
191 if (it
== stream_to_smif_
.end()) {
192 VLOG(2) << "Dropping frame from unknown stream " << stream_id
;
193 if (!valid_spdy_session_
)
194 close_on_error_
= true;
198 SMInterface
* interface
= it
->second
;
199 if (acceptor_
->flip_handler_type_
== FLIP_HANDLER_PROXY
)
200 interface
->ProcessWriteInput(data
, len
);
203 void SpdySM::OnSynStream(SpdyStreamId stream_id
,
204 SpdyStreamId associated_stream_id
,
205 SpdyPriority priority
,
206 uint8 credential_slot
,
209 const SpdyHeaderBlock
& headers
) {
210 std::string http_data
;
211 bool is_https_scheme
;
212 int ret
= SpdyHandleNewStream(stream_id
, priority
, headers
, http_data
,
215 LOG(ERROR
) << "SpdySM: Could not convert spdy into http.";
218 // We've seen a valid looking SYN_STREAM, consider this to have
219 // been a real spdy session.
220 valid_spdy_session_
= true;
222 if (acceptor_
->flip_handler_type_
== FLIP_HANDLER_PROXY
) {
223 std::string server_ip
;
224 std::string server_port
;
225 if (is_https_scheme
) {
226 server_ip
= acceptor_
->https_server_ip_
;
227 server_port
= acceptor_
->https_server_port_
;
229 server_ip
= acceptor_
->http_server_ip_
;
230 server_port
= acceptor_
->http_server_port_
;
232 SMInterface
* sm_http_interface
=
233 FindOrMakeNewSMConnectionInterface(server_ip
, server_port
);
234 stream_to_smif_
[stream_id
] = sm_http_interface
;
235 sm_http_interface
->SetStreamID(stream_id
);
236 sm_http_interface
->ProcessWriteInput(http_data
.c_str(),
241 void SpdySM::OnSynReply(SpdyStreamId stream_id
,
243 const SpdyHeaderBlock
& headers
) {
244 // TODO(willchan): if there is an error parsing headers, we
245 // should send a RST_STREAM.
246 VLOG(2) << ACCEPTOR_CLIENT_IDENT
<< "SpdySM: OnSynReply("
250 void SpdySM::OnHeaders(SpdyStreamId stream_id
,
252 const SpdyHeaderBlock
& headers
) {
253 VLOG(2) << ACCEPTOR_CLIENT_IDENT
<< "SpdySM: OnHeaders("
257 void SpdySM::OnRstStream(SpdyStreamId stream_id
,
258 SpdyStatusCodes status
) {
259 VLOG(2) << ACCEPTOR_CLIENT_IDENT
<< "SpdySM: OnRstStream("
261 client_output_ordering_
.RemoveStreamId(stream_id
);
264 size_t SpdySM::ProcessReadInput(const char* data
, size_t len
) {
265 return buffered_spdy_framer_
->ProcessInput(data
, len
);
268 size_t SpdySM::ProcessWriteInput(const char* data
, size_t len
) {
272 bool SpdySM::MessageFullyRead() const {
273 return buffered_spdy_framer_
->MessageFullyRead();
276 bool SpdySM::Error() const {
277 return close_on_error_
|| buffered_spdy_framer_
->HasError();
280 const char* SpdySM::ErrorAsString() const {
282 return SpdyFramer::ErrorCodeToString(buffered_spdy_framer_
->error_code());
285 void SpdySM::ResetForNewInterface(int32 server_idx
) {
286 VLOG(2) << ACCEPTOR_CLIENT_IDENT
<< "SpdySM: Reset for new interface: "
287 << "server_idx: " << server_idx
;
288 unused_server_interface_list
.push_back(server_idx
);
291 void SpdySM::ResetForNewConnection() {
292 // seq_num is not cleared, intentionally.
293 delete buffered_spdy_framer_
;
294 buffered_spdy_framer_
= new BufferedSpdyFramer(2, true);
295 buffered_spdy_framer_
->set_visitor(this);
296 valid_spdy_session_
= false;
297 client_output_ordering_
.Reset();
298 next_outgoing_stream_id_
= 2;
301 // Send a settings frame
302 int SpdySM::PostAcceptHook() {
303 SettingsMap settings
;
304 settings
[SETTINGS_MAX_CONCURRENT_STREAMS
] =
305 SettingsFlagsAndValue(SETTINGS_FLAG_NONE
, 100);
306 SpdySettingsControlFrame
* settings_frame
=
307 buffered_spdy_framer_
->CreateSettings(settings
);
309 VLOG(1) << ACCEPTOR_CLIENT_IDENT
<< "Sending Settings Frame";
310 EnqueueDataFrame(new SpdyFrameDataFrame(settings_frame
));
314 void SpdySM::NewStream(uint32 stream_id
,
316 const std::string
& filename
) {
318 mci
.stream_id
= stream_id
;
319 mci
.priority
= priority
;
320 if (acceptor_
->flip_handler_type_
== FLIP_HANDLER_SPDY_SERVER
) {
321 if (!memory_cache_
->AssignFileData(filename
, &mci
)) {
322 // error creating new stream.
323 VLOG(1) << ACCEPTOR_CLIENT_IDENT
<< "Sending ErrorNotFound";
324 SendErrorNotFound(stream_id
);
326 AddToOutputOrder(mci
);
329 AddToOutputOrder(mci
);
333 void SpdySM::AddToOutputOrder(const MemCacheIter
& mci
) {
334 client_output_ordering_
.AddToOutputOrder(mci
);
337 void SpdySM::SendEOF(uint32 stream_id
) {
338 SendEOFImpl(stream_id
);
341 void SpdySM::SendErrorNotFound(uint32 stream_id
) {
342 SendErrorNotFoundImpl(stream_id
);
345 void SpdySM::SendOKResponse(uint32 stream_id
, std::string
* output
) {
346 SendOKResponseImpl(stream_id
, output
);
349 size_t SpdySM::SendSynStream(uint32 stream_id
, const BalsaHeaders
& headers
) {
350 return SendSynStreamImpl(stream_id
, headers
);
353 size_t SpdySM::SendSynReply(uint32 stream_id
, const BalsaHeaders
& headers
) {
354 return SendSynReplyImpl(stream_id
, headers
);
357 void SpdySM::SendDataFrame(uint32 stream_id
, const char* data
, int64 len
,
358 uint32 flags
, bool compress
) {
359 SpdyDataFlags spdy_flags
= static_cast<SpdyDataFlags
>(flags
);
360 SendDataFrameImpl(stream_id
, data
, len
, spdy_flags
, compress
);
363 void SpdySM::SendEOFImpl(uint32 stream_id
) {
364 SendDataFrame(stream_id
, NULL
, 0, DATA_FLAG_FIN
, false);
365 VLOG(2) << ACCEPTOR_CLIENT_IDENT
<< "SpdySM: Sending EOF: " << stream_id
;
366 KillStream(stream_id
);
367 stream_to_smif_
.erase(stream_id
);
370 void SpdySM::SendErrorNotFoundImpl(uint32 stream_id
) {
371 BalsaHeaders my_headers
;
372 my_headers
.SetFirstlineFromStringPieces("HTTP/1.1", "404", "Not Found");
373 SendSynReplyImpl(stream_id
, my_headers
);
374 SendDataFrame(stream_id
, "wtf?", 4, DATA_FLAG_FIN
, false);
375 client_output_ordering_
.RemoveStreamId(stream_id
);
378 void SpdySM::SendOKResponseImpl(uint32 stream_id
, std::string
* output
) {
379 BalsaHeaders my_headers
;
380 my_headers
.SetFirstlineFromStringPieces("HTTP/1.1", "200", "OK");
381 SendSynReplyImpl(stream_id
, my_headers
);
383 stream_id
, output
->c_str(), output
->size(), DATA_FLAG_FIN
, false);
384 client_output_ordering_
.RemoveStreamId(stream_id
);
387 void SpdySM::KillStream(uint32 stream_id
) {
388 client_output_ordering_
.RemoveStreamId(stream_id
);
391 void SpdySM::CopyHeaders(SpdyHeaderBlock
& dest
, const BalsaHeaders
& headers
) {
392 for (BalsaHeaders::const_header_lines_iterator hi
=
393 headers
.header_lines_begin();
394 hi
!= headers
.header_lines_end();
396 // It is illegal to send SPDY headers with empty value or header
398 if (!hi
->first
.length() || !hi
->second
.length())
401 // Key must be all lower case in SPDY headers.
402 std::string key
= hi
->first
.as_string();
403 std::transform(key
.begin(), key
.end(), key
.begin(), ::tolower
);
404 SpdyHeaderBlock::iterator fhi
= dest
.find(key
);
405 if (fhi
== dest
.end()) {
406 dest
[key
] = hi
->second
.as_string();
409 std::string(fhi
->second
.data(), fhi
->second
.size()) + "\0" +
410 std::string(hi
->second
.data(), hi
->second
.size()));
414 // These headers have no value
415 dest
.erase("X-Associated-Content"); // TODO(mbelshe): case-sensitive
416 dest
.erase("X-Original-Url"); // TODO(mbelshe): case-sensitive
419 size_t SpdySM::SendSynStreamImpl(uint32 stream_id
,
420 const BalsaHeaders
& headers
) {
421 SpdyHeaderBlock block
;
422 block
["method"] = headers
.request_method().as_string();
423 if (!headers
.HasHeader("status"))
424 block
["status"] = headers
.response_code().as_string();
425 if (!headers
.HasHeader("version"))
426 block
["version"] =headers
.response_version().as_string();
427 if (headers
.HasHeader("X-Original-Url")) {
428 std::string original_url
= headers
.GetHeader("X-Original-Url").as_string();
429 block
["path"] = UrlUtilities::GetUrlPath(original_url
);
431 block
["path"] = headers
.request_uri().as_string();
433 CopyHeaders(block
, headers
);
435 SpdySynStreamControlFrame
* fsrcf
= buffered_spdy_framer_
->CreateSynStream(
436 stream_id
, 0, 0, 0, CONTROL_FLAG_NONE
, true, &block
);
437 size_t df_size
= fsrcf
->length() + SpdyFrame::kHeaderSize
;
438 EnqueueDataFrame(new SpdyFrameDataFrame(fsrcf
));
440 VLOG(2) << ACCEPTOR_CLIENT_IDENT
<< "SpdySM: Sending SynStreamheader "
445 size_t SpdySM::SendSynReplyImpl(uint32 stream_id
, const BalsaHeaders
& headers
) {
446 SpdyHeaderBlock block
;
447 CopyHeaders(block
, headers
);
448 block
["status"] = headers
.response_code().as_string() + " " +
449 headers
.response_reason_phrase().as_string();
450 block
["version"] = headers
.response_version().as_string();
452 SpdySynReplyControlFrame
* fsrcf
= buffered_spdy_framer_
->CreateSynReply(
453 stream_id
, CONTROL_FLAG_NONE
, true, &block
);
454 size_t df_size
= fsrcf
->length() + SpdyFrame::kHeaderSize
;
455 EnqueueDataFrame(new SpdyFrameDataFrame(fsrcf
));
457 VLOG(2) << ACCEPTOR_CLIENT_IDENT
<< "SpdySM: Sending SynReplyheader "
462 void SpdySM::SendDataFrameImpl(uint32 stream_id
, const char* data
, int64 len
,
463 SpdyDataFlags flags
, bool compress
) {
464 // TODO(mbelshe): We can't compress here - before going into the
465 // priority queue. Compression needs to be done
466 // with late binding.
468 SpdyDataFrame
* fdf
= buffered_spdy_framer_
->CreateDataFrame(
469 stream_id
, data
, len
, flags
);
470 EnqueueDataFrame(new SpdyFrameDataFrame(fdf
));
474 // Chop data frames into chunks so that one stream can't monopolize the
477 int64 size
= std::min(len
, static_cast<int64
>(kSpdySegmentSize
));
478 SpdyDataFlags chunk_flags
= flags
;
480 // If we chunked this block, and the FIN flag was set, there is more
481 // data coming. So, remove the flag.
482 if ((size
< len
) && (flags
& DATA_FLAG_FIN
))
483 chunk_flags
= static_cast<SpdyDataFlags
>(chunk_flags
& ~DATA_FLAG_FIN
);
485 SpdyDataFrame
* fdf
= buffered_spdy_framer_
->CreateDataFrame(
486 stream_id
, data
, size
, chunk_flags
);
487 EnqueueDataFrame(new SpdyFrameDataFrame(fdf
));
489 VLOG(2) << ACCEPTOR_CLIENT_IDENT
<< "SpdySM: Sending data frame "
490 << stream_id
<< " [" << size
<< "] shrunk to " << fdf
->length()
491 << ", flags=" << flags
;
498 void SpdySM::EnqueueDataFrame(DataFrame
* df
) {
499 connection_
->EnqueueDataFrame(df
);
502 void SpdySM::GetOutput() {
503 while (client_output_list_
->size() < 2) {
504 MemCacheIter
* mci
= client_output_ordering_
.GetIter();
506 VLOG(2) << ACCEPTOR_CLIENT_IDENT
507 << "SpdySM: GetOutput: nothing to output!?";
510 if (!mci
->transformed_header
) {
511 mci
->transformed_header
= true;
512 VLOG(2) << ACCEPTOR_CLIENT_IDENT
<< "SpdySM: GetOutput transformed "
513 << "header stream_id: [" << mci
->stream_id
<< "]";
514 if ((mci
->stream_id
% 2) == 0) {
515 // this is a server initiated stream.
516 // Ideally, we'd do a 'syn-push' here, instead of a syn-reply.
517 BalsaHeaders headers
;
518 headers
.CopyFrom(*(mci
->file_data
->headers
));
519 headers
.ReplaceOrAppendHeader("status", "200");
520 headers
.ReplaceOrAppendHeader("version", "http/1.1");
521 headers
.SetRequestFirstlineFromStringPieces("PUSH",
522 mci
->file_data
->filename
,
524 mci
->bytes_sent
= SendSynStream(mci
->stream_id
, headers
);
526 BalsaHeaders headers
;
527 headers
.CopyFrom(*(mci
->file_data
->headers
));
528 mci
->bytes_sent
= SendSynReply(mci
->stream_id
, headers
);
532 if (mci
->body_bytes_consumed
>= mci
->file_data
->body
.size()) {
533 VLOG(2) << ACCEPTOR_CLIENT_IDENT
<< "SpdySM: GetOutput "
534 << "remove_stream_id: [" << mci
->stream_id
<< "]";
535 SendEOF(mci
->stream_id
);
538 size_t num_to_write
=
539 mci
->file_data
->body
.size() - mci
->body_bytes_consumed
;
540 if (num_to_write
> mci
->max_segment_size
)
541 num_to_write
= mci
->max_segment_size
;
543 bool should_compress
= false;
544 if (!mci
->file_data
->headers
->HasHeader("content-encoding")) {
545 if (mci
->file_data
->headers
->HasHeader("content-type")) {
546 std::string content_type
=
547 mci
->file_data
->headers
->GetHeader("content-type").as_string();
548 if (content_type
.find("image") == content_type
.npos
)
549 should_compress
= true;
553 SendDataFrame(mci
->stream_id
,
554 mci
->file_data
->body
.data() + mci
->body_bytes_consumed
,
555 num_to_write
, 0, should_compress
);
556 VLOG(2) << ACCEPTOR_CLIENT_IDENT
<< "SpdySM: GetOutput SendDataFrame["
557 << mci
->stream_id
<< "]: " << num_to_write
;
558 mci
->body_bytes_consumed
+= num_to_write
;
559 mci
->bytes_sent
+= num_to_write
;