Clone of chromium aad1ce808763f59c7a3753e08f1500a104ecc6fd refs/remotes/origin/HEAD
diff --git a/net/tools/flip_server/acceptor_thread.cc b/net/tools/flip_server/acceptor_thread.cc
new file mode 100644
index 0000000..2b65e5d
--- /dev/null
+++ b/net/tools/flip_server/acceptor_thread.cc
@@ -0,0 +1,212 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/tools/flip_server/acceptor_thread.h"
+
+#include <errno.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>  // For TCP_NODELAY
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <string>
+
+#include "net/tools/flip_server/constants.h"
+#include "net/tools/flip_server/flip_config.h"
+#include "net/tools/flip_server/sm_connection.h"
+#include "net/tools/flip_server/spdy_ssl.h"
+#include "openssl/err.h"
+#include "openssl/ssl.h"
+
+namespace net {
+
+SMAcceptorThread::SMAcceptorThread(FlipAcceptor* acceptor,
+                                   MemoryCache* memory_cache)
+    : SimpleThread("SMAcceptorThread"),
+      acceptor_(acceptor),
+      ssl_state_(NULL),
+      use_ssl_(false),
+      idle_socket_timeout_s_(acceptor->idle_socket_timeout_s_),
+      quitting_(false),
+      memory_cache_(memory_cache) {
+  if (!acceptor->ssl_cert_filename_.empty() &&
+      !acceptor->ssl_key_filename_.empty()) {
+    ssl_state_ = new SSLState;
+    bool use_npn = true;
+    if (acceptor_->flip_handler_type_ == FLIP_HANDLER_HTTP_SERVER) {
+      use_npn = false;
+    }
+    InitSSL(ssl_state_,
+            acceptor_->ssl_cert_filename_,
+            acceptor_->ssl_key_filename_,
+            use_npn,
+            acceptor_->ssl_session_expiry_,
+            acceptor_->ssl_disable_compression_);
+    use_ssl_ = true;
+  }
+}
+
+SMAcceptorThread::~SMAcceptorThread() {
+  for (std::vector<SMConnection*>::iterator i =
+           allocated_server_connections_.begin();
+       i != allocated_server_connections_.end();
+       ++i) {
+    delete *i;
+  }
+  delete ssl_state_;
+}
+
+SMConnection* SMAcceptorThread::NewConnection() {
+  SMConnection* server = SMConnection::NewSMConnection(
+      &epoll_server_, ssl_state_, memory_cache_, acceptor_, "client_conn: ");
+  allocated_server_connections_.push_back(server);
+  VLOG(2) << ACCEPTOR_CLIENT_IDENT << "Acceptor: Making new server.";
+  return server;
+}
+
+SMConnection* SMAcceptorThread::FindOrMakeNewSMConnection() {
+  if (unused_server_connections_.empty()) {
+    return NewConnection();
+  }
+  SMConnection* server = unused_server_connections_.back();
+  unused_server_connections_.pop_back();
+  VLOG(2) << ACCEPTOR_CLIENT_IDENT << "Acceptor: Reusing server.";
+  return server;
+}
+
+void SMAcceptorThread::InitWorker() {
+  epoll_server_.RegisterFD(acceptor_->listen_fd_, this, EPOLLIN | EPOLLET);
+}
+
+void SMAcceptorThread::HandleConnection(int server_fd,
+                                        struct sockaddr_in* remote_addr) {
+  int on = 1;
+  int rc;
+  if (acceptor_->disable_nagle_) {
+    rc = setsockopt(server_fd,
+                    IPPROTO_TCP,
+                    TCP_NODELAY,
+                    reinterpret_cast<char*>(&on),
+                    sizeof(on));
+    if (rc < 0) {
+      close(server_fd);
+      LOG(ERROR) << "setsockopt() failed fd=" << server_fd;
+      return;
+    }
+  }
+
+  SMConnection* server_connection = FindOrMakeNewSMConnection();
+  if (server_connection == NULL) {
+    VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Acceptor: Closing fd " << server_fd;
+    close(server_fd);
+    return;
+  }
+  std::string remote_ip = inet_ntoa(remote_addr->sin_addr);
+  server_connection->InitSMConnection(this,
+                                      NULL,
+                                      &epoll_server_,
+                                      server_fd,
+                                      std::string(),
+                                      std::string(),
+                                      remote_ip,
+                                      use_ssl_);
+  if (server_connection->initialized())
+    active_server_connections_.push_back(server_connection);
+}
+
+void SMAcceptorThread::AcceptFromListenFD() {
+  if (acceptor_->accepts_per_wake_ > 0) {
+    for (int i = 0; i < acceptor_->accepts_per_wake_; ++i) {
+      struct sockaddr address;
+      socklen_t socklen = sizeof(address);
+      int fd = accept(acceptor_->listen_fd_, &address, &socklen);
+      if (fd == -1) {
+        if (errno != 11) {
+          VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Acceptor: accept fail("
+                  << acceptor_->listen_fd_ << "): " << errno << ": "
+                  << strerror(errno);
+        }
+        break;
+      }
+      VLOG(1) << ACCEPTOR_CLIENT_IDENT << " Accepted connection";
+      HandleConnection(fd, (struct sockaddr_in*)&address);
+    }
+  } else {
+    while (true) {
+      struct sockaddr address;
+      socklen_t socklen = sizeof(address);
+      int fd = accept(acceptor_->listen_fd_, &address, &socklen);
+      if (fd == -1) {
+        if (errno != 11) {
+          VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Acceptor: accept fail("
+                  << acceptor_->listen_fd_ << "): " << errno << ": "
+                  << strerror(errno);
+        }
+        break;
+      }
+      VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Accepted connection";
+      HandleConnection(fd, (struct sockaddr_in*)&address);
+    }
+  }
+}
+
+void SMAcceptorThread::HandleConnectionIdleTimeout() {
+  static time_t oldest_time = time(NULL);
+
+  int cur_time = time(NULL);
+  // Only iterate the list if we speculate that a connection is ready to be
+  // expired
+  if ((cur_time - oldest_time) < idle_socket_timeout_s_)
+    return;
+
+  // TODO(mbelshe): This code could be optimized, active_server_connections_
+  //                is already in-order.
+  std::list<SMConnection*>::iterator iter = active_server_connections_.begin();
+  while (iter != active_server_connections_.end()) {
+    SMConnection* conn = *iter;
+    int elapsed_time = (cur_time - conn->last_read_time_);
+    if (elapsed_time > idle_socket_timeout_s_) {
+      conn->Cleanup("Connection idle timeout reached.");
+      iter = active_server_connections_.erase(iter);
+      continue;
+    }
+    if (conn->last_read_time_ < oldest_time)
+      oldest_time = conn->last_read_time_;
+    iter++;
+  }
+  if ((cur_time - oldest_time) >= idle_socket_timeout_s_)
+    oldest_time = cur_time;
+}
+
+void SMAcceptorThread::Run() {
+  while (!quitting_.HasBeenNotified()) {
+    epoll_server_.set_timeout_in_us(10 * 1000);  // 10 ms
+    epoll_server_.WaitForEventsAndExecuteCallbacks();
+    if (tmp_unused_server_connections_.size()) {
+      VLOG(2) << "have " << tmp_unused_server_connections_.size()
+              << " additional unused connections.  Total = "
+              << unused_server_connections_.size();
+      unused_server_connections_.insert(unused_server_connections_.end(),
+                                        tmp_unused_server_connections_.begin(),
+                                        tmp_unused_server_connections_.end());
+      tmp_unused_server_connections_.clear();
+    }
+    HandleConnectionIdleTimeout();
+  }
+}
+
+void SMAcceptorThread::OnEvent(int fd, EpollEvent* event) {
+  if (event->in_events | EPOLLIN) {
+    VLOG(2) << ACCEPTOR_CLIENT_IDENT
+            << "Acceptor: Accepting based upon epoll events";
+    AcceptFromListenFD();
+  }
+}
+
+void SMAcceptorThread::SMConnectionDone(SMConnection* sc) {
+  VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Done with connection.";
+  tmp_unused_server_connections_.push_back(sc);
+}
+
+}  // namespace net
diff --git a/net/tools/flip_server/acceptor_thread.h b/net/tools/flip_server/acceptor_thread.h
new file mode 100644
index 0000000..bcaa43e
--- /dev/null
+++ b/net/tools/flip_server/acceptor_thread.h
@@ -0,0 +1,97 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_TOOLS_FLIP_SERVER_ACCEPTOR_THREAD_H_
+#define NET_TOOLS_FLIP_SERVER_ACCEPTOR_THREAD_H_
+
+#include <list>
+#include <string>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/threading/simple_thread.h"
+#include "net/tools/epoll_server/epoll_server.h"
+#include "net/tools/flip_server/sm_interface.h"
+#include "openssl/ssl.h"
+
+struct sockaddr_in;
+
+namespace net {
+
+class FlipAcceptor;
+class MemoryCache;
+class SMConnection;
+struct SSLState;
+
+// TODO(mbelshe):  Get rid of this class; we don't need a lock just to set
+//    a bool cross threads - especially one which only is set once...
+class Notification {
+ public:
+  explicit Notification(bool value) : value_(value) {}
+
+  void Notify() {
+    base::AutoLock al(lock_);
+    value_ = true;
+  }
+  bool HasBeenNotified() {
+    base::AutoLock al(lock_);
+    return value_;
+  }
+  bool value_;
+  base::Lock lock_;
+};
+
+class SMAcceptorThread : public base::SimpleThread,
+                         public EpollCallbackInterface,
+                         public SMConnectionPoolInterface {
+ public:
+  SMAcceptorThread(FlipAcceptor* acceptor, MemoryCache* memory_cache);
+  virtual ~SMAcceptorThread();
+
+  // EpollCallbackInteface interface
+  virtual void OnRegistration(EpollServer* eps,
+                              int fd,
+                              int event_mask) OVERRIDE {}
+  virtual void OnModification(int fd, int event_mask) OVERRIDE {}
+  virtual void OnEvent(int fd, EpollEvent* event) OVERRIDE;
+  virtual void OnUnregistration(int fd, bool replaced) OVERRIDE {}
+  virtual void OnShutdown(EpollServer* eps, int fd) OVERRIDE {}
+
+  // SMConnectionPool interface
+  virtual void SMConnectionDone(SMConnection* sc) OVERRIDE;
+
+  // TODO(mbelshe): figure out if we can move these to private functions.
+  SMConnection* NewConnection();
+  SMConnection* FindOrMakeNewSMConnection();
+  void InitWorker();
+  void HandleConnection(int server_fd, struct sockaddr_in* remote_addr);
+  void AcceptFromListenFD();
+
+  // Notify the Accept thread that it is time to terminate.
+  void Quit() { quitting_.Notify(); }
+
+  // Iterates through a list of active connections expiring any that have been
+  // idle longer than the configured timeout.
+  void HandleConnectionIdleTimeout();
+
+  virtual void Run() OVERRIDE;
+
+ private:
+  EpollServer epoll_server_;
+  FlipAcceptor* acceptor_;
+  SSLState* ssl_state_;
+  bool use_ssl_;
+  int idle_socket_timeout_s_;
+
+  std::vector<SMConnection*> unused_server_connections_;
+  std::vector<SMConnection*> tmp_unused_server_connections_;
+  std::vector<SMConnection*> allocated_server_connections_;
+  std::list<SMConnection*> active_server_connections_;
+  Notification quitting_;
+  MemoryCache* memory_cache_;
+};
+
+}  // namespace net
+
+#endif  // NET_TOOLS_FLIP_SERVER_ACCEPTOR_THREAD_H_
diff --git a/net/tools/flip_server/constants.h b/net/tools/flip_server/constants.h
new file mode 100644
index 0000000..5516509
--- /dev/null
+++ b/net/tools/flip_server/constants.h
@@ -0,0 +1,25 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_TOOLS_FLIP_SERVER_CONSTANTS_H_
+#define NET_TOOLS_FLIP_SERVER_CONSTANTS_H_
+
+#include "net/spdy/spdy_protocol.h"
+
+const int kMSS = 1460;
+const int kSSLOverhead = 25;
+const int kSpdyOverhead = 8;
+const int kInitialDataSendersThreshold = (2 * kMSS) - kSpdyOverhead;
+const int kSSLSegmentSize = (1 * kMSS) - kSSLOverhead;
+const int kSpdySegmentSize = kSSLSegmentSize - kSpdyOverhead;
+
+#define ACCEPTOR_CLIENT_IDENT \
+  acceptor_->listen_ip_ << ":" << acceptor_->listen_port_ << " "
+
+#define IPV4_PRINTABLE_FORMAT(IP) (((IP)>>0)&0xff), (((IP)>>8)&0xff), \
+                                  (((IP)>>16)&0xff), (((IP)>>24)&0xff)
+
+#define PIDFILE "/var/run/flip-server.pid"
+
+#endif  // NET_TOOLS_FLIP_SERVER_CONSTANTS_H_
diff --git a/net/tools/flip_server/create_listener.cc b/net/tools/flip_server/create_listener.cc
new file mode 100644
index 0000000..c1c0896
--- /dev/null
+++ b/net/tools/flip_server/create_listener.cc
@@ -0,0 +1,296 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/tools/flip_server/create_listener.h"
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+
+namespace net {
+
+// used to ensure we delete the addrinfo structure
+// alloc'd by getaddrinfo
+class AddrinfoGuard {
+ protected:
+  struct addrinfo* addrinfo_ptr_;
+
+ public:
+  explicit AddrinfoGuard(struct addrinfo* addrinfo_ptr)
+      : addrinfo_ptr_(addrinfo_ptr) {}
+
+  ~AddrinfoGuard() { freeaddrinfo(addrinfo_ptr_); }
+};
+
+// Summary:
+//   Closes a socket, with option to attempt it multiple times.
+//   Why do this? Well, if the system-call gets interrupted, close
+//   can fail with EINTR. In that case you should just retry.. Unfortunately,
+//   we can't be sure that errno is properly set since we're using a
+//   multithreaded approach in the filter proxy, so we should just retry.
+// Args:
+//   fd - the socket to close
+//   tries - the number of tries to close the socket.
+// Returns:
+//   true - if socket was closed
+//   false - if socket was NOT closed.
+// Side-effects:
+//   sets *fd to -1 if socket was closed.
+//
+bool CloseSocket(int* fd, int tries) {
+  for (int i = 0; i < tries; ++i) {
+    if (!close(*fd)) {
+      *fd = -1;
+      return true;
+    }
+  }
+  return false;
+}
+
+// Sets an FD to be nonblocking.
+void FlipSetNonBlocking(int fd) {
+  DCHECK_GE(fd, 0);
+
+  int fcntl_return = fcntl(fd, F_GETFL, 0);
+  CHECK_NE(fcntl_return, -1) << "error doing fcntl(fd, F_GETFL, 0) fd: " << fd
+                             << " errno=" << errno;
+
+  if (fcntl_return & O_NONBLOCK)
+    return;
+
+  fcntl_return = fcntl(fd, F_SETFL, fcntl_return | O_NONBLOCK);
+  CHECK_NE(fcntl_return, -1)
+      << "error doing fcntl(fd, F_SETFL, fcntl_return) fd: " << fd
+      << " errno=" << errno;
+}
+
+int SetDisableNagle(int fd) {
+  int on = 1;
+  int rc;
+  rc = setsockopt(
+      fd, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char*>(&on), sizeof(on));
+  if (rc < 0) {
+    close(fd);
+    LOG(FATAL) << "setsockopt() TCP_NODELAY: failed on fd " << fd;
+    return 0;
+  }
+  return 1;
+}
+
+// see header for documentation of this function.
+int CreateListeningSocket(const std::string& host,
+                          const std::string& port,
+                          bool is_numeric_host_address,
+                          int backlog,
+                          bool reuseaddr,
+                          bool reuseport,
+                          bool wait_for_iface,
+                          bool disable_nagle,
+                          int* listen_fd) {
+  // start out by assuming things will fail.
+  *listen_fd = -1;
+
+  const char* node = NULL;
+  const char* service = NULL;
+
+  if (!host.empty())
+    node = host.c_str();
+  if (!port.empty())
+    service = port.c_str();
+
+  struct addrinfo* results = 0;
+  struct addrinfo hints;
+  memset(&hints, 0, sizeof(hints));
+
+  if (is_numeric_host_address) {
+    hints.ai_flags = AI_NUMERICHOST;  // iff you know the name is numeric.
+  }
+  hints.ai_flags |= AI_PASSIVE;
+
+  hints.ai_family = PF_INET;
+  hints.ai_socktype = SOCK_STREAM;
+
+  int err = 0;
+  if ((err = getaddrinfo(node, service, &hints, &results))) {
+    // gai_strerror -is- threadsafe, so we get to use it here.
+    LOG(ERROR) << "getaddrinfo "
+               << " for (" << host << ":" << port << ") " << gai_strerror(err)
+               << "\n";
+    return -1;
+  }
+  // this will delete the addrinfo memory when we return from this function.
+  AddrinfoGuard addrinfo_guard(results);
+
+  int sock =
+      socket(results->ai_family, results->ai_socktype, results->ai_protocol);
+  if (sock == -1) {
+    LOG(ERROR) << "Unable to create socket for (" << host << ":" << port
+               << "): " << strerror(errno) << "\n";
+    return -1;
+  }
+
+  if (reuseaddr) {
+    // set SO_REUSEADDR on the listening socket.
+    int on = 1;
+    int rc;
+    rc = setsockopt(sock,
+                    SOL_SOCKET,
+                    SO_REUSEADDR,
+                    reinterpret_cast<char*>(&on),
+                    sizeof(on));
+    if (rc < 0) {
+      close(sock);
+      LOG(FATAL) << "setsockopt() failed fd=" << listen_fd << "\n";
+    }
+  }
+#ifndef SO_REUSEPORT
+#define SO_REUSEPORT 15
+#endif
+  if (reuseport) {
+    // set SO_REUSEADDR on the listening socket.
+    int on = 1;
+    int rc;
+    rc = setsockopt(sock,
+                    SOL_SOCKET,
+                    SO_REUSEPORT,
+                    reinterpret_cast<char*>(&on),
+                    sizeof(on));
+    if (rc < 0) {
+      close(sock);
+      LOG(FATAL) << "setsockopt() failed fd=" << listen_fd << "\n";
+    }
+  }
+
+  if (bind(sock, results->ai_addr, results->ai_addrlen)) {
+    // If we are waiting for the interface to be raised, such as in an
+    // HA environment, ignore reporting any errors.
+    int saved_errno = errno;
+    if (!wait_for_iface || errno != EADDRNOTAVAIL) {
+      LOG(ERROR) << "Bind was unsuccessful for (" << host << ":" << port
+                 << "): " << strerror(errno) << "\n";
+    }
+    // if we knew that we were not multithreaded, we could do the following:
+    // " : " << strerror(errno) << "\n";
+    if (CloseSocket(&sock, 100)) {
+      if (saved_errno == EADDRNOTAVAIL) {
+        return -3;
+      }
+      return -2;
+    } else {
+      // couldn't even close the dang socket?!
+      LOG(ERROR) << "Unable to close the socket.. Considering this a fatal "
+                    "error, and exiting\n";
+      exit(EXIT_FAILURE);
+      return -1;
+    }
+  }
+
+  if (disable_nagle) {
+    if (!SetDisableNagle(sock)) {
+      return -1;
+    }
+  }
+
+  if (listen(sock, backlog)) {
+    // listen was unsuccessful.
+    LOG(ERROR) << "Listen was unsuccessful for (" << host << ":" << port
+               << "): " << strerror(errno) << "\n";
+    // if we knew that we were not multithreaded, we could do the following:
+    // " : " << strerror(errno) << "\n";
+
+    if (CloseSocket(&sock, 100)) {
+      sock = -1;
+      return -1;
+    } else {
+      // couldn't even close the dang socket?!
+      LOG(FATAL) << "Unable to close the socket.. Considering this a fatal "
+                    "error, and exiting\n";
+    }
+  }
+
+  // If we've gotten to here, Yeay! Success!
+  *listen_fd = sock;
+
+  return 0;
+}
+
+int CreateConnectedSocket(int* connect_fd,
+                          const std::string& host,
+                          const std::string& port,
+                          bool is_numeric_host_address,
+                          bool disable_nagle) {
+  const char* node = NULL;
+  const char* service = NULL;
+
+  *connect_fd = -1;
+  if (!host.empty())
+    node = host.c_str();
+  if (!port.empty())
+    service = port.c_str();
+
+  struct addrinfo* results = 0;
+  struct addrinfo hints;
+  memset(&hints, 0, sizeof(hints));
+
+  if (is_numeric_host_address)
+    hints.ai_flags = AI_NUMERICHOST;  // iff you know the name is numeric.
+  hints.ai_flags |= AI_PASSIVE;
+
+  hints.ai_family = PF_INET;
+  hints.ai_socktype = SOCK_STREAM;
+
+  int err = 0;
+  if ((err = getaddrinfo(node, service, &hints, &results))) {
+    // gai_strerror -is- threadsafe, so we get to use it here.
+    LOG(ERROR) << "getaddrinfo for (" << node << ":" << service
+               << "): " << gai_strerror(err);
+    return -1;
+  }
+  // this will delete the addrinfo memory when we return from this function.
+  AddrinfoGuard addrinfo_guard(results);
+
+  int sock =
+      socket(results->ai_family, results->ai_socktype, results->ai_protocol);
+  if (sock == -1) {
+    LOG(ERROR) << "Unable to create socket for (" << node << ":" << service
+               << "): " << strerror(errno);
+    return -1;
+  }
+
+  FlipSetNonBlocking(sock);
+
+  if (disable_nagle) {
+    if (!SetDisableNagle(sock)) {
+      return -1;
+    }
+  }
+
+  int ret_val = 0;
+  if (connect(sock, results->ai_addr, results->ai_addrlen)) {
+    if (errno != EINPROGRESS) {
+      LOG(ERROR) << "Connect was unsuccessful for (" << node << ":" << service
+                 << "): " << strerror(errno);
+      close(sock);
+      return -1;
+    }
+  } else {
+    ret_val = 1;
+  }
+
+  // If we've gotten to here, Yeay! Success!
+  *connect_fd = sock;
+
+  return ret_val;
+}
+
+}  // namespace net
diff --git a/net/tools/flip_server/create_listener.h b/net/tools/flip_server/create_listener.h
new file mode 100644
index 0000000..8ab619c
--- /dev/null
+++ b/net/tools/flip_server/create_listener.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_TOOLS_FLIP_SERVER_CREATE_LISTENER_H__
+#define NET_TOOLS_FLIP_SERVER_CREATE_LISTENER_H__
+
+#include <iosfwd>
+#include <string>
+
+namespace net {
+
+void FlipSetNonBlocking(int fd);
+
+// Summary:
+//   creates a socket for listening, and bind()s and listen()s it.
+// Args:
+//   host - hostname or numeric address, or empty-string if you want
+//          to bind to listen on all addresses
+//   port - a port number or service name. By service name I mean a
+//          -real- service name, not a Google service name. I'd suggest
+//          you just stick to a numeric representation like "80"
+//   is_numeric_host_address -
+//           if you know that the host address has already been looked-up,
+//           and will be provided in numeric form like "130.207.244.244",
+//           then you can set this to true, and it will save you the time
+//           of a DNS lookup.
+//   backlog - passed into listen. This is the number of pending incoming
+//             connections a socket which is listening may have acquired before
+//             the OS starts rejecting new incoming connections.
+//   reuseaddr - if true sets SO_REUSEADDR on the listening socket
+//   reuseport - if true sets SO_REUSEPORT on the listening socket
+//   wait_for_iface - A boolean indicating that CreateListeningSocket should
+//                    block until the interface that it will bind to has been
+//                    raised. This is intended for HA environments.
+//   disable_nagle - if true sets TCP_NODELAY on the listening socket.
+//   listen_fd - this will be assigned a positive value if the socket is
+//               successfully created, else it will be assigned -1.
+int CreateListeningSocket(const std::string& host,
+                          const std::string& port,
+                          bool is_numeric_host_address,
+                          int backlog,
+                          bool reuseaddr,
+                          bool reuseport,
+                          bool wait_for_iface,
+                          bool disable_nagle,
+                          int* listen_fd);
+
+int CreateConnectedSocket(int* connect_fd,
+                          const std::string& host,
+                          const std::string& port,
+                          bool is_numeric_host_address,
+                          bool disable_nagle);
+}  // namespace net
+
+#endif  // NET_TOOLS_FLIP_SERVER_CREATE_LISTENER_H__
diff --git a/net/tools/flip_server/flip_config.cc b/net/tools/flip_server/flip_config.cc
new file mode 100644
index 0000000..8be1fe0
--- /dev/null
+++ b/net/tools/flip_server/flip_config.cc
@@ -0,0 +1,138 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/tools/flip_server/flip_config.h"
+
+#include <unistd.h>
+
+namespace net {
+
+FlipAcceptor::FlipAcceptor(enum FlipHandlerType flip_handler_type,
+                           std::string listen_ip,
+                           std::string listen_port,
+                           std::string ssl_cert_filename,
+                           std::string ssl_key_filename,
+                           std::string http_server_ip,
+                           std::string http_server_port,
+                           std::string https_server_ip,
+                           std::string https_server_port,
+                           int spdy_only,
+                           int accept_backlog_size,
+                           bool disable_nagle,
+                           int accepts_per_wake,
+                           bool reuseport,
+                           bool wait_for_iface,
+                           void* memory_cache)
+    : flip_handler_type_(flip_handler_type),
+      listen_ip_(listen_ip),
+      listen_port_(listen_port),
+      ssl_cert_filename_(ssl_cert_filename),
+      ssl_key_filename_(ssl_key_filename),
+      http_server_ip_(http_server_ip),
+      http_server_port_(http_server_port),
+      https_server_ip_(https_server_ip),
+      https_server_port_(https_server_port),
+      spdy_only_(spdy_only),
+      accept_backlog_size_(accept_backlog_size),
+      disable_nagle_(disable_nagle),
+      accepts_per_wake_(accepts_per_wake),
+      memory_cache_(memory_cache),
+      ssl_session_expiry_(300),  // TODO(mbelshe):  Hook these up!
+      ssl_disable_compression_(false),
+      idle_socket_timeout_s_(300) {
+  VLOG(1) << "Attempting to listen on " << listen_ip_.c_str() << ":"
+          << listen_port_.c_str();
+  if (!https_server_ip_.size())
+    https_server_ip_ = http_server_ip_;
+  if (!https_server_port_.size())
+    https_server_port_ = http_server_port_;
+
+  while (1) {
+    int ret = CreateListeningSocket(listen_ip_,
+                                    listen_port_,
+                                    true,
+                                    accept_backlog_size_,
+                                    true,
+                                    reuseport,
+                                    wait_for_iface,
+                                    disable_nagle_,
+                                    &listen_fd_);
+    if (ret == 0) {
+      break;
+    } else if (ret == -3 && wait_for_iface) {
+      // Binding error EADDRNOTAVAIL was encounted. We need
+      // to wait for the interfaces to raised. try again.
+      usleep(200000);
+    } else {
+      LOG(ERROR) << "Unable to create listening socket for: ret = " << ret
+                 << ": " << listen_ip_.c_str() << ":" << listen_port_.c_str();
+      return;
+    }
+  }
+
+  FlipSetNonBlocking(listen_fd_);
+  VLOG(1) << "Listening on socket: ";
+  if (flip_handler_type == FLIP_HANDLER_PROXY)
+    VLOG(1) << "\tType         : Proxy";
+  else if (FLIP_HANDLER_SPDY_SERVER)
+    VLOG(1) << "\tType         : SPDY Server";
+  else if (FLIP_HANDLER_HTTP_SERVER)
+    VLOG(1) << "\tType         : HTTP Server";
+  VLOG(1) << "\tIP           : " << listen_ip_;
+  VLOG(1) << "\tPort         : " << listen_port_;
+  VLOG(1) << "\tHTTP Server  : " << http_server_ip_ << ":" << http_server_port_;
+  VLOG(1) << "\tHTTPS Server : " << https_server_ip_ << ":"
+          << https_server_port_;
+  VLOG(1) << "\tSSL          : " << (ssl_cert_filename.size() ? "true"
+                                                              : "false");
+  VLOG(1) << "\tCertificate  : " << ssl_cert_filename;
+  VLOG(1) << "\tKey          : " << ssl_key_filename;
+  VLOG(1) << "\tSpdy Only    : " << (spdy_only ? "true" : "false");
+}
+
+FlipAcceptor::~FlipAcceptor() {}
+
+FlipConfig::FlipConfig()
+    : server_think_time_in_s_(0),
+      log_destination_(logging::LOG_TO_SYSTEM_DEBUG_LOG),
+      wait_for_iface_(false) {}
+
+FlipConfig::~FlipConfig() {}
+
+void FlipConfig::AddAcceptor(enum FlipHandlerType flip_handler_type,
+                             std::string listen_ip,
+                             std::string listen_port,
+                             std::string ssl_cert_filename,
+                             std::string ssl_key_filename,
+                             std::string http_server_ip,
+                             std::string http_server_port,
+                             std::string https_server_ip,
+                             std::string https_server_port,
+                             int spdy_only,
+                             int accept_backlog_size,
+                             bool disable_nagle,
+                             int accepts_per_wake,
+                             bool reuseport,
+                             bool wait_for_iface,
+                             void* memory_cache) {
+  // TODO(mbelshe): create a struct FlipConfigArgs{} for the arguments.
+  acceptors_.push_back(new FlipAcceptor(flip_handler_type,
+                                        listen_ip,
+                                        listen_port,
+                                        ssl_cert_filename,
+                                        ssl_key_filename,
+                                        http_server_ip,
+                                        http_server_port,
+                                        https_server_ip,
+                                        https_server_port,
+                                        spdy_only,
+                                        accept_backlog_size,
+                                        disable_nagle,
+                                        accepts_per_wake,
+                                        reuseport,
+                                        wait_for_iface,
+                                        memory_cache));
+}
+
+}  // namespace net
diff --git a/net/tools/flip_server/flip_config.h b/net/tools/flip_server/flip_config.h
new file mode 100644
index 0000000..90a4199
--- /dev/null
+++ b/net/tools/flip_server/flip_config.h
@@ -0,0 +1,98 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_TOOLS_FLIP_SERVER_FLIP_CONFIG_H_
+#define NET_TOOLS_FLIP_SERVER_FLIP_CONFIG_H_
+
+#include <arpa/inet.h>
+
+#include <string>
+#include <vector>
+
+#include "base/logging.h"
+#include "net/tools/flip_server/create_listener.h"
+
+namespace net {
+
+enum FlipHandlerType {
+  FLIP_HANDLER_PROXY,
+  FLIP_HANDLER_SPDY_SERVER,
+  FLIP_HANDLER_HTTP_SERVER
+};
+
+class FlipAcceptor {
+ public:
+  FlipAcceptor(enum FlipHandlerType flip_handler_type,
+               std::string listen_ip,
+               std::string listen_port,
+               std::string ssl_cert_filename,
+               std::string ssl_key_filename,
+               std::string http_server_ip,
+               std::string http_server_port,
+               std::string https_server_ip,
+               std::string https_server_port,
+               int spdy_only,
+               int accept_backlog_size,
+               bool disable_nagle,
+               int accepts_per_wake,
+               bool reuseport,
+               bool wait_for_iface,
+               void* memory_cache);
+  ~FlipAcceptor();
+
+  enum FlipHandlerType flip_handler_type_;
+  std::string listen_ip_;
+  std::string listen_port_;
+  std::string ssl_cert_filename_;
+  std::string ssl_key_filename_;
+  std::string http_server_ip_;
+  std::string http_server_port_;
+  std::string https_server_ip_;
+  std::string https_server_port_;
+  int spdy_only_;
+  int accept_backlog_size_;
+  bool disable_nagle_;
+  int accepts_per_wake_;
+  int listen_fd_;
+  void* memory_cache_;
+  int ssl_session_expiry_;
+  bool ssl_disable_compression_;
+  int idle_socket_timeout_s_;
+};
+
+class FlipConfig {
+ public:
+  FlipConfig();
+  ~FlipConfig();
+
+  void AddAcceptor(enum FlipHandlerType flip_handler_type,
+                   std::string listen_ip,
+                   std::string listen_port,
+                   std::string ssl_cert_filename,
+                   std::string ssl_key_filename,
+                   std::string http_server_ip,
+                   std::string http_server_port,
+                   std::string https_server_ip,
+                   std::string https_server_port,
+                   int spdy_only,
+                   int accept_backlog_size,
+                   bool disable_nagle,
+                   int accepts_per_wake,
+                   bool reuseport,
+                   bool wait_for_iface,
+                   void* memory_cache);
+
+  std::vector<FlipAcceptor*> acceptors_;
+  double server_think_time_in_s_;
+  enum logging::LoggingDestination log_destination_;
+  std::string log_filename_;
+  bool wait_for_iface_;
+  int ssl_session_expiry_;
+  bool ssl_disable_compression_;
+  int idle_socket_timeout_s_;
+};
+
+}  // namespace net
+
+#endif  // NET_TOOLS_FLIP_SERVER_FLIP_CONFIG_H_
diff --git a/net/tools/flip_server/flip_in_mem_edsm_server.cc b/net/tools/flip_server/flip_in_mem_edsm_server.cc
new file mode 100644
index 0000000..a8459b6
--- /dev/null
+++ b/net/tools/flip_server/flip_in_mem_edsm_server.cc
@@ -0,0 +1,422 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+
+#include <string>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/synchronization/lock.h"
+#include "base/timer/timer.h"
+#include "net/tools/balsa/split.h"
+#include "net/tools/flip_server/acceptor_thread.h"
+#include "net/tools/flip_server/constants.h"
+#include "net/tools/flip_server/flip_config.h"
+#include "net/tools/flip_server/output_ordering.h"
+#include "net/tools/flip_server/sm_connection.h"
+#include "net/tools/flip_server/sm_interface.h"
+#include "net/tools/flip_server/spdy_interface.h"
+#include "net/tools/flip_server/streamer_interface.h"
+
+// If true, then disables the nagle algorithm);
+bool FLAGS_disable_nagle = true;
+
+// The number of times that accept() will be called when the
+//  alarm goes off when the accept_using_alarm flag is set to true.
+//  If set to 0, accept() will be performed until the accept queue
+//  is completely drained and the accept() call returns an error);
+int32 FLAGS_accepts_per_wake = 0;
+
+// The size of the TCP accept backlog);
+int32 FLAGS_accept_backlog_size = 1024;
+
+// If set to false a single socket will be used. If set to true
+//  then a new socket will be created for each accept thread.
+//  Note that this only works with kernels that support
+//  SO_REUSEPORT);
+bool FLAGS_reuseport = false;
+
+// Flag to force spdy, even if NPN is not negotiated.
+bool FLAGS_force_spdy = false;
+
+// The amount of time the server delays before sending back the
+//  reply);
+double FLAGS_server_think_time_in_s = 0;
+
+net::FlipConfig g_proxy_config;
+
+std::vector<std::string>& split(const std::string& s,
+                                char delim,
+                                std::vector<std::string>& elems) {
+  std::stringstream ss(s);
+  std::string item;
+  while (std::getline(ss, item, delim)) {
+    elems.push_back(item);
+  }
+  return elems;
+}
+
+std::vector<std::string> split(const std::string& s, char delim) {
+  std::vector<std::string> elems;
+  return split(s, delim, elems);
+}
+
+bool GotQuitFromStdin() {
+  // Make stdin nonblocking. Yes this is done each time. Oh well.
+  fcntl(0, F_SETFL, O_NONBLOCK);
+  char c;
+  std::string maybequit;
+  while (read(0, &c, 1) > 0) {
+    maybequit += c;
+  }
+  if (maybequit.size()) {
+    VLOG(1) << "scanning string: \"" << maybequit << "\"";
+  }
+  return (maybequit.size() > 1 &&
+          (maybequit.c_str()[0] == 'q' || maybequit.c_str()[0] == 'Q'));
+}
+
+const char* BoolToStr(bool b) {
+  if (b)
+    return "true";
+  return "false";
+}
+
+static bool wantExit = false;
+static bool wantLogClose = false;
+void SignalHandler(int signum) {
+  switch (signum) {
+    case SIGTERM:
+    case SIGINT:
+      wantExit = true;
+      break;
+    case SIGHUP:
+      wantLogClose = true;
+      break;
+  }
+}
+
+static int OpenPidFile(const char* pidfile) {
+  int fd;
+  struct stat pid_stat;
+  int ret;
+
+  fd = open(pidfile, O_RDWR | O_CREAT, 0600);
+  if (fd == -1) {
+    fprintf(stderr, "Could not open pid file '%s' for reading.\n", pidfile);
+    exit(1);
+  }
+
+  ret = flock(fd, LOCK_EX | LOCK_NB);
+  if (ret == -1) {
+    if (errno == EWOULDBLOCK) {
+      fprintf(stderr, "Flip server is already running.\n");
+    } else {
+      perror("Error getting lock on pid file");
+    }
+    exit(1);
+  }
+
+  if (fstat(fd, &pid_stat) == -1) {
+    fprintf(
+        stderr, "Could not stat pid file '%s': %s\n", pidfile, strerror(errno));
+    exit(1);
+  }
+  if (pid_stat.st_size != 0) {
+    if (ftruncate(fd, pid_stat.st_size) == -1) {
+      fprintf(stderr,
+              "Could not truncate pid file '%s': %s\n",
+              pidfile,
+              strerror(errno));
+      exit(1);
+    }
+  }
+
+  char pid_str[8];
+  snprintf(pid_str, sizeof(pid_str), "%d", getpid());
+  int bytes = static_cast<int>(strlen(pid_str));
+  if (write(fd, pid_str, strlen(pid_str)) != bytes) {
+    perror("Could not write pid file");
+    close(fd);
+    exit(1);
+  }
+
+  return fd;
+}
+
+int main(int argc, char** argv) {
+  unsigned int i = 0;
+  bool wait_for_iface = false;
+  int pidfile_fd;
+
+  signal(SIGPIPE, SIG_IGN);
+  signal(SIGTERM, SignalHandler);
+  signal(SIGINT, SignalHandler);
+  signal(SIGHUP, SignalHandler);
+
+  base::CommandLine::Init(argc, argv);
+  base::CommandLine cl(argc, argv);
+
+  if (cl.HasSwitch("help") || argc < 2) {
+    printf("%s <options>\n", argv[0]);
+    printf("  Proxy options:\n");
+    printf(
+        "\t--proxy<1..n>=\"<listen ip>,<listen port>,"
+        "<ssl cert filename>,\n"
+        "\t               <ssl key filename>,<http server ip>,"
+        "<http server port>,\n"
+        "\t               [https server ip],[https server port],"
+        "<spdy only 0|1>\"\n"
+        "\t  * The https server ip and port may be left empty if they are"
+        " the same as\n"
+        "\t    the http server fields.\n"
+        "\t  * spdy only prevents non-spdy https connections from being"
+        " passed\n"
+        "\t    through the proxy listen ip:port.\n"
+        "\t--forward-ip-header=<header name>\n"
+        "\n  Server options:\n"
+        "\t--spdy-server=\"<listen ip>,<listen port>,[ssl cert filename],"
+        "\n\t               [ssl key filename]\"\n"
+        "\t--http-server=\"<listen ip>,<listen port>,[ssl cert filename],"
+        "\n\t               [ssl key filename]\"\n"
+        "\t  * Leaving the ssl cert and key fields empty will disable ssl"
+        " for the\n"
+        "\t    http and spdy flip servers\n"
+        "\n  Global options:\n"
+        "\t--logdest=<file|system|both>\n"
+        "\t--logfile=<logfile>\n"
+        "\t--wait-for-iface\n"
+        "\t  * The flip server will block until the listen ip has been"
+        " raised.\n"
+        "\t--ssl-session-expiry=<seconds> (default is 300)\n"
+        "\t--ssl-disable-compression\n"
+        "\t--idle-timeout=<seconds> (default is 300)\n"
+        "\t--pidfile=<filepath> (default /var/run/flip-server.pid)\n"
+        "\t--help\n");
+    exit(0);
+  }
+
+  if (cl.HasSwitch("pidfile")) {
+    pidfile_fd = OpenPidFile(cl.GetSwitchValueASCII("pidfile").c_str());
+  } else {
+    pidfile_fd = OpenPidFile(PIDFILE);
+  }
+
+  net::OutputOrdering::set_server_think_time_in_s(FLAGS_server_think_time_in_s);
+
+  if (cl.HasSwitch("forward-ip-header")) {
+    net::SpdySM::set_forward_ip_header(
+        cl.GetSwitchValueASCII("forward-ip-header"));
+    net::StreamerSM::set_forward_ip_header(
+        cl.GetSwitchValueASCII("forward-ip-header"));
+  }
+
+  if (cl.HasSwitch("logdest")) {
+    std::string log_dest_value = cl.GetSwitchValueASCII("logdest");
+    if (log_dest_value.compare("file") == 0) {
+      g_proxy_config.log_destination_ = logging::LOG_TO_FILE;
+    } else if (log_dest_value.compare("system") == 0) {
+      g_proxy_config.log_destination_ = logging::LOG_TO_SYSTEM_DEBUG_LOG;
+    } else if (log_dest_value.compare("both") == 0) {
+      g_proxy_config.log_destination_ = logging::LOG_TO_ALL;
+    } else {
+      LOG(FATAL) << "Invalid logging destination value: " << log_dest_value;
+    }
+  } else {
+    g_proxy_config.log_destination_ = logging::LOG_NONE;
+  }
+
+  if (cl.HasSwitch("logfile")) {
+    g_proxy_config.log_filename_ = cl.GetSwitchValueASCII("logfile");
+    if (g_proxy_config.log_destination_ == logging::LOG_NONE) {
+      g_proxy_config.log_destination_ = logging::LOG_TO_FILE;
+    }
+  } else if ((g_proxy_config.log_destination_ & logging::LOG_TO_FILE) != 0) {
+    LOG(FATAL) << "Logging destination requires a log file to be specified.";
+  }
+
+  if (cl.HasSwitch("wait-for-iface")) {
+    wait_for_iface = true;
+  }
+
+  if (cl.HasSwitch("ssl-session-expiry")) {
+    std::string session_expiry = cl.GetSwitchValueASCII("ssl-session-expiry");
+    g_proxy_config.ssl_session_expiry_ = atoi(session_expiry.c_str());
+  }
+
+  if (cl.HasSwitch("ssl-disable-compression")) {
+    g_proxy_config.ssl_disable_compression_ = true;
+  }
+
+  if (cl.HasSwitch("idle-timeout")) {
+    g_proxy_config.idle_socket_timeout_s_ =
+        atoi(cl.GetSwitchValueASCII("idle-timeout").c_str());
+  }
+
+  if (cl.HasSwitch("force_spdy"))
+    net::SMConnection::set_force_spdy(true);
+
+  logging::LoggingSettings settings;
+  settings.logging_dest = g_proxy_config.log_destination_;
+  settings.log_file = g_proxy_config.log_filename_.c_str();
+  settings.lock_log = logging::DONT_LOCK_LOG_FILE;
+  logging::InitLogging(settings);
+
+  LOG(INFO) << "Flip SPDY proxy started with configuration:";
+  LOG(INFO) << "Logging destination     : " << g_proxy_config.log_destination_;
+  LOG(INFO) << "Log file                : " << g_proxy_config.log_filename_;
+  LOG(INFO) << "Forward IP Header       : "
+            << (net::SpdySM::forward_ip_header().length()
+                    ? net::SpdySM::forward_ip_header()
+                    : "<disabled>");
+  LOG(INFO) << "Wait for interfaces     : " << (wait_for_iface ? "true"
+                                                               : "false");
+  LOG(INFO) << "Accept backlog size     : " << FLAGS_accept_backlog_size;
+  LOG(INFO) << "Accepts per wake        : " << FLAGS_accepts_per_wake;
+  LOG(INFO) << "Disable nagle           : " << (FLAGS_disable_nagle ? "true"
+                                                                    : "false");
+  LOG(INFO) << "Reuseport               : " << (FLAGS_reuseport ? "true"
+                                                                : "false");
+  LOG(INFO) << "Force SPDY              : " << (FLAGS_force_spdy ? "true"
+                                                                 : "false");
+  LOG(INFO) << "SSL session expiry      : "
+            << g_proxy_config.ssl_session_expiry_;
+  LOG(INFO) << "SSL disable compression : "
+            << g_proxy_config.ssl_disable_compression_;
+  LOG(INFO) << "Connection idle timeout : "
+            << g_proxy_config.idle_socket_timeout_s_;
+
+  // Proxy Acceptors
+  while (true) {
+    i += 1;
+    std::stringstream name;
+    name << "proxy" << i;
+    if (!cl.HasSwitch(name.str())) {
+      break;
+    }
+    std::string value = cl.GetSwitchValueASCII(name.str());
+    std::vector<std::string> valueArgs = split(value, ',');
+    CHECK_EQ((unsigned int)9, valueArgs.size());
+    int spdy_only = atoi(valueArgs[8].c_str());
+    // If wait_for_iface is enabled, then this call will block
+    // indefinitely until the interface is raised.
+    g_proxy_config.AddAcceptor(net::FLIP_HANDLER_PROXY,
+                               valueArgs[0],
+                               valueArgs[1],
+                               valueArgs[2],
+                               valueArgs[3],
+                               valueArgs[4],
+                               valueArgs[5],
+                               valueArgs[6],
+                               valueArgs[7],
+                               spdy_only,
+                               FLAGS_accept_backlog_size,
+                               FLAGS_disable_nagle,
+                               FLAGS_accepts_per_wake,
+                               FLAGS_reuseport,
+                               wait_for_iface,
+                               NULL);
+  }
+
+  // Spdy Server Acceptor
+  net::MemoryCache spdy_memory_cache;
+  if (cl.HasSwitch("spdy-server")) {
+    spdy_memory_cache.AddFiles();
+    std::string value = cl.GetSwitchValueASCII("spdy-server");
+    std::vector<std::string> valueArgs = split(value, ',');
+    while (valueArgs.size() < 4)
+      valueArgs.push_back(std::string());
+    g_proxy_config.AddAcceptor(net::FLIP_HANDLER_SPDY_SERVER,
+                               valueArgs[0],
+                               valueArgs[1],
+                               valueArgs[2],
+                               valueArgs[3],
+                               std::string(),
+                               std::string(),
+                               std::string(),
+                               std::string(),
+                               0,
+                               FLAGS_accept_backlog_size,
+                               FLAGS_disable_nagle,
+                               FLAGS_accepts_per_wake,
+                               FLAGS_reuseport,
+                               wait_for_iface,
+                               &spdy_memory_cache);
+  }
+
+  // Spdy Server Acceptor
+  net::MemoryCache http_memory_cache;
+  if (cl.HasSwitch("http-server")) {
+    http_memory_cache.AddFiles();
+    std::string value = cl.GetSwitchValueASCII("http-server");
+    std::vector<std::string> valueArgs = split(value, ',');
+    while (valueArgs.size() < 4)
+      valueArgs.push_back(std::string());
+    g_proxy_config.AddAcceptor(net::FLIP_HANDLER_HTTP_SERVER,
+                               valueArgs[0],
+                               valueArgs[1],
+                               valueArgs[2],
+                               valueArgs[3],
+                               std::string(),
+                               std::string(),
+                               std::string(),
+                               std::string(),
+                               0,
+                               FLAGS_accept_backlog_size,
+                               FLAGS_disable_nagle,
+                               FLAGS_accepts_per_wake,
+                               FLAGS_reuseport,
+                               wait_for_iface,
+                               &http_memory_cache);
+  }
+
+  std::vector<net::SMAcceptorThread*> sm_worker_threads_;
+
+  for (i = 0; i < g_proxy_config.acceptors_.size(); i++) {
+    net::FlipAcceptor* acceptor = g_proxy_config.acceptors_[i];
+
+    sm_worker_threads_.push_back(new net::SMAcceptorThread(
+        acceptor, (net::MemoryCache*)acceptor->memory_cache_));
+    // Note that spdy_memory_cache is not threadsafe, it is merely
+    // thread compatible. Thus, if ever we are to spawn multiple threads,
+    // we either must make the MemoryCache threadsafe, or use
+    // a separate MemoryCache for each thread.
+    //
+    // The latter is what is currently being done as we spawn
+    // a separate thread for each http and spdy server acceptor.
+
+    sm_worker_threads_.back()->InitWorker();
+    sm_worker_threads_.back()->Start();
+  }
+
+  while (!wantExit) {
+    // Close logfile when HUP signal is received. Logging system will
+    // automatically reopen on next log message.
+    if (wantLogClose) {
+      wantLogClose = false;
+      VLOG(1) << "HUP received, reopening log file.";
+      logging::CloseLogFile();
+    }
+    if (GotQuitFromStdin()) {
+      for (unsigned int i = 0; i < sm_worker_threads_.size(); ++i) {
+        sm_worker_threads_[i]->Quit();
+      }
+      for (unsigned int i = 0; i < sm_worker_threads_.size(); ++i) {
+        sm_worker_threads_[i]->Join();
+      }
+      break;
+    }
+    usleep(1000 * 10);  // 10 ms
+  }
+
+  unlink(PIDFILE);
+  close(pidfile_fd);
+  return 0;
+}
diff --git a/net/tools/flip_server/flip_test_utils.cc b/net/tools/flip_server/flip_test_utils.cc
new file mode 100644
index 0000000..c99d7d6
--- /dev/null
+++ b/net/tools/flip_server/flip_test_utils.cc
@@ -0,0 +1,13 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/tools/flip_server/flip_test_utils.h"
+
+namespace net {
+
+MockSMInterface::MockSMInterface() {}
+
+MockSMInterface::~MockSMInterface() {}
+
+}  // namespace net
diff --git a/net/tools/flip_server/flip_test_utils.h b/net/tools/flip_server/flip_test_utils.h
new file mode 100644
index 0000000..0a61a60
--- /dev/null
+++ b/net/tools/flip_server/flip_test_utils.h
@@ -0,0 +1,54 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_TOOLS_FLIP_SERVER_FLIP_TEST_UTILS_H_
+#define NET_TOOLS_FLIP_SERVER_FLIP_TEST_UTILS_H_
+
+#include <string>
+
+#include "net/tools/flip_server/sm_interface.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+class MockSMInterface : public SMInterface {
+ public:
+  MockSMInterface();
+  virtual ~MockSMInterface();
+
+  MOCK_METHOD2(InitSMInterface, void(SMInterface*, int32));
+  MOCK_METHOD8(InitSMConnection,
+               void(SMConnectionPoolInterface*,
+                    SMInterface*,
+                    EpollServer*,
+                    int,
+                    std::string,
+                    std::string,
+                    std::string,
+                    bool));
+  MOCK_METHOD2(ProcessReadInput, size_t(const char*, size_t));
+  MOCK_METHOD2(ProcessWriteInput, size_t(const char*, size_t));
+  MOCK_METHOD1(SetStreamID, void(uint32 stream_id));
+  MOCK_CONST_METHOD0(MessageFullyRead, bool());
+  MOCK_CONST_METHOD0(Error, bool());
+  MOCK_CONST_METHOD0(ErrorAsString, const char*());
+  MOCK_METHOD0(Reset, void());
+  MOCK_METHOD1(ResetForNewInterface, void(int32 server_idx));
+  MOCK_METHOD0(ResetForNewConnection, void());
+  MOCK_METHOD0(Cleanup, void());
+  MOCK_METHOD0(PostAcceptHook, int());
+  MOCK_METHOD3(NewStream, void(uint32, uint32, const std::string&));
+  MOCK_METHOD1(SendEOF, void(uint32 stream_id));
+  MOCK_METHOD1(SendErrorNotFound, void(uint32 stream_id));
+  MOCK_METHOD2(SendSynStream, size_t(uint32, const BalsaHeaders&));
+  MOCK_METHOD2(SendSynReply, size_t(uint32, const BalsaHeaders&));
+  MOCK_METHOD5(SendDataFrame, void(uint32, const char*, int64, uint32, bool));
+  MOCK_METHOD0(GetOutput, void());
+  MOCK_METHOD0(set_is_request, void());
+};
+
+}  // namespace net
+
+#endif  // NET_TOOLS_FLIP_SERVER_FLIP_TEST_UTILS_H_
diff --git a/net/tools/flip_server/http_interface.cc b/net/tools/flip_server/http_interface.cc
new file mode 100644
index 0000000..b939c5f
--- /dev/null
+++ b/net/tools/flip_server/http_interface.cc
@@ -0,0 +1,340 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/tools/flip_server/http_interface.h"
+
+#include "net/tools/balsa/balsa_frame.h"
+#include "net/tools/dump_cache/url_utilities.h"
+#include "net/tools/flip_server/flip_config.h"
+#include "net/tools/flip_server/sm_connection.h"
+#include "net/tools/flip_server/spdy_util.h"
+
+namespace net {
+
+HttpSM::HttpSM(SMConnection* connection,
+               SMInterface* sm_spdy_interface,
+               MemoryCache* memory_cache,
+               FlipAcceptor* acceptor)
+    : http_framer_(new BalsaFrame),
+      stream_id_(0),
+      server_idx_(-1),
+      connection_(connection),
+      sm_spdy_interface_(sm_spdy_interface),
+      output_list_(connection->output_list()),
+      output_ordering_(connection),
+      memory_cache_(connection->memory_cache()),
+      acceptor_(acceptor) {
+  http_framer_->set_balsa_visitor(this);
+  http_framer_->set_balsa_headers(&headers_);
+  if (acceptor_->flip_handler_type_ == FLIP_HANDLER_PROXY)
+    http_framer_->set_is_request(false);
+}
+HttpSM::~HttpSM() {
+  Reset();
+  delete http_framer_;
+}
+
+void HttpSM::ProcessBodyData(const char* input, size_t size) {
+  if (acceptor_->flip_handler_type_ == FLIP_HANDLER_PROXY) {
+    VLOG(2) << ACCEPTOR_CLIENT_IDENT << "HttpSM: Process Body Data: stream "
+            << stream_id_ << ": size " << size;
+    sm_spdy_interface_->SendDataFrame(stream_id_, input, size, 0, false);
+  }
+}
+
+void HttpSM::ProcessHeaders(const BalsaHeaders& headers) {
+  if (acceptor_->flip_handler_type_ == FLIP_HANDLER_HTTP_SERVER) {
+    std::string host =
+        UrlUtilities::GetUrlHost(headers.GetHeader("Host").as_string());
+    std::string method = headers.request_method().as_string();
+    VLOG(1) << ACCEPTOR_CLIENT_IDENT
+            << "Received Request: " << headers.request_uri().as_string() << " "
+            << method;
+    std::string filename =
+        EncodeURL(headers.request_uri().as_string(), host, method);
+    NewStream(stream_id_, 0, filename);
+    stream_id_ += 2;
+  } else {
+    VLOG(1) << ACCEPTOR_CLIENT_IDENT << "HttpSM: Received Response from "
+            << connection_->server_ip_ << ":" << connection_->server_port_
+            << " ";
+    sm_spdy_interface_->SendSynReply(stream_id_, headers);
+  }
+}
+
+void HttpSM::MessageDone() {
+  if (acceptor_->flip_handler_type_ == FLIP_HANDLER_PROXY) {
+    VLOG(2) << ACCEPTOR_CLIENT_IDENT << "HttpSM: MessageDone. Sending EOF: "
+            << "stream " << stream_id_;
+    sm_spdy_interface_->SendEOF(stream_id_);
+  } else {
+    VLOG(2) << ACCEPTOR_CLIENT_IDENT << "HttpSM: MessageDone.";
+  }
+}
+
+void HttpSM::HandleHeaderError(BalsaFrame* framer) { HandleError(); }
+
+void HttpSM::HandleChunkingError(BalsaFrame* framer) { HandleError(); }
+
+void HttpSM::HandleBodyError(BalsaFrame* framer) { HandleError(); }
+
+void HttpSM::HandleError() {
+  VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Error detected";
+}
+
+void HttpSM::AddToOutputOrder(const MemCacheIter& mci) {
+  output_ordering_.AddToOutputOrder(mci);
+}
+
+void HttpSM::InitSMInterface(SMInterface* sm_spdy_interface, int32 server_idx) {
+  sm_spdy_interface_ = sm_spdy_interface;
+  server_idx_ = server_idx;
+}
+
+void HttpSM::InitSMConnection(SMConnectionPoolInterface* connection_pool,
+                              SMInterface* sm_interface,
+                              EpollServer* epoll_server,
+                              int fd,
+                              std::string server_ip,
+                              std::string server_port,
+                              std::string remote_ip,
+                              bool use_ssl) {
+  VLOG(2) << ACCEPTOR_CLIENT_IDENT << "HttpSM: Initializing server "
+          << "connection.";
+  connection_->InitSMConnection(connection_pool,
+                                sm_interface,
+                                epoll_server,
+                                fd,
+                                server_ip,
+                                server_port,
+                                remote_ip,
+                                use_ssl);
+}
+
+size_t HttpSM::ProcessReadInput(const char* data, size_t len) {
+  VLOG(2) << ACCEPTOR_CLIENT_IDENT << "HttpSM: Process read input: stream "
+          << stream_id_;
+  return http_framer_->ProcessInput(data, len);
+}
+
+size_t HttpSM::ProcessWriteInput(const char* data, size_t len) {
+  VLOG(2) << ACCEPTOR_CLIENT_IDENT << "HttpSM: Process write input: size "
+          << len << ": stream " << stream_id_;
+  char* dataPtr = new char[len];
+  memcpy(dataPtr, data, len);
+  DataFrame* data_frame = new DataFrame;
+  data_frame->data = dataPtr;
+  data_frame->size = len;
+  data_frame->delete_when_done = true;
+  connection_->EnqueueDataFrame(data_frame);
+  return len;
+}
+
+bool HttpSM::MessageFullyRead() const {
+  return http_framer_->MessageFullyRead();
+}
+
+void HttpSM::SetStreamID(uint32 stream_id) { stream_id_ = stream_id; }
+
+bool HttpSM::Error() const { return http_framer_->Error(); }
+
+const char* HttpSM::ErrorAsString() const {
+  return BalsaFrameEnums::ErrorCodeToString(http_framer_->ErrorCode());
+}
+
+void HttpSM::Reset() {
+  VLOG(1) << ACCEPTOR_CLIENT_IDENT << "HttpSM: Reset: stream " << stream_id_;
+  http_framer_->Reset();
+}
+
+void HttpSM::ResetForNewConnection() {
+  if (acceptor_->flip_handler_type_ == FLIP_HANDLER_PROXY) {
+    VLOG(1) << ACCEPTOR_CLIENT_IDENT << "HttpSM: Server connection closing "
+            << "to: " << connection_->server_ip_ << ":"
+            << connection_->server_port_ << " ";
+  }
+  // Message has not been fully read, either it is incomplete or the
+  // server is closing the connection to signal message end.
+  if (!MessageFullyRead()) {
+    VLOG(2) << "HTTP response closed before end of file detected. "
+            << "Sending EOF to spdy.";
+    sm_spdy_interface_->SendEOF(stream_id_);
+  }
+  output_ordering_.Reset();
+  http_framer_->Reset();
+  if (sm_spdy_interface_) {
+    sm_spdy_interface_->ResetForNewInterface(server_idx_);
+  }
+}
+
+void HttpSM::Cleanup() {
+  if (!(acceptor_->flip_handler_type_ == FLIP_HANDLER_HTTP_SERVER)) {
+    VLOG(2) << "HttpSM Request Fully Read; stream_id: " << stream_id_;
+    connection_->Cleanup("request complete");
+  }
+}
+
+int HttpSM::PostAcceptHook() { return 1; }
+
+void HttpSM::NewStream(uint32 stream_id,
+                       uint32 priority,
+                       const std::string& filename) {
+  MemCacheIter mci;
+  mci.stream_id = stream_id;
+  mci.priority = priority;
+  if (!memory_cache_->AssignFileData(filename, &mci)) {
+    // error creating new stream.
+    VLOG(2) << ACCEPTOR_CLIENT_IDENT << "Sending ErrorNotFound";
+    SendErrorNotFound(stream_id);
+  } else {
+    AddToOutputOrder(mci);
+  }
+}
+
+void HttpSM::SendEOF(uint32 stream_id) {
+  SendEOFImpl(stream_id);
+  if (acceptor_->flip_handler_type_ == FLIP_HANDLER_PROXY) {
+    sm_spdy_interface_->ResetForNewInterface(server_idx_);
+  }
+}
+
+void HttpSM::SendErrorNotFound(uint32 stream_id) {
+  SendErrorNotFoundImpl(stream_id);
+}
+
+size_t HttpSM::SendSynStream(uint32 stream_id, const BalsaHeaders& headers) {
+  return 0;
+}
+
+size_t HttpSM::SendSynReply(uint32 stream_id, const BalsaHeaders& headers) {
+  return SendSynReplyImpl(stream_id, headers);
+}
+
+void HttpSM::SendDataFrame(uint32 stream_id,
+                           const char* data,
+                           int64 len,
+                           uint32 flags,
+                           bool compress) {
+  SendDataFrameImpl(stream_id, data, len, flags, compress);
+}
+
+void HttpSM::SendEOFImpl(uint32 stream_id) {
+  DataFrame* df = new DataFrame;
+  df->data = "0\r\n\r\n";
+  df->size = 5;
+  df->delete_when_done = false;
+  EnqueueDataFrame(df);
+  if (acceptor_->flip_handler_type_ == FLIP_HANDLER_HTTP_SERVER) {
+    Reset();
+  }
+}
+
+void HttpSM::SendErrorNotFoundImpl(uint32 stream_id) {
+  BalsaHeaders my_headers;
+  my_headers.SetFirstlineFromStringPieces("HTTP/1.1", "404", "Not Found");
+  my_headers.RemoveAllOfHeader("content-length");
+  my_headers.AppendHeader("transfer-encoding", "chunked");
+  SendSynReplyImpl(stream_id, my_headers);
+  SendDataFrame(stream_id, "page not found", 14, 0, false);
+  SendEOFImpl(stream_id);
+  output_ordering_.RemoveStreamId(stream_id);
+}
+
+size_t HttpSM::SendSynReplyImpl(uint32 stream_id, const BalsaHeaders& headers) {
+  SimpleBuffer sb;
+  headers.WriteHeaderAndEndingToBuffer(&sb);
+  DataFrame* df = new DataFrame;
+  df->size = sb.ReadableBytes();
+  char* buffer = new char[df->size];
+  df->data = buffer;
+  df->delete_when_done = true;
+  sb.Read(buffer, df->size);
+  VLOG(2) << ACCEPTOR_CLIENT_IDENT << "Sending HTTP Reply header "
+          << stream_id_;
+  size_t df_size = df->size;
+  EnqueueDataFrame(df);
+  return df_size;
+}
+
+size_t HttpSM::SendSynStreamImpl(uint32 stream_id,
+                                 const BalsaHeaders& headers) {
+  SimpleBuffer sb;
+  headers.WriteHeaderAndEndingToBuffer(&sb);
+  DataFrame* df = new DataFrame;
+  df->size = sb.ReadableBytes();
+  char* buffer = new char[df->size];
+  df->data = buffer;
+  df->delete_when_done = true;
+  sb.Read(buffer, df->size);
+  VLOG(2) << ACCEPTOR_CLIENT_IDENT << "Sending HTTP Reply header "
+          << stream_id_;
+  size_t df_size = df->size;
+  EnqueueDataFrame(df);
+  return df_size;
+}
+
+void HttpSM::SendDataFrameImpl(uint32 stream_id,
+                               const char* data,
+                               int64 len,
+                               uint32 flags,
+                               bool compress) {
+  char chunk_buf[128];
+  snprintf(chunk_buf, sizeof(chunk_buf), "%x\r\n", (unsigned int)len);
+  std::string chunk_description(chunk_buf);
+  DataFrame* df = new DataFrame;
+  df->size = chunk_description.size() + len + 2;
+  char* buffer = new char[df->size];
+  df->data = buffer;
+  df->delete_when_done = true;
+  memcpy(buffer, chunk_description.data(), chunk_description.size());
+  memcpy(buffer + chunk_description.size(), data, len);
+  memcpy(buffer + chunk_description.size() + len, "\r\n", 2);
+  EnqueueDataFrame(df);
+}
+
+void HttpSM::EnqueueDataFrame(DataFrame* df) {
+  VLOG(2) << ACCEPTOR_CLIENT_IDENT << "HttpSM: Enqueue data frame: stream "
+          << stream_id_;
+  connection_->EnqueueDataFrame(df);
+}
+
+void HttpSM::GetOutput() {
+  MemCacheIter* mci = output_ordering_.GetIter();
+  if (mci == NULL) {
+    VLOG(2) << ACCEPTOR_CLIENT_IDENT << "HttpSM: GetOutput: nothing to "
+            << "output!?: stream " << stream_id_;
+    return;
+  }
+  if (!mci->transformed_header) {
+    mci->bytes_sent =
+        SendSynReply(mci->stream_id, *(mci->file_data->headers()));
+    mci->transformed_header = true;
+    VLOG(2) << ACCEPTOR_CLIENT_IDENT << "HttpSM: GetOutput transformed "
+            << "header stream_id: [" << mci->stream_id << "]";
+    return;
+  }
+  if (mci->body_bytes_consumed >= mci->file_data->body().size()) {
+    SendEOF(mci->stream_id);
+    output_ordering_.RemoveStreamId(mci->stream_id);
+    VLOG(2) << ACCEPTOR_CLIENT_IDENT << "GetOutput remove_stream_id: ["
+            << mci->stream_id << "]";
+    return;
+  }
+  size_t num_to_write =
+      mci->file_data->body().size() - mci->body_bytes_consumed;
+  if (num_to_write > mci->max_segment_size)
+    num_to_write = mci->max_segment_size;
+
+  SendDataFrame(mci->stream_id,
+                mci->file_data->body().data() + mci->body_bytes_consumed,
+                num_to_write,
+                0,
+                true);
+  VLOG(2) << ACCEPTOR_CLIENT_IDENT << "HttpSM: GetOutput SendDataFrame["
+          << mci->stream_id << "]: " << num_to_write;
+  mci->body_bytes_consumed += num_to_write;
+  mci->bytes_sent += num_to_write;
+}
+
+}  // namespace net
diff --git a/net/tools/flip_server/http_interface.h b/net/tools/flip_server/http_interface.h
new file mode 100644
index 0000000..30b7979
--- /dev/null
+++ b/net/tools/flip_server/http_interface.h
@@ -0,0 +1,142 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_TOOLS_FLIP_SERVER_HTTP_INTERFACE_H_
+#define NET_TOOLS_FLIP_SERVER_HTTP_INTERFACE_H_
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "net/tools/balsa/balsa_headers.h"
+#include "net/tools/balsa/balsa_visitor_interface.h"
+#include "net/tools/flip_server/output_ordering.h"
+#include "net/tools/flip_server/sm_connection.h"
+#include "net/tools/flip_server/sm_interface.h"
+
+namespace net {
+
+class BalsaFrame;
+class DataFrame;
+class EpollServer;
+class FlipAcceptor;
+class MemoryCache;
+
+class HttpSM : public BalsaVisitorInterface, public SMInterface {
+ public:
+  HttpSM(SMConnection* connection,
+         SMInterface* sm_spdy_interface,
+         MemoryCache* memory_cache,
+         FlipAcceptor* acceptor);
+  virtual ~HttpSM();
+
+ private:
+  // BalsaVisitorInterface:
+  virtual void ProcessBodyInput(const char* input, size_t size) OVERRIDE {}
+  virtual void ProcessBodyData(const char* input, size_t size) OVERRIDE;
+  virtual void ProcessHeaderInput(const char* input, size_t size) OVERRIDE {}
+  virtual void ProcessTrailerInput(const char* input, size_t size) OVERRIDE {}
+  virtual void ProcessHeaders(const BalsaHeaders& headers) OVERRIDE;
+  virtual void ProcessRequestFirstLine(const char* line_input,
+                                       size_t line_length,
+                                       const char* method_input,
+                                       size_t method_length,
+                                       const char* request_uri_input,
+                                       size_t request_uri_length,
+                                       const char* version_input,
+                                       size_t version_length) OVERRIDE {}
+  virtual void ProcessResponseFirstLine(const char* line_input,
+                                        size_t line_length,
+                                        const char* version_input,
+                                        size_t version_length,
+                                        const char* status_input,
+                                        size_t status_length,
+                                        const char* reason_input,
+                                        size_t reason_length) OVERRIDE {}
+  virtual void ProcessChunkLength(size_t chunk_length) OVERRIDE {}
+  virtual void ProcessChunkExtensions(const char* input, size_t size) OVERRIDE {
+  }
+  virtual void HeaderDone() OVERRIDE {}
+  virtual void MessageDone() OVERRIDE;
+  virtual void HandleHeaderError(BalsaFrame* framer) OVERRIDE;
+  virtual void HandleHeaderWarning(BalsaFrame* framer) OVERRIDE {}
+  virtual void HandleChunkingError(BalsaFrame* framer) OVERRIDE;
+  virtual void HandleBodyError(BalsaFrame* framer) OVERRIDE;
+
+  void HandleError();
+
+ public:
+  void AddToOutputOrder(const MemCacheIter& mci);
+  BalsaFrame* spdy_framer() { return http_framer_; }
+  virtual void set_is_request() OVERRIDE {}
+  const OutputOrdering& output_ordering() const { return output_ordering_; }
+
+  // SMInterface:
+  virtual void InitSMInterface(SMInterface* sm_spdy_interface,
+                               int32 server_idx) OVERRIDE;
+  virtual void InitSMConnection(SMConnectionPoolInterface* connection_pool,
+                                SMInterface* sm_interface,
+                                EpollServer* epoll_server,
+                                int fd,
+                                std::string server_ip,
+                                std::string server_port,
+                                std::string remote_ip,
+                                bool use_ssl) OVERRIDE;
+  virtual size_t ProcessReadInput(const char* data, size_t len) OVERRIDE;
+  virtual size_t ProcessWriteInput(const char* data, size_t len) OVERRIDE;
+  virtual bool MessageFullyRead() const OVERRIDE;
+  virtual void SetStreamID(uint32 stream_id) OVERRIDE;
+  virtual bool Error() const OVERRIDE;
+  virtual const char* ErrorAsString() const OVERRIDE;
+  virtual void Reset() OVERRIDE;
+  virtual void ResetForNewInterface(int32 server_idx) OVERRIDE {}
+  virtual void ResetForNewConnection() OVERRIDE;
+  virtual void Cleanup() OVERRIDE;
+  virtual int PostAcceptHook() OVERRIDE;
+
+  virtual void NewStream(uint32 stream_id,
+                         uint32 priority,
+                         const std::string& filename) OVERRIDE;
+  virtual void SendEOF(uint32 stream_id) OVERRIDE;
+  virtual void SendErrorNotFound(uint32 stream_id) OVERRIDE;
+  virtual size_t SendSynStream(uint32 stream_id,
+                               const BalsaHeaders& headers) OVERRIDE;
+  virtual size_t SendSynReply(uint32 stream_id,
+                              const BalsaHeaders& headers) OVERRIDE;
+  virtual void SendDataFrame(uint32 stream_id,
+                             const char* data,
+                             int64 len,
+                             uint32 flags,
+                             bool compress) OVERRIDE;
+
+ private:
+  void SendEOFImpl(uint32 stream_id);
+  void SendErrorNotFoundImpl(uint32 stream_id);
+  void SendOKResponseImpl(uint32 stream_id, const std::string& output);
+  size_t SendSynReplyImpl(uint32 stream_id, const BalsaHeaders& headers);
+  size_t SendSynStreamImpl(uint32 stream_id, const BalsaHeaders& headers);
+  void SendDataFrameImpl(uint32 stream_id,
+                         const char* data,
+                         int64 len,
+                         uint32 flags,
+                         bool compress);
+  void EnqueueDataFrame(DataFrame* df);
+  virtual void GetOutput() OVERRIDE;
+
+ private:
+  BalsaFrame* http_framer_;
+  BalsaHeaders headers_;
+  uint32 stream_id_;
+  int32 server_idx_;
+
+  SMConnection* connection_;
+  SMInterface* sm_spdy_interface_;
+  OutputList* output_list_;
+  OutputOrdering output_ordering_;
+  MemoryCache* memory_cache_;
+  FlipAcceptor* acceptor_;
+};
+
+}  // namespace net
+
+#endif  // NET_TOOLS_FLIP_SERVER_HTTP_INTERFACE_H_
diff --git a/net/tools/flip_server/http_interface_test.cc b/net/tools/flip_server/http_interface_test.cc
new file mode 100644
index 0000000..969607b
--- /dev/null
+++ b/net/tools/flip_server/http_interface_test.cc
@@ -0,0 +1,491 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/tools/flip_server/http_interface.h"
+
+#include <list>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/stl_util.h"
+#include "base/strings/string_piece.h"
+#include "net/tools/balsa/balsa_enums.h"
+#include "net/tools/balsa/balsa_frame.h"
+#include "net/tools/balsa/balsa_headers.h"
+#include "net/tools/flip_server/flip_config.h"
+#include "net/tools/flip_server/flip_test_utils.h"
+#include "net/tools/flip_server/mem_cache.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+using ::base::StringPiece;
+using ::testing::_;
+using ::testing::InSequence;
+
+namespace {
+
+class MockSMConnection : public SMConnection {
+ public:
+  MockSMConnection(EpollServer* epoll_server,
+                   SSLState* ssl_state,
+                   MemoryCache* memory_cache,
+                   FlipAcceptor* acceptor,
+                   std::string log_prefix)
+      : SMConnection(epoll_server,
+                     ssl_state,
+                     memory_cache,
+                     acceptor,
+                     log_prefix) {}
+
+  MOCK_METHOD0(Cleanup, void());
+  MOCK_METHOD8(InitSMConnection,
+               void(SMConnectionPoolInterface*,
+                    SMInterface*,
+                    EpollServer*,
+                    int,
+                    std::string,
+                    std::string,
+                    std::string,
+                    bool));
+};
+
+class FlipHttpSMTest : public ::testing::Test {
+ public:
+  explicit FlipHttpSMTest(FlipHandlerType type = FLIP_HANDLER_PROXY) {
+    SSLState* ssl_state = NULL;
+    mock_another_interface_.reset(new MockSMInterface);
+    memory_cache_.reset(new MemoryCache);
+    acceptor_.reset(new FlipAcceptor(type,
+                                     "127.0.0.1",
+                                     "8941",
+                                     "ssl_cert_filename",
+                                     "ssl_key_filename",
+                                     "127.0.0.1",
+                                     "8942",
+                                     "127.0.0.1",
+                                     "8943",
+                                     1,
+                                     0,
+                                     true,
+                                     1,
+                                     false,
+                                     true,
+                                     NULL));
+    epoll_server_.reset(new EpollServer);
+    connection_.reset(new MockSMConnection(epoll_server_.get(),
+                                           ssl_state,
+                                           memory_cache_.get(),
+                                           acceptor_.get(),
+                                           "log_prefix"));
+
+    interface_.reset(new HttpSM(connection_.get(),
+                                mock_another_interface_.get(),
+                                memory_cache_.get(),
+                                acceptor_.get()));
+  }
+
+  virtual void TearDown() OVERRIDE {
+    if (acceptor_->listen_fd_ >= 0) {
+      epoll_server_->UnregisterFD(acceptor_->listen_fd_);
+      close(acceptor_->listen_fd_);
+      acceptor_->listen_fd_ = -1;
+    }
+    STLDeleteElements(connection_->output_list());
+  }
+
+  bool HasStream(uint32 stream_id) {
+    return interface_->output_ordering().ExistsInPriorityMaps(stream_id);
+  }
+
+ protected:
+  scoped_ptr<MockSMInterface> mock_another_interface_;
+  scoped_ptr<MemoryCache> memory_cache_;
+  scoped_ptr<FlipAcceptor> acceptor_;
+  scoped_ptr<EpollServer> epoll_server_;
+  scoped_ptr<MockSMConnection> connection_;
+  scoped_ptr<HttpSM> interface_;
+};
+
+class FlipHttpSMProxyTest : public FlipHttpSMTest {
+ public:
+  FlipHttpSMProxyTest() : FlipHttpSMTest(FLIP_HANDLER_PROXY) {}
+  virtual ~FlipHttpSMProxyTest() {}
+};
+
+class FlipHttpSMHttpTest : public FlipHttpSMTest {
+ public:
+  FlipHttpSMHttpTest() : FlipHttpSMTest(FLIP_HANDLER_HTTP_SERVER) {}
+  virtual ~FlipHttpSMHttpTest() {}
+};
+
+class FlipHttpSMSpdyTest : public FlipHttpSMTest {
+ public:
+  FlipHttpSMSpdyTest() : FlipHttpSMTest(FLIP_HANDLER_SPDY_SERVER) {}
+  virtual ~FlipHttpSMSpdyTest() {}
+};
+
+TEST_F(FlipHttpSMTest, Construct) {
+  ASSERT_FALSE(interface_->spdy_framer()->is_request());
+}
+
+TEST_F(FlipHttpSMTest, AddToOutputOrder) {
+  uint32 stream_id = 13;
+  MemCacheIter mci;
+  mci.stream_id = stream_id;
+
+  {
+    BalsaHeaders headers;
+    std::string filename = "foobar";
+    memory_cache_->InsertFile(&headers, filename, "");
+    mci.file_data = memory_cache_->GetFileData(filename);
+  }
+
+  interface_->AddToOutputOrder(mci);
+  ASSERT_TRUE(HasStream(stream_id));
+}
+
+TEST_F(FlipHttpSMTest, InitSMInterface) {
+  scoped_ptr<MockSMInterface> mock(new MockSMInterface);
+  {
+    InSequence s;
+    EXPECT_CALL(*mock_another_interface_, SendEOF(_));
+    EXPECT_CALL(*mock_another_interface_, ResetForNewInterface(_));
+    EXPECT_CALL(*mock, SendEOF(_));
+    EXPECT_CALL(*mock, ResetForNewInterface(_));
+  }
+
+  interface_->ResetForNewConnection();
+  interface_->InitSMInterface(mock.get(), 0);
+  interface_->ResetForNewConnection();
+}
+
+TEST_F(FlipHttpSMTest, InitSMConnection) {
+  EXPECT_CALL(*connection_, InitSMConnection(_, _, _, _, _, _, _, _));
+
+  interface_->InitSMConnection(NULL, NULL, NULL, 0, "", "", "", false);
+}
+
+TEST_F(FlipHttpSMTest, ProcessReadInput) {
+  std::string data =
+      "HTTP/1.1 200 OK\r\n"
+      "Content-Length: 14\r\n\r\n"
+      "hello, world\r\n";
+  testing::MockFunction<void(int)> checkpoint;  // NOLINT
+  {
+    InSequence s;
+    EXPECT_CALL(*mock_another_interface_, SendSynReply(_, _));
+    EXPECT_CALL(checkpoint, Call(0));
+    EXPECT_CALL(*mock_another_interface_, SendDataFrame(_, _, _, _, _));
+    EXPECT_CALL(*mock_another_interface_, SendEOF(_));
+  }
+
+  ASSERT_EQ(BalsaFrameEnums::READING_HEADER_AND_FIRSTLINE,
+            interface_->spdy_framer()->ParseState());
+
+  size_t read = interface_->ProcessReadInput(data.data(), data.size());
+  ASSERT_EQ(39u, read);
+  checkpoint.Call(0);
+  read += interface_->ProcessReadInput(&data.data()[read], data.size() - read);
+  ASSERT_EQ(data.size(), read);
+  ASSERT_EQ(BalsaFrameEnums::MESSAGE_FULLY_READ,
+            interface_->spdy_framer()->ParseState());
+  ASSERT_TRUE(interface_->MessageFullyRead());
+}
+
+TEST_F(FlipHttpSMTest, ProcessWriteInput) {
+  std::string data = "hello, world";
+  interface_->ProcessWriteInput(data.data(), data.size());
+
+  ASSERT_EQ(1u, connection_->output_list()->size());
+  std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin();
+  DataFrame* df = *i++;
+  ASSERT_EQ(data, StringPiece(df->data, df->size));
+  ASSERT_EQ(connection_->output_list()->end(), i);
+}
+
+TEST_F(FlipHttpSMTest, Reset) {
+  std::string data = "HTTP/1.1 200 OK\r\n\r\n";
+  testing::MockFunction<void(int)> checkpoint;  // NOLINT
+  {
+    InSequence s;
+    EXPECT_CALL(*mock_another_interface_, SendSynReply(_, _));
+    EXPECT_CALL(checkpoint, Call(0));
+  }
+
+  ASSERT_EQ(BalsaFrameEnums::READING_HEADER_AND_FIRSTLINE,
+            interface_->spdy_framer()->ParseState());
+
+  interface_->ProcessReadInput(data.data(), data.size());
+  checkpoint.Call(0);
+  ASSERT_FALSE(interface_->MessageFullyRead());
+  ASSERT_EQ(BalsaFrameEnums::READING_UNTIL_CLOSE,
+            interface_->spdy_framer()->ParseState());
+
+  interface_->Reset();
+  ASSERT_EQ(BalsaFrameEnums::READING_HEADER_AND_FIRSTLINE,
+            interface_->spdy_framer()->ParseState());
+}
+
+TEST_F(FlipHttpSMTest, ResetForNewConnection) {
+  std::string data = "HTTP/1.1 200 OK\r\n\r\n";
+  testing::MockFunction<void(int)> checkpoint;  // NOLINT
+  {
+    InSequence s;
+    EXPECT_CALL(*mock_another_interface_, SendSynReply(_, _));
+    EXPECT_CALL(checkpoint, Call(0));
+    EXPECT_CALL(*mock_another_interface_, SendEOF(_));
+    EXPECT_CALL(*mock_another_interface_, ResetForNewInterface(_));
+  }
+
+  ASSERT_EQ(BalsaFrameEnums::READING_HEADER_AND_FIRSTLINE,
+            interface_->spdy_framer()->ParseState());
+
+  interface_->ProcessReadInput(data.data(), data.size());
+  checkpoint.Call(0);
+  ASSERT_FALSE(interface_->MessageFullyRead());
+  ASSERT_EQ(BalsaFrameEnums::READING_UNTIL_CLOSE,
+            interface_->spdy_framer()->ParseState());
+
+  interface_->ResetForNewConnection();
+  ASSERT_EQ(BalsaFrameEnums::READING_HEADER_AND_FIRSTLINE,
+            interface_->spdy_framer()->ParseState());
+}
+
+TEST_F(FlipHttpSMTest, NewStream) {
+  uint32 stream_id = 4;
+  {
+    BalsaHeaders headers;
+    std::string filename = "foobar";
+    memory_cache_->InsertFile(&headers, filename, "");
+  }
+
+  interface_->NewStream(stream_id, 1, "foobar");
+  ASSERT_TRUE(HasStream(stream_id));
+}
+
+TEST_F(FlipHttpSMTest, NewStreamError) {
+  std::string syn_reply =
+      "HTTP/1.1 404 Not Found\r\n"
+      "transfer-encoding: chunked\r\n\r\n";
+  std::string body = "e\r\npage not found\r\n";
+  uint32 stream_id = 4;
+
+  ASSERT_FALSE(HasStream(stream_id));
+  interface_->NewStream(stream_id, 1, "foobar");
+
+  ASSERT_EQ(3u, connection_->output_list()->size());
+  std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin();
+  DataFrame* df = *i++;
+  ASSERT_EQ(syn_reply, StringPiece(df->data, df->size));
+  df = *i++;
+  ASSERT_EQ(body, StringPiece(df->data, df->size));
+  df = *i++;
+  ASSERT_EQ("0\r\n\r\n", StringPiece(df->data, df->size));
+  ASSERT_FALSE(HasStream(stream_id));
+}
+
+TEST_F(FlipHttpSMTest, SendErrorNotFound) {
+  std::string syn_reply =
+      "HTTP/1.1 404 Not Found\r\n"
+      "transfer-encoding: chunked\r\n\r\n";
+  std::string body = "e\r\npage not found\r\n";
+  uint32 stream_id = 13;
+  MemCacheIter mci;
+  mci.stream_id = stream_id;
+
+  {
+    BalsaHeaders headers;
+    std::string filename = "foobar";
+    memory_cache_->InsertFile(&headers, filename, "");
+    mci.file_data = memory_cache_->GetFileData(filename);
+  }
+
+  interface_->AddToOutputOrder(mci);
+  ASSERT_TRUE(HasStream(stream_id));
+  interface_->SendErrorNotFound(stream_id);
+
+  ASSERT_EQ(3u, connection_->output_list()->size());
+  std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin();
+  DataFrame* df = *i++;
+  ASSERT_EQ(syn_reply, StringPiece(df->data, df->size));
+  df = *i++;
+  ASSERT_EQ(body, StringPiece(df->data, df->size));
+  df = *i++;
+  ASSERT_EQ("0\r\n\r\n", StringPiece(df->data, df->size));
+  ASSERT_FALSE(HasStream(stream_id));
+}
+
+TEST_F(FlipHttpSMTest, SendSynStream) {
+  std::string expected =
+      "GET / HTTP/1.0\r\n"
+      "key1: value1\r\n\r\n";
+  BalsaHeaders headers;
+  headers.SetResponseFirstlineFromStringPieces("GET", "/path", "HTTP/1.0");
+  headers.AppendHeader("key1", "value1");
+  interface_->SendSynStream(18, headers);
+
+  // TODO(yhirano): Is this behavior correct?
+  ASSERT_EQ(0u, connection_->output_list()->size());
+}
+
+TEST_F(FlipHttpSMTest, SendSynReply) {
+  std::string expected =
+      "HTTP/1.1 200 OK\r\n"
+      "key1: value1\r\n\r\n";
+  BalsaHeaders headers;
+  headers.SetResponseFirstlineFromStringPieces("HTTP/1.1", "200", "OK");
+  headers.AppendHeader("key1", "value1");
+  interface_->SendSynReply(18, headers);
+
+  ASSERT_EQ(1u, connection_->output_list()->size());
+  DataFrame* df = connection_->output_list()->front();
+  ASSERT_EQ(expected, StringPiece(df->data, df->size));
+}
+
+TEST_F(FlipHttpSMTest, SendDataFrame) {
+  std::string data = "foo bar baz";
+  interface_->SendDataFrame(12, data.data(), data.size(), 0, false);
+
+  ASSERT_EQ(1u, connection_->output_list()->size());
+  DataFrame* df = connection_->output_list()->front();
+  ASSERT_EQ("b\r\nfoo bar baz\r\n", StringPiece(df->data, df->size));
+}
+
+TEST_F(FlipHttpSMProxyTest, ProcessBodyData) {
+  BalsaVisitorInterface* visitor = interface_.get();
+  std::string data = "hello, world";
+  {
+    InSequence s;
+    EXPECT_CALL(*mock_another_interface_,
+                SendDataFrame(0, data.data(), data.size(), 0, false));
+  }
+  visitor->ProcessBodyData(data.data(), data.size());
+}
+
+// --
+// FlipHttpSMProxyTest
+
+TEST_F(FlipHttpSMProxyTest, ProcessHeaders) {
+  BalsaVisitorInterface* visitor = interface_.get();
+  {
+    InSequence s;
+    EXPECT_CALL(*mock_another_interface_, SendSynReply(0, _));
+  }
+  BalsaHeaders headers;
+  visitor->ProcessHeaders(headers);
+}
+
+TEST_F(FlipHttpSMProxyTest, MessageDone) {
+  BalsaVisitorInterface* visitor = interface_.get();
+  {
+    InSequence s;
+    EXPECT_CALL(*mock_another_interface_, SendEOF(0));
+  }
+  visitor->MessageDone();
+}
+
+TEST_F(FlipHttpSMProxyTest, Cleanup) {
+  EXPECT_CALL(*connection_, Cleanup()).Times(0);
+  interface_->Cleanup();
+}
+
+TEST_F(FlipHttpSMProxyTest, SendEOF) {
+  {
+    InSequence s;
+    EXPECT_CALL(*mock_another_interface_, ResetForNewInterface(_));
+  }
+  interface_->SendEOF(32);
+  ASSERT_EQ(1u, connection_->output_list()->size());
+  DataFrame* df = connection_->output_list()->front();
+  ASSERT_EQ("0\r\n\r\n", StringPiece(df->data, df->size));
+}
+
+// --
+// FlipHttpSMHttpTest
+
+TEST_F(FlipHttpSMHttpTest, ProcessHeaders) {
+  BalsaVisitorInterface* visitor = interface_.get();
+  {
+    BalsaHeaders headers;
+    std::string filename = "GET_/path/file";
+    memory_cache_->InsertFile(&headers, filename, "");
+  }
+
+  BalsaHeaders headers;
+  headers.AppendHeader("Host", "example.com");
+  headers.SetRequestFirstlineFromStringPieces("GET", "/path/file", "HTTP/1.0");
+  uint32 stream_id = 133;
+  interface_->SetStreamID(stream_id);
+  ASSERT_FALSE(HasStream(stream_id));
+  visitor->ProcessHeaders(headers);
+  ASSERT_TRUE(HasStream(stream_id));
+}
+
+TEST_F(FlipHttpSMHttpTest, MessageDone) {
+  BalsaVisitorInterface* visitor = interface_.get();
+  {
+    InSequence s;
+    EXPECT_CALL(*mock_another_interface_, SendEOF(0)).Times(0);
+  }
+  visitor->MessageDone();
+}
+
+TEST_F(FlipHttpSMHttpTest, Cleanup) {
+  EXPECT_CALL(*connection_, Cleanup()).Times(0);
+  interface_->Cleanup();
+}
+
+TEST_F(FlipHttpSMHttpTest, SendEOF) {
+  {
+    InSequence s;
+    EXPECT_CALL(*mock_another_interface_, ResetForNewInterface(_)).Times(0);
+  }
+  interface_->SendEOF(32);
+  ASSERT_EQ(1u, connection_->output_list()->size());
+  DataFrame* df = connection_->output_list()->front();
+  ASSERT_EQ("0\r\n\r\n", StringPiece(df->data, df->size));
+}
+
+// --
+// FlipHttpSMSpdyTest
+
+TEST_F(FlipHttpSMSpdyTest, ProcessHeaders) {
+  BalsaVisitorInterface* visitor = interface_.get();
+  {
+    InSequence s;
+    EXPECT_CALL(*mock_another_interface_, SendSynReply(0, _));
+  }
+  BalsaHeaders headers;
+  visitor->ProcessHeaders(headers);
+}
+
+TEST_F(FlipHttpSMSpdyTest, MessageDone) {
+  BalsaVisitorInterface* visitor = interface_.get();
+  {
+    InSequence s;
+    EXPECT_CALL(*mock_another_interface_, SendEOF(0)).Times(0);
+  }
+  visitor->MessageDone();
+}
+
+TEST_F(FlipHttpSMSpdyTest, Cleanup) {
+  EXPECT_CALL(*connection_, Cleanup()).Times(0);
+  interface_->Cleanup();
+}
+
+TEST_F(FlipHttpSMSpdyTest, SendEOF) {
+  {
+    InSequence s;
+    EXPECT_CALL(*mock_another_interface_, ResetForNewInterface(_)).Times(0);
+  }
+  interface_->SendEOF(32);
+  ASSERT_EQ(1u, connection_->output_list()->size());
+  DataFrame* df = connection_->output_list()->front();
+  ASSERT_EQ("0\r\n\r\n", StringPiece(df->data, df->size));
+}
+
+}  // namespace
+
+}  // namespace net
diff --git a/net/tools/flip_server/http_message_constants.cc b/net/tools/flip_server/http_message_constants.cc
new file mode 100644
index 0000000..a4a8b4d
--- /dev/null
+++ b/net/tools/flip_server/http_message_constants.cc
@@ -0,0 +1,103 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/tools/flip_server/http_message_constants.h"
+
+namespace net {
+
+const char* get_http_status_message(int status_message) {
+  switch (status_message) {
+    case 100:
+      return "Continue";
+    case 101:
+      return "Switching Protocols";
+    case 200:
+      return "OK";
+    case 201:
+      return "Created";
+    case 202:
+      return "Accepted";
+    case 203:
+      return "Non-Authoritative Information";
+    case 204:
+      return "No Content";
+    case 205:
+      return "Reset Content";
+    case 206:
+      return "Partial Content";
+    case 300:
+      return "Multiple Choices";
+    case 301:
+      return "Moved Permanently";
+    case 302:
+      return "Found";
+    case 303:
+      return "See Other";
+    case 304:
+      return "Not Modified";
+    case 305:
+      return "Use Proxy";
+    case 307:
+      return "Temporary Redirect";
+    case 400:
+      return "Bad Request";
+    case 401:
+      return "Unauthorized";
+    case 402:
+      return "Payment Required";
+    case 403:
+      return "Forbidden";
+    case 404:
+      return "Not Found";
+    case 405:
+      return "Method Not Allowed";
+    case 406:
+      return "Not Acceptable";
+    case 407:
+      return "Proxy Authentication Required";
+    case 408:
+      return "Request Time-out";
+    case 409:
+      return "Conflict";
+    case 410:
+      return "Gone";
+    case 411:
+      return "Length Required";
+    case 412:
+      return "Precondition Failed";
+    case 413:
+      return "Request Entity Too Large";
+    case 414:
+      return "Request-URI Too Large";
+    case 415:
+      return "Unsupported Media Type";
+    case 416:
+      return "Requested range not satisfiable";
+    case 417:
+      return "Expectation Failed";
+    case 500:
+      return "Internal Server Error";
+    case 501:
+      return "Not Implemented";
+    case 502:
+      return "Bad Gateway";
+    case 503:
+      return "Service Unavailable";
+    case 504:
+      return "Gateway Time-out";
+    case 505:
+      return "HTTP Version not supported";
+  }
+  return "unknown";
+}
+
+const int http_status_codes[] = {
+    100, 101, 200, 201, 202, 203, 204, 205, 206, 300, 301, 302, 303, 304,
+    305, 307, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411,
+    412, 413, 414, 415, 416, 417, 500, 501, 502, 503, 504, 505};
+
+const int http_status_code_count =
+    sizeof(http_status_codes) / sizeof(http_status_codes[0]);
+
+}  // namespace net
diff --git a/net/tools/flip_server/loadtime_measurement.h b/net/tools/flip_server/loadtime_measurement.h
new file mode 100644
index 0000000..d18ac2f
--- /dev/null
+++ b/net/tools/flip_server/loadtime_measurement.h
@@ -0,0 +1,96 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_TOOLS_FLIP_SERVER_LOADTIME_MEASUREMENT_H_
+#define NET_TOOLS_FLIP_SERVER_LOADTIME_MEASUREMENT_H_
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/files/file_util.h"
+#include "base/strings/string_split.h"
+
+// Class to handle loadtime measure related urls, which all start with testing
+// The in memory server has a singleton object of this class. It includes a
+// html file containing javascript to go through a list of urls and upload the
+// loadtime. The users can modify urls.txt to define the urls they want to
+// measure and start with downloading the html file from browser.
+class LoadtimeMeasurement {
+ public:
+  LoadtimeMeasurement(const std::string& urls_file,
+                      const std::string& pageload_html_file)
+      : num_urls_(0), pageload_html_file_(pageload_html_file) {
+    std::string urls_string;
+    base::ReadFileToString(urls_file, &urls_string);
+    base::SplitString(urls_string, '\n', &urls_);
+    num_urls_ = urls_.size();
+  }
+
+  // This is the entry function for all the loadtime measure related urls
+  // It handles the request to html file, get_total_iteration to get number
+  // of urls in the urls file, get each url, report the loadtime for
+  // each url, and the test is completed.
+  void ProcessRequest(const std::string& uri, std::string& output) {
+    // remove "/testing/" from uri to get the action
+    std::string action = uri.substr(9);
+    if (pageload_html_file_.find(action) != std::string::npos) {
+      base::ReadFileToString(pageload_html_file_, &output);
+      return;
+    }
+    if (action.find("get_total_iteration") == 0) {
+      char buffer[16];
+      snprintf(buffer, sizeof(buffer), "%d", num_urls_);
+      output.append(buffer, strlen(buffer));
+      return;
+    }
+    if (action.find("geturl") == 0) {
+      size_t b = action.find_first_of('=');
+      if (b != std::string::npos) {
+        int num = atoi(action.substr(b + 1).c_str());
+        if (num < num_urls_) {
+          output.append(urls_[num]);
+        }
+      }
+      return;
+    }
+    if (action.find("test_complete") == 0) {
+      for (std::map<std::string, int>::const_iterator it = loadtimes_.begin();
+           it != loadtimes_.end();
+           ++it) {
+        LOG(INFO) << it->first << " " << it->second;
+      }
+      loadtimes_.clear();
+      output.append("OK");
+      return;
+    }
+    if (action.find("record_page_load") == 0) {
+      std::vector<std::string> query;
+      base::SplitString(action, '?', &query);
+      std::vector<std::string> params;
+      base::SplitString(query[1], '&', &params);
+      std::vector<std::string> url;
+      std::vector<std::string> loadtime;
+      base::SplitString(params[1], '=', &url);
+      base::SplitString(params[2], '=', &loadtime);
+      loadtimes_[url[1]] = atoi(loadtime[1].c_str());
+      output.append("OK");
+      return;
+    }
+  }
+
+ private:
+  int num_urls_;
+  std::vector<std::string> urls_;
+  std::map<std::string, int> loadtimes_;
+  const std::string pageload_html_file_;
+};
+
+#endif  // NET_TOOLS_FLIP_SERVER_LOADTIME_MEASUREMENT_H_
diff --git a/net/tools/flip_server/mem_cache.cc b/net/tools/flip_server/mem_cache.cc
new file mode 100644
index 0000000..521f56c
--- /dev/null
+++ b/net/tools/flip_server/mem_cache.cc
@@ -0,0 +1,252 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/tools/flip_server/mem_cache.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <deque>
+#include <map>
+#include <string>
+
+#include "base/strings/string_util.h"
+#include "net/tools/balsa/balsa_frame.h"
+#include "net/tools/balsa/balsa_headers.h"
+#include "net/tools/dump_cache/url_to_filename_encoder.h"
+#include "net/tools/dump_cache/url_utilities.h"
+
+namespace {
+// The directory where cache locates);
+const char FLAGS_cache_base_dir[] = ".";
+}  // namespace
+
+namespace net {
+
+void StoreBodyAndHeadersVisitor::ProcessBodyData(const char* input,
+                                                 size_t size) {
+  body.append(input, size);
+}
+
+void StoreBodyAndHeadersVisitor::HandleHeaderError(BalsaFrame* framer) {
+  HandleError();
+}
+
+void StoreBodyAndHeadersVisitor::HandleHeaderWarning(BalsaFrame* framer) {
+  HandleError();
+}
+
+void StoreBodyAndHeadersVisitor::HandleChunkingError(BalsaFrame* framer) {
+  HandleError();
+}
+
+void StoreBodyAndHeadersVisitor::HandleBodyError(BalsaFrame* framer) {
+  HandleError();
+}
+
+FileData::FileData(const BalsaHeaders* headers,
+                   const std::string& filename,
+                   const std::string& body)
+    : filename_(filename), body_(body) {
+  if (headers) {
+    headers_.reset(new BalsaHeaders);
+    headers_->CopyFrom(*headers);
+  }
+}
+
+FileData::FileData() {}
+
+FileData::~FileData() {}
+
+MemoryCache::MemoryCache() : cwd_(FLAGS_cache_base_dir) {}
+
+MemoryCache::~MemoryCache() { ClearFiles(); }
+
+void MemoryCache::CloneFrom(const MemoryCache& mc) {
+  DCHECK_NE(this, &mc);
+  ClearFiles();
+  files_ = mc.files_;
+  cwd_ = mc.cwd_;
+}
+
+void MemoryCache::AddFiles() {
+  std::deque<std::string> paths;
+  paths.push_back(cwd_ + "/GET_");
+  DIR* current_dir = NULL;
+  while (!paths.empty()) {
+    while (current_dir == NULL && !paths.empty()) {
+      std::string current_dir_name = paths.front();
+      VLOG(1) << "Attempting to open dir: \"" << current_dir_name << "\"";
+      current_dir = opendir(current_dir_name.c_str());
+      paths.pop_front();
+
+      if (current_dir == NULL) {
+        perror("Unable to open directory. ");
+        current_dir_name.clear();
+        continue;
+      }
+
+      if (current_dir) {
+        VLOG(1) << "Succeeded opening";
+        for (struct dirent* dir_data = readdir(current_dir); dir_data != NULL;
+             dir_data = readdir(current_dir)) {
+          std::string current_entry_name =
+              current_dir_name + "/" + dir_data->d_name;
+          if (dir_data->d_type == DT_REG) {
+            VLOG(1) << "Found file: " << current_entry_name;
+            ReadAndStoreFileContents(current_entry_name.c_str());
+          } else if (dir_data->d_type == DT_DIR) {
+            VLOG(1) << "Found subdir: " << current_entry_name;
+            if (std::string(dir_data->d_name) != "." &&
+                std::string(dir_data->d_name) != "..") {
+              VLOG(1) << "Adding to search path: " << current_entry_name;
+              paths.push_front(current_entry_name);
+            }
+          }
+        }
+        VLOG(1) << "Oops, no data left. Closing dir.";
+        closedir(current_dir);
+        current_dir = NULL;
+      }
+    }
+  }
+}
+
+void MemoryCache::ReadToString(const char* filename, std::string* output) {
+  output->clear();
+  int fd = open(filename, 0, "r");
+  if (fd == -1)
+    return;
+  char buffer[4096];
+  ssize_t read_status = read(fd, buffer, sizeof(buffer));
+  while (read_status > 0) {
+    output->append(buffer, static_cast<size_t>(read_status));
+    do {
+      read_status = read(fd, buffer, sizeof(buffer));
+    } while (read_status <= 0 && errno == EINTR);
+  }
+  close(fd);
+}
+
+void MemoryCache::ReadAndStoreFileContents(const char* filename) {
+  StoreBodyAndHeadersVisitor visitor;
+  BalsaFrame framer;
+  framer.set_balsa_visitor(&visitor);
+  framer.set_balsa_headers(&(visitor.headers));
+  std::string filename_contents;
+  ReadToString(filename, &filename_contents);
+
+  // Ugly hack to make everything look like 1.1.
+  if (filename_contents.find("HTTP/1.0") == 0)
+    filename_contents[7] = '1';
+
+  size_t pos = 0;
+  size_t old_pos = 0;
+  while (true) {
+    old_pos = pos;
+    pos += framer.ProcessInput(filename_contents.data() + pos,
+                               filename_contents.size() - pos);
+    if (framer.Error() || pos == old_pos) {
+      LOG(ERROR) << "Unable to make forward progress, or error"
+                    " framing file: " << filename;
+      if (framer.Error()) {
+        LOG(INFO) << "********************************************ERROR!";
+        return;
+      }
+      return;
+    }
+    if (framer.MessageFullyRead()) {
+      // If no Content-Length or Transfer-Encoding was captured in the
+      // file, then the rest of the data is the body.  Many of the captures
+      // from within Chrome don't have content-lengths.
+      if (!visitor.body.length())
+        visitor.body = filename_contents.substr(pos);
+      break;
+    }
+  }
+  visitor.headers.RemoveAllOfHeader("content-length");
+  visitor.headers.RemoveAllOfHeader("transfer-encoding");
+  visitor.headers.RemoveAllOfHeader("connection");
+  visitor.headers.AppendHeader("transfer-encoding", "chunked");
+  visitor.headers.AppendHeader("connection", "keep-alive");
+
+// Experiment with changing headers for forcing use of cached
+// versions of content.
+// TODO(mbelshe) REMOVE ME
+#if 0
+  // TODO(mbelshe) append current date.
+  visitor.headers.RemoveAllOfHeader("date");
+  if (visitor.headers.HasHeader("expires")) {
+    visitor.headers.RemoveAllOfHeader("expires");
+    visitor.headers.AppendHeader("expires",
+                               "Fri, 30 Aug, 2019 12:00:00 GMT");
+  }
+#endif
+  DCHECK_GE(std::string(filename).size(), cwd_.size() + 1);
+  DCHECK_EQ(std::string(filename).substr(0, cwd_.size()), cwd_);
+  DCHECK_EQ(filename[cwd_.size()], '/');
+  std::string filename_stripped = std::string(filename).substr(cwd_.size() + 1);
+  LOG(INFO) << "Adding file (" << visitor.body.length()
+            << " bytes): " << filename_stripped;
+  size_t slash_pos = filename_stripped.find('/');
+  if (slash_pos == std::string::npos) {
+    slash_pos = filename_stripped.size();
+  }
+  InsertFile(
+      &visitor.headers, filename_stripped.substr(0, slash_pos), visitor.body);
+}
+
+FileData* MemoryCache::GetFileData(const std::string& filename) {
+  Files::iterator fi = files_.end();
+  if (EndsWith(filename, ".html", true)) {
+    fi = files_.find(filename.substr(0, filename.size() - 5) + ".http");
+  }
+  if (fi == files_.end())
+    fi = files_.find(filename);
+
+  if (fi == files_.end()) {
+    return NULL;
+  }
+  return fi->second;
+}
+
+bool MemoryCache::AssignFileData(const std::string& filename,
+                                 MemCacheIter* mci) {
+  mci->file_data = GetFileData(filename);
+  if (mci->file_data == NULL) {
+    LOG(ERROR) << "Could not find file data for " << filename;
+    return false;
+  }
+  return true;
+}
+
+void MemoryCache::InsertFile(const BalsaHeaders* headers,
+                             const std::string& filename,
+                             const std::string& body) {
+  InsertFile(new FileData(headers, filename, body));
+}
+
+void MemoryCache::InsertFile(FileData* file_data) {
+  Files::iterator it = files_.find(file_data->filename());
+  if (it != files_.end()) {
+    delete it->second;
+    it->second = file_data;
+  } else {
+    files_.insert(std::make_pair(file_data->filename(), file_data));
+  }
+}
+
+void MemoryCache::ClearFiles() {
+  for (Files::const_iterator i = files_.begin(); i != files_.end(); ++i) {
+    delete i->second;
+  }
+  files_.clear();
+}
+
+}  // namespace net
diff --git a/net/tools/flip_server/mem_cache.h b/net/tools/flip_server/mem_cache.h
new file mode 100644
index 0000000..76ffc95
--- /dev/null
+++ b/net/tools/flip_server/mem_cache.h
@@ -0,0 +1,154 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_TOOLS_FLIP_SERVER_MEM_CACHE_H_
+#define NET_TOOLS_FLIP_SERVER_MEM_CACHE_H_
+
+#include <map>
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/tools/balsa/balsa_headers.h"
+#include "net/tools/balsa/balsa_visitor_interface.h"
+#include "net/tools/flip_server/constants.h"
+
+namespace net {
+
+class StoreBodyAndHeadersVisitor : public BalsaVisitorInterface {
+ public:
+  void HandleError() { error_ = true; }
+
+  // BalsaVisitorInterface:
+  virtual void ProcessBodyInput(const char* input, size_t size) OVERRIDE {}
+  virtual void ProcessBodyData(const char* input, size_t size) OVERRIDE;
+  virtual void ProcessHeaderInput(const char* input, size_t size) OVERRIDE {}
+  virtual void ProcessTrailerInput(const char* input, size_t size) OVERRIDE {}
+  virtual void ProcessHeaders(const BalsaHeaders& headers) OVERRIDE {
+    // nothing to do here-- we're assuming that the BalsaFrame has
+    // been handed our headers.
+  }
+  virtual void ProcessRequestFirstLine(const char* line_input,
+                                       size_t line_length,
+                                       const char* method_input,
+                                       size_t method_length,
+                                       const char* request_uri_input,
+                                       size_t request_uri_length,
+                                       const char* version_input,
+                                       size_t version_length) OVERRIDE {}
+  virtual void ProcessResponseFirstLine(const char* line_input,
+                                        size_t line_length,
+                                        const char* version_input,
+                                        size_t version_length,
+                                        const char* status_input,
+                                        size_t status_length,
+                                        const char* reason_input,
+                                        size_t reason_length) OVERRIDE {}
+  virtual void ProcessChunkLength(size_t chunk_length) OVERRIDE {}
+  virtual void ProcessChunkExtensions(const char* input, size_t size) OVERRIDE {
+  }
+  virtual void HeaderDone() OVERRIDE {}
+  virtual void MessageDone() OVERRIDE {}
+  virtual void HandleHeaderError(BalsaFrame* framer) OVERRIDE;
+  virtual void HandleHeaderWarning(BalsaFrame* framer) OVERRIDE;
+  virtual void HandleChunkingError(BalsaFrame* framer) OVERRIDE;
+  virtual void HandleBodyError(BalsaFrame* framer) OVERRIDE;
+
+  BalsaHeaders headers;
+  std::string body;
+  bool error_;
+};
+
+class FileData {
+ public:
+  FileData();
+  FileData(const BalsaHeaders* headers,
+           const std::string& filename,
+           const std::string& body);
+  ~FileData();
+
+  BalsaHeaders* headers() { return headers_.get(); }
+  const BalsaHeaders* headers() const { return headers_.get(); }
+
+  const std::string& filename() { return filename_; }
+  const std::string& body() { return body_; }
+
+ private:
+  scoped_ptr<BalsaHeaders> headers_;
+  std::string filename_;
+  std::string body_;
+
+  DISALLOW_COPY_AND_ASSIGN(FileData);
+};
+
+class MemCacheIter {
+ public:
+  MemCacheIter()
+      : file_data(NULL),
+        priority(0),
+        transformed_header(false),
+        body_bytes_consumed(0),
+        stream_id(0),
+        max_segment_size(kInitialDataSendersThreshold),
+        bytes_sent(0) {}
+  explicit MemCacheIter(FileData* fd)
+      : file_data(fd),
+        priority(0),
+        transformed_header(false),
+        body_bytes_consumed(0),
+        stream_id(0),
+        max_segment_size(kInitialDataSendersThreshold),
+        bytes_sent(0) {}
+  FileData* file_data;
+  int priority;
+  bool transformed_header;
+  size_t body_bytes_consumed;
+  uint32 stream_id;
+  uint32 max_segment_size;
+  size_t bytes_sent;
+};
+
+class MemoryCache {
+ public:
+  typedef std::map<std::string, FileData*> Files;
+
+ public:
+  MemoryCache();
+  virtual ~MemoryCache();
+
+  void CloneFrom(const MemoryCache& mc);
+
+  void AddFiles();
+
+  // virtual for unittests
+  virtual void ReadToString(const char* filename, std::string* output);
+
+  void ReadAndStoreFileContents(const char* filename);
+
+  FileData* GetFileData(const std::string& filename);
+
+  bool AssignFileData(const std::string& filename, MemCacheIter* mci);
+
+  // For unittests
+  void InsertFile(const BalsaHeaders* headers,
+                  const std::string& filename,
+                  const std::string& body);
+
+ private:
+  void InsertFile(FileData* file_data);
+  void ClearFiles();
+
+  Files files_;
+  std::string cwd_;
+};
+
+class NotifierInterface {
+ public:
+  virtual ~NotifierInterface() {}
+  virtual void Notify() = 0;
+};
+
+}  // namespace net
+
+#endif  // NET_TOOLS_FLIP_SERVER_MEM_CACHE_H_
diff --git a/net/tools/flip_server/mem_cache_test.cc b/net/tools/flip_server/mem_cache_test.cc
new file mode 100644
index 0000000..5e45bb5
--- /dev/null
+++ b/net/tools/flip_server/mem_cache_test.cc
@@ -0,0 +1,101 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/tools/flip_server/mem_cache.h"
+
+#include "net/tools/balsa/balsa_headers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+class MemoryCacheWithFakeReadToString : public MemoryCache {
+ public:
+  virtual ~MemoryCacheWithFakeReadToString() {}
+
+  virtual void ReadToString(const char* filename,
+                            std::string* output) OVERRIDE {
+    *output = data_map_[filename];
+  }
+
+  std::map<std::string, std::string> data_map_;
+};
+
+class FlipMemoryCacheTest : public ::testing::Test {
+ public:
+  FlipMemoryCacheTest() : mem_cache_(new MemoryCacheWithFakeReadToString) {}
+
+ protected:
+  scoped_ptr<MemoryCacheWithFakeReadToString> mem_cache_;
+};
+
+TEST_F(FlipMemoryCacheTest, EmptyCache) {
+  MemCacheIter mci;
+  mci.stream_id = 0;
+  ASSERT_EQ(NULL, mem_cache_->GetFileData("./foo"));
+  ASSERT_EQ(NULL, mem_cache_->GetFileData("./bar"));
+  ASSERT_FALSE(mem_cache_->AssignFileData("./hello", &mci));
+}
+
+TEST_F(FlipMemoryCacheTest, ReadAndStoreFileContents) {
+  FileData* foo;
+  FileData* hello;
+
+  mem_cache_->data_map_["./foo"] = "bar";
+  mem_cache_->data_map_["./hello"] =
+      "HTTP/1.0 200 OK\r\n"
+      "key1: value1\r\n"
+      "key2: value2\r\n\r\n"
+      "body: body\r\n";
+  mem_cache_->ReadAndStoreFileContents("./foo");
+  mem_cache_->ReadAndStoreFileContents("./hello");
+
+  foo = mem_cache_->GetFileData("foo");
+  hello = mem_cache_->GetFileData("hello");
+
+  // "./foo" content is broken.
+  ASSERT_EQ(NULL, foo);
+  ASSERT_FALSE(NULL == hello);
+  ASSERT_EQ(hello, mem_cache_->GetFileData("hello"));
+
+  // "HTTP/1.0" is rewritten to "HTTP/1.1".
+  ASSERT_EQ("HTTP/1.1", hello->headers()->response_version());
+  ASSERT_EQ("200", hello->headers()->response_code());
+  ASSERT_EQ("OK", hello->headers()->response_reason_phrase());
+  ASSERT_EQ(4,
+            std::distance(hello->headers()->header_lines_begin(),
+                          hello->headers()->header_lines_end()));
+  ASSERT_TRUE(hello->headers()->HasHeader("key1"));
+  ASSERT_TRUE(hello->headers()->HasHeader("key2"));
+  ASSERT_TRUE(hello->headers()->HasHeader("transfer-encoding"));
+  ASSERT_TRUE(hello->headers()->HasHeader("connection"));
+  ASSERT_EQ("value1", hello->headers()->GetHeaderPosition("key1")->second);
+  ASSERT_EQ("value2", hello->headers()->GetHeaderPosition("key2")->second);
+  ASSERT_EQ("chunked",
+            hello->headers()->GetHeaderPosition("transfer-encoding")->second);
+  ASSERT_EQ("keep-alive",
+            hello->headers()->GetHeaderPosition("connection")->second);
+  ASSERT_EQ("body: body\r\n", hello->body());
+  ASSERT_EQ("hello", hello->filename());
+}
+
+TEST_F(FlipMemoryCacheTest, GetFileDataForHtmlFile) {
+  FileData* hello_html;
+
+  mem_cache_->data_map_["./hello.http"] =
+      "HTTP/1.0 200 OK\r\n"
+      "key1: value1\r\n"
+      "key2: value2\r\n\r\n"
+      "body: body\r\n";
+
+  mem_cache_->ReadAndStoreFileContents("./hello.http");
+  hello_html = mem_cache_->GetFileData("hello.html");
+  ASSERT_FALSE(NULL == hello_html);
+  ASSERT_EQ(hello_html, mem_cache_->GetFileData("hello.http"));
+}
+
+}  // namespace
+
+}  // namespace net
diff --git a/net/tools/flip_server/output_ordering.cc b/net/tools/flip_server/output_ordering.cc
new file mode 100644
index 0000000..9659954
--- /dev/null
+++ b/net/tools/flip_server/output_ordering.cc
@@ -0,0 +1,175 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/tools/flip_server/output_ordering.h"
+
+#include <utility>
+
+#include "net/tools/flip_server/flip_config.h"
+#include "net/tools/flip_server/sm_connection.h"
+
+namespace net {
+
+OutputOrdering::PriorityMapPointer::PriorityMapPointer()
+    : ring(NULL), alarm_enabled(false) {}
+
+OutputOrdering::PriorityMapPointer::~PriorityMapPointer() {}
+
+// static
+double OutputOrdering::server_think_time_in_s_ = 0.0;
+
+OutputOrdering::OutputOrdering(SMConnectionInterface* connection)
+    : first_data_senders_threshold_(kInitialDataSendersThreshold),
+      connection_(connection) {
+  if (connection)
+    epoll_server_ = connection->epoll_server();
+}
+
+OutputOrdering::~OutputOrdering() { Reset(); }
+
+void OutputOrdering::Reset() {
+  while (!stream_ids_.empty()) {
+    StreamIdToPriorityMap::iterator sitpmi = stream_ids_.begin();
+    PriorityMapPointer& pmp = sitpmi->second;
+    if (pmp.alarm_enabled) {
+      epoll_server_->UnregisterAlarm(pmp.alarm_token);
+    }
+    stream_ids_.erase(sitpmi);
+  }
+  priority_map_.clear();
+  first_data_senders_.clear();
+}
+
+bool OutputOrdering::ExistsInPriorityMaps(uint32 stream_id) const {
+  StreamIdToPriorityMap::const_iterator sitpmi = stream_ids_.find(stream_id);
+  return sitpmi != stream_ids_.end();
+}
+
+OutputOrdering::BeginOutputtingAlarm::BeginOutputtingAlarm(
+    OutputOrdering* oo,
+    OutputOrdering::PriorityMapPointer* pmp,
+    const MemCacheIter& mci)
+    : output_ordering_(oo), pmp_(pmp), mci_(mci), epoll_server_(NULL) {}
+
+OutputOrdering::BeginOutputtingAlarm::~BeginOutputtingAlarm() {
+  if (epoll_server_ && pmp_->alarm_enabled)
+    epoll_server_->UnregisterAlarm(pmp_->alarm_token);
+}
+
+int64 OutputOrdering::BeginOutputtingAlarm::OnAlarm() {
+  OnUnregistration();
+  output_ordering_->MoveToActive(pmp_, mci_);
+  VLOG(2) << "ON ALARM! Should now start to output...";
+  delete this;
+  return 0;
+}
+
+void OutputOrdering::BeginOutputtingAlarm::OnRegistration(
+    const EpollServer::AlarmRegToken& tok,
+    EpollServer* eps) {
+  epoll_server_ = eps;
+  pmp_->alarm_token = tok;
+  pmp_->alarm_enabled = true;
+}
+
+void OutputOrdering::BeginOutputtingAlarm::OnUnregistration() {
+  pmp_->alarm_enabled = false;
+  delete this;
+}
+
+void OutputOrdering::BeginOutputtingAlarm::OnShutdown(EpollServer* eps) {
+  OnUnregistration();
+}
+
+void OutputOrdering::MoveToActive(PriorityMapPointer* pmp, MemCacheIter mci) {
+  VLOG(2) << "Moving to active!";
+  first_data_senders_.push_back(mci);
+  pmp->ring = &first_data_senders_;
+  pmp->it = first_data_senders_.end();
+  --pmp->it;
+  connection_->ReadyToSend();
+}
+
+void OutputOrdering::AddToOutputOrder(const MemCacheIter& mci) {
+  if (ExistsInPriorityMaps(mci.stream_id))
+    LOG(ERROR) << "OOps, already was inserted here?!";
+
+  double think_time_in_s = server_think_time_in_s_;
+  std::string x_server_latency =
+      mci.file_data->headers()->GetHeader("X-Server-Latency").as_string();
+  if (!x_server_latency.empty()) {
+    char* endp;
+    double tmp_think_time_in_s = strtod(x_server_latency.c_str(), &endp);
+    if (endp != x_server_latency.c_str() + x_server_latency.size()) {
+      LOG(ERROR) << "Unable to understand X-Server-Latency of: "
+                 << x_server_latency
+                 << " for resource: " << mci.file_data->filename().c_str();
+    } else {
+      think_time_in_s = tmp_think_time_in_s;
+    }
+  }
+  StreamIdToPriorityMap::iterator sitpmi;
+  sitpmi = stream_ids_.insert(std::pair<uint32, PriorityMapPointer>(
+                                  mci.stream_id, PriorityMapPointer())).first;
+  PriorityMapPointer& pmp = sitpmi->second;
+
+  BeginOutputtingAlarm* boa = new BeginOutputtingAlarm(this, &pmp, mci);
+  VLOG(1) << "Server think time: " << think_time_in_s;
+  epoll_server_->RegisterAlarmApproximateDelta(think_time_in_s * 1000000, boa);
+}
+
+void OutputOrdering::SpliceToPriorityRing(PriorityRing::iterator pri) {
+  MemCacheIter& mci = *pri;
+  PriorityMap::iterator pmi = priority_map_.find(mci.priority);
+  if (pmi == priority_map_.end()) {
+    pmi = priority_map_.insert(std::pair<uint32, PriorityRing>(
+                                   mci.priority, PriorityRing())).first;
+  }
+
+  pmi->second.splice(pmi->second.end(), first_data_senders_, pri);
+  StreamIdToPriorityMap::iterator sitpmi = stream_ids_.find(mci.stream_id);
+  sitpmi->second.ring = &(pmi->second);
+}
+
+MemCacheIter* OutputOrdering::GetIter() {
+  while (!first_data_senders_.empty()) {
+    MemCacheIter& mci = first_data_senders_.front();
+    if (mci.bytes_sent >= first_data_senders_threshold_) {
+      SpliceToPriorityRing(first_data_senders_.begin());
+    } else {
+      first_data_senders_.splice(first_data_senders_.end(),
+                                 first_data_senders_,
+                                 first_data_senders_.begin());
+      mci.max_segment_size = kInitialDataSendersThreshold;
+      return &mci;
+    }
+  }
+  while (!priority_map_.empty()) {
+    PriorityRing& first_ring = priority_map_.begin()->second;
+    if (first_ring.empty()) {
+      priority_map_.erase(priority_map_.begin());
+      continue;
+    }
+    MemCacheIter& mci = first_ring.front();
+    first_ring.splice(first_ring.end(), first_ring, first_ring.begin());
+    mci.max_segment_size = kSpdySegmentSize;
+    return &mci;
+  }
+  return NULL;
+}
+
+void OutputOrdering::RemoveStreamId(uint32 stream_id) {
+  StreamIdToPriorityMap::iterator sitpmi = stream_ids_.find(stream_id);
+  if (sitpmi == stream_ids_.end())
+    return;
+
+  PriorityMapPointer& pmp = sitpmi->second;
+  if (pmp.alarm_enabled)
+    epoll_server_->UnregisterAlarm(pmp.alarm_token);
+  else
+    pmp.ring->erase(pmp.it);
+  stream_ids_.erase(sitpmi);
+}
+
+}  // namespace net
diff --git a/net/tools/flip_server/output_ordering.h b/net/tools/flip_server/output_ordering.h
new file mode 100644
index 0000000..596f382
--- /dev/null
+++ b/net/tools/flip_server/output_ordering.h
@@ -0,0 +1,89 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_TOOLS_FLIP_SERVER_OUTPUT_ORDERING_H_
+#define NET_TOOLS_FLIP_SERVER_OUTPUT_ORDERING_H_
+
+#include <list>
+#include <map>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "net/tools/epoll_server/epoll_server.h"
+#include "net/tools/flip_server/constants.h"
+#include "net/tools/flip_server/mem_cache.h"
+
+namespace net {
+
+class SMConnectionInterface;
+
+class OutputOrdering {
+ public:
+  typedef std::list<MemCacheIter> PriorityRing;
+  typedef std::map<uint32, PriorityRing> PriorityMap;
+
+  struct PriorityMapPointer {
+    PriorityMapPointer();
+    ~PriorityMapPointer();
+    PriorityRing* ring;
+    PriorityRing::iterator it;
+    bool alarm_enabled;
+    EpollServer::AlarmRegToken alarm_token;
+  };
+
+  typedef std::map<uint32, PriorityMapPointer> StreamIdToPriorityMap;
+
+  StreamIdToPriorityMap stream_ids_;
+  PriorityMap priority_map_;
+  PriorityRing first_data_senders_;
+  uint32 first_data_senders_threshold_;  // when you've passed this, you're no
+                                         // longer a first_data_sender...
+  SMConnectionInterface* connection_;
+  EpollServer* epoll_server_;
+
+  explicit OutputOrdering(SMConnectionInterface* connection);
+  ~OutputOrdering();
+  void Reset();
+  bool ExistsInPriorityMaps(uint32 stream_id) const;
+
+  struct BeginOutputtingAlarm : public EpollAlarmCallbackInterface {
+   public:
+    BeginOutputtingAlarm(OutputOrdering* oo,
+                         OutputOrdering::PriorityMapPointer* pmp,
+                         const MemCacheIter& mci);
+    virtual ~BeginOutputtingAlarm();
+
+    // EpollAlarmCallbackInterface:
+    virtual int64 OnAlarm() OVERRIDE;
+    virtual void OnRegistration(const EpollServer::AlarmRegToken& tok,
+                                EpollServer* eps) OVERRIDE;
+    virtual void OnUnregistration() OVERRIDE;
+    virtual void OnShutdown(EpollServer* eps) OVERRIDE;
+
+   private:
+    OutputOrdering* output_ordering_;
+    OutputOrdering::PriorityMapPointer* pmp_;
+    MemCacheIter mci_;
+    EpollServer* epoll_server_;
+  };
+
+  void MoveToActive(PriorityMapPointer* pmp, MemCacheIter mci);
+  void AddToOutputOrder(const MemCacheIter& mci);
+  void SpliceToPriorityRing(PriorityRing::iterator pri);
+  MemCacheIter* GetIter();
+  void RemoveStreamId(uint32 stream_id);
+
+  static double server_think_time_in_s() { return server_think_time_in_s_; }
+  static void set_server_think_time_in_s(double value) {
+    server_think_time_in_s_ = value;
+  }
+
+ private:
+  static double server_think_time_in_s_;
+};
+
+}  // namespace net
+
+#endif  // NET_TOOLS_FLIP_SERVER_OUTPUT_ORDERING_H_
diff --git a/net/tools/flip_server/ring_buffer.cc b/net/tools/flip_server/ring_buffer.cc
new file mode 100644
index 0000000..16c278b
--- /dev/null
+++ b/net/tools/flip_server/ring_buffer.cc
@@ -0,0 +1,241 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/tools/flip_server/ring_buffer.h"
+#include "base/logging.h"
+
+namespace net {
+
+RingBuffer::RingBuffer(int buffer_size)
+    : buffer_(new char[buffer_size]),
+      buffer_size_(buffer_size),
+      bytes_used_(0),
+      read_idx_(0),
+      write_idx_(0) {}
+
+RingBuffer::~RingBuffer() {}
+
+int RingBuffer::ReadableBytes() const { return bytes_used_; }
+
+int RingBuffer::BufferSize() const { return buffer_size_; }
+
+int RingBuffer::BytesFree() const { return BufferSize() - ReadableBytes(); }
+
+bool RingBuffer::Empty() const { return ReadableBytes() == 0; }
+
+bool RingBuffer::Full() const { return ReadableBytes() == BufferSize(); }
+
+// Returns the number of characters written.
+// Appends up-to-'size' bytes to the ringbuffer.
+int RingBuffer::Write(const char* bytes, int size) {
+  CHECK_GE(size, 0);
+#if 1
+  char* wptr;
+  int wsize;
+  GetWritablePtr(&wptr, &wsize);
+  int bytes_remaining = size;
+  int bytes_written = 0;
+
+  while (wsize && bytes_remaining) {
+    if (wsize > bytes_remaining) {
+      wsize = bytes_remaining;
+    }
+    memcpy(wptr, bytes + bytes_written, wsize);
+    bytes_written += wsize;
+    bytes_remaining -= wsize;
+    AdvanceWritablePtr(wsize);
+    GetWritablePtr(&wptr, &wsize);
+  }
+  return bytes_written;
+#else
+  const char* p = bytes;
+
+  int bytes_to_write = size;
+  int bytes_available = BytesFree();
+  if (bytes_available < bytes_to_write) {
+    bytes_to_write = bytes_available;
+  }
+  const char* end = bytes + bytes_to_write;
+
+  while (p != end) {
+    this->buffer_[this->write_idx_] = *p;
+    ++p;
+    ++this->write_idx_;
+    if (this->write_idx_ >= this->buffer_size_) {
+      this->write_idx_ = 0;
+    }
+  }
+  bytes_used_ += bytes_to_write;
+  return bytes_to_write;
+#endif
+}
+
+// Sets *ptr to the beginning of writable memory, and sets *size to the size
+// available for writing using this pointer.
+void RingBuffer::GetWritablePtr(char** ptr, int* size) const {
+  *ptr = buffer_.get() + write_idx_;
+
+  if (bytes_used_ == buffer_size_) {
+    *size = 0;
+  } else if (read_idx_ > write_idx_) {
+    *size = read_idx_ - write_idx_;
+  } else {
+    *size = buffer_size_ - write_idx_;
+  }
+}
+
+// Sets *ptr to the beginning of readable memory, and sets *size to the size
+// available for reading using this pointer.
+void RingBuffer::GetReadablePtr(char** ptr, int* size) const {
+  *ptr = buffer_.get() + read_idx_;
+
+  if (bytes_used_ == 0) {
+    *size = 0;
+  } else if (write_idx_ > read_idx_) {
+    *size = write_idx_ - read_idx_;
+  } else {
+    *size = buffer_size_ - read_idx_;
+  }
+}
+
+// returns the number of bytes read into
+int RingBuffer::Read(char* bytes, int size) {
+  CHECK_GE(size, 0);
+#if 1
+  char* rptr;
+  int rsize;
+  GetReadablePtr(&rptr, &rsize);
+  int bytes_remaining = size;
+  int bytes_read = 0;
+
+  while (rsize && bytes_remaining) {
+    if (rsize > bytes_remaining) {
+      rsize = bytes_remaining;
+    }
+    memcpy(bytes + bytes_read, rptr, rsize);
+    bytes_read += rsize;
+    bytes_remaining -= rsize;
+    AdvanceReadablePtr(rsize);
+    GetReadablePtr(&rptr, &rsize);
+  }
+  return bytes_read;
+#else
+  char* p = bytes;
+  int bytes_to_read = size;
+  int bytes_used = ReadableBytes();
+  if (bytes_used < bytes_to_read) {
+    bytes_to_read = bytes_used;
+  }
+  char* end = bytes + bytes_to_read;
+
+  while (p != end) {
+    *p = this->buffer_[this->read_idx_];
+    ++p;
+    ++this->read_idx_;
+    if (this->read_idx_ >= this->buffer_size_) {
+      this->read_idx_ = 0;
+    }
+  }
+  this->bytes_used_ -= bytes_to_read;
+  return bytes_to_read;
+#endif
+}
+
+void RingBuffer::Clear() {
+  bytes_used_ = 0;
+  write_idx_ = 0;
+  read_idx_ = 0;
+}
+
+bool RingBuffer::Reserve(int size) {
+  DCHECK_GT(size, 0);
+  char* write_ptr = NULL;
+  int write_size = 0;
+  GetWritablePtr(&write_ptr, &write_size);
+
+  if (write_size < size) {
+    char* read_ptr = NULL;
+    int read_size = 0;
+    GetReadablePtr(&read_ptr, &read_size);
+    if (size <= BytesFree()) {
+      // The fact that the total Free size is big enough but writable size is
+      // not means that the writeable region is broken into two pieces: only
+      // possible if the read_idx < write_idx. If write_idx < read_idx, then
+      // the writeable region must be contiguous: [write_idx, read_idx). There
+      // is no work to be done for the latter.
+      DCHECK_LE(read_idx_, write_idx_);
+      DCHECK_EQ(read_size, ReadableBytes());
+      if (read_idx_ < write_idx_) {
+        // Writeable area fragmented, consolidate it.
+        memmove(buffer_.get(), read_ptr, read_size);
+        read_idx_ = 0;
+        write_idx_ = read_size;
+      } else if (read_idx_ == write_idx_) {
+        // No unconsumed data in the buffer, simply reset the indexes.
+        DCHECK_EQ(ReadableBytes(), 0);
+        read_idx_ = 0;
+        write_idx_ = 0;
+      }
+    } else {
+      Resize(ReadableBytes() + size);
+    }
+  }
+  DCHECK_LE(size, buffer_size_ - write_idx_);
+  return true;
+}
+
+void RingBuffer::AdvanceReadablePtr(int amount_to_consume) {
+  CHECK_GE(amount_to_consume, 0);
+  if (amount_to_consume >= bytes_used_) {
+    Clear();
+    return;
+  }
+  read_idx_ += amount_to_consume;
+  read_idx_ %= buffer_size_;
+  bytes_used_ -= amount_to_consume;
+}
+
+void RingBuffer::AdvanceWritablePtr(int amount_to_produce) {
+  CHECK_GE(amount_to_produce, 0);
+  CHECK_LE(amount_to_produce, BytesFree());
+  write_idx_ += amount_to_produce;
+  write_idx_ %= buffer_size_;
+  bytes_used_ += amount_to_produce;
+}
+
+void RingBuffer::Resize(int buffer_size) {
+  CHECK_GE(buffer_size, 0);
+  if (buffer_size == buffer_size_)
+    return;
+
+  char* new_buffer = new char[buffer_size];
+  if (buffer_size < bytes_used_) {
+    // consume the oldest data.
+    AdvanceReadablePtr(bytes_used_ - buffer_size);
+  }
+
+  int bytes_written = 0;
+  int bytes_used = bytes_used_;
+  while (true) {
+    int size;
+    char* ptr;
+    GetReadablePtr(&ptr, &size);
+    if (size == 0)
+      break;
+    if (size > buffer_size) {
+      size = buffer_size;
+    }
+    memcpy(new_buffer + bytes_written, ptr, size);
+    bytes_written += size;
+    AdvanceReadablePtr(size);
+  }
+  buffer_.reset(new_buffer);
+
+  buffer_size_ = buffer_size;
+  bytes_used_ = bytes_used;
+  read_idx_ = 0;
+  write_idx_ = bytes_used_ % buffer_size_;
+}
+
+}  // namespace net
diff --git a/net/tools/flip_server/ring_buffer.h b/net/tools/flip_server/ring_buffer.h
new file mode 100644
index 0000000..b129ee8
--- /dev/null
+++ b/net/tools/flip_server/ring_buffer.h
@@ -0,0 +1,112 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_TOOLS_FLIP_SERVER_RING_BUFFER_H__
+#define NET_TOOLS_FLIP_SERVER_RING_BUFFER_H__
+
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/tools/balsa/buffer_interface.h"
+
+namespace net {
+
+// The ring buffer is a circular buffer, that is, reads or writes may wrap
+// around the end of the linear memory contained by the class (and back to
+// the beginning). This is a good choice when you want to use a fixed amount
+// of buffering and don't want to be moving memory around a lot.
+//
+// What is the penalty for using this over a normal, linear buffer?
+// Reading all the data may take two operations, and
+// writing all the data may take two operations.
+//
+// In the proxy, this class is used as a fixed size buffer between
+// clients and servers (so that the memory size is constrained).
+
+class RingBuffer : public BufferInterface {
+ public:
+  explicit RingBuffer(int buffer_size);
+  virtual ~RingBuffer();
+
+  // Resize the buffer to the size specified here.  If the buffer_size passed
+  // in here is smaller than the amount of data in the buffer, then the oldest
+  // data will be dropped, but all other data will be saved.
+  // This means: If the buffer size is increasing, all data  that was resident
+  // in the buffer prior to this call will be resident after this call.
+  void Resize(int buffer_size);
+
+  // The following functions all override pure virtual functions
+  // in BufferInterface. See buffer_interface.h for a description
+  // of what they do if the function isn't documented here.
+  virtual int ReadableBytes() const OVERRIDE;
+  virtual int BufferSize() const OVERRIDE;
+  virtual int BytesFree() const OVERRIDE;
+
+  virtual bool Empty() const OVERRIDE;
+  virtual bool Full() const OVERRIDE;
+
+  // returns the number of characters written.
+  // appends up-to-'size' bytes to the ringbuffer.
+  virtual int Write(const char* bytes, int size) OVERRIDE;
+
+  // Stores a pointer into the ring buffer in *ptr,  and stores the number of
+  // characters which are allowed to be written in *size.
+  // If there are no writable bytes available, then *size will contain 0.
+  virtual void GetWritablePtr(char** ptr, int* size) const OVERRIDE;
+
+  // Stores a pointer into the ring buffer in *ptr,  and stores the number of
+  // characters which are allowed to be read in *size.
+  // If there are no readable bytes available, then *size will contain 0.
+  virtual void GetReadablePtr(char** ptr, int* size) const OVERRIDE;
+
+  // Returns the number of bytes read into 'bytes'.
+  virtual int Read(char* bytes, int size) OVERRIDE;
+
+  // Removes all data from the ring buffer.
+  virtual void Clear() OVERRIDE;
+
+  // Reserves contiguous writable empty space in the buffer of size bytes.
+  // Since the point of this class is to have a fixed size buffer, be careful
+  // not to inadvertently resize the buffer using Reserve(). If the reserve
+  // size is <= BytesFree(), it is guaranteed that the buffer size will not
+  // change.
+  // This can be an expensive operation, it may new a buffer copy all existing
+  // data and delete the old data. Even if the existing buffer does not need
+  // to be resized, unread data may still need to be non-destructively copied
+  // to consolidate fragmented free space. If the size requested is less than
+  // or equal to BytesFree(), it is guaranteed that the buffer size will not
+  // change.
+  virtual bool Reserve(int size) OVERRIDE;
+
+  // Removes the oldest 'amount_to_advance' characters.
+  // If amount_to_consume > ReadableBytes(), this performs a Clear() instead.
+  virtual void AdvanceReadablePtr(int amount_to_advance) OVERRIDE;
+
+  // Moves the internal pointers around such that the  amount of data specified
+  // here is expected to already be resident (as if it was Written).
+  virtual void AdvanceWritablePtr(int amount_to_advance) OVERRIDE;
+
+ protected:
+  int read_idx() const { return read_idx_; }
+  int write_idx() const { return write_idx_; }
+  int bytes_used() const { return bytes_used_; }
+  int buffer_size() const { return buffer_size_; }
+  const char* buffer() const { return buffer_.get(); }
+
+  int set_read_idx(int idx) { return read_idx_ = idx; }
+  int set_write_idx(int idx) { return write_idx_ = idx; }
+
+ private:
+  scoped_ptr<char[]> buffer_;
+  int buffer_size_;
+  int bytes_used_;
+  int read_idx_;
+  int write_idx_;
+
+  RingBuffer(const RingBuffer&);
+  void operator=(const RingBuffer&);
+};
+
+}  // namespace net
+
+#endif  // NET_TOOLS_FLIP_SERVER_RING_BUFFER_H__
diff --git a/net/tools/flip_server/run_all_tests.cc b/net/tools/flip_server/run_all_tests.cc
new file mode 100644
index 0000000..6acc286
--- /dev/null
+++ b/net/tools/flip_server/run_all_tests.cc
@@ -0,0 +1,8 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/test_suite.h"
+#include "build/build_config.h"
+
+int main(int argc, char** argv) { return base::TestSuite(argc, argv).Run(); }
diff --git a/net/tools/flip_server/sm_connection.cc b/net/tools/flip_server/sm_connection.cc
new file mode 100644
index 0000000..4acdea4
--- /dev/null
+++ b/net/tools/flip_server/sm_connection.cc
@@ -0,0 +1,666 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/tools/flip_server/sm_connection.h"
+
+#include <errno.h>
+#include <netinet/tcp.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <list>
+#include <string>
+
+#include "net/tools/flip_server/constants.h"
+#include "net/tools/flip_server/flip_config.h"
+#include "net/tools/flip_server/http_interface.h"
+#include "net/tools/flip_server/spdy_interface.h"
+#include "net/tools/flip_server/spdy_ssl.h"
+#include "net/tools/flip_server/streamer_interface.h"
+
+namespace net {
+
+// static
+bool SMConnection::force_spdy_ = false;
+
+DataFrame::~DataFrame() {
+  if (delete_when_done)
+    delete[] data;
+}
+
+SMConnection::SMConnection(EpollServer* epoll_server,
+                           SSLState* ssl_state,
+                           MemoryCache* memory_cache,
+                           FlipAcceptor* acceptor,
+                           std::string log_prefix)
+    : last_read_time_(0),
+      fd_(-1),
+      events_(0),
+      registered_in_epoll_server_(false),
+      initialized_(false),
+      protocol_detected_(false),
+      connection_complete_(false),
+      connection_pool_(NULL),
+      epoll_server_(epoll_server),
+      ssl_state_(ssl_state),
+      memory_cache_(memory_cache),
+      acceptor_(acceptor),
+      read_buffer_(kSpdySegmentSize * 40),
+      sm_spdy_interface_(NULL),
+      sm_http_interface_(NULL),
+      sm_streamer_interface_(NULL),
+      sm_interface_(NULL),
+      log_prefix_(log_prefix),
+      max_bytes_sent_per_dowrite_(4096),
+      ssl_(NULL) {}
+
+SMConnection::~SMConnection() {
+  if (initialized())
+    Reset();
+}
+
+EpollServer* SMConnection::epoll_server() { return epoll_server_; }
+
+void SMConnection::ReadyToSend() {
+  VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
+          << "Setting ready to send: EPOLLIN | EPOLLOUT";
+  epoll_server_->SetFDReady(fd_, EPOLLIN | EPOLLOUT);
+}
+
+void SMConnection::EnqueueDataFrame(DataFrame* df) {
+  output_list_.push_back(df);
+  VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT << "EnqueueDataFrame: "
+          << "size = " << df->size << ": Setting FD ready.";
+  ReadyToSend();
+}
+
+void SMConnection::InitSMConnection(SMConnectionPoolInterface* connection_pool,
+                                    SMInterface* sm_interface,
+                                    EpollServer* epoll_server,
+                                    int fd,
+                                    std::string server_ip,
+                                    std::string server_port,
+                                    std::string remote_ip,
+                                    bool use_ssl) {
+  if (initialized_) {
+    LOG(FATAL) << "Attempted to initialize already initialized server";
+    return;
+  }
+
+  client_ip_ = remote_ip;
+
+  if (fd == -1) {
+    // If fd == -1, then we are initializing a new connection that will
+    // connect to the backend.
+    //
+    // ret:  -1 == error
+    //        0 == connection in progress
+    //        1 == connection complete
+    // TODO(kelindsay): is_numeric_host_address value needs to be detected
+    server_ip_ = server_ip;
+    server_port_ = server_port;
+    int ret = CreateConnectedSocket(
+        &fd_, server_ip, server_port, true, acceptor_->disable_nagle_);
+
+    if (ret < 0) {
+      LOG(ERROR) << "-1 Could not create connected socket";
+      return;
+    } else if (ret == 1) {
+      DCHECK_NE(-1, fd_);
+      connection_complete_ = true;
+      VLOG(1) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
+              << "Connection complete to: " << server_ip_ << ":" << server_port_
+              << " ";
+    }
+    VLOG(1) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
+            << "Connecting to server: " << server_ip_ << ":" << server_port_
+            << " ";
+  } else {
+    // If fd != -1 then we are initializing a connection that has just been
+    // accepted from the listen socket.
+    connection_complete_ = true;
+    if (epoll_server_ && registered_in_epoll_server_ && fd_ != -1) {
+      epoll_server_->UnregisterFD(fd_);
+    }
+    if (fd_ != -1) {
+      VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
+              << "Closing pre-existing fd";
+      close(fd_);
+      fd_ = -1;
+    }
+
+    fd_ = fd;
+  }
+
+  registered_in_epoll_server_ = false;
+  // Set the last read time here as the idle checker will start from
+  // now.
+  last_read_time_ = time(NULL);
+  initialized_ = true;
+
+  connection_pool_ = connection_pool;
+  epoll_server_ = epoll_server;
+
+  if (sm_interface) {
+    sm_interface_ = sm_interface;
+    protocol_detected_ = true;
+  }
+
+  read_buffer_.Clear();
+
+  epoll_server_->RegisterFD(fd_, this, EPOLLIN | EPOLLOUT | EPOLLET);
+
+  if (use_ssl) {
+    ssl_ = CreateSSLContext(ssl_state_->ssl_ctx);
+    SSL_set_fd(ssl_, fd_);
+    PrintSslError();
+  }
+}
+
+void SMConnection::CorkSocket() {
+  int state = 1;
+  int rv = setsockopt(fd_, IPPROTO_TCP, TCP_CORK, &state, sizeof(state));
+  if (rv < 0)
+    VLOG(1) << "setsockopt(CORK): " << errno;
+}
+
+void SMConnection::UncorkSocket() {
+  int state = 0;
+  int rv = setsockopt(fd_, IPPROTO_TCP, TCP_CORK, &state, sizeof(state));
+  if (rv < 0)
+    VLOG(1) << "setsockopt(CORK): " << errno;
+}
+
+int SMConnection::Send(const char* data, int len, int flags) {
+  int rv = 0;
+  CorkSocket();
+  if (ssl_) {
+    ssize_t bytes_written = 0;
+    // Write smallish chunks to SSL so that we don't have large
+    // multi-packet TLS records to receive before being able to handle
+    // the data.  We don't have to be too careful here, because our data
+    // frames are already getting chunked appropriately, and those are
+    // the most likely "big" frames.
+    while (len > 0) {
+      const int kMaxTLSRecordSize = 1500;
+      const char* ptr = &(data[bytes_written]);
+      int chunksize = std::min(len, kMaxTLSRecordSize);
+      rv = SSL_write(ssl_, ptr, chunksize);
+      VLOG(2) << "SSLWrite(" << chunksize << " bytes): " << rv;
+      if (rv <= 0) {
+        switch (SSL_get_error(ssl_, rv)) {
+          case SSL_ERROR_WANT_READ:
+          case SSL_ERROR_WANT_WRITE:
+          case SSL_ERROR_WANT_ACCEPT:
+          case SSL_ERROR_WANT_CONNECT:
+            rv = -2;
+            break;
+          default:
+            PrintSslError();
+            break;
+        }
+        break;
+      }
+      bytes_written += rv;
+      len -= rv;
+      if (rv != chunksize)
+        break;  // If we couldn't write everything, we're implicitly stalled
+    }
+    // If we wrote some data, return that count.  Otherwise
+    // return the stall error.
+    if (bytes_written > 0)
+      rv = bytes_written;
+  } else {
+    rv = send(fd_, data, len, flags);
+  }
+  if (!(flags & MSG_MORE))
+    UncorkSocket();
+  return rv;
+}
+
+void SMConnection::OnRegistration(EpollServer* eps, int fd, int event_mask) {
+  registered_in_epoll_server_ = true;
+}
+
+void SMConnection::OnEvent(int fd, EpollEvent* event) {
+  events_ |= event->in_events;
+  HandleEvents();
+  if (events_) {
+    event->out_ready_mask = events_;
+    events_ = 0;
+  }
+}
+
+void SMConnection::OnUnregistration(int fd, bool replaced) {
+  registered_in_epoll_server_ = false;
+}
+
+void SMConnection::OnShutdown(EpollServer* eps, int fd) {
+  Cleanup("OnShutdown");
+  return;
+}
+
+void SMConnection::Cleanup(const char* cleanup) {
+  VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT << "Cleanup: " << cleanup;
+  if (!initialized_)
+    return;
+  Reset();
+  if (connection_pool_)
+    connection_pool_->SMConnectionDone(this);
+  if (sm_interface_)
+    sm_interface_->ResetForNewConnection();
+  last_read_time_ = 0;
+}
+
+void SMConnection::HandleEvents() {
+  VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
+          << "Received: " << EpollServer::EventMaskToString(events_).c_str();
+
+  if (events_ & EPOLLIN) {
+    if (!DoRead())
+      goto handle_close_or_error;
+  }
+
+  if (events_ & EPOLLOUT) {
+    // Check if we have connected or not
+    if (connection_complete_ == false) {
+      int sock_error;
+      socklen_t sock_error_len = sizeof(sock_error);
+      int ret =
+          getsockopt(fd_, SOL_SOCKET, SO_ERROR, &sock_error, &sock_error_len);
+      if (ret != 0) {
+        VLOG(1) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
+                << "getsockopt error: " << errno << ": " << strerror(errno);
+        goto handle_close_or_error;
+      }
+      if (sock_error == 0) {
+        connection_complete_ = true;
+        VLOG(1) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
+                << "Connection complete to " << server_ip_ << ":"
+                << server_port_ << " ";
+      } else if (sock_error == EINPROGRESS) {
+        return;
+      } else {
+        VLOG(1) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
+                << "error connecting to server";
+        goto handle_close_or_error;
+      }
+    }
+    if (!DoWrite())
+      goto handle_close_or_error;
+  }
+
+  if (events_ & (EPOLLHUP | EPOLLERR)) {
+    VLOG(1) << log_prefix_ << ACCEPTOR_CLIENT_IDENT << "!!! Got HUP or ERR";
+    goto handle_close_or_error;
+  }
+  return;
+
+ handle_close_or_error:
+  Cleanup("HandleEvents");
+}
+
+// Decide if SPDY was negotiated.
+bool SMConnection::WasSpdyNegotiated(SpdyMajorVersion* version_negotiated) {
+  *version_negotiated = SPDY3;
+  if (force_spdy())
+    return true;
+
+  // If this is an SSL connection, check if NPN specifies SPDY.
+  if (ssl_) {
+    const unsigned char* npn_proto;
+    unsigned int npn_proto_len;
+    SSL_get0_next_proto_negotiated(ssl_, &npn_proto, &npn_proto_len);
+    if (npn_proto_len > 0) {
+      std::string npn_proto_str((const char*)npn_proto, npn_proto_len);
+      VLOG(1) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
+              << "NPN protocol detected: " << npn_proto_str;
+      if (!strncmp(reinterpret_cast<const char*>(npn_proto),
+                   "spdy/2",
+                   npn_proto_len)) {
+        *version_negotiated = SPDY2;
+        return true;
+      }
+      if (!strncmp(reinterpret_cast<const char*>(npn_proto),
+                   "spdy/3",
+                   npn_proto_len)) {
+        *version_negotiated = SPDY3;
+        return true;
+      }
+      if (!strncmp(reinterpret_cast<const char*>(npn_proto),
+                   "spdy/4a2",
+                   npn_proto_len)) {
+        *version_negotiated = SPDY4;
+        return true;
+      }
+    }
+  }
+
+  return false;
+}
+
+bool SMConnection::SetupProtocolInterfaces() {
+  DCHECK(!protocol_detected_);
+  protocol_detected_ = true;
+
+  SpdyMajorVersion version;
+  bool spdy_negotiated = WasSpdyNegotiated(&version);
+  bool using_ssl = ssl_ != NULL;
+
+  if (using_ssl)
+    VLOG(1) << (SSL_session_reused(ssl_) ? "Resumed" : "Renegotiated")
+            << " SSL Session.";
+
+  if (acceptor_->spdy_only_ && !spdy_negotiated) {
+    VLOG(1) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
+            << "SPDY proxy only, closing HTTPS connection.";
+    return false;
+  }
+
+  switch (acceptor_->flip_handler_type_) {
+    case FLIP_HANDLER_HTTP_SERVER: {
+      DCHECK(!spdy_negotiated);
+      VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
+              << (sm_http_interface_ ? "Creating" : "Reusing")
+              << " HTTP interface.";
+      if (!sm_http_interface_)
+        sm_http_interface_ = new HttpSM(this, NULL, memory_cache_, acceptor_);
+      sm_interface_ = sm_http_interface_;
+      break;
+    }
+    case FLIP_HANDLER_PROXY: {
+      VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
+              << (sm_streamer_interface_ ? "Creating" : "Reusing")
+              << " PROXY Streamer interface.";
+      if (!sm_streamer_interface_) {
+        sm_streamer_interface_ =
+            new StreamerSM(this, NULL, epoll_server_, acceptor_);
+        sm_streamer_interface_->set_is_request();
+      }
+      sm_interface_ = sm_streamer_interface_;
+      // If spdy is not negotiated, the streamer interface will proxy all
+      // data to the origin server.
+      if (!spdy_negotiated)
+        break;
+    }
+    // Otherwise fall through into the case below.
+    case FLIP_HANDLER_SPDY_SERVER: {
+      DCHECK(spdy_negotiated);
+      VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
+              << (sm_spdy_interface_ ? "Creating" : "Reusing")
+              << " SPDY interface.";
+      if (sm_spdy_interface_)
+        sm_spdy_interface_->CreateFramer(version);
+      else
+        sm_spdy_interface_ = new SpdySM(
+            this, NULL, epoll_server_, memory_cache_, acceptor_, version);
+      sm_interface_ = sm_spdy_interface_;
+      break;
+    }
+  }
+
+  CorkSocket();
+  if (!sm_interface_->PostAcceptHook())
+    return false;
+
+  return true;
+}
+
+bool SMConnection::DoRead() {
+  VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT << "DoRead()";
+  while (!read_buffer_.Full()) {
+    char* bytes;
+    int size;
+    if (fd_ == -1) {
+      VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
+              << "DoRead(): fd_ == -1. Invalid FD. Returning false";
+      return false;
+    }
+    read_buffer_.GetWritablePtr(&bytes, &size);
+    ssize_t bytes_read = 0;
+    if (ssl_) {
+      bytes_read = SSL_read(ssl_, bytes, size);
+      if (bytes_read < 0) {
+        int err = SSL_get_error(ssl_, bytes_read);
+        switch (err) {
+          case SSL_ERROR_WANT_READ:
+          case SSL_ERROR_WANT_WRITE:
+          case SSL_ERROR_WANT_ACCEPT:
+          case SSL_ERROR_WANT_CONNECT:
+            events_ &= ~EPOLLIN;
+            VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
+                    << "DoRead: SSL WANT_XXX: " << err;
+            goto done;
+          default:
+            PrintSslError();
+            goto error_or_close;
+        }
+      }
+    } else {
+      bytes_read = recv(fd_, bytes, size, MSG_DONTWAIT);
+    }
+    int stored_errno = errno;
+    if (bytes_read == -1) {
+      switch (stored_errno) {
+        case EAGAIN:
+          events_ &= ~EPOLLIN;
+          VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
+                  << "Got EAGAIN while reading";
+          goto done;
+        case EINTR:
+          VLOG(1) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
+                  << "Got EINTR while reading";
+          continue;
+        default:
+          VLOG(1) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
+                  << "While calling recv, got error: "
+                  << (ssl_ ? "(ssl error)" : strerror(stored_errno));
+          goto error_or_close;
+      }
+    } else if (bytes_read > 0) {
+      VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT << "read " << bytes_read
+              << " bytes";
+      last_read_time_ = time(NULL);
+      // If the protocol hasn't been detected yet, set up the handlers
+      // we'll need.
+      if (!protocol_detected_) {
+        if (!SetupProtocolInterfaces()) {
+          LOG(ERROR) << "Error setting up protocol interfaces.";
+          goto error_or_close;
+        }
+      }
+      read_buffer_.AdvanceWritablePtr(bytes_read);
+      if (!DoConsumeReadData())
+        goto error_or_close;
+      continue;
+    } else {  // bytes_read == 0
+      VLOG(1) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
+              << "0 bytes read with recv call.";
+    }
+    goto error_or_close;
+  }
+ done:
+  VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT << "DoRead done!";
+  return true;
+
+ error_or_close:
+  VLOG(1) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
+          << "DoRead(): error_or_close. "
+          << "Cleaning up, then returning false";
+  Cleanup("DoRead");
+  return false;
+}
+
+bool SMConnection::DoConsumeReadData() {
+  char* bytes;
+  int size;
+  read_buffer_.GetReadablePtr(&bytes, &size);
+  while (size != 0) {
+    size_t bytes_consumed = sm_interface_->ProcessReadInput(bytes, size);
+    VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT << "consumed "
+            << bytes_consumed << " bytes";
+    if (bytes_consumed == 0) {
+      break;
+    }
+    read_buffer_.AdvanceReadablePtr(bytes_consumed);
+    if (sm_interface_->MessageFullyRead()) {
+      VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
+              << "HandleRequestFullyRead: Setting EPOLLOUT";
+      HandleResponseFullyRead();
+      events_ |= EPOLLOUT;
+    } else if (sm_interface_->Error()) {
+      LOG(ERROR) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
+                 << "Framer error detected: Setting EPOLLOUT: "
+                 << sm_interface_->ErrorAsString();
+      // this causes everything to be closed/cleaned up.
+      events_ |= EPOLLOUT;
+      return false;
+    }
+    read_buffer_.GetReadablePtr(&bytes, &size);
+  }
+  return true;
+}
+
+void SMConnection::HandleResponseFullyRead() { sm_interface_->Cleanup(); }
+
+bool SMConnection::DoWrite() {
+  size_t bytes_sent = 0;
+  int flags = MSG_NOSIGNAL | MSG_DONTWAIT;
+  if (fd_ == -1) {
+    VLOG(1) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
+            << "DoWrite: fd == -1. Returning false.";
+    return false;
+  }
+  if (output_list_.empty()) {
+    VLOG(2) << log_prefix_ << "DoWrite: Output list empty.";
+    if (sm_interface_) {
+      sm_interface_->GetOutput();
+    }
+    if (output_list_.empty()) {
+      events_ &= ~EPOLLOUT;
+    }
+  }
+  while (!output_list_.empty()) {
+    VLOG(2) << log_prefix_
+            << "DoWrite: Items in output list: " << output_list_.size();
+    if (bytes_sent >= max_bytes_sent_per_dowrite_) {
+      VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
+              << " byte sent >= max bytes sent per write: Setting EPOLLOUT: "
+              << bytes_sent;
+      events_ |= EPOLLOUT;
+      break;
+    }
+    if (sm_interface_ && output_list_.size() < 2) {
+      sm_interface_->GetOutput();
+    }
+    DataFrame* data_frame = output_list_.front();
+    const char* bytes = data_frame->data;
+    int size = data_frame->size;
+    bytes += data_frame->index;
+    size -= data_frame->index;
+    DCHECK_GE(size, 0);
+    if (size <= 0) {
+      output_list_.pop_front();
+      delete data_frame;
+      continue;
+    }
+
+    flags = MSG_NOSIGNAL | MSG_DONTWAIT;
+    // Look for a queue size > 1 because |this| frame is remains on the list
+    // until it has finished sending.
+    if (output_list_.size() > 1) {
+      VLOG(2) << log_prefix_ << "Outlist size: " << output_list_.size()
+              << ": Adding MSG_MORE flag";
+      flags |= MSG_MORE;
+    }
+    VLOG(2) << log_prefix_ << "Attempting to send " << size << " bytes.";
+    ssize_t bytes_written = Send(bytes, size, flags);
+    int stored_errno = errno;
+    if (bytes_written == -1) {
+      switch (stored_errno) {
+        case EAGAIN:
+          events_ &= ~EPOLLOUT;
+          VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
+                  << "Got EAGAIN while writing";
+          goto done;
+        case EINTR:
+          VLOG(1) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
+                  << "Got EINTR while writing";
+          continue;
+        default:
+          VLOG(1) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
+                  << "While calling send, got error: " << stored_errno << ": "
+                  << (ssl_ ? "" : strerror(stored_errno));
+          goto error_or_close;
+      }
+    } else if (bytes_written > 0) {
+      VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
+              << "Wrote: " << bytes_written << " bytes";
+      data_frame->index += bytes_written;
+      bytes_sent += bytes_written;
+      continue;
+    } else if (bytes_written == -2) {
+      // -2 handles SSL_ERROR_WANT_* errors
+      events_ &= ~EPOLLOUT;
+      goto done;
+    }
+    VLOG(1) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
+            << "0 bytes written with send call.";
+    goto error_or_close;
+  }
+ done:
+  UncorkSocket();
+  return true;
+
+ error_or_close:
+  VLOG(1) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
+          << "DoWrite: error_or_close. Returning false "
+          << "after cleaning up";
+  Cleanup("DoWrite");
+  UncorkSocket();
+  return false;
+}
+
+void SMConnection::Reset() {
+  VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT << "Resetting";
+  if (ssl_) {
+    SSL_shutdown(ssl_);
+    PrintSslError();
+    SSL_free(ssl_);
+    PrintSslError();
+    ssl_ = NULL;
+  }
+  if (registered_in_epoll_server_) {
+    epoll_server_->UnregisterFD(fd_);
+    registered_in_epoll_server_ = false;
+  }
+  if (fd_ >= 0) {
+    VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT << "Closing connection";
+    close(fd_);
+    fd_ = -1;
+  }
+  read_buffer_.Clear();
+  initialized_ = false;
+  protocol_detected_ = false;
+  events_ = 0;
+  for (std::list<DataFrame*>::iterator i = output_list_.begin();
+       i != output_list_.end();
+       ++i) {
+    delete *i;
+  }
+  output_list_.clear();
+}
+
+// static
+SMConnection* SMConnection::NewSMConnection(EpollServer* epoll_server,
+                                            SSLState* ssl_state,
+                                            MemoryCache* memory_cache,
+                                            FlipAcceptor* acceptor,
+                                            std::string log_prefix) {
+  return new SMConnection(
+      epoll_server, ssl_state, memory_cache, acceptor, log_prefix);
+}
+
+}  // namespace net
diff --git a/net/tools/flip_server/sm_connection.h b/net/tools/flip_server/sm_connection.h
new file mode 100644
index 0000000..2fe3228
--- /dev/null
+++ b/net/tools/flip_server/sm_connection.h
@@ -0,0 +1,165 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_TOOLS_FLIP_SERVER_SM_CONNECTION_H_
+#define NET_TOOLS_FLIP_SERVER_SM_CONNECTION_H_
+
+#include <arpa/inet.h>  // in_addr_t
+#include <time.h>
+
+#include <list>
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "net/spdy/spdy_protocol.h"
+#include "net/tools/epoll_server/epoll_server.h"
+#include "net/tools/flip_server/create_listener.h"
+#include "net/tools/flip_server/mem_cache.h"
+#include "net/tools/flip_server/ring_buffer.h"
+#include "net/tools/flip_server/sm_interface.h"
+#include "openssl/ssl.h"
+
+namespace net {
+
+class FlipAcceptor;
+class MemoryCache;
+struct SSLState;
+class SpdySM;
+
+// A frame of data to be sent.
+class DataFrame {
+ public:
+  const char* data;
+  size_t size;
+  bool delete_when_done;
+  size_t index;
+  DataFrame() : data(NULL), size(0), delete_when_done(false), index(0) {}
+  virtual ~DataFrame();
+};
+
+typedef std::list<DataFrame*> OutputList;
+
+class SMConnection : public SMConnectionInterface,
+                     public EpollCallbackInterface,
+                     public NotifierInterface {
+ public:
+  virtual ~SMConnection();
+
+  static SMConnection* NewSMConnection(EpollServer* epoll_server,
+                                       SSLState* ssl_state,
+                                       MemoryCache* memory_cache,
+                                       FlipAcceptor* acceptor,
+                                       std::string log_prefix);
+
+  // TODO(mbelshe): Make these private.
+  time_t last_read_time_;
+  std::string server_ip_;
+  std::string server_port_;
+
+  virtual EpollServer* epoll_server() OVERRIDE;
+  OutputList* output_list() { return &output_list_; }
+  MemoryCache* memory_cache() { return memory_cache_; }
+  virtual void ReadyToSend() OVERRIDE;
+  void EnqueueDataFrame(DataFrame* df);
+
+  int fd() const { return fd_; }
+  bool initialized() const { return initialized_; }
+  std::string client_ip() const { return client_ip_; }
+
+  virtual void InitSMConnection(SMConnectionPoolInterface* connection_pool,
+                                SMInterface* sm_interface,
+                                EpollServer* epoll_server,
+                                int fd,
+                                std::string server_ip,
+                                std::string server_port,
+                                std::string remote_ip,
+                                bool use_ssl);
+
+  void CorkSocket();
+  void UncorkSocket();
+
+  int Send(const char* data, int len, int flags);
+
+  // EpollCallbackInterface interface.
+  virtual void OnRegistration(EpollServer* eps,
+                              int fd,
+                              int event_mask) OVERRIDE;
+  virtual void OnModification(int fd, int event_mask) OVERRIDE {}
+  virtual void OnEvent(int fd, EpollEvent* event) OVERRIDE;
+  virtual void OnUnregistration(int fd, bool replaced) OVERRIDE;
+  virtual void OnShutdown(EpollServer* eps, int fd) OVERRIDE;
+
+  // NotifierInterface interface.
+  virtual void Notify() OVERRIDE {}
+
+  void Cleanup(const char* cleanup);
+
+  // Flag indicating if we should force spdy on all connections.
+  static bool force_spdy() { return force_spdy_; }
+  static void set_force_spdy(bool value) { force_spdy_ = value; }
+
+ private:
+  // Decide if SPDY was negotiated.
+  bool WasSpdyNegotiated(SpdyMajorVersion* version_negotiated);
+
+  // Initialize the protocol interfaces we'll need for this connection.
+  // Returns true if successful, false otherwise.
+  bool SetupProtocolInterfaces();
+
+  bool DoRead();
+  bool DoWrite();
+  bool DoConsumeReadData();
+  void Reset();
+
+  void HandleEvents();
+  void HandleResponseFullyRead();
+
+ protected:
+  friend std::ostream& operator<<(std::ostream& os, const SMConnection& c) {
+    os << &c << "\n";
+    return os;
+  }
+
+  SMConnection(EpollServer* epoll_server,
+               SSLState* ssl_state,
+               MemoryCache* memory_cache,
+               FlipAcceptor* acceptor,
+               std::string log_prefix);
+
+ private:
+  int fd_;
+  int events_;
+
+  bool registered_in_epoll_server_;
+  bool initialized_;
+  bool protocol_detected_;
+  bool connection_complete_;
+
+  SMConnectionPoolInterface* connection_pool_;
+
+  EpollServer* epoll_server_;
+  SSLState* ssl_state_;
+  MemoryCache* memory_cache_;
+  FlipAcceptor* acceptor_;
+  std::string client_ip_;
+
+  RingBuffer read_buffer_;
+
+  OutputList output_list_;
+  SpdySM* sm_spdy_interface_;
+  SMInterface* sm_http_interface_;
+  SMInterface* sm_streamer_interface_;
+  SMInterface* sm_interface_;
+  std::string log_prefix_;
+
+  size_t max_bytes_sent_per_dowrite_;
+
+  SSL* ssl_;
+
+  static bool force_spdy_;
+};
+
+}  // namespace net
+
+#endif  // NET_TOOLS_FLIP_SERVER_SM_CONNECTION_H_
diff --git a/net/tools/flip_server/sm_interface.h b/net/tools/flip_server/sm_interface.h
new file mode 100644
index 0000000..389c683
--- /dev/null
+++ b/net/tools/flip_server/sm_interface.h
@@ -0,0 +1,84 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_TOOLS_FLIP_SERVER_SM_INTERFACE_H_
+#define NET_TOOLS_FLIP_SERVER_SM_INTERFACE_H_
+
+// State Machine Interfaces
+
+#include <string>
+
+#include "net/tools/balsa/balsa_headers.h"
+
+namespace net {
+
+class EpollServer;
+class SMConnectionPoolInterface;
+class SMConnection;
+
+class SMInterface {
+ public:
+  virtual void InitSMInterface(SMInterface* sm_other_interface,
+                               int32 server_idx) = 0;
+  virtual void InitSMConnection(SMConnectionPoolInterface* connection_pool,
+                                SMInterface* sm_interface,
+                                EpollServer* epoll_server,
+                                int fd,
+                                std::string server_ip,
+                                std::string server_port,
+                                std::string remote_ip,
+                                bool use_ssl) = 0;
+  virtual size_t ProcessReadInput(const char* data, size_t len) = 0;
+  virtual size_t ProcessWriteInput(const char* data, size_t len) = 0;
+  virtual void SetStreamID(uint32 stream_id) = 0;
+  virtual bool MessageFullyRead() const = 0;
+  virtual bool Error() const = 0;
+  virtual const char* ErrorAsString() const = 0;
+  virtual void Reset() = 0;
+  virtual void ResetForNewInterface(int32 server_idx) = 0;
+  // ResetForNewConnection is used for interfaces which control SMConnection
+  // objects. When called an interface may put its connection object into
+  // a reusable instance pool. Currently this is what the HttpSM interface
+  // does.
+  virtual void ResetForNewConnection() = 0;
+  virtual void Cleanup() = 0;
+
+  virtual int PostAcceptHook() = 0;
+
+  virtual void NewStream(uint32 stream_id,
+                         uint32 priority,
+                         const std::string& filename) = 0;
+  virtual void SendEOF(uint32 stream_id) = 0;
+  virtual void SendErrorNotFound(uint32 stream_id) = 0;
+  virtual size_t SendSynStream(uint32 stream_id,
+                               const BalsaHeaders& headers) = 0;
+  virtual size_t SendSynReply(uint32 stream_id,
+                              const BalsaHeaders& headers) = 0;
+  virtual void SendDataFrame(uint32 stream_id,
+                             const char* data,
+                             int64 len,
+                             uint32 flags,
+                             bool compress) = 0;
+  virtual void GetOutput() = 0;
+  virtual void set_is_request() = 0;
+
+  virtual ~SMInterface() {}
+};
+
+class SMConnectionInterface {
+ public:
+  virtual ~SMConnectionInterface() {}
+  virtual void ReadyToSend() = 0;
+  virtual EpollServer* epoll_server() = 0;
+};
+
+class SMConnectionPoolInterface {
+ public:
+  virtual ~SMConnectionPoolInterface() {}
+  virtual void SMConnectionDone(SMConnection* connection) = 0;
+};
+
+}  // namespace net
+
+#endif  // NET_TOOLS_FLIP_SERVER_SM_INTERFACE_H_
diff --git a/net/tools/flip_server/spdy_interface.cc b/net/tools/flip_server/spdy_interface.cc
new file mode 100644
index 0000000..eec20c0
--- /dev/null
+++ b/net/tools/flip_server/spdy_interface.cc
@@ -0,0 +1,628 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/tools/flip_server/spdy_interface.h"
+
+#include <algorithm>
+#include <string>
+
+#include "net/spdy/spdy_framer.h"
+#include "net/spdy/spdy_protocol.h"
+#include "net/tools/dump_cache/url_utilities.h"
+#include "net/tools/flip_server/constants.h"
+#include "net/tools/flip_server/flip_config.h"
+#include "net/tools/flip_server/http_interface.h"
+#include "net/tools/flip_server/spdy_util.h"
+
+namespace net {
+
+// static
+std::string SpdySM::forward_ip_header_;
+
+class SpdyFrameDataFrame : public DataFrame {
+ public:
+  explicit SpdyFrameDataFrame(SpdyFrame* spdy_frame) : frame(spdy_frame) {
+    data = spdy_frame->data();
+    size = spdy_frame->size();
+  }
+
+  virtual ~SpdyFrameDataFrame() { delete frame; }
+
+  const SpdyFrame* frame;
+};
+
+SpdySM::SpdySM(SMConnection* connection,
+               SMInterface* sm_http_interface,
+               EpollServer* epoll_server,
+               MemoryCache* memory_cache,
+               FlipAcceptor* acceptor,
+               SpdyMajorVersion spdy_version)
+    : buffered_spdy_framer_(new BufferedSpdyFramer(spdy_version, true)),
+      valid_spdy_session_(false),
+      connection_(connection),
+      client_output_list_(connection->output_list()),
+      client_output_ordering_(connection),
+      next_outgoing_stream_id_(2),
+      epoll_server_(epoll_server),
+      acceptor_(acceptor),
+      memory_cache_(memory_cache),
+      close_on_error_(false) {
+  buffered_spdy_framer_->set_visitor(this);
+}
+
+SpdySM::~SpdySM() { }
+
+void SpdySM::InitSMConnection(SMConnectionPoolInterface* connection_pool,
+                              SMInterface* sm_interface,
+                              EpollServer* epoll_server,
+                              int fd,
+                              std::string server_ip,
+                              std::string server_port,
+                              std::string remote_ip,
+                              bool use_ssl) {
+  VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Initializing server connection.";
+  connection_->InitSMConnection(connection_pool,
+                                sm_interface,
+                                epoll_server,
+                                fd,
+                                server_ip,
+                                server_port,
+                                remote_ip,
+                                use_ssl);
+}
+
+SMInterface* SpdySM::NewConnectionInterface() {
+  SMConnection* server_connection =
+      SMConnection::NewSMConnection(epoll_server_,
+                                    NULL,
+                                    memory_cache_,
+                                    acceptor_,
+                                    "http_conn: ");
+  if (server_connection == NULL) {
+    LOG(ERROR) << "SpdySM: Could not create server connection";
+    return NULL;
+  }
+  VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Creating new HTTP interface";
+  SMInterface* sm_http_interface =
+      new HttpSM(server_connection, this, memory_cache_, acceptor_);
+  return sm_http_interface;
+}
+
+SMInterface* SpdySM::FindOrMakeNewSMConnectionInterface(
+    const std::string& server_ip,
+    const std::string& server_port) {
+  SMInterface* sm_http_interface;
+  int32 server_idx;
+  if (unused_server_interface_list.empty()) {
+    sm_http_interface = NewConnectionInterface();
+    server_idx = server_interface_list.size();
+    server_interface_list.push_back(sm_http_interface);
+    VLOG(2) << ACCEPTOR_CLIENT_IDENT
+            << "SpdySM: Making new server connection on index: " << server_idx;
+  } else {
+    server_idx = unused_server_interface_list.back();
+    unused_server_interface_list.pop_back();
+    sm_http_interface = server_interface_list.at(server_idx);
+    VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Reusing connection on "
+            << "index: " << server_idx;
+  }
+
+  sm_http_interface->InitSMInterface(this, server_idx);
+  sm_http_interface->InitSMConnection(NULL,
+                                      sm_http_interface,
+                                      epoll_server_,
+                                      -1,
+                                      server_ip,
+                                      server_port,
+                                      std::string(),
+                                      false);
+
+  return sm_http_interface;
+}
+
+int SpdySM::SpdyHandleNewStream(SpdyStreamId stream_id,
+                                SpdyPriority priority,
+                                const SpdyHeaderBlock& headers,
+                                std::string& http_data,
+                                bool* is_https_scheme) {
+  *is_https_scheme = false;
+  VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: OnSyn(" << stream_id << ")";
+  VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: # headers: " << headers.size();
+
+  SpdyHeaderBlock::const_iterator method = headers.end();
+  SpdyHeaderBlock::const_iterator host = headers.end();
+  SpdyHeaderBlock::const_iterator path = headers.end();
+  SpdyHeaderBlock::const_iterator scheme = headers.end();
+  SpdyHeaderBlock::const_iterator version = headers.end();
+  SpdyHeaderBlock::const_iterator url = headers.end();
+
+  std::string path_string, host_string, version_string;
+
+  if (spdy_version() == SPDY2) {
+    url = headers.find("url");
+    method = headers.find("method");
+    version = headers.find("version");
+    scheme = headers.find("scheme");
+    if (url == headers.end() || method == headers.end() ||
+        version == headers.end() || scheme == headers.end()) {
+      VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: A mandatory header is "
+              << "missing. Not creating stream";
+      return 0;
+    }
+    // url->second here only ever seems to contain just the path. When this
+    // path contains a query string with a http:// in one of its values,
+    // UrlUtilities::GetUrlPath will fail and always return a / breaking
+    // the request. GetUrlPath assumes the absolute URL is being passed in.
+    path_string = UrlUtilities::GetUrlPath(url->second);
+    host_string = UrlUtilities::GetUrlHost(url->second);
+    version_string = version->second;
+  } else {
+    method = headers.find(":method");
+    host = headers.find(":host");
+    path = headers.find(":path");
+    scheme = headers.find(":scheme");
+    if (method == headers.end() || host == headers.end() ||
+        path == headers.end() || scheme == headers.end()) {
+      VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: A mandatory header is "
+              << "missing. Not creating stream";
+      return 0;
+    }
+    host_string = host->second;
+    path_string = path->second;
+    version_string = "HTTP/1.1";
+  }
+
+  if (scheme->second.compare("https") == 0) {
+    *is_https_scheme = true;
+  }
+
+  if (acceptor_->flip_handler_type_ == FLIP_HANDLER_SPDY_SERVER) {
+    VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Request: " << method->second
+            << " " << path_string;
+    std::string filename = EncodeURL(path_string,
+                                     host_string,
+                                     method->second);
+    NewStream(stream_id, priority, filename);
+  } else {
+    http_data +=
+        method->second + " " + path_string + " " + version_string + "\r\n";
+    VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Request: " << method->second << " "
+            << path_string << " " << version_string;
+    http_data += "Host: " + (*is_https_scheme ?
+                             acceptor_->https_server_ip_ :
+                             acceptor_->http_server_ip_) + "\r\n";
+    for (SpdyHeaderBlock::const_iterator i = headers.begin();
+         i != headers.end(); ++i) {
+      if ((i->first.size() > 0 && i->first[0] == ':') ||
+          i->first == "host" ||
+          i == method ||
+          i == host ||
+          i == path ||
+          i == scheme ||
+          i == version ||
+          i == url) {
+        // Ignore the entry.
+      } else {
+        http_data += i->first + ": " + i->second + "\r\n";
+        VLOG(2) << ACCEPTOR_CLIENT_IDENT << i->first.c_str() << ":"
+                << i->second.c_str();
+      }
+    }
+    if (forward_ip_header_.length()) {
+      // X-Client-Cluster-IP header
+      http_data += forward_ip_header_ + ": " +
+          connection_->client_ip() + "\r\n";
+    }
+    http_data += "\r\n";
+  }
+
+  VLOG(3) << ACCEPTOR_CLIENT_IDENT << "SpdySM: HTTP Request:\n" << http_data;
+  return 1;
+}
+
+void SpdySM::OnStreamFrameData(SpdyStreamId stream_id,
+                               const char* data,
+                               size_t len,
+                               bool fin) {
+  VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: StreamData(" << stream_id
+          << ", [" << len << "])";
+  StreamToSmif::iterator it = stream_to_smif_.find(stream_id);
+  if (it == stream_to_smif_.end()) {
+    VLOG(2) << "Dropping frame from unknown stream " << stream_id;
+    if (!valid_spdy_session_)
+      close_on_error_ = true;
+    return;
+  }
+
+  SMInterface* interface = it->second;
+  if (acceptor_->flip_handler_type_ == FLIP_HANDLER_PROXY)
+    interface->ProcessWriteInput(data, len);
+}
+
+void SpdySM::OnSynStream(SpdyStreamId stream_id,
+                         SpdyStreamId associated_stream_id,
+                         SpdyPriority priority,
+                         bool fin,
+                         bool unidirectional,
+                         const SpdyHeaderBlock& headers) {
+  std::string http_data;
+  bool is_https_scheme;
+  int ret = SpdyHandleNewStream(
+      stream_id, priority, headers, http_data, &is_https_scheme);
+  if (!ret) {
+    LOG(ERROR) << "SpdySM: Could not convert spdy into http.";
+    return;
+  }
+  // We've seen a valid looking SYN_STREAM, consider this to have
+  // been a real spdy session.
+  valid_spdy_session_ = true;
+
+  if (acceptor_->flip_handler_type_ == FLIP_HANDLER_PROXY) {
+    std::string server_ip;
+    std::string server_port;
+    if (is_https_scheme) {
+      server_ip = acceptor_->https_server_ip_;
+      server_port = acceptor_->https_server_port_;
+    } else {
+      server_ip = acceptor_->http_server_ip_;
+      server_port = acceptor_->http_server_port_;
+    }
+    SMInterface* sm_http_interface =
+        FindOrMakeNewSMConnectionInterface(server_ip, server_port);
+    stream_to_smif_[stream_id] = sm_http_interface;
+    sm_http_interface->SetStreamID(stream_id);
+    sm_http_interface->ProcessWriteInput(http_data.c_str(), http_data.size());
+  }
+}
+
+void SpdySM::OnSynReply(SpdyStreamId stream_id,
+                        bool fin,
+                        const SpdyHeaderBlock& headers) {
+  // TODO(willchan): if there is an error parsing headers, we
+  // should send a RST_STREAM.
+  VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: OnSynReply(" << stream_id << ")";
+}
+
+void SpdySM::OnHeaders(SpdyStreamId stream_id,
+                       bool fin,
+                       const SpdyHeaderBlock& headers) {
+  VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: OnHeaders(" << stream_id << ")";
+}
+
+void SpdySM::OnRstStream(SpdyStreamId stream_id, SpdyRstStreamStatus status) {
+  VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: OnRstStream(" << stream_id
+          << ")";
+  client_output_ordering_.RemoveStreamId(stream_id);
+}
+
+bool SpdySM::OnUnknownFrame(SpdyStreamId stream_id, int frame_type) {
+  return false;
+}
+
+size_t SpdySM::ProcessReadInput(const char* data, size_t len) {
+  DCHECK(buffered_spdy_framer_);
+  return buffered_spdy_framer_->ProcessInput(data, len);
+}
+
+size_t SpdySM::ProcessWriteInput(const char* data, size_t len) { return 0; }
+
+bool SpdySM::MessageFullyRead() const {
+  DCHECK(buffered_spdy_framer_);
+  return buffered_spdy_framer_->MessageFullyRead();
+}
+
+bool SpdySM::Error() const {
+  DCHECK(buffered_spdy_framer_);
+  return close_on_error_ || buffered_spdy_framer_->HasError();
+}
+
+const char* SpdySM::ErrorAsString() const {
+  DCHECK(Error());
+  DCHECK(buffered_spdy_framer_);
+  return SpdyFramer::ErrorCodeToString(buffered_spdy_framer_->error_code());
+}
+
+void SpdySM::ResetForNewInterface(int32 server_idx) {
+  VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Reset for new interface: "
+          << "server_idx: " << server_idx;
+  unused_server_interface_list.push_back(server_idx);
+}
+
+void SpdySM::ResetForNewConnection() {
+  // seq_num is not cleared, intentionally.
+  buffered_spdy_framer_.reset();
+  valid_spdy_session_ = false;
+  client_output_ordering_.Reset();
+  next_outgoing_stream_id_ = 2;
+}
+
+// Send a settings frame
+int SpdySM::PostAcceptHook() {
+  // We should have buffered_spdy_framer_ set after reuse
+  DCHECK(buffered_spdy_framer_);
+  SettingsMap settings;
+  settings[SETTINGS_MAX_CONCURRENT_STREAMS] =
+      SettingsFlagsAndValue(SETTINGS_FLAG_NONE, 100);
+  SpdyFrame* settings_frame = buffered_spdy_framer_->CreateSettings(settings);
+
+  VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Sending Settings Frame";
+  EnqueueDataFrame(new SpdyFrameDataFrame(settings_frame));
+  return 1;
+}
+
+void SpdySM::NewStream(uint32 stream_id,
+                       uint32 priority,
+                       const std::string& filename) {
+  MemCacheIter mci;
+  mci.stream_id = stream_id;
+  mci.priority = priority;
+  // TODO(yhirano): The program will crash when
+  // acceptor_->flip_handler_type_ != FLIP_HANDLER_SPDY_SERVER.
+  // It should be fixed or an assertion should be placed.
+  if (acceptor_->flip_handler_type_ == FLIP_HANDLER_SPDY_SERVER) {
+    if (!memory_cache_->AssignFileData(filename, &mci)) {
+      // error creating new stream.
+      VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Sending ErrorNotFound";
+      SendErrorNotFound(stream_id);
+    } else {
+      AddToOutputOrder(mci);
+    }
+  } else {
+    AddToOutputOrder(mci);
+  }
+}
+
+void SpdySM::AddToOutputOrder(const MemCacheIter& mci) {
+  client_output_ordering_.AddToOutputOrder(mci);
+}
+
+void SpdySM::SendEOF(uint32 stream_id) { SendEOFImpl(stream_id); }
+
+void SpdySM::SendErrorNotFound(uint32 stream_id) {
+  SendErrorNotFoundImpl(stream_id);
+}
+
+size_t SpdySM::SendSynStream(uint32 stream_id, const BalsaHeaders& headers) {
+  return SendSynStreamImpl(stream_id, headers);
+}
+
+size_t SpdySM::SendSynReply(uint32 stream_id, const BalsaHeaders& headers) {
+  return SendSynReplyImpl(stream_id, headers);
+}
+
+void SpdySM::SendDataFrame(uint32 stream_id,
+                           const char* data,
+                           int64 len,
+                           uint32 flags,
+                           bool compress) {
+  SpdyDataFlags spdy_flags = static_cast<SpdyDataFlags>(flags);
+  SendDataFrameImpl(stream_id, data, len, spdy_flags, compress);
+}
+
+void SpdySM::SendEOFImpl(uint32 stream_id) {
+  SendDataFrame(stream_id, NULL, 0, DATA_FLAG_FIN, false);
+  VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Sending EOF: " << stream_id;
+  KillStream(stream_id);
+  stream_to_smif_.erase(stream_id);
+}
+
+void SpdySM::SendErrorNotFoundImpl(uint32 stream_id) {
+  BalsaHeaders my_headers;
+  my_headers.SetFirstlineFromStringPieces("HTTP/1.1", "404", "Not Found");
+  SendSynReplyImpl(stream_id, my_headers);
+  SendDataFrame(stream_id, "wtf?", 4, DATA_FLAG_FIN, false);
+  client_output_ordering_.RemoveStreamId(stream_id);
+}
+
+void SpdySM::KillStream(uint32 stream_id) {
+  client_output_ordering_.RemoveStreamId(stream_id);
+}
+
+void SpdySM::CopyHeaders(SpdyHeaderBlock& dest, const BalsaHeaders& headers) {
+  for (BalsaHeaders::const_header_lines_iterator hi =
+           headers.header_lines_begin();
+       hi != headers.header_lines_end();
+       ++hi) {
+    // It is illegal to send SPDY headers with empty value or header
+    // names.
+    if (!hi->first.length() || !hi->second.length())
+      continue;
+
+    // Key must be all lower case in SPDY headers.
+    std::string key = hi->first.as_string();
+    std::transform(key.begin(), key.end(), key.begin(), ::tolower);
+    SpdyHeaderBlock::iterator fhi = dest.find(key);
+    if (fhi == dest.end()) {
+      dest[key] = hi->second.as_string();
+    } else {
+      dest[key] = (std::string(fhi->second.data(), fhi->second.size()) + "\0" +
+                   std::string(hi->second.data(), hi->second.size()));
+    }
+  }
+
+  // These headers have no value
+  dest.erase("X-Associated-Content");  // TODO(mbelshe): case-sensitive
+  dest.erase("X-Original-Url");        // TODO(mbelshe): case-sensitive
+}
+
+size_t SpdySM::SendSynStreamImpl(uint32 stream_id,
+                                 const BalsaHeaders& headers) {
+  SpdyHeaderBlock block;
+  CopyHeaders(block, headers);
+  if (spdy_version() == SPDY2) {
+    block["method"] = headers.request_method().as_string();
+    if (!headers.HasHeader("version"))
+      block["version"] = headers.request_version().as_string();
+    if (headers.HasHeader("X-Original-Url")) {
+      std::string original_url =
+          headers.GetHeader("X-Original-Url").as_string();
+      block["url"] = UrlUtilities::GetUrlPath(original_url);
+    } else {
+      block["url"] = headers.request_uri().as_string();
+    }
+  } else {
+    block[":method"] = headers.request_method().as_string();
+    block[":version"] = headers.request_version().as_string();
+    if (headers.HasHeader("X-Original-Url")) {
+      std::string original_url =
+          headers.GetHeader("X-Original-Url").as_string();
+      block[":path"] = UrlUtilities::GetUrlPath(original_url);
+      block[":host"] = UrlUtilities::GetUrlPath(original_url);
+    } else {
+      block[":path"] = headers.request_uri().as_string();
+      if (block.find("host") != block.end()) {
+        block[":host"] = headers.GetHeader("Host").as_string();
+        block.erase("host");
+      }
+    }
+  }
+
+  DCHECK(buffered_spdy_framer_);
+  SpdyFrame* fsrcf = buffered_spdy_framer_->CreateSynStream(
+      stream_id, 0, 0, CONTROL_FLAG_NONE, &block);
+  size_t df_size = fsrcf->size();
+  EnqueueDataFrame(new SpdyFrameDataFrame(fsrcf));
+
+  VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Sending SynStreamheader "
+          << stream_id;
+  return df_size;
+}
+
+size_t SpdySM::SendSynReplyImpl(uint32 stream_id, const BalsaHeaders& headers) {
+  SpdyHeaderBlock block;
+  CopyHeaders(block, headers);
+  if (spdy_version() == SPDY2) {
+    block["status"] = headers.response_code().as_string() + " " +
+        headers.response_reason_phrase().as_string();
+    block["version"] = headers.response_version().as_string();
+  } else {
+    block[":status"] = headers.response_code().as_string() + " " +
+        headers.response_reason_phrase().as_string();
+    block[":version"] = headers.response_version().as_string();
+  }
+
+  DCHECK(buffered_spdy_framer_);
+  SpdyFrame* fsrcf = buffered_spdy_framer_->CreateSynReply(
+      stream_id, CONTROL_FLAG_NONE, &block);
+  size_t df_size = fsrcf->size();
+  EnqueueDataFrame(new SpdyFrameDataFrame(fsrcf));
+
+  VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Sending SynReplyheader "
+          << stream_id;
+  return df_size;
+}
+
+void SpdySM::SendDataFrameImpl(uint32 stream_id,
+                               const char* data,
+                               int64 len,
+                               SpdyDataFlags flags,
+                               bool compress) {
+  DCHECK(buffered_spdy_framer_);
+  // TODO(mbelshe):  We can't compress here - before going into the
+  //                 priority queue.  Compression needs to be done
+  //                 with late binding.
+  if (len == 0) {
+    SpdyFrame* fdf =
+        buffered_spdy_framer_->CreateDataFrame(stream_id, data, len, flags);
+    EnqueueDataFrame(new SpdyFrameDataFrame(fdf));
+    return;
+  }
+
+  // Chop data frames into chunks so that one stream can't monopolize the
+  // output channel.
+  while (len > 0) {
+    int64 size = std::min(len, static_cast<int64>(kSpdySegmentSize));
+    SpdyDataFlags chunk_flags = flags;
+
+    // If we chunked this block, and the FIN flag was set, there is more
+    // data coming.  So, remove the flag.
+    if ((size < len) && (flags & DATA_FLAG_FIN))
+      chunk_flags = static_cast<SpdyDataFlags>(chunk_flags & ~DATA_FLAG_FIN);
+
+    SpdyFrame* fdf = buffered_spdy_framer_->CreateDataFrame(
+        stream_id, data, size, chunk_flags);
+    EnqueueDataFrame(new SpdyFrameDataFrame(fdf));
+
+    VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Sending data frame "
+            << stream_id << " [" << size << "] shrunk to "
+            << (fdf->size() - kSpdyOverhead) << ", flags=" << flags;
+
+    data += size;
+    len -= size;
+  }
+}
+
+void SpdySM::EnqueueDataFrame(DataFrame* df) {
+  connection_->EnqueueDataFrame(df);
+}
+
+void SpdySM::GetOutput() {
+  while (client_output_list_->size() < 2) {
+    MemCacheIter* mci = client_output_ordering_.GetIter();
+    if (mci == NULL) {
+      VLOG(2) << ACCEPTOR_CLIENT_IDENT
+              << "SpdySM: GetOutput: nothing to output!?";
+      return;
+    }
+    if (!mci->transformed_header) {
+      mci->transformed_header = true;
+      VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: GetOutput transformed "
+              << "header stream_id: [" << mci->stream_id << "]";
+      if ((mci->stream_id % 2) == 0) {
+        // this is a server initiated stream.
+        // Ideally, we'd do a 'syn-push' here, instead of a syn-reply.
+        BalsaHeaders headers;
+        headers.CopyFrom(*(mci->file_data->headers()));
+        headers.ReplaceOrAppendHeader("status", "200");
+        headers.ReplaceOrAppendHeader("version", "http/1.1");
+        headers.SetRequestFirstlineFromStringPieces(
+            "PUSH", mci->file_data->filename(), "");
+        mci->bytes_sent = SendSynStream(mci->stream_id, headers);
+      } else {
+        BalsaHeaders headers;
+        headers.CopyFrom(*(mci->file_data->headers()));
+        mci->bytes_sent = SendSynReply(mci->stream_id, headers);
+      }
+      return;
+    }
+    if (mci->body_bytes_consumed >= mci->file_data->body().size()) {
+      VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: GetOutput "
+              << "remove_stream_id: [" << mci->stream_id << "]";
+      SendEOF(mci->stream_id);
+      return;
+    }
+    size_t num_to_write =
+        mci->file_data->body().size() - mci->body_bytes_consumed;
+    if (num_to_write > mci->max_segment_size)
+      num_to_write = mci->max_segment_size;
+
+    bool should_compress = false;
+    if (!mci->file_data->headers()->HasHeader("content-encoding")) {
+      if (mci->file_data->headers()->HasHeader("content-type")) {
+        std::string content_type =
+            mci->file_data->headers()->GetHeader("content-type").as_string();
+        if (content_type.find("image") == content_type.npos)
+          should_compress = true;
+      }
+    }
+
+    SendDataFrame(mci->stream_id,
+                  mci->file_data->body().data() + mci->body_bytes_consumed,
+                  num_to_write,
+                  0,
+                  should_compress);
+    VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: GetOutput SendDataFrame["
+            << mci->stream_id << "]: " << num_to_write;
+    mci->body_bytes_consumed += num_to_write;
+    mci->bytes_sent += num_to_write;
+  }
+}
+
+void SpdySM::CreateFramer(SpdyMajorVersion spdy_version) {
+  DCHECK(!buffered_spdy_framer_);
+  buffered_spdy_framer_.reset(new BufferedSpdyFramer(spdy_version, true));
+  buffered_spdy_framer_->set_visitor(this);
+}
+
+}  // namespace net
diff --git a/net/tools/flip_server/spdy_interface.h b/net/tools/flip_server/spdy_interface.h
new file mode 100644
index 0000000..2174274
--- /dev/null
+++ b/net/tools/flip_server/spdy_interface.h
@@ -0,0 +1,222 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_TOOLS_FLIP_SERVER_SPDY_INTERFACE_H_
+#define NET_TOOLS_FLIP_SERVER_SPDY_INTERFACE_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/spdy/buffered_spdy_framer.h"
+#include "net/spdy/spdy_protocol.h"
+#include "net/tools/balsa/balsa_headers.h"
+#include "net/tools/balsa/balsa_visitor_interface.h"
+#include "net/tools/flip_server/output_ordering.h"
+#include "net/tools/flip_server/sm_connection.h"
+#include "net/tools/flip_server/sm_interface.h"
+
+namespace net {
+
+class FlipAcceptor;
+class MemoryCache;
+
+class SpdySM : public BufferedSpdyFramerVisitorInterface, public SMInterface {
+ public:
+  SpdySM(SMConnection* connection,
+         SMInterface* sm_http_interface,
+         EpollServer* epoll_server,
+         MemoryCache* memory_cache,
+         FlipAcceptor* acceptor,
+         SpdyMajorVersion spdy_version);
+  virtual ~SpdySM();
+
+  virtual void InitSMInterface(SMInterface* sm_http_interface,
+                               int32 server_idx) OVERRIDE {}
+
+  virtual void InitSMConnection(SMConnectionPoolInterface* connection_pool,
+                                SMInterface* sm_interface,
+                                EpollServer* epoll_server,
+                                int fd,
+                                std::string server_ip,
+                                std::string server_port,
+                                std::string remote_ip,
+                                bool use_ssl) OVERRIDE;
+
+  // Create new SPDY framer after reusing SpdySM and negotiating new version
+  void CreateFramer(SpdyMajorVersion spdy_version);
+
+ private:
+  virtual void set_is_request() OVERRIDE {}
+  SMInterface* NewConnectionInterface();
+  // virtual for tests
+  virtual SMInterface* FindOrMakeNewSMConnectionInterface(
+      const std::string& server_ip,
+      const std::string& server_port);
+  int SpdyHandleNewStream(SpdyStreamId stream_id,
+                          SpdyPriority priority,
+                          const SpdyHeaderBlock& headers,
+                          std::string& http_data,
+                          bool* is_https_scheme);
+
+  // BufferedSpdyFramerVisitorInterface:
+  virtual void OnError(SpdyFramer::SpdyError error_code) OVERRIDE {}
+  virtual void OnStreamError(SpdyStreamId stream_id,
+                             const std::string& description) OVERRIDE {}
+  // Called after all the header data for SYN_STREAM control frame is received.
+  virtual void OnSynStream(SpdyStreamId stream_id,
+                           SpdyStreamId associated_stream_id,
+                           SpdyPriority priority,
+                           bool fin,
+                           bool unidirectional,
+                           const SpdyHeaderBlock& headers) OVERRIDE;
+
+  // Called after all the header data for SYN_REPLY control frame is received.
+  virtual void OnSynReply(SpdyStreamId stream_id,
+                          bool fin,
+                          const SpdyHeaderBlock& headers) OVERRIDE;
+
+  // Called after all the header data for HEADERS control frame is received.
+  virtual void OnHeaders(SpdyStreamId stream_id,
+                         bool fin,
+                         const SpdyHeaderBlock& headers) OVERRIDE;
+
+  // Called when data frame header is received.
+  virtual void OnDataFrameHeader(SpdyStreamId stream_id,
+                                 size_t length,
+                                 bool fin) OVERRIDE {}
+
+  // Called when data is received.
+  // |stream_id| The stream receiving data.
+  // |data| A buffer containing the data received.
+  // |len| The length of the data buffer.
+  // When the other side has finished sending data on this stream,
+  // this method will be called with a zero-length buffer.
+  virtual void OnStreamFrameData(SpdyStreamId stream_id,
+                                 const char* data,
+                                 size_t len,
+                                 bool fin) OVERRIDE;
+
+  // Called when a SETTINGS frame is received.
+  // |clear_persisted| True if the respective flag is set on the SETTINGS frame.
+  virtual void OnSettings(bool clear_persisted) OVERRIDE {}
+
+  // Called when an individual setting within a SETTINGS frame has been parsed
+  // and validated.
+  virtual void OnSetting(SpdySettingsIds id,
+                         uint8 flags,
+                         uint32 value) OVERRIDE {}
+
+  // Called when a PING frame has been parsed.
+  virtual void OnPing(SpdyPingId unique_id, bool is_ack) OVERRIDE {}
+
+  // Called when a RST_STREAM frame has been parsed.
+  virtual void OnRstStream(SpdyStreamId stream_id,
+                           SpdyRstStreamStatus status) OVERRIDE;
+
+  // Called when a GOAWAY frame has been parsed.
+  virtual void OnGoAway(SpdyStreamId last_accepted_stream_id,
+                        SpdyGoAwayStatus status) OVERRIDE {}
+
+  // Called when a WINDOW_UPDATE frame has been parsed.
+  virtual void OnWindowUpdate(SpdyStreamId stream_id,
+                              uint32 delta_window_size) OVERRIDE {}
+
+  // Called when a PUSH_PROMISE frame has been parsed.
+  virtual void OnPushPromise(SpdyStreamId stream_id,
+                             SpdyStreamId promised_stream_id,
+                             const SpdyHeaderBlock& headers) OVERRIDE {}
+
+  virtual bool OnUnknownFrame(SpdyStreamId stream_id, int frame_type) OVERRIDE;
+
+ public:
+  virtual size_t ProcessReadInput(const char* data, size_t len) OVERRIDE;
+  virtual size_t ProcessWriteInput(const char* data, size_t len) OVERRIDE;
+  virtual bool MessageFullyRead() const OVERRIDE;
+  virtual void SetStreamID(uint32 stream_id) OVERRIDE {}
+  virtual bool Error() const OVERRIDE;
+  virtual const char* ErrorAsString() const OVERRIDE;
+  virtual void Reset() OVERRIDE {}
+  virtual void ResetForNewInterface(int32 server_idx) OVERRIDE;
+  virtual void ResetForNewConnection() OVERRIDE;
+  // SMInterface's Cleanup is currently only called by SMConnection after a
+  // protocol message as been fully read. Spdy's SMInterface does not need
+  // to do any cleanup at this time.
+  // TODO(klindsay) This method is probably not being used properly and
+  // some logic review and method renaming is probably in order.
+  virtual void Cleanup() OVERRIDE {}
+  // Send a settings frame
+  virtual int PostAcceptHook() OVERRIDE;
+  virtual void NewStream(uint32 stream_id,
+                         uint32 priority,
+                         const std::string& filename) OVERRIDE;
+  void AddToOutputOrder(const MemCacheIter& mci);
+  virtual void SendEOF(uint32 stream_id) OVERRIDE;
+  virtual void SendErrorNotFound(uint32 stream_id) OVERRIDE;
+  virtual size_t SendSynStream(uint32 stream_id,
+                               const BalsaHeaders& headers) OVERRIDE;
+  virtual size_t SendSynReply(uint32 stream_id,
+                              const BalsaHeaders& headers) OVERRIDE;
+  virtual void SendDataFrame(uint32 stream_id,
+                             const char* data,
+                             int64 len,
+                             uint32 flags,
+                             bool compress) OVERRIDE;
+  BufferedSpdyFramer* spdy_framer() { return buffered_spdy_framer_.get(); }
+
+  const OutputOrdering& output_ordering() const {
+    return client_output_ordering_;
+  }
+
+  static std::string forward_ip_header() { return forward_ip_header_; }
+  static void set_forward_ip_header(const std::string& value) {
+    forward_ip_header_ = value;
+  }
+  SpdyMajorVersion spdy_version() const {
+    DCHECK(buffered_spdy_framer_);
+    return buffered_spdy_framer_->protocol_version();
+  }
+
+ private:
+  void SendEOFImpl(uint32 stream_id);
+  void SendErrorNotFoundImpl(uint32 stream_id);
+  void KillStream(uint32 stream_id);
+  void CopyHeaders(SpdyHeaderBlock& dest, const BalsaHeaders& headers);
+  size_t SendSynStreamImpl(uint32 stream_id, const BalsaHeaders& headers);
+  size_t SendSynReplyImpl(uint32 stream_id, const BalsaHeaders& headers);
+  void SendDataFrameImpl(uint32 stream_id,
+                         const char* data,
+                         int64 len,
+                         SpdyDataFlags flags,
+                         bool compress);
+  void EnqueueDataFrame(DataFrame* df);
+  virtual void GetOutput() OVERRIDE;
+
+ private:
+  scoped_ptr<BufferedSpdyFramer> buffered_spdy_framer_;
+  bool valid_spdy_session_;  // True if we have seen valid data on this session.
+                             // Use this to fail fast when junk is sent to our
+                             // port.
+
+  SMConnection* connection_;
+  OutputList* client_output_list_;
+  OutputOrdering client_output_ordering_;
+  uint32 next_outgoing_stream_id_;
+  EpollServer* epoll_server_;
+  FlipAcceptor* acceptor_;
+  MemoryCache* memory_cache_;
+  std::vector<SMInterface*> server_interface_list;
+  std::vector<int32> unused_server_interface_list;
+  typedef std::map<uint32, SMInterface*> StreamToSmif;
+  StreamToSmif stream_to_smif_;
+  bool close_on_error_;
+
+  static std::string forward_ip_header_;
+};
+
+}  // namespace net
+
+#endif  // NET_TOOLS_FLIP_SERVER_SPDY_INTERFACE_H_
diff --git a/net/tools/flip_server/spdy_interface_test.cc b/net/tools/flip_server/spdy_interface_test.cc
new file mode 100644
index 0000000..9e66d0c
--- /dev/null
+++ b/net/tools/flip_server/spdy_interface_test.cc
@@ -0,0 +1,887 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/tools/flip_server/spdy_interface.h"
+
+#include <list>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_piece.h"
+#include "net/spdy/buffered_spdy_framer.h"
+#include "net/tools/balsa/balsa_enums.h"
+#include "net/tools/balsa/balsa_headers.h"
+#include "net/tools/flip_server/flip_config.h"
+#include "net/tools/flip_server/flip_test_utils.h"
+#include "net/tools/flip_server/mem_cache.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+using ::base::StringPiece;
+using ::testing::_;
+using ::testing::InSequence;
+using ::testing::InvokeWithoutArgs;
+using ::testing::Return;
+using ::testing::SaveArg;
+using ::testing::Values;
+
+namespace {
+
+struct StringSaver {
+ public:
+  StringSaver() : data(NULL), size(0) {}
+  void Save() { string = std::string(data, size); }
+
+  const char* data;
+  size_t size;
+  std::string string;
+};
+
+class SpdyFramerVisitor : public BufferedSpdyFramerVisitorInterface {
+ public:
+  virtual ~SpdyFramerVisitor() {}
+  MOCK_METHOD1(OnError, void(SpdyFramer::SpdyError));
+  MOCK_METHOD2(OnStreamError, void(SpdyStreamId, const std::string&));
+  MOCK_METHOD6(OnSynStream,
+               void(SpdyStreamId,
+                    SpdyStreamId,
+                    SpdyPriority,
+                    bool,
+                    bool,
+                    const SpdyHeaderBlock&));
+  MOCK_METHOD3(OnSynReply, void(SpdyStreamId, bool, const SpdyHeaderBlock&));
+  MOCK_METHOD3(OnHeaders, void(SpdyStreamId, bool, const SpdyHeaderBlock&));
+  MOCK_METHOD3(OnDataFrameHeader, void(SpdyStreamId, size_t, bool));
+  MOCK_METHOD4(OnStreamFrameData, void(SpdyStreamId,
+                                       const char*,
+                                       size_t,
+                                       bool));
+  MOCK_METHOD1(OnSettings, void(bool clear_persisted));
+  MOCK_METHOD3(OnSetting, void(SpdySettingsIds, uint8, uint32));
+  MOCK_METHOD2(OnPing, void(SpdyPingId unique_id, bool is_ack));
+  MOCK_METHOD2(OnRstStream, void(SpdyStreamId, SpdyRstStreamStatus));
+  MOCK_METHOD2(OnGoAway, void(SpdyStreamId, SpdyGoAwayStatus));
+  MOCK_METHOD2(OnWindowUpdate, void(SpdyStreamId, uint32));
+  MOCK_METHOD3(OnPushPromise,
+               void(SpdyStreamId, SpdyStreamId, const SpdyHeaderBlock&));
+  MOCK_METHOD2(OnUnknownFrame, bool(SpdyStreamId stream_id, int frame_type));
+};
+
+class FakeSMConnection : public SMConnection {
+ public:
+  FakeSMConnection(EpollServer* epoll_server,
+                   SSLState* ssl_state,
+                   MemoryCache* memory_cache,
+                   FlipAcceptor* acceptor,
+                   std::string log_prefix)
+      : SMConnection(epoll_server,
+                     ssl_state,
+                     memory_cache,
+                     acceptor,
+                     log_prefix) {}
+
+  MOCK_METHOD0(Cleanup, void());
+  MOCK_METHOD8(InitSMConnection,
+               void(SMConnectionPoolInterface*,
+                    SMInterface*,
+                    EpollServer*,
+                    int,
+                    std::string,
+                    std::string,
+                    std::string,
+                    bool));
+};
+
+// This class is almost SpdySM, except one function.
+// This class is the test target of tests in this file.
+class TestSpdySM : public SpdySM {
+ public:
+  virtual ~TestSpdySM() {}
+  TestSpdySM(SMConnection* connection,
+             SMInterface* sm_http_interface,
+             EpollServer* epoll_server,
+             MemoryCache* memory_cache,
+             FlipAcceptor* acceptor,
+             SpdyMajorVersion version)
+      : SpdySM(connection,
+               sm_http_interface,
+               epoll_server,
+               memory_cache,
+               acceptor,
+               version) {}
+
+  MOCK_METHOD2(FindOrMakeNewSMConnectionInterface,
+               SMInterface*(const std::string&, const std::string&));
+};
+
+class SpdySMTestBase : public ::testing::TestWithParam<SpdyMajorVersion> {
+ public:
+  explicit SpdySMTestBase(FlipHandlerType type) {
+    SSLState* ssl_state = NULL;
+    mock_another_interface_.reset(new MockSMInterface);
+    memory_cache_.reset(new MemoryCache);
+    acceptor_.reset(new FlipAcceptor(type,
+                                     "127.0.0.1",
+                                     "8941",
+                                     "ssl_cert_filename",
+                                     "ssl_key_filename",
+                                     "127.0.0.1",
+                                     "8942",
+                                     "127.0.0.1",
+                                     "8943",
+                                     1,
+                                     0,
+                                     true,
+                                     1,
+                                     false,
+                                     true,
+                                     NULL));
+    epoll_server_.reset(new EpollServer);
+    connection_.reset(new FakeSMConnection(epoll_server_.get(),
+                                           ssl_state,
+                                           memory_cache_.get(),
+                                           acceptor_.get(),
+                                           "log_prefix"));
+
+    interface_.reset(new TestSpdySM(connection_.get(),
+                                    mock_another_interface_.get(),
+                                    epoll_server_.get(),
+                                    memory_cache_.get(),
+                                    acceptor_.get(),
+                                    GetParam()));
+
+    spdy_framer_.reset(new BufferedSpdyFramer(GetParam(), true));
+    spdy_framer_visitor_.reset(new SpdyFramerVisitor);
+    spdy_framer_->set_visitor(spdy_framer_visitor_.get());
+  }
+
+  virtual ~SpdySMTestBase() {
+    if (acceptor_->listen_fd_ >= 0) {
+      epoll_server_->UnregisterFD(acceptor_->listen_fd_);
+      close(acceptor_->listen_fd_);
+      acceptor_->listen_fd_ = -1;
+    }
+    OutputList& output_list = *connection_->output_list();
+    for (OutputList::const_iterator i = output_list.begin();
+         i != output_list.end();
+         ++i) {
+      delete *i;
+    }
+    output_list.clear();
+  }
+
+  bool HasStream(uint32 stream_id) {
+    return interface_->output_ordering().ExistsInPriorityMaps(stream_id);
+  }
+
+ protected:
+  scoped_ptr<MockSMInterface> mock_another_interface_;
+  scoped_ptr<MemoryCache> memory_cache_;
+  scoped_ptr<FlipAcceptor> acceptor_;
+  scoped_ptr<EpollServer> epoll_server_;
+  scoped_ptr<FakeSMConnection> connection_;
+  scoped_ptr<TestSpdySM> interface_;
+  scoped_ptr<BufferedSpdyFramer> spdy_framer_;
+  scoped_ptr<SpdyFramerVisitor> spdy_framer_visitor_;
+};
+
+class SpdySMProxyTest : public SpdySMTestBase {
+ public:
+  SpdySMProxyTest() : SpdySMTestBase(FLIP_HANDLER_PROXY) {}
+  virtual ~SpdySMProxyTest() {}
+};
+
+class SpdySMServerTest : public SpdySMTestBase {
+ public:
+  SpdySMServerTest() : SpdySMTestBase(FLIP_HANDLER_SPDY_SERVER) {}
+  virtual ~SpdySMServerTest() {}
+};
+
+INSTANTIATE_TEST_CASE_P(SpdySMProxyTest,
+                        SpdySMProxyTest,
+                        Values(SPDY2, SPDY3, SPDY4));
+INSTANTIATE_TEST_CASE_P(SpdySMServerTest, SpdySMServerTest, Values(SPDY2));
+
+TEST_P(SpdySMProxyTest, InitSMConnection) {
+  {
+    InSequence s;
+    EXPECT_CALL(*connection_, InitSMConnection(_, _, _, _, _, _, _, _));
+  }
+  interface_->InitSMConnection(
+      NULL, NULL, epoll_server_.get(), -1, "", "", "", false);
+}
+
+TEST_P(SpdySMProxyTest, OnSynStream_SPDY2) {
+  if (GetParam() != SPDY2) {
+    // This test case is for SPDY2.
+    return;
+  }
+  BufferedSpdyFramerVisitorInterface* visitor = interface_.get();
+  scoped_ptr<MockSMInterface> mock_interface(new MockSMInterface);
+  uint32 stream_id = 92;
+  uint32 associated_id = 43;
+  std::string expected = "GET /path HTTP/1.0\r\n"
+      "Host: 127.0.0.1\r\n"
+      "hoge: fuga\r\n\r\n";
+  SpdyHeaderBlock block;
+  block["method"] = "GET";
+  block["url"] = "/path";
+  block["scheme"] = "http";
+  block["version"] = "HTTP/1.0";
+  block["hoge"] = "fuga";
+  StringSaver saver;
+  {
+    InSequence s;
+    EXPECT_CALL(*interface_, FindOrMakeNewSMConnectionInterface(_, _))
+        .WillOnce(Return(mock_interface.get()));
+    EXPECT_CALL(*mock_interface, SetStreamID(stream_id));
+    EXPECT_CALL(*mock_interface, ProcessWriteInput(_, _))
+        .WillOnce(DoAll(SaveArg<0>(&saver.data),
+                        SaveArg<1>(&saver.size),
+                        InvokeWithoutArgs(&saver, &StringSaver::Save),
+                        Return(0)));
+  }
+  visitor->OnSynStream(stream_id, associated_id, 0, false, false, block);
+  ASSERT_EQ(expected, saver.string);
+}
+
+TEST_P(SpdySMProxyTest, OnSynStream) {
+  if (GetParam() == SPDY2) {
+    // This test case is not for SPDY2.
+    return;
+  }
+  BufferedSpdyFramerVisitorInterface* visitor = interface_.get();
+  scoped_ptr<MockSMInterface> mock_interface(new MockSMInterface);
+  uint32 stream_id = 92;
+  uint32 associated_id = 43;
+  std::string expected = "GET /path HTTP/1.1\r\n"
+      "Host: 127.0.0.1\r\n"
+      "foo: bar\r\n\r\n";
+  SpdyHeaderBlock block;
+  block[":method"] = "GET";
+  block[":host"] = "www.example.com";
+  block[":path"] = "/path";
+  block[":scheme"] = "http";
+  block["foo"] = "bar";
+  StringSaver saver;
+  {
+    InSequence s;
+    EXPECT_CALL(*interface_,
+                FindOrMakeNewSMConnectionInterface(_, _))
+        .WillOnce(Return(mock_interface.get()));
+    EXPECT_CALL(*mock_interface, SetStreamID(stream_id));
+    EXPECT_CALL(*mock_interface, ProcessWriteInput(_, _))
+        .WillOnce(DoAll(SaveArg<0>(&saver.data),
+                        SaveArg<1>(&saver.size),
+                        InvokeWithoutArgs(&saver, &StringSaver::Save),
+                        Return(0)));
+  }
+  visitor->OnSynStream(stream_id, associated_id, 0, false, false, block);
+  ASSERT_EQ(expected, saver.string);
+}
+
+TEST_P(SpdySMProxyTest, OnStreamFrameData_SPDY2) {
+  if (GetParam() != SPDY2) {
+    // This test case is for SPDY2.
+    return;
+  }
+  BufferedSpdyFramerVisitorInterface* visitor = interface_.get();
+  scoped_ptr<MockSMInterface> mock_interface(new MockSMInterface);
+  uint32 stream_id = 92;
+  uint32 associated_id = 43;
+  SpdyHeaderBlock block;
+  testing::MockFunction<void(int)> checkpoint;  // NOLINT
+
+  scoped_ptr<SpdyFrame> frame(spdy_framer_->CreatePingFrame(12, false));
+  block["method"] = "GET";
+  block["url"] = "http://www.example.com/path";
+  block["scheme"] = "http";
+  block["version"] = "HTTP/1.0";
+  {
+    InSequence s;
+    EXPECT_CALL(*interface_, FindOrMakeNewSMConnectionInterface(_, _))
+        .WillOnce(Return(mock_interface.get()));
+    EXPECT_CALL(*mock_interface, SetStreamID(stream_id));
+    EXPECT_CALL(*mock_interface, ProcessWriteInput(_, _)).Times(1);
+    EXPECT_CALL(checkpoint, Call(0));
+    EXPECT_CALL(*mock_interface,
+                ProcessWriteInput(frame->data(), frame->size())).Times(1);
+  }
+
+  visitor->OnSynStream(stream_id, associated_id, 0, false, false, block);
+  checkpoint.Call(0);
+  visitor->OnStreamFrameData(stream_id, frame->data(), frame->size(), true);
+}
+
+TEST_P(SpdySMProxyTest, OnStreamFrameData) {
+  if (GetParam() == SPDY2) {
+    // This test case is not for SPDY2.
+    return;
+  }
+  BufferedSpdyFramerVisitorInterface* visitor = interface_.get();
+  scoped_ptr<MockSMInterface> mock_interface(new MockSMInterface);
+  uint32 stream_id = 92;
+  uint32 associated_id = 43;
+  SpdyHeaderBlock block;
+  testing::MockFunction<void(int)> checkpoint;  // NOLINT
+
+  scoped_ptr<SpdyFrame> frame(spdy_framer_->CreatePingFrame(12, false));
+  block[":method"] = "GET";
+  block[":host"] = "www.example.com";
+  block[":path"] = "/path";
+  block[":scheme"] = "http";
+  block["foo"] = "bar";
+  {
+    InSequence s;
+    EXPECT_CALL(*interface_,
+                FindOrMakeNewSMConnectionInterface(_, _))
+        .WillOnce(Return(mock_interface.get()));
+    EXPECT_CALL(*mock_interface, SetStreamID(stream_id));
+    EXPECT_CALL(*mock_interface, ProcessWriteInput(_, _)).Times(1);
+    EXPECT_CALL(checkpoint, Call(0));
+    EXPECT_CALL(*mock_interface,
+                ProcessWriteInput(frame->data(), frame->size())).Times(1);
+  }
+
+  visitor->OnSynStream(stream_id, associated_id, 0, false, false, block);
+  checkpoint.Call(0);
+  visitor->OnStreamFrameData(stream_id, frame->data(), frame->size(), true);
+}
+
+TEST_P(SpdySMProxyTest, OnRstStream) {
+  BufferedSpdyFramerVisitorInterface* visitor = interface_.get();
+  uint32 stream_id = 82;
+  MemCacheIter mci;
+  mci.stream_id = stream_id;
+
+  {
+    BalsaHeaders headers;
+    std::string filename = "foobar";
+    memory_cache_->InsertFile(&headers, filename, "");
+    mci.file_data = memory_cache_->GetFileData(filename);
+  }
+
+  interface_->AddToOutputOrder(mci);
+  ASSERT_TRUE(HasStream(stream_id));
+  visitor->OnRstStream(stream_id, RST_STREAM_INVALID);
+  ASSERT_FALSE(HasStream(stream_id));
+}
+
+TEST_P(SpdySMProxyTest, ProcessReadInput) {
+  ASSERT_EQ(SpdyFramer::SPDY_RESET, interface_->spdy_framer()->state());
+  interface_->ProcessReadInput("", 1);
+  ASSERT_EQ(SpdyFramer::SPDY_READING_COMMON_HEADER,
+            interface_->spdy_framer()->state());
+}
+
+TEST_P(SpdySMProxyTest, ResetForNewConnection) {
+  uint32 stream_id = 13;
+  MemCacheIter mci;
+  mci.stream_id = stream_id;
+  // incomplete input
+  const char input[] = {'\0', '\0', '\0'};
+
+  {
+    BalsaHeaders headers;
+    std::string filename = "foobar";
+    memory_cache_->InsertFile(&headers, filename, "");
+    mci.file_data = memory_cache_->GetFileData(filename);
+  }
+
+  interface_->AddToOutputOrder(mci);
+  ASSERT_TRUE(HasStream(stream_id));
+  interface_->ProcessReadInput(input, sizeof(input));
+  ASSERT_NE(SpdyFramer::SPDY_RESET, interface_->spdy_framer()->state());
+
+  interface_->ResetForNewConnection();
+  ASSERT_FALSE(HasStream(stream_id));
+  ASSERT_TRUE(interface_->spdy_framer() == NULL);
+}
+
+TEST_P(SpdySMProxyTest, CreateFramer) {
+  interface_->ResetForNewConnection();
+  interface_->CreateFramer(SPDY2);
+  ASSERT_TRUE(interface_->spdy_framer() != NULL);
+  ASSERT_EQ(interface_->spdy_version(), SPDY2);
+
+  interface_->ResetForNewConnection();
+  interface_->CreateFramer(SPDY3);
+  ASSERT_TRUE(interface_->spdy_framer() != NULL);
+  ASSERT_EQ(interface_->spdy_version(), SPDY3);
+}
+
+TEST_P(SpdySMProxyTest, PostAcceptHook) {
+  interface_->PostAcceptHook();
+
+  ASSERT_EQ(1u, connection_->output_list()->size());
+  std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin();
+  DataFrame* df = *i++;
+
+  {
+    InSequence s;
+    EXPECT_CALL(*spdy_framer_visitor_, OnSettings(false));
+    EXPECT_CALL(*spdy_framer_visitor_,
+                OnSetting(SETTINGS_MAX_CONCURRENT_STREAMS, 0u, 100u));
+  }
+  spdy_framer_->ProcessInput(df->data, df->size);
+}
+
+TEST_P(SpdySMProxyTest, NewStream) {
+  // TODO(yhirano): SpdySM::NewStream leads to crash when
+  // acceptor_->flip_handler_type_ != FLIP_HANDLER_SPDY_SERVER.
+  // It should be fixed though I don't know the solution now.
+}
+
+TEST_P(SpdySMProxyTest, AddToOutputOrder) {
+  uint32 stream_id = 13;
+  MemCacheIter mci;
+  mci.stream_id = stream_id;
+
+  {
+    BalsaHeaders headers;
+    std::string filename = "foobar";
+    memory_cache_->InsertFile(&headers, filename, "");
+    mci.file_data = memory_cache_->GetFileData(filename);
+  }
+
+  interface_->AddToOutputOrder(mci);
+  ASSERT_TRUE(HasStream(stream_id));
+}
+
+TEST_P(SpdySMProxyTest, SendErrorNotFound_SPDY2) {
+  if (GetParam() != SPDY2) {
+    // This test is for SPDY2.
+    return;
+  }
+  uint32 stream_id = 82;
+  SpdyHeaderBlock actual_header_block;
+  const char* actual_data;
+  size_t actual_size;
+  testing::MockFunction<void(int)> checkpoint;  // NOLINT
+
+  interface_->SendErrorNotFound(stream_id);
+
+  ASSERT_EQ(2u, connection_->output_list()->size());
+
+  {
+    InSequence s;
+    if (GetParam() < SPDY4) {
+      EXPECT_CALL(*spdy_framer_visitor_, OnSynReply(stream_id, false, _))
+          .WillOnce(SaveArg<2>(&actual_header_block));
+    } else {
+      EXPECT_CALL(*spdy_framer_visitor_, OnHeaders(stream_id, false, _))
+          .WillOnce(SaveArg<2>(&actual_header_block));
+    }
+    EXPECT_CALL(checkpoint, Call(0));
+    EXPECT_CALL(*spdy_framer_visitor_,
+                OnDataFrameHeader(stream_id, _, true));
+    EXPECT_CALL(*spdy_framer_visitor_,
+                OnStreamFrameData(stream_id, _, _, false)).Times(1)
+        .WillOnce(DoAll(SaveArg<1>(&actual_data),
+                        SaveArg<2>(&actual_size)));
+    EXPECT_CALL(*spdy_framer_visitor_,
+                OnStreamFrameData(stream_id, NULL, 0, true)).Times(1);
+  }
+
+  std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin();
+  DataFrame* df = *i++;
+  spdy_framer_->ProcessInput(df->data, df->size);
+  checkpoint.Call(0);
+  df = *i++;
+  spdy_framer_->ProcessInput(df->data, df->size);
+
+  ASSERT_EQ(2, spdy_framer_->frames_received());
+  ASSERT_EQ(2u, actual_header_block.size());
+  ASSERT_EQ("404 Not Found", actual_header_block["status"]);
+  ASSERT_EQ("HTTP/1.1", actual_header_block["version"]);
+  ASSERT_EQ("wtf?", StringPiece(actual_data, actual_size));
+}
+
+TEST_P(SpdySMProxyTest, SendErrorNotFound) {
+  if (GetParam() == SPDY2) {
+    // This test is not for SPDY2.
+    return;
+  }
+  uint32 stream_id = 82;
+  SpdyHeaderBlock actual_header_block;
+  const char* actual_data;
+  size_t actual_size;
+  testing::MockFunction<void(int)> checkpoint;  // NOLINT
+
+  interface_->SendErrorNotFound(stream_id);
+
+  ASSERT_EQ(2u, connection_->output_list()->size());
+
+  {
+    InSequence s;
+    if (GetParam() < SPDY4) {
+      EXPECT_CALL(*spdy_framer_visitor_,
+                  OnSynReply(stream_id, false, _))
+          .WillOnce(SaveArg<2>(&actual_header_block));
+    } else {
+      EXPECT_CALL(*spdy_framer_visitor_,
+                  OnHeaders(stream_id, false, _))
+          .WillOnce(SaveArg<2>(&actual_header_block));
+    }
+    EXPECT_CALL(checkpoint, Call(0));
+    EXPECT_CALL(*spdy_framer_visitor_,
+                OnDataFrameHeader(stream_id, _, true));
+    EXPECT_CALL(*spdy_framer_visitor_,
+                OnStreamFrameData(stream_id, _, _, false)).Times(1)
+        .WillOnce(DoAll(SaveArg<1>(&actual_data),
+                        SaveArg<2>(&actual_size)));
+    EXPECT_CALL(*spdy_framer_visitor_,
+                OnStreamFrameData(stream_id, NULL, 0, true)).Times(1);
+  }
+
+  std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin();
+  DataFrame* df = *i++;
+  spdy_framer_->ProcessInput(df->data, df->size);
+  checkpoint.Call(0);
+  df = *i++;
+  spdy_framer_->ProcessInput(df->data, df->size);
+
+  ASSERT_EQ(2, spdy_framer_->frames_received());
+  ASSERT_EQ(2u, actual_header_block.size());
+  ASSERT_EQ("404 Not Found", actual_header_block[":status"]);
+  ASSERT_EQ("HTTP/1.1", actual_header_block[":version"]);
+  ASSERT_EQ("wtf?", StringPiece(actual_data, actual_size));
+}
+
+TEST_P(SpdySMProxyTest, SendSynStream_SPDY2) {
+  if (GetParam() != SPDY2) {
+    // This test is for SPDY2.
+    return;
+  }
+  uint32 stream_id = 82;
+  BalsaHeaders headers;
+  SpdyHeaderBlock actual_header_block;
+  headers.AppendHeader("key1", "value1");
+  headers.SetRequestFirstlineFromStringPieces("GET", "/path", "HTTP/1.0");
+
+  interface_->SendSynStream(stream_id, headers);
+
+  ASSERT_EQ(1u, connection_->output_list()->size());
+  std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin();
+  DataFrame* df = *i++;
+
+  {
+    InSequence s;
+    EXPECT_CALL(*spdy_framer_visitor_,
+                OnSynStream(stream_id, 0, _, false, false, _))
+        .WillOnce(SaveArg<5>(&actual_header_block));
+  }
+
+  spdy_framer_->ProcessInput(df->data, df->size);
+  ASSERT_EQ(1, spdy_framer_->frames_received());
+  ASSERT_EQ(4u, actual_header_block.size());
+  ASSERT_EQ("GET", actual_header_block["method"]);
+  ASSERT_EQ("HTTP/1.0", actual_header_block["version"]);
+  ASSERT_EQ("/path", actual_header_block["url"]);
+  ASSERT_EQ("value1", actual_header_block["key1"]);
+}
+
+TEST_P(SpdySMProxyTest, SendSynStream) {
+  if (GetParam() == SPDY2) {
+    // This test is not for SPDY2.
+    return;
+  }
+  uint32 stream_id = 82;
+  BalsaHeaders headers;
+  SpdyHeaderBlock actual_header_block;
+  headers.AppendHeader("key1", "value1");
+  headers.AppendHeader("Host", "www.example.com");
+  headers.SetRequestFirstlineFromStringPieces("GET", "/path", "HTTP/1.1");
+
+  interface_->SendSynStream(stream_id, headers);
+
+  ASSERT_EQ(1u, connection_->output_list()->size());
+  std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin();
+  DataFrame* df = *i++;
+
+  {
+    InSequence s;
+    EXPECT_CALL(*spdy_framer_visitor_,
+                OnSynStream(stream_id, 0, _, false, false, _))
+        .WillOnce(SaveArg<5>(&actual_header_block));
+  }
+
+  spdy_framer_->ProcessInput(df->data, df->size);
+  ASSERT_EQ(1, spdy_framer_->frames_received());
+  ASSERT_EQ(5u, actual_header_block.size());
+  ASSERT_EQ("GET", actual_header_block[":method"]);
+  ASSERT_EQ("HTTP/1.1", actual_header_block[":version"]);
+  ASSERT_EQ("/path", actual_header_block[":path"]);
+  ASSERT_EQ("www.example.com", actual_header_block[":host"]);
+  ASSERT_EQ("value1", actual_header_block["key1"]);
+}
+
+TEST_P(SpdySMProxyTest, SendSynReply_SPDY2) {
+  if (GetParam() != SPDY2) {
+    // This test is for SPDY2.
+    return;
+  }
+  uint32 stream_id = 82;
+  BalsaHeaders headers;
+  SpdyHeaderBlock actual_header_block;
+  headers.AppendHeader("key1", "value1");
+  headers.SetResponseFirstlineFromStringPieces("HTTP/1.1", "200", "OK");
+
+  interface_->SendSynReply(stream_id, headers);
+
+  ASSERT_EQ(1u, connection_->output_list()->size());
+  std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin();
+  DataFrame* df = *i++;
+
+  {
+    InSequence s;
+    if (GetParam() < SPDY4) {
+      EXPECT_CALL(*spdy_framer_visitor_, OnSynReply(stream_id, false, _))
+          .WillOnce(SaveArg<2>(&actual_header_block));
+    } else {
+      EXPECT_CALL(*spdy_framer_visitor_, OnHeaders(stream_id, false, _))
+          .WillOnce(SaveArg<2>(&actual_header_block));
+    }
+  }
+
+  spdy_framer_->ProcessInput(df->data, df->size);
+  ASSERT_EQ(1, spdy_framer_->frames_received());
+  ASSERT_EQ(3u, actual_header_block.size());
+  ASSERT_EQ("200 OK", actual_header_block["status"]);
+  ASSERT_EQ("HTTP/1.1", actual_header_block["version"]);
+  ASSERT_EQ("value1", actual_header_block["key1"]);
+}
+
+TEST_P(SpdySMProxyTest, SendSynReply) {
+  if (GetParam() == SPDY2) {
+    // This test is not for SPDY2.
+    return;
+  }
+  uint32 stream_id = 82;
+  BalsaHeaders headers;
+  SpdyHeaderBlock actual_header_block;
+  headers.AppendHeader("key1", "value1");
+  headers.SetResponseFirstlineFromStringPieces("HTTP/1.1", "200", "OK");
+
+  interface_->SendSynReply(stream_id, headers);
+
+  ASSERT_EQ(1u, connection_->output_list()->size());
+  std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin();
+  DataFrame* df = *i++;
+
+  {
+    InSequence s;
+    if (GetParam() < SPDY4) {
+      EXPECT_CALL(*spdy_framer_visitor_, OnSynReply(stream_id, false, _))
+          .WillOnce(SaveArg<2>(&actual_header_block));
+    } else {
+      EXPECT_CALL(*spdy_framer_visitor_, OnHeaders(stream_id, false, _))
+          .WillOnce(SaveArg<2>(&actual_header_block));
+    }
+  }
+
+  spdy_framer_->ProcessInput(df->data, df->size);
+  ASSERT_EQ(1, spdy_framer_->frames_received());
+  ASSERT_EQ(3u, actual_header_block.size());
+  ASSERT_EQ("200 OK", actual_header_block[":status"]);
+  ASSERT_EQ("HTTP/1.1", actual_header_block[":version"]);
+  ASSERT_EQ("value1", actual_header_block["key1"]);
+}
+
+TEST_P(SpdySMProxyTest, SendDataFrame) {
+  uint32 stream_id = 133;
+  SpdyDataFlags flags = DATA_FLAG_NONE;
+  const char* actual_data;
+  size_t actual_size;
+
+  interface_->SendDataFrame(stream_id, "hello", 5, flags, true);
+
+  ASSERT_EQ(1u, connection_->output_list()->size());
+  std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin();
+  DataFrame* df = *i++;
+
+  {
+    InSequence s;
+    EXPECT_CALL(*spdy_framer_visitor_,
+                OnDataFrameHeader(stream_id, _, false));
+    EXPECT_CALL(*spdy_framer_visitor_,
+                OnStreamFrameData(stream_id, _, _, false))
+        .WillOnce(DoAll(SaveArg<1>(&actual_data), SaveArg<2>(&actual_size)));
+  }
+
+  spdy_framer_->ProcessInput(df->data, df->size);
+  ASSERT_EQ(1, spdy_framer_->frames_received());
+  ASSERT_EQ("hello", StringPiece(actual_data, actual_size));
+}
+
+TEST_P(SpdySMProxyTest, SendLongDataFrame) {
+  uint32 stream_id = 133;
+  SpdyDataFlags flags = DATA_FLAG_NONE;
+  const char* actual_data;
+  size_t actual_size;
+
+  std::string data = std::string(kSpdySegmentSize, 'a') +
+                     std::string(kSpdySegmentSize, 'b') + "c";
+  interface_->SendDataFrame(stream_id, data.data(), data.size(), flags, true);
+
+  {
+    InSequence s;
+    for (int i = 0; i < 3; ++i) {
+        EXPECT_CALL(*spdy_framer_visitor_,
+                    OnDataFrameHeader(stream_id, _, false));
+        EXPECT_CALL(*spdy_framer_visitor_,
+                    OnStreamFrameData(stream_id, _, _, false))
+            .WillOnce(DoAll(SaveArg<1>(&actual_data),
+                            SaveArg<2>(&actual_size)));
+    }
+  }
+
+  ASSERT_EQ(3u, connection_->output_list()->size());
+  std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin();
+  DataFrame* df = *i++;
+  spdy_framer_->ProcessInput(df->data, df->size);
+  ASSERT_EQ(std::string(kSpdySegmentSize, 'a'),
+            StringPiece(actual_data, actual_size));
+
+  df = *i++;
+  spdy_framer_->ProcessInput(df->data, df->size);
+  ASSERT_EQ(std::string(kSpdySegmentSize, 'b'),
+            StringPiece(actual_data, actual_size));
+
+  df = *i++;
+  spdy_framer_->ProcessInput(df->data, df->size);
+  ASSERT_EQ("c", StringPiece(actual_data, actual_size));
+}
+
+TEST_P(SpdySMProxyTest, SendEOF_SPDY2) {
+  // This test is for SPDY2.
+  if (GetParam() != SPDY2) {
+    return;
+  }
+
+  uint32 stream_id = 82;
+  // SPDY2 data frame
+  char empty_data_frame[] = {'\0', '\0', '\0', '\x52', '\x1', '\0', '\0', '\0'};
+  MemCacheIter mci;
+  mci.stream_id = stream_id;
+
+  {
+    BalsaHeaders headers;
+    std::string filename = "foobar";
+    memory_cache_->InsertFile(&headers, filename, "");
+    mci.file_data = memory_cache_->GetFileData(filename);
+  }
+
+  interface_->AddToOutputOrder(mci);
+  ASSERT_TRUE(HasStream(stream_id));
+  interface_->SendEOF(stream_id);
+  ASSERT_FALSE(HasStream(stream_id));
+
+  ASSERT_EQ(1u, connection_->output_list()->size());
+  std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin();
+  DataFrame* df = *i++;
+  ASSERT_EQ(StringPiece(empty_data_frame, sizeof(empty_data_frame)),
+            StringPiece(df->data, df->size));
+}
+
+TEST_P(SpdySMProxyTest, SendEmptyDataFrame_SPDY2) {
+  // This test is for SPDY2.
+  if (GetParam() != SPDY2) {
+    return;
+  }
+
+  uint32 stream_id = 133;
+  SpdyDataFlags flags = DATA_FLAG_NONE;
+  // SPDY2 data frame
+  char expected[] = {'\0', '\0', '\0', '\x85', '\0', '\0', '\0', '\0'};
+
+  interface_->SendDataFrame(stream_id, "hello", 0, flags, true);
+
+  ASSERT_EQ(1u, connection_->output_list()->size());
+  std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin();
+  DataFrame* df = *i++;
+
+  ASSERT_EQ(StringPiece(expected, sizeof(expected)),
+            StringPiece(df->data, df->size));
+}
+
+TEST_P(SpdySMServerTest, OnSynStream) {
+  BufferedSpdyFramerVisitorInterface* visitor = interface_.get();
+  uint32 stream_id = 82;
+  SpdyHeaderBlock spdy_headers;
+  spdy_headers["url"] = "http://www.example.com/path";
+  spdy_headers["method"] = "GET";
+  spdy_headers["scheme"] = "http";
+  spdy_headers["version"] = "HTTP/1.1";
+
+  {
+    BalsaHeaders headers;
+    memory_cache_->InsertFile(&headers, "GET_/path", "");
+  }
+  visitor->OnSynStream(stream_id, 0, 0, true, true, spdy_headers);
+  ASSERT_TRUE(HasStream(stream_id));
+}
+
+TEST_P(SpdySMServerTest, NewStream) {
+  uint32 stream_id = 13;
+  std::string filename = "foobar";
+
+  {
+    BalsaHeaders headers;
+    memory_cache_->InsertFile(&headers, filename, "");
+  }
+
+  interface_->NewStream(stream_id, 0, filename);
+  ASSERT_TRUE(HasStream(stream_id));
+}
+
+TEST_P(SpdySMServerTest, NewStreamError) {
+  uint32 stream_id = 82;
+  SpdyHeaderBlock actual_header_block;
+  const char* actual_data;
+  size_t actual_size;
+  testing::MockFunction<void(int)> checkpoint;  // NOLINT
+
+  interface_->NewStream(stream_id, 0, "nonexistingfile");
+
+  ASSERT_EQ(2u, connection_->output_list()->size());
+
+  {
+    InSequence s;
+    if (GetParam() < SPDY4) {
+      EXPECT_CALL(*spdy_framer_visitor_, OnSynReply(stream_id, false, _))
+          .WillOnce(SaveArg<2>(&actual_header_block));
+    } else {
+      EXPECT_CALL(*spdy_framer_visitor_, OnHeaders(stream_id, false, _))
+          .WillOnce(SaveArg<2>(&actual_header_block));
+    }
+    EXPECT_CALL(checkpoint, Call(0));
+    EXPECT_CALL(*spdy_framer_visitor_,
+                OnDataFrameHeader(stream_id, _, true));
+    EXPECT_CALL(*spdy_framer_visitor_,
+                OnStreamFrameData(stream_id, _, _, false)).Times(1)
+        .WillOnce(DoAll(SaveArg<1>(&actual_data),
+                        SaveArg<2>(&actual_size)));
+    EXPECT_CALL(*spdy_framer_visitor_,
+                OnStreamFrameData(stream_id, NULL, 0, true)).Times(1);
+  }
+
+  std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin();
+  DataFrame* df = *i++;
+  spdy_framer_->ProcessInput(df->data, df->size);
+  checkpoint.Call(0);
+  df = *i++;
+  spdy_framer_->ProcessInput(df->data, df->size);
+
+  ASSERT_EQ(2, spdy_framer_->frames_received());
+  ASSERT_EQ(2u, actual_header_block.size());
+  ASSERT_EQ("404 Not Found", actual_header_block["status"]);
+  ASSERT_EQ("HTTP/1.1", actual_header_block["version"]);
+  ASSERT_EQ("wtf?", StringPiece(actual_data, actual_size));
+}
+
+}  // namespace
+
+}  // namespace net
diff --git a/net/tools/flip_server/spdy_ssl.cc b/net/tools/flip_server/spdy_ssl.cc
new file mode 100644
index 0000000..19ea52e
--- /dev/null
+++ b/net/tools/flip_server/spdy_ssl.cc
@@ -0,0 +1,114 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/tools/flip_server/spdy_ssl.h"
+
+#include "base/logging.h"
+#include "openssl/err.h"
+#include "openssl/ssl.h"
+
+namespace net {
+
+// Each element consists of <the length of the string><string> .
+#define NEXT_PROTO_STRING \
+  "\x08spdy/4a2" \
+  "\x06spdy/3" \
+  "\x06spdy/2" \
+  "\x08http/1.1" \
+  "\x08http/1.0"
+#define SSL_CIPHER_LIST "!aNULL:!ADH:!eNull:!LOW:!EXP:RC4+RSA:MEDIUM:HIGH"
+
+int ssl_set_npn_callback(SSL* s,
+                         const unsigned char** data,
+                         unsigned int* len,
+                         void* arg) {
+  VLOG(1) << "SSL NPN callback: advertising protocols.";
+  *data = (const unsigned char*)NEXT_PROTO_STRING;
+  *len = strlen(NEXT_PROTO_STRING);
+  return SSL_TLSEXT_ERR_OK;
+}
+
+void InitSSL(SSLState* state,
+             std::string ssl_cert_name,
+             std::string ssl_key_name,
+             bool use_npn,
+             int session_expiration_time,
+             bool disable_ssl_compression) {
+  SSL_library_init();
+  PrintSslError();
+
+  SSL_load_error_strings();
+  PrintSslError();
+
+  state->ssl_method = SSLv23_method();
+  state->ssl_ctx = SSL_CTX_new(state->ssl_method);
+  if (!state->ssl_ctx) {
+    PrintSslError();
+    LOG(FATAL) << "Unable to create SSL context";
+  }
+  // Disable SSLv2 support.
+  SSL_CTX_set_options(state->ssl_ctx,
+                      SSL_OP_NO_SSLv2 | SSL_OP_CIPHER_SERVER_PREFERENCE);
+  if (SSL_CTX_use_certificate_chain_file(state->ssl_ctx,
+                                         ssl_cert_name.c_str()) <= 0) {
+    PrintSslError();
+    LOG(FATAL) << "Unable to use cert.pem as SSL cert.";
+  }
+  if (SSL_CTX_use_PrivateKey_file(
+          state->ssl_ctx, ssl_key_name.c_str(), SSL_FILETYPE_PEM) <= 0) {
+    PrintSslError();
+    LOG(FATAL) << "Unable to use key.pem as SSL key.";
+  }
+  if (!SSL_CTX_check_private_key(state->ssl_ctx)) {
+    PrintSslError();
+    LOG(FATAL) << "The cert.pem and key.pem files don't match";
+  }
+  if (use_npn) {
+    SSL_CTX_set_next_protos_advertised_cb(
+        state->ssl_ctx, ssl_set_npn_callback, NULL);
+  }
+  VLOG(1) << "SSL CTX default cipher list: " << SSL_CIPHER_LIST;
+  SSL_CTX_set_cipher_list(state->ssl_ctx, SSL_CIPHER_LIST);
+
+  VLOG(1) << "SSL CTX session expiry: " << session_expiration_time
+          << " seconds";
+  SSL_CTX_set_timeout(state->ssl_ctx, session_expiration_time);
+
+#ifdef SSL_MODE_RELEASE_BUFFERS
+  VLOG(1) << "SSL CTX: Setting Release Buffers mode.";
+  SSL_CTX_set_mode(state->ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
+#endif
+
+#if !defined(OPENSSL_IS_BORINGSSL)
+  // Proper methods to disable compression don't exist until 0.9.9+. For now
+  // we must manipulate the stack of compression methods directly.
+  if (disable_ssl_compression) {
+    STACK_OF(SSL_COMP)* ssl_comp_methods = SSL_COMP_get_compression_methods();
+    int num_methods = sk_SSL_COMP_num(ssl_comp_methods);
+    int i;
+    for (i = 0; i < num_methods; i++) {
+      static_cast<void>(sk_SSL_COMP_delete(ssl_comp_methods, i));
+    }
+  }
+#endif
+}
+
+SSL* CreateSSLContext(SSL_CTX* ssl_ctx) {
+  SSL* ssl = SSL_new(ssl_ctx);
+  SSL_set_accept_state(ssl);
+  PrintSslError();
+  return ssl;
+}
+
+void PrintSslError() {
+  char buf[128];  // this buffer must be at least 120 chars long.
+  int error_num = ERR_get_error();
+  while (error_num != 0) {
+    ERR_error_string_n(error_num, buf, sizeof(buf));
+    LOG(ERROR) << buf;
+    error_num = ERR_get_error();
+  }
+}
+
+}  // namespace net
diff --git a/net/tools/flip_server/spdy_ssl.h b/net/tools/flip_server/spdy_ssl.h
new file mode 100644
index 0000000..8b55733
--- /dev/null
+++ b/net/tools/flip_server/spdy_ssl.h
@@ -0,0 +1,30 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_TOOLS_FLIP_SERVER_SPDY_SSL_H_
+#define NET_TOOLS_FLIP_SERVER_SPDY_SSL_H_
+
+#include <string>
+
+#include "openssl/ssl.h"
+
+namespace net {
+
+struct SSLState {
+  const SSL_METHOD* ssl_method;
+  SSL_CTX* ssl_ctx;
+};
+
+void InitSSL(SSLState* state,
+             std::string ssl_cert_name,
+             std::string ssl_key_name,
+             bool use_npn,
+             int session_expiration_time,
+             bool disable_ssl_compression);
+SSL* CreateSSLContext(SSL_CTX* ssl_ctx);
+void PrintSslError();
+
+}  // namespace net
+
+#endif  // NET_TOOLS_FLIP_SERVER_SPDY_SSL_H_
diff --git a/net/tools/flip_server/spdy_util.cc b/net/tools/flip_server/spdy_util.cc
new file mode 100644
index 0000000..311bdb9
--- /dev/null
+++ b/net/tools/flip_server/spdy_util.cc
@@ -0,0 +1,33 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/tools/flip_server/spdy_util.h"
+
+#include <string>
+
+#include "net/tools/dump_cache/url_to_filename_encoder.h"
+
+namespace net {
+
+bool g_need_to_encode_url = false;
+
+// Encode the URL.
+std::string EncodeURL(std::string uri, std::string host, std::string method) {
+  if (!g_need_to_encode_url) {
+    // TODO(mbelshe): if uri is fully qualified, need to strip protocol/host.
+    return std::string(method + "_" + uri);
+  }
+
+  std::string filename;
+  if (uri[0] == '/') {
+    // uri is not fully qualified.
+    filename = UrlToFilenameEncoder::Encode(
+        "http://" + host + uri, method + "_/", false);
+  } else {
+    filename = UrlToFilenameEncoder::Encode(uri, method + "_/", false);
+  }
+  return filename;
+}
+
+}  // namespace net
diff --git a/net/tools/flip_server/spdy_util.h b/net/tools/flip_server/spdy_util.h
new file mode 100644
index 0000000..6f00200
--- /dev/null
+++ b/net/tools/flip_server/spdy_util.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_TOOLS_FLIP_SERVER_SPDY_UTIL_H_
+#define NET_TOOLS_FLIP_SERVER_SPDY_UTIL_H_
+
+#include <string>
+
+namespace net {
+
+// Flag indicating if we need to encode urls into filenames (legacy).
+extern bool g_need_to_encode_url;
+
+// Encode the URL.
+std::string EncodeURL(std::string uri, std::string host, std::string method);
+
+}  // namespace net
+
+#endif  // NET_TOOLS_FLIP_SERVER_SPDY_UTIL_H_
diff --git a/net/tools/flip_server/streamer_interface.cc b/net/tools/flip_server/streamer_interface.cc
new file mode 100644
index 0000000..25a4964
--- /dev/null
+++ b/net/tools/flip_server/streamer_interface.cc
@@ -0,0 +1,200 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/tools/flip_server/streamer_interface.h"
+
+#include <string>
+
+#include "net/tools/balsa/balsa_frame.h"
+#include "net/tools/flip_server/constants.h"
+#include "net/tools/flip_server/flip_config.h"
+#include "net/tools/flip_server/sm_connection.h"
+
+namespace net {
+
+std::string StreamerSM::forward_ip_header_;
+
+StreamerSM::StreamerSM(SMConnection* connection,
+                       SMInterface* sm_other_interface,
+                       EpollServer* epoll_server,
+                       FlipAcceptor* acceptor)
+    : connection_(connection),
+      sm_other_interface_(sm_other_interface),
+      epoll_server_(epoll_server),
+      acceptor_(acceptor),
+      is_request_(false),
+      http_framer_(new BalsaFrame) {
+  VLOG(2) << ACCEPTOR_CLIENT_IDENT << "Creating StreamerSM object";
+  http_framer_->set_balsa_visitor(this);
+  http_framer_->set_balsa_headers(&headers_);
+  http_framer_->set_is_request(false);
+}
+
+StreamerSM::~StreamerSM() {
+  VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Destroying StreamerSM object";
+  Reset();
+  delete http_framer_;
+}
+
+void StreamerSM::set_is_request() {
+  is_request_ = true;
+  http_framer_->set_is_request(true);
+}
+
+void StreamerSM::InitSMInterface(SMInterface* sm_other_interface,
+                                 int32 server_idx) {
+  sm_other_interface_ = sm_other_interface;
+}
+
+void StreamerSM::InitSMConnection(SMConnectionPoolInterface* connection_pool,
+                                  SMInterface* sm_interface,
+                                  EpollServer* epoll_server,
+                                  int fd,
+                                  std::string server_ip,
+                                  std::string server_port,
+                                  std::string remote_ip,
+                                  bool use_ssl) {
+  VLOG(2) << ACCEPTOR_CLIENT_IDENT << "StreamerSM: Initializing server "
+          << "connection.";
+  connection_->InitSMConnection(connection_pool,
+                                sm_interface,
+                                epoll_server,
+                                fd,
+                                server_ip,
+                                server_port,
+                                remote_ip,
+                                use_ssl);
+}
+
+size_t StreamerSM::ProcessReadInput(const char* data, size_t len) {
+  // For now we only want to parse http requests. Just stream responses
+  if (is_request_) {
+    return http_framer_->ProcessInput(data, len);
+  } else {
+    return sm_other_interface_->ProcessWriteInput(data, len);
+  }
+}
+
+size_t StreamerSM::ProcessWriteInput(const char* data, size_t len) {
+  char* dataPtr = new char[len];
+  memcpy(dataPtr, data, len);
+  DataFrame* df = new DataFrame;
+  df->data = (const char*)dataPtr;
+  df->size = len;
+  df->delete_when_done = true;
+  connection_->EnqueueDataFrame(df);
+  return len;
+}
+
+bool StreamerSM::Error() const { return false; }
+
+const char* StreamerSM::ErrorAsString() const { return "(none)"; }
+
+bool StreamerSM::MessageFullyRead() const {
+  if (is_request_) {
+    return http_framer_->MessageFullyRead();
+  } else {
+    return false;
+  }
+}
+
+void StreamerSM::Reset() {
+  VLOG(1) << ACCEPTOR_CLIENT_IDENT << "StreamerSM: Reset";
+  connection_->Cleanup("Server Reset");
+  http_framer_->Reset();
+}
+
+void StreamerSM::ResetForNewConnection() {
+  http_framer_->Reset();
+  sm_other_interface_->Reset();
+}
+
+void StreamerSM::Cleanup() {
+  if (is_request_)
+    http_framer_->Reset();
+}
+
+int StreamerSM::PostAcceptHook() {
+  if (!sm_other_interface_) {
+    SMConnection* server_connection = SMConnection::NewSMConnection(
+        epoll_server_, NULL, NULL, acceptor_, "server_conn: ");
+    if (server_connection == NULL) {
+      LOG(ERROR) << "StreamerSM: Could not create server conenction.";
+      return 0;
+    }
+    VLOG(2) << ACCEPTOR_CLIENT_IDENT << "StreamerSM: Creating new server "
+            << "connection.";
+    sm_other_interface_ =
+        new StreamerSM(server_connection, this, epoll_server_, acceptor_);
+    sm_other_interface_->InitSMInterface(this, 0);
+  }
+  // The Streamer interface is used to stream HTTPS connections, so we
+  // will always use the https_server_ip/port here.
+  sm_other_interface_->InitSMConnection(NULL,
+                                        sm_other_interface_,
+                                        epoll_server_,
+                                        -1,
+                                        acceptor_->https_server_ip_,
+                                        acceptor_->https_server_port_,
+                                        std::string(),
+                                        false);
+
+  return 1;
+}
+
+size_t StreamerSM::SendSynStream(uint32 stream_id,
+                                 const BalsaHeaders& headers) {
+  return 0;
+}
+
+size_t StreamerSM::SendSynReply(uint32 stream_id, const BalsaHeaders& headers) {
+  return 0;
+}
+
+void StreamerSM::ProcessBodyInput(const char* input, size_t size) {
+  VLOG(2) << ACCEPTOR_CLIENT_IDENT
+          << "StreamerHttpSM: Process Body Input Data: "
+          << "size " << size;
+  sm_other_interface_->ProcessWriteInput(input, size);
+}
+
+void StreamerSM::MessageDone() {
+  if (acceptor_->flip_handler_type_ == FLIP_HANDLER_PROXY) {
+    VLOG(2) << ACCEPTOR_CLIENT_IDENT << "StreamerHttpSM: MessageDone.";
+    // TODO(kelindsay): anything need to be done ehre?
+  } else {
+    VLOG(2) << ACCEPTOR_CLIENT_IDENT << "StraemerHttpSM: MessageDone.";
+  }
+}
+
+void StreamerSM::ProcessHeaders(const BalsaHeaders& headers) {
+  VLOG(2) << ACCEPTOR_CLIENT_IDENT << "HttpStreamerSM: Process Headers";
+  BalsaHeaders mod_headers;
+  mod_headers.CopyFrom(headers);
+  if (forward_ip_header_.length()) {
+    LOG(INFO) << "Adding forward header: " << forward_ip_header_;
+    mod_headers.ReplaceOrAppendHeader(forward_ip_header_,
+                                      connection_->client_ip());
+  } else {
+    LOG(INFO) << "NOT adding forward header.";
+  }
+  SimpleBuffer sb;
+  char* buffer;
+  int size;
+  mod_headers.WriteHeaderAndEndingToBuffer(&sb);
+  sb.GetReadablePtr(&buffer, &size);
+  sm_other_interface_->ProcessWriteInput(buffer, size);
+}
+
+void StreamerSM::HandleHeaderError(BalsaFrame* framer) { HandleError(); }
+
+void StreamerSM::HandleChunkingError(BalsaFrame* framer) { HandleError(); }
+
+void StreamerSM::HandleBodyError(BalsaFrame* framer) { HandleError(); }
+
+void StreamerSM::HandleError() {
+  VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Error detected";
+}
+
+}  // namespace net
diff --git a/net/tools/flip_server/streamer_interface.h b/net/tools/flip_server/streamer_interface.h
new file mode 100644
index 0000000..0114491
--- /dev/null
+++ b/net/tools/flip_server/streamer_interface.h
@@ -0,0 +1,137 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_TOOLS_FLIP_SERVER_STREAMER_INTERFACE_H_
+#define NET_TOOLS_FLIP_SERVER_STREAMER_INTERFACE_H_
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "net/tools/balsa/balsa_headers.h"
+#include "net/tools/balsa/balsa_visitor_interface.h"
+#include "net/tools/flip_server/sm_interface.h"
+
+namespace net {
+
+class BalsaFrame;
+class FlipAcceptor;
+class MemCacheIter;
+class SMConnection;
+class EpollServer;
+
+class StreamerSM : public BalsaVisitorInterface, public SMInterface {
+ public:
+  StreamerSM(SMConnection* connection,
+             SMInterface* sm_other_interface,
+             EpollServer* epoll_server,
+             FlipAcceptor* acceptor);
+  virtual ~StreamerSM();
+
+  void AddToOutputOrder(const MemCacheIter& mci) {}
+
+  virtual void InitSMInterface(SMInterface* sm_other_interface,
+                               int32 server_idx) OVERRIDE;
+  virtual void InitSMConnection(SMConnectionPoolInterface* connection_pool,
+                                SMInterface* sm_interface,
+                                EpollServer* epoll_server,
+                                int fd,
+                                std::string server_ip,
+                                std::string server_port,
+                                std::string remote_ip,
+                                bool use_ssl) OVERRIDE;
+
+  virtual size_t ProcessReadInput(const char* data, size_t len) OVERRIDE;
+  virtual size_t ProcessWriteInput(const char* data, size_t len) OVERRIDE;
+  virtual bool MessageFullyRead() const OVERRIDE;
+  virtual void SetStreamID(uint32 stream_id) OVERRIDE {}
+  virtual bool Error() const OVERRIDE;
+  virtual const char* ErrorAsString() const OVERRIDE;
+  virtual void Reset() OVERRIDE;
+  virtual void ResetForNewInterface(int32 server_idx) OVERRIDE {}
+  virtual void ResetForNewConnection() OVERRIDE;
+  virtual void Cleanup() OVERRIDE;
+  virtual int PostAcceptHook() OVERRIDE;
+  virtual void NewStream(uint32 stream_id,
+                         uint32 priority,
+                         const std::string& filename) OVERRIDE {}
+  virtual void SendEOF(uint32 stream_id) OVERRIDE {}
+  virtual void SendErrorNotFound(uint32 stream_id) OVERRIDE {}
+  virtual void SendOKResponse(uint32 stream_id, std::string output) {}
+  virtual size_t SendSynStream(uint32 stream_id,
+                               const BalsaHeaders& headers) OVERRIDE;
+  virtual size_t SendSynReply(uint32 stream_id,
+                              const BalsaHeaders& headers) OVERRIDE;
+  virtual void SendDataFrame(uint32 stream_id,
+                             const char* data,
+                             int64 len,
+                             uint32 flags,
+                             bool compress) OVERRIDE {}
+  virtual void set_is_request() OVERRIDE;
+  static std::string forward_ip_header() { return forward_ip_header_; }
+  static void set_forward_ip_header(std::string value) {
+    forward_ip_header_ = value;
+  }
+
+ private:
+  void SendEOFImpl(uint32 stream_id) {}
+  void SendErrorNotFoundImpl(uint32 stream_id) {}
+  void SendOKResponseImpl(uint32 stream_id, std::string* output) {}
+  size_t SendSynReplyImpl(uint32 stream_id, const BalsaHeaders& headers) {
+    return 0;
+  }
+  size_t SendSynStreamImpl(uint32 stream_id, const BalsaHeaders& headers) {
+    return 0;
+  }
+  void SendDataFrameImpl(uint32 stream_id,
+                         const char* data,
+                         int64 len,
+                         uint32 flags,
+                         bool compress) {}
+  virtual void GetOutput() OVERRIDE {}
+
+  virtual void ProcessBodyInput(const char* input, size_t size) OVERRIDE;
+  virtual void MessageDone() OVERRIDE;
+  virtual void ProcessHeaders(const BalsaHeaders& headers) OVERRIDE;
+  virtual void ProcessBodyData(const char* input, size_t size) OVERRIDE {}
+  virtual void ProcessHeaderInput(const char* input, size_t size) OVERRIDE {}
+  virtual void ProcessTrailerInput(const char* input, size_t size) OVERRIDE {}
+  virtual void ProcessRequestFirstLine(const char* line_input,
+                                       size_t line_length,
+                                       const char* method_input,
+                                       size_t method_length,
+                                       const char* request_uri_input,
+                                       size_t request_uri_length,
+                                       const char* version_input,
+                                       size_t version_length) OVERRIDE {}
+  virtual void ProcessResponseFirstLine(const char* line_input,
+                                        size_t line_length,
+                                        const char* version_input,
+                                        size_t version_length,
+                                        const char* status_input,
+                                        size_t status_length,
+                                        const char* reason_input,
+                                        size_t reason_length) OVERRIDE {}
+  virtual void ProcessChunkLength(size_t chunk_length) OVERRIDE {}
+  virtual void ProcessChunkExtensions(const char* input, size_t size) OVERRIDE {
+  }
+  virtual void HeaderDone() OVERRIDE {}
+  virtual void HandleHeaderError(BalsaFrame* framer) OVERRIDE;
+  virtual void HandleHeaderWarning(BalsaFrame* framer) OVERRIDE {}
+  virtual void HandleChunkingError(BalsaFrame* framer) OVERRIDE;
+  virtual void HandleBodyError(BalsaFrame* framer) OVERRIDE;
+  void HandleError();
+
+  SMConnection* connection_;
+  SMInterface* sm_other_interface_;
+  EpollServer* epoll_server_;
+  FlipAcceptor* acceptor_;
+  bool is_request_;
+  BalsaFrame* http_framer_;
+  BalsaHeaders headers_;
+  static std::string forward_ip_header_;
+};
+
+}  // namespace net
+
+#endif  // NET_TOOLS_FLIP_SERVER_STREAMER_INTERFACE_H_