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