- add sources.
[platform/framework/web/crosswalk.git] / src / net / tools / flip_server / spdy_interface.cc
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
18 namespace net {
19
20 // static
21 std::string SpdySM::forward_ip_header_;
22
23 class SpdyFrameDataFrame : public DataFrame {
24  public:
25   explicit 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
38 SpdySM::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(SPDY2, 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
56 SpdySM::~SpdySM() {
57   delete buffered_spdy_framer_;
58 }
59
60 void 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
75 SMInterface* 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                                               memory_cache_,
90                                               acceptor_);
91   return sm_http_interface;
92 }
93
94 SMInterface* SpdySM::FindOrMakeNewSMConnectionInterface(
95     const std::string& server_ip,
96     const 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
127 int 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
190 void 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
209 void 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
247 void 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
256 void 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
263 void 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
270 size_t SpdySM::ProcessReadInput(const char* data, size_t len) {
271   return buffered_spdy_framer_->ProcessInput(data, len);
272 }
273
274 size_t SpdySM::ProcessWriteInput(const char* data, size_t len) {
275   return 0;
276 }
277
278 bool SpdySM::MessageFullyRead() const {
279   return buffered_spdy_framer_->MessageFullyRead();
280 }
281
282 bool SpdySM::Error() const {
283   return close_on_error_ || buffered_spdy_framer_->HasError();
284 }
285
286 const char* SpdySM::ErrorAsString() const {
287   DCHECK(Error());
288   return SpdyFramer::ErrorCodeToString(buffered_spdy_framer_->error_code());
289 }
290
291 void 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
297 void SpdySM::ResetForNewConnection() {
298   // seq_num is not cleared, intentionally.
299   delete buffered_spdy_framer_;
300   buffered_spdy_framer_ = new BufferedSpdyFramer(SPDY2, 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
308 int 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
320 void 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   // TODO(yhirano): The program will crash when
327   // acceptor_->flip_handler_type_ != FLIP_HANDLER_SPDY_SERVER.
328   // It should be fixed or an assertion should be placed.
329   if (acceptor_->flip_handler_type_ == FLIP_HANDLER_SPDY_SERVER) {
330     if (!memory_cache_->AssignFileData(filename, &mci)) {
331       // error creating new stream.
332       VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Sending ErrorNotFound";
333       SendErrorNotFound(stream_id);
334     } else {
335       AddToOutputOrder(mci);
336     }
337   } else {
338     AddToOutputOrder(mci);
339   }
340 }
341
342 void SpdySM::AddToOutputOrder(const MemCacheIter& mci) {
343   client_output_ordering_.AddToOutputOrder(mci);
344 }
345
346 void SpdySM::SendEOF(uint32 stream_id) {
347   SendEOFImpl(stream_id);
348 }
349
350 void SpdySM::SendErrorNotFound(uint32 stream_id) {
351   SendErrorNotFoundImpl(stream_id);
352 }
353
354 size_t SpdySM::SendSynStream(uint32 stream_id, const BalsaHeaders& headers) {
355   return SendSynStreamImpl(stream_id, headers);
356 }
357
358 size_t SpdySM::SendSynReply(uint32 stream_id, const BalsaHeaders& headers) {
359   return SendSynReplyImpl(stream_id, headers);
360 }
361
362 void SpdySM::SendDataFrame(uint32 stream_id, const char* data, int64 len,
363                    uint32 flags, bool compress) {
364   SpdyDataFlags spdy_flags = static_cast<SpdyDataFlags>(flags);
365   SendDataFrameImpl(stream_id, data, len, spdy_flags, compress);
366 }
367
368 void SpdySM::SendEOFImpl(uint32 stream_id) {
369   SendDataFrame(stream_id, NULL, 0, DATA_FLAG_FIN, false);
370   VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Sending EOF: " << stream_id;
371   KillStream(stream_id);
372   stream_to_smif_.erase(stream_id);
373 }
374
375 void SpdySM::SendErrorNotFoundImpl(uint32 stream_id) {
376   BalsaHeaders my_headers;
377   my_headers.SetFirstlineFromStringPieces("HTTP/1.1", "404", "Not Found");
378   SendSynReplyImpl(stream_id, my_headers);
379   SendDataFrame(stream_id, "wtf?", 4, DATA_FLAG_FIN, false);
380   client_output_ordering_.RemoveStreamId(stream_id);
381 }
382
383 void SpdySM::KillStream(uint32 stream_id) {
384   client_output_ordering_.RemoveStreamId(stream_id);
385 }
386
387 void SpdySM::CopyHeaders(SpdyHeaderBlock& dest, const BalsaHeaders& headers) {
388   for (BalsaHeaders::const_header_lines_iterator hi =
389        headers.header_lines_begin();
390        hi != headers.header_lines_end();
391        ++hi) {
392     // It is illegal to send SPDY headers with empty value or header
393     // names.
394     if (!hi->first.length() || !hi->second.length())
395       continue;
396
397     // Key must be all lower case in SPDY headers.
398     std::string key = hi->first.as_string();
399     std::transform(key.begin(), key.end(), key.begin(), ::tolower);
400     SpdyHeaderBlock::iterator fhi = dest.find(key);
401     if (fhi == dest.end()) {
402       dest[key] = hi->second.as_string();
403     } else {
404       dest[key] = (
405           std::string(fhi->second.data(), fhi->second.size()) + "\0" +
406           std::string(hi->second.data(), hi->second.size()));
407     }
408   }
409
410   // These headers have no value
411   dest.erase("X-Associated-Content");  // TODO(mbelshe): case-sensitive
412   dest.erase("X-Original-Url");  // TODO(mbelshe): case-sensitive
413 }
414
415 size_t SpdySM::SendSynStreamImpl(uint32 stream_id,
416                                  const BalsaHeaders& headers) {
417   SpdyHeaderBlock block;
418   block["method"] = headers.request_method().as_string();
419   if (!headers.HasHeader("version"))
420     block["version"] =headers.request_version().as_string();
421   if (headers.HasHeader("X-Original-Url")) {
422     std::string original_url = headers.GetHeader("X-Original-Url").as_string();
423     block["url"] = UrlUtilities::GetUrlPath(original_url);
424   } else {
425     block["url"] = headers.request_uri().as_string();
426   }
427   CopyHeaders(block, headers);
428
429   SpdyFrame* fsrcf = buffered_spdy_framer_->CreateSynStream(
430       stream_id, 0, 0, 0, CONTROL_FLAG_NONE, true, &block);
431   size_t df_size = fsrcf->size();
432   EnqueueDataFrame(new SpdyFrameDataFrame(fsrcf));
433
434   VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Sending SynStreamheader "
435           << stream_id;
436   return df_size;
437 }
438
439 size_t SpdySM::SendSynReplyImpl(uint32 stream_id, const BalsaHeaders& headers) {
440   SpdyHeaderBlock block;
441   CopyHeaders(block, headers);
442   block["status"] = headers.response_code().as_string() + " " +
443                     headers.response_reason_phrase().as_string();
444   block["version"] = headers.response_version().as_string();
445
446   SpdyFrame* fsrcf = buffered_spdy_framer_->CreateSynReply(
447       stream_id, CONTROL_FLAG_NONE, true, &block);
448   size_t df_size = fsrcf->size();
449   EnqueueDataFrame(new SpdyFrameDataFrame(fsrcf));
450
451   VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Sending SynReplyheader "
452           << stream_id;
453   return df_size;
454 }
455
456 void SpdySM::SendDataFrameImpl(uint32 stream_id, const char* data, int64 len,
457                        SpdyDataFlags flags, bool compress) {
458   // TODO(mbelshe):  We can't compress here - before going into the
459   //                 priority queue.  Compression needs to be done
460   //                 with late binding.
461   if (len == 0) {
462     SpdyFrame* fdf = buffered_spdy_framer_->CreateDataFrame(
463         stream_id, data, len, flags);
464     EnqueueDataFrame(new SpdyFrameDataFrame(fdf));
465     return;
466   }
467
468   // Chop data frames into chunks so that one stream can't monopolize the
469   // output channel.
470   while (len > 0) {
471     int64 size = std::min(len, static_cast<int64>(kSpdySegmentSize));
472     SpdyDataFlags chunk_flags = flags;
473
474     // If we chunked this block, and the FIN flag was set, there is more
475     // data coming.  So, remove the flag.
476     if ((size < len) && (flags & DATA_FLAG_FIN))
477       chunk_flags = static_cast<SpdyDataFlags>(chunk_flags & ~DATA_FLAG_FIN);
478
479     SpdyFrame* fdf = buffered_spdy_framer_->CreateDataFrame(
480         stream_id, data, size, chunk_flags);
481     EnqueueDataFrame(new SpdyFrameDataFrame(fdf));
482
483     VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Sending data frame "
484             << stream_id << " [" << size << "] shrunk to "
485             << (fdf->size() - kSpdyOverhead) << ", flags=" << flags;
486
487     data += size;
488     len -= size;
489   }
490 }
491
492 void SpdySM::EnqueueDataFrame(DataFrame* df) {
493   connection_->EnqueueDataFrame(df);
494 }
495
496 void SpdySM::GetOutput() {
497   while (client_output_list_->size() < 2) {
498     MemCacheIter* mci = client_output_ordering_.GetIter();
499     if (mci == NULL) {
500       VLOG(2) << ACCEPTOR_CLIENT_IDENT
501               << "SpdySM: GetOutput: nothing to output!?";
502       return;
503     }
504     if (!mci->transformed_header) {
505       mci->transformed_header = true;
506       VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: GetOutput transformed "
507               << "header stream_id: [" << mci->stream_id << "]";
508       if ((mci->stream_id % 2) == 0) {
509         // this is a server initiated stream.
510         // Ideally, we'd do a 'syn-push' here, instead of a syn-reply.
511         BalsaHeaders headers;
512         headers.CopyFrom(*(mci->file_data->headers()));
513         headers.ReplaceOrAppendHeader("status", "200");
514         headers.ReplaceOrAppendHeader("version", "http/1.1");
515         headers.SetRequestFirstlineFromStringPieces("PUSH",
516                                                     mci->file_data->filename(),
517                                                     "");
518         mci->bytes_sent = SendSynStream(mci->stream_id, headers);
519       } else {
520         BalsaHeaders headers;
521         headers.CopyFrom(*(mci->file_data->headers()));
522         mci->bytes_sent = SendSynReply(mci->stream_id, headers);
523       }
524       return;
525     }
526     if (mci->body_bytes_consumed >= mci->file_data->body().size()) {
527       VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: GetOutput "
528               << "remove_stream_id: [" << mci->stream_id << "]";
529       SendEOF(mci->stream_id);
530       return;
531     }
532     size_t num_to_write =
533         mci->file_data->body().size() - mci->body_bytes_consumed;
534     if (num_to_write > mci->max_segment_size)
535       num_to_write = mci->max_segment_size;
536
537     bool should_compress = false;
538     if (!mci->file_data->headers()->HasHeader("content-encoding")) {
539       if (mci->file_data->headers()->HasHeader("content-type")) {
540         std::string content_type =
541             mci->file_data->headers()->GetHeader("content-type").as_string();
542         if (content_type.find("image") == content_type.npos)
543           should_compress = true;
544       }
545     }
546
547     SendDataFrame(mci->stream_id,
548                   mci->file_data->body().data() + mci->body_bytes_consumed,
549                   num_to_write, 0, should_compress);
550     VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: GetOutput SendDataFrame["
551             << mci->stream_id << "]: " << num_to_write;
552     mci->body_bytes_consumed += num_to_write;
553     mci->bytes_sent += num_to_write;
554   }
555 }
556
557 }  // namespace net