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