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