By moving the call to Load() up in SearchProvider::Start(), we are giving a chance...
[chromium-blink-merge.git] / net / tools / flip_server / spdy_interface.cc
blobd6ea40aa6c730314ea39ae8e60d77fcaaf81179a
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/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"
17 namespace net {
19 // static
20 std::string SpdySM::forward_ip_header_;
22 class SpdyFrameDataFrame : public DataFrame {
23 public:
24 SpdyFrameDataFrame(SpdyFrame* spdy_frame)
25 : frame(spdy_frame) {
26 data = spdy_frame->data();
27 size = spdy_frame->length() + SpdyFrame::kHeaderSize;
30 virtual ~SpdyFrameDataFrame() {
31 delete frame;
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),
49 acceptor_(acceptor),
50 memory_cache_(memory_cache),
51 close_on_error_(false) {
52 buffered_spdy_framer_->set_visitor(this);
55 SpdySM::~SpdySM() {
56 delete buffered_spdy_framer_;
59 void SpdySM::InitSMConnection(SMConnectionPoolInterface* connection_pool,
60 SMInterface* sm_interface,
61 EpollServer* epoll_server,
62 int fd,
63 std::string server_ip,
64 std::string server_port,
65 std::string remote_ip,
66 bool use_ssl) {
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,
71 remote_ip, use_ssl);
74 SMInterface* SpdySM::NewConnectionInterface() {
75 SMConnection* server_connection =
76 SMConnection::NewSMConnection(epoll_server_,
77 NULL,
78 memory_cache_,
79 acceptor_,
80 "http_conn: ");
81 if (server_connection == NULL) {
82 LOG(ERROR) << "SpdySM: Could not create server connection";
83 return NULL;
85 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Creating new HTTP interface";
86 SMInterface *sm_http_interface = new HttpSM(server_connection,
87 this,
88 epoll_server_,
89 memory_cache_,
90 acceptor_);
91 return sm_http_interface;
94 SMInterface* SpdySM::FindOrMakeNewSMConnectionInterface(
95 std::string server_ip, std::string server_port) {
96 SMInterface *sm_http_interface;
97 int32 server_idx;
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: "
104 << server_idx;
105 } else {
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,
115 epoll_server_, -1,
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("
129 << stream_id << ")";
130 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: # headers: "
131 << headers.size();
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";
138 return 0;
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.
150 std::string uri;
151 if (url->second.compare(0,4,"http") == 0)
152 uri = UrlUtilities::GetUrlPath(url->second);
153 else
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
158 << " " << uri;
159 std::string filename = EncodeURL(uri, host, method->second);
160 NewStream(stream_id, priority, filename);
161 } else {
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";
177 http_data += "\r\n";
180 VLOG(3) << ACCEPTOR_CLIENT_IDENT << "SpdySM: HTTP Request:\n" << http_data;
181 return 1;
184 void SpdySM::OnStreamFrameData(SpdyStreamId stream_id,
185 const char* data,
186 size_t len,
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;
195 return;
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,
207 bool fin,
208 bool unidirectional,
209 const SpdyHeaderBlock& headers) {
210 std::string http_data;
211 bool is_https_scheme;
212 int ret = SpdyHandleNewStream(stream_id, priority, headers, http_data,
213 &is_https_scheme);
214 if (!ret) {
215 LOG(ERROR) << "SpdySM: Could not convert spdy into http.";
216 return;
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_;
228 } else {
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(),
237 http_data.size());
241 void SpdySM::OnSynReply(SpdyStreamId stream_id,
242 bool fin,
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("
247 << stream_id << ")";
250 void SpdySM::OnHeaders(SpdyStreamId stream_id,
251 bool fin,
252 const SpdyHeaderBlock& headers) {
253 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: OnHeaders("
254 << stream_id << ")";
257 void SpdySM::OnRstStream(SpdyStreamId stream_id,
258 SpdyStatusCodes status) {
259 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: OnRstStream("
260 << stream_id << ")";
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) {
269 return 0;
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 {
281 DCHECK(Error());
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));
311 return 1;
314 void SpdySM::NewStream(uint32 stream_id,
315 uint32 priority,
316 const std::string& filename) {
317 MemCacheIter mci;
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);
325 } else {
326 AddToOutputOrder(mci);
328 } else {
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);
382 SendDataFrame(
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();
395 ++hi) {
396 // It is illegal to send SPDY headers with empty value or header
397 // names.
398 if (!hi->first.length() || !hi->second.length())
399 continue;
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();
407 } else {
408 dest[key] = (
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);
430 } else {
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 "
441 << stream_id;
442 return df_size;
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 "
458 << stream_id;
459 return df_size;
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.
467 if (len == 0) {
468 SpdyDataFrame* fdf = buffered_spdy_framer_->CreateDataFrame(
469 stream_id, data, len, flags);
470 EnqueueDataFrame(new SpdyFrameDataFrame(fdf));
471 return;
474 // Chop data frames into chunks so that one stream can't monopolize the
475 // output channel.
476 while (len > 0) {
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;
493 data += size;
494 len -= size;
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();
505 if (mci == NULL) {
506 VLOG(2) << ACCEPTOR_CLIENT_IDENT
507 << "SpdySM: GetOutput: nothing to output!?";
508 return;
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,
523 "");
524 mci->bytes_sent = SendSynStream(mci->stream_id, headers);
525 } else {
526 BalsaHeaders headers;
527 headers.CopyFrom(*(mci->file_data->headers));
528 mci->bytes_sent = SendSynReply(mci->stream_id, headers);
530 return;
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);
536 return;
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;
563 } // namespace net