httpd-cvs mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From j..@apache.org
Subject svn commit: r1590597 [4/14] - in /httpd/httpd/trunk/modules/spdy: ./ apache/ apache/filters/ apache/testing/ common/ common/testing/ support/ support/base/ support/base/metrics/ support/build/ support/install/ support/install/common/ support/install/de...
Date Mon, 28 Apr 2014 10:55:22 GMT
Added: httpd/httpd/trunk/modules/spdy/apache/sockaddr_util.cc
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/spdy/apache/sockaddr_util.cc?rev=1590597&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/spdy/apache/sockaddr_util.cc (added)
+++ httpd/httpd/trunk/modules/spdy/apache/sockaddr_util.cc Mon Apr 28 10:55:17 2014
@@ -0,0 +1,55 @@
+// Copyright 2012 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "mod_spdy/apache/sockaddr_util.h"
+
+#include <cstddef> // for ptrdiff_t
+#include <cstring>
+
+#include "apr_strings.h"
+
+namespace mod_spdy {
+
+apr_sockaddr_t* DeepCopySockAddr(const apr_sockaddr_t* in, apr_pool_t* pool) {
+  apr_sockaddr_t* out = static_cast<apr_sockaddr_t*>(
+      apr_palloc(pool, sizeof(apr_sockaddr_t)));
+  std::memcpy(out, in, sizeof(apr_sockaddr_t));
+  out->pool = pool;
+
+  if (in->hostname != NULL) {
+    out->hostname = apr_pstrdup(pool, in->hostname);
+  }
+
+  if (in->servname != NULL) {
+    out->servname = apr_pstrdup(pool, in->servname);
+  }
+
+  if (in->ipaddr_ptr != NULL) {
+    // ipaddr_ptr points inside the struct, towards the bits containing
+    // the actual IPv4/IPv6 address (e.g. to ->sa.sin.sin_addr or
+    // ->sa.sin6.sin6_addr). We point to the same offset in 'out' as was used
+    // in 'in'.
+    ptrdiff_t ipaddr_ptr_offset =
+        static_cast<char*>(in->ipaddr_ptr) - reinterpret_cast<const char*>(in);
+    out->ipaddr_ptr = reinterpret_cast<char*>(out) + ipaddr_ptr_offset;
+  }
+
+  if (in->next != NULL) {
+    out->next = DeepCopySockAddr(in->next, pool);
+  }
+
+  return out;
+}
+
+}  // namespace mod_spdy

Added: httpd/httpd/trunk/modules/spdy/apache/sockaddr_util.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/spdy/apache/sockaddr_util.h?rev=1590597&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/spdy/apache/sockaddr_util.h (added)
+++ httpd/httpd/trunk/modules/spdy/apache/sockaddr_util.h Mon Apr 28 10:55:17 2014
@@ -0,0 +1,29 @@
+// Copyright 2012 Google Inc
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef MOD_SPDY_APACHE_SOCKADDR_UTIL_H_
+#define MOD_SPDY_APACHE_SOCKADDR_UTIL_H_
+
+#include "apr_pools.h"
+#include "apr_network_io.h"
+
+namespace mod_spdy {
+
+// Deep-copies the apr_sockaddr_t 'in', with the result being allocated in the
+// pool 'pool'.
+apr_sockaddr_t* DeepCopySockAddr(const apr_sockaddr_t* in, apr_pool_t* pool);
+
+}  // namespace mod_spdy
+
+#endif  // MOD_SPDY_APACHE_SOCKADDR_UTIL_H_

Propchange: httpd/httpd/trunk/modules/spdy/apache/sockaddr_util.h
------------------------------------------------------------------------------
    svn:eol-style = native

Added: httpd/httpd/trunk/modules/spdy/apache/sockaddr_util_test.cc
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/spdy/apache/sockaddr_util_test.cc?rev=1590597&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/spdy/apache/sockaddr_util_test.cc (added)
+++ httpd/httpd/trunk/modules/spdy/apache/sockaddr_util_test.cc Mon Apr 28 10:55:17 2014
@@ -0,0 +1,104 @@
+// Copyright 2012 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "mod_spdy/apache/sockaddr_util.h"
+
+#include "apr_strings.h"
+
+#include "base/basictypes.h"
+#include "mod_spdy/apache/pool_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+void VerifySameAddr(apr_sockaddr_t* exp, apr_sockaddr_t* actual) {
+  // apr_sockaddr_equal checks the actual IP (4 or 6) portion of the address,
+  // and nothing else.
+  EXPECT_NE(0, apr_sockaddr_equal(exp, actual));
+
+  // Annoyingly this means we have to touch other fields directly.
+  EXPECT_STREQ(exp->hostname, actual->hostname);
+  EXPECT_STREQ(exp->servname, actual->servname);
+  EXPECT_EQ(exp->port, actual->port);
+  EXPECT_EQ(exp->salen, actual->salen);
+  EXPECT_EQ(exp->ipaddr_len, actual->ipaddr_len);
+  EXPECT_EQ(exp->addr_str_len, actual->addr_str_len);
+
+  // next fields must both be either null or non-null.
+  EXPECT_TRUE((exp->next == NULL) == (actual->next == NULL));
+  if (exp->next != NULL) {
+    VerifySameAddr(exp->next, actual->next);
+  }
+}
+
+TEST(SockAddrUtilTest, CloneIpv4) {
+  mod_spdy::LocalPool local, other;
+
+  apr_sockaddr_t* original = NULL;
+  ASSERT_EQ(APR_SUCCESS,
+            apr_sockaddr_info_get(
+                &original, "127.1.2.3", APR_INET, 80, 0, local.pool()));
+  original->hostname = apr_pstrdup(local.pool(), "localhost");
+  original->servname = apr_pstrdup(local.pool(), "http");
+
+  apr_sockaddr_t* clone = mod_spdy::DeepCopySockAddr(original, other.pool());
+  EXPECT_EQ(other.pool(), clone->pool);
+  VerifySameAddr(original, clone);
+}
+
+TEST(SockAddrUtilTest, CloneIpv6) {
+  mod_spdy::LocalPool local, other;
+
+  // The IPv6 address below was that of example.com on 2012-07-20.
+  apr_sockaddr_t* original = NULL;
+  ASSERT_EQ(APR_SUCCESS,
+            apr_sockaddr_info_get(
+                &original, "2001:500:88:200::10", APR_INET6,
+                443, 0, local.pool()));
+  original->hostname = apr_pstrdup(local.pool(), "example.com");
+  original->servname = apr_pstrdup(local.pool(), "https");
+
+  apr_sockaddr_t* clone = mod_spdy::DeepCopySockAddr(original, other.pool());
+  EXPECT_EQ(other.pool(), clone->pool);
+  VerifySameAddr(original, clone);
+}
+
+TEST(SockAddrUtilTest, Clone2Records) {
+  // Test where ->next links an IpV4 record from IPv6 one.
+  mod_spdy::LocalPool local, other;
+
+  // Both addresses are of example.com as of 2012-07-20.
+  apr_sockaddr_t* original = NULL;
+  ASSERT_EQ(APR_SUCCESS,
+            apr_sockaddr_info_get(
+                &original, "2001:500:88:200::10", APR_INET6,
+                443, 0, local.pool()));
+  original->hostname = apr_pstrdup(local.pool(), "example.com");
+  original->servname = apr_pstrdup(local.pool(), "https");
+
+  apr_sockaddr_t* original4 = NULL;
+  ASSERT_EQ(APR_SUCCESS,
+            apr_sockaddr_info_get(
+                &original4, "192.0.43.10", APR_INET,
+                443, 0, local.pool()));
+  original4->hostname = apr_pstrdup(local.pool(), "example.com");
+  original4->servname = apr_pstrdup(local.pool(), "https");
+  original->next = original4;
+
+  apr_sockaddr_t* clone = mod_spdy::DeepCopySockAddr(original, other.pool());
+  EXPECT_EQ(other.pool(), clone->pool);
+  VerifySameAddr(original, clone);
+}
+
+}  // namespace

Added: httpd/httpd/trunk/modules/spdy/apache/ssl_util.cc
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/spdy/apache/ssl_util.cc?rev=1590597&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/spdy/apache/ssl_util.cc (added)
+++ httpd/httpd/trunk/modules/spdy/apache/ssl_util.cc Mon Apr 28 10:55:17 2014
@@ -0,0 +1,79 @@
+// Copyright 2012 Google Inc
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "mod_spdy/apache/ssl_util.h"
+
+#include "apr_optional.h"
+#include "apr_optional_hooks.h"
+
+#include "base/logging.h"
+
+// This file contains some utility functions for communicating to mod_ssl.
+
+// Declaring mod_ssl's optional hooks here (so that we don't need mod_ssl.h).
+APR_DECLARE_OPTIONAL_FN(int, ssl_engine_disable, (conn_rec*));
+APR_DECLARE_OPTIONAL_FN(int, ssl_is_https, (conn_rec*));
+
+namespace mod_spdy {
+
+namespace {
+
+// These global variables store pointers to "optional functions" defined in
+// mod_ssl.  See TAMB 10.1.2 for more about optional functions.  These are
+// assigned just once, at start-up, so concurrency is not an issue.
+int (*gDisableSslForConnection)(conn_rec*) = NULL;
+int (*gIsUsingSslForConnection)(conn_rec*) = NULL;
+
+}  // namespace
+
+void RetrieveModSslFunctions() {
+  gDisableSslForConnection = APR_RETRIEVE_OPTIONAL_FN(ssl_engine_disable);
+  gIsUsingSslForConnection = APR_RETRIEVE_OPTIONAL_FN(ssl_is_https);
+  // If mod_ssl isn't installed, we'll get back NULL for these functions.  Our
+  // other hook functions will fail gracefully (i.e. do nothing) if these
+  // functions are NULL, but if the user installed mod_spdy without mod_ssl and
+  // expected it to do anything, we should warn them otherwise.
+  //
+  // Note: Alternatively, it may be that there's no mod_ssl, but mod_spdy has
+  // been configured to assume SPDY for non-SSL connections, in which case this
+  // warning is untrue.  But there's no easy way to check the server config
+  // from here, and normal users should never use that config option anyway
+  // (it's for debugging), so I don't think the spurious warning is a big deal.
+  if (gDisableSslForConnection == NULL &&
+      gIsUsingSslForConnection == NULL) {
+    LOG(WARNING) << "It seems that mod_spdy is installed but mod_ssl isn't.  "
+                 << "Without SSL, the server cannot ever use SPDY.";
+  }
+  // Whether or not mod_ssl is installed, either both functions should be
+  // non-NULL or both functions should be NULL.  Otherwise, something is wrong
+  // (like, maybe some kind of bizarre mutant mod_ssl is installed) and
+  // mod_spdy probably won't work correctly.
+  if ((gDisableSslForConnection == NULL) ^
+      (gIsUsingSslForConnection == NULL)) {
+    LOG(DFATAL) << "Some, but not all, of mod_ssl's optional functions are "
+                << "available.  What's going on?";
+  }
+}
+
+bool DisableSslForConnection(conn_rec* connection) {
+  return (gDisableSslForConnection != NULL) &&
+         (gDisableSslForConnection(connection) != 0);
+}
+
+bool IsUsingSslForConnection(conn_rec* connection) {
+  return (gIsUsingSslForConnection != NULL) &&
+         (gIsUsingSslForConnection(connection) != 0);
+}
+
+}  // namespace mod_spdy

Added: httpd/httpd/trunk/modules/spdy/apache/ssl_util.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/spdy/apache/ssl_util.h?rev=1590597&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/spdy/apache/ssl_util.h (added)
+++ httpd/httpd/trunk/modules/spdy/apache/ssl_util.h Mon Apr 28 10:55:17 2014
@@ -0,0 +1,43 @@
+// Copyright 2012 Google Inc
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef MOD_SPDY_APACHE_SSL_UTIL_H_
+#define MOD_SPDY_APACHE_SSL_UTIL_H_
+
+// This file contains some utility functions for communicating to mod_ssl.
+
+struct conn_rec;
+
+namespace mod_spdy {
+
+// This must be called from optional_fn_retrieve hook at startup before
+// using any of the methods below.
+void RetrieveModSslFunctions();
+
+// Disables SSL on a connection. Returns true if successful, false if failed
+// for some reason (such as the optional function not being available, or
+// mod_ssl indicating a problem).
+bool DisableSslForConnection(conn_rec* connection);
+
+// Returns true if the given connection is using SSL. Note that this answers
+// whether the connection is really using SSL rather than whether we should tell
+// others that it is. This distinction matters for slave connection belonging to
+// an SSL master --- we're not really using SSL for them (so this method
+// will return false), but will claim we are (since they'll be encrypted once
+// going to other outside world).
+bool IsUsingSslForConnection(conn_rec* connection);
+
+}  // namespace mod_spdy
+
+#endif  // MOD_SPDY_APACHE_SSL_UTIL_H_

Propchange: httpd/httpd/trunk/modules/spdy/apache/ssl_util.h
------------------------------------------------------------------------------
    svn:eol-style = native

Added: httpd/httpd/trunk/modules/spdy/apache/testing/dummy_util_filter.cc
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/spdy/apache/testing/dummy_util_filter.cc?rev=1590597&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/spdy/apache/testing/dummy_util_filter.cc (added)
+++ httpd/httpd/trunk/modules/spdy/apache/testing/dummy_util_filter.cc Mon Apr 28 10:55:17 2014
@@ -0,0 +1,32 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "httpd.h"
+#include "apr_buckets.h"
+#include "util_filter.h"
+
+// For unit tests, we don't link in Apache's util_filter.c, which defines the
+// below functions.  To make our lives easier, we define dummy versions of them
+// here that simply report success.
+
+extern "C" {
+
+AP_DECLARE(apr_status_t) ap_pass_brigade(
+    ap_filter_t* filter, apr_bucket_brigade* bucket) {
+  return APR_SUCCESS;
+}
+
+AP_DECLARE(void) ap_remove_output_filter(ap_filter_t* filter) {}
+
+}  // extern "C"

Added: httpd/httpd/trunk/modules/spdy/apache/testing/spdy_apache_test_main.cc
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/spdy/apache/testing/spdy_apache_test_main.cc?rev=1590597&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/spdy/apache/testing/spdy_apache_test_main.cc (added)
+++ httpd/httpd/trunk/modules/spdy/apache/testing/spdy_apache_test_main.cc Mon Apr 28 10:55:17 2014
@@ -0,0 +1,46 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <iostream>
+
+#include "apr_general.h"
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+// Class to ensure that APR gets initialized and torn down.
+class AprInitializer {
+ public:
+  AprInitializer() {
+    const apr_status_t status = apr_initialize();
+    CHECK(status == APR_SUCCESS) << "APR initialization failed.";
+  }
+  ~AprInitializer() {
+    apr_terminate();
+  }
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AprInitializer);
+};
+
+}  // namespace
+
+int main(int argc, char **argv) {
+  std::cout << "Running main() from spdy_apache_test_main.cc\n";
+  testing::InitGoogleTest(&argc, argv);
+  AprInitializer apr_initializer;
+  return RUN_ALL_TESTS();
+}

Added: httpd/httpd/trunk/modules/spdy/common/VERSION
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/spdy/common/VERSION?rev=1590597&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/spdy/common/VERSION (added)
+++ httpd/httpd/trunk/modules/spdy/common/VERSION Mon Apr 28 10:55:17 2014
@@ -0,0 +1,4 @@
+MAJOR=0
+MINOR=1
+BUILD=0
+PATCH=0

Added: httpd/httpd/trunk/modules/spdy/common/executor.cc
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/spdy/common/executor.cc?rev=1590597&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/spdy/common/executor.cc (added)
+++ httpd/httpd/trunk/modules/spdy/common/executor.cc Mon Apr 28 10:55:17 2014
@@ -0,0 +1,23 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "mod_spdy/common/executor.h"
+
+namespace mod_spdy {
+
+Executor::Executor() {}
+
+Executor::~Executor() {}
+
+}  // namespace mod_spdy

Added: httpd/httpd/trunk/modules/spdy/common/executor.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/spdy/common/executor.h?rev=1590597&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/spdy/common/executor.h (added)
+++ httpd/httpd/trunk/modules/spdy/common/executor.h Mon Apr 28 10:55:17 2014
@@ -0,0 +1,53 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef MOD_SPDY_COMMON_EXECUTOR_H_
+#define MOD_SPDY_COMMON_EXECUTOR_H_
+
+#include "base/basictypes.h"
+#include "net/spdy/spdy_protocol.h"
+
+namespace net_instaweb { class Function; }
+
+namespace mod_spdy {
+
+// An interface for a service that can execute tasks.  A thread pool (using
+// net_instaweb::QueuedWorkerPool or an apr_thread_pool_t) would be one obvious
+// implementation.  In the future we may want to adjust this interface for use
+// in an event-driven environment (e.g. Nginx).
+class Executor {
+ public:
+  Executor();
+  virtual ~Executor();
+
+  // Add a new task to be run; the executor takes ownership of the task.  The
+  // priority argument hints at how important this task is to get done, but the
+  // executor is free to ignore it.  If Stop has already been called, the
+  // executor may immediately cancel the task rather than running it.
+  virtual void AddTask(net_instaweb::Function* task,
+                       net::SpdyPriority priority) = 0;
+
+  // Stop the executor.  Cancel all tasks that were pushed onto this executor
+  // but that have not yet begun to run.  Tasks that were already running will
+  // continue to run, and this function must block until they have completed.
+  // It must be safe to call this method more than once.
+  virtual void Stop() = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(Executor);
+};
+
+}  // namespace mod_spdy
+
+#endif  // MOD_SPDY_COMMON_EXECUTOR_H_

Propchange: httpd/httpd/trunk/modules/spdy/common/executor.h
------------------------------------------------------------------------------
    svn:eol-style = native

Added: httpd/httpd/trunk/modules/spdy/common/http_request_visitor_interface.cc
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/spdy/common/http_request_visitor_interface.cc?rev=1590597&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/spdy/common/http_request_visitor_interface.cc (added)
+++ httpd/httpd/trunk/modules/spdy/common/http_request_visitor_interface.cc Mon Apr 28 10:55:17 2014
@@ -0,0 +1,22 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "mod_spdy/common/http_request_visitor_interface.h"
+
+namespace mod_spdy {
+
+HttpRequestVisitorInterface::HttpRequestVisitorInterface() {}
+HttpRequestVisitorInterface::~HttpRequestVisitorInterface() {}
+
+}  // namespace mod_spdy

Added: httpd/httpd/trunk/modules/spdy/common/http_request_visitor_interface.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/spdy/common/http_request_visitor_interface.h?rev=1590597&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/spdy/common/http_request_visitor_interface.h (added)
+++ httpd/httpd/trunk/modules/spdy/common/http_request_visitor_interface.h Mon Apr 28 10:55:17 2014
@@ -0,0 +1,86 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef MOD_SPDY_COMMON_HTTP_REQUEST_VISITOR_INTERFACE_H_
+#define MOD_SPDY_COMMON_HTTP_REQUEST_VISITOR_INTERFACE_H_
+
+#include "base/basictypes.h"
+#include "base/strings/string_piece.h"
+
+namespace mod_spdy {
+
+// Interface that gets called back as an HTTP stream is visited.
+class HttpRequestVisitorInterface {
+ public:
+  HttpRequestVisitorInterface();
+  virtual ~HttpRequestVisitorInterface();
+
+  // Called when an HTTP request line is visited. Indicates that a new HTTP
+  // request is being visited.
+  virtual void OnRequestLine(const base::StringPiece& method,
+                             const base::StringPiece& path,
+                             const base::StringPiece& version) = 0;
+
+  // Called zero or more times, once for each leading (i.e. normal, not
+  // trailing) HTTP header.  This is called after OnRequestLine but before
+  // OnLeadingHeadersComplete.
+  virtual void OnLeadingHeader(const base::StringPiece& key,
+                               const base::StringPiece& value) = 0;
+
+  // Called after the leading HTTP headers have been visited.  This will be
+  // called exactly once when the leading headers are done (even if there were
+  // no leading headers).
+  virtual void OnLeadingHeadersComplete() = 0;
+
+  // Called zero or more times, after OnLeadingHeadersComplete.  This method is
+  // mutually exclusive with OnDataChunk and OnDataChunksComplete; either data
+  // will be raw or chunked, but not both.  If raw data is used, there cannot
+  // be trailing headers; the raw data section will be terminated by the call
+  // to OnComplete.
+  virtual void OnRawData(const base::StringPiece& data) = 0;
+
+  // Called zero or more times, after OnLeadingHeadersComplete, once for each
+  // "chunk" of the HTTP body.  This method is mutually exclusive with
+  // OnRawData; either data will be raw or chunked, but not both.
+  virtual void OnDataChunk(const base::StringPiece& data) = 0;
+
+  // Called when there will be no more data chunks.  There may still be
+  // trailing headers, however.  This method is mutually exclusive with
+  // OnRawData; either data will be raw or chunked, but not both.
+  virtual void OnDataChunksComplete() = 0;
+
+  // Called zero or more times, once for each trailing header.  This is called
+  // after OnDataChunksComplete but before OnTrailingHeadersComplete.  It
+  // cannot be called if OnRawData was used.
+  virtual void OnTrailingHeader(const base::StringPiece& key,
+                                const base::StringPiece& value) = 0;
+
+  // Called after all the trailing HTTP headers have been visited.  If there
+  // were any trailing headers, this will definitely be called; if there were
+  // no trailing headers, it is optional.
+  virtual void OnTrailingHeadersComplete() = 0;
+
+  // Called once when the HTTP request is totally done.  This is called
+  // immediately after one of OnLeadingHeadersComplete, OnRawData,
+  // OnDataChunksComplete, or OnTrailingHeadersComplete.  After this, no more
+  // methods will be called.
+  virtual void OnComplete() = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HttpRequestVisitorInterface);
+};
+
+}  // namespace mod_spdy
+
+#endif  // MOD_SPDY_COMMON_HTTP_REQUEST_VISITOR_INTERFACE_H_

Propchange: httpd/httpd/trunk/modules/spdy/common/http_request_visitor_interface.h
------------------------------------------------------------------------------
    svn:eol-style = native

Added: httpd/httpd/trunk/modules/spdy/common/http_response_parser.cc
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/spdy/common/http_response_parser.cc?rev=1590597&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/spdy/common/http_response_parser.cc (added)
+++ httpd/httpd/trunk/modules/spdy/common/http_response_parser.cc Mon Apr 28 10:55:17 2014
@@ -0,0 +1,367 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "mod_spdy/common/http_response_parser.h"
+
+#include <string>
+
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
+#include "mod_spdy/common/http_response_visitor_interface.h"
+#include "mod_spdy/common/protocol_util.h"
+
+namespace {
+
+// If the given position in the string is npos, then return the position of the
+// end of the string; otherwise, return the given position unchanged.
+size_t NposToEnd(const base::StringPiece& str, size_t pos) {
+  return pos == base::StringPiece::npos ? str.size() : pos;
+}
+
+}  // namespace
+
+namespace mod_spdy {
+
+HttpResponseParser::HttpResponseParser(HttpResponseVisitorInterface* visitor)
+    : visitor_(visitor),
+      state_(STATUS_LINE),
+      body_type_(NO_BODY),
+      remaining_bytes_(0) {}
+
+HttpResponseParser::~HttpResponseParser() {}
+
+bool HttpResponseParser::ProcessInput(const base::StringPiece& input_data) {
+  // Keep track of the slice of data we are currently looking at.  We will
+  // modify this variable as we go.
+  base::StringPiece data = input_data;
+
+  size_t last_length = data.size() + 1;
+  while (!data.empty()) {
+    // Safety check to avoid infinite loops in case our code is buggy; the
+    // slice of data we are looking at should get strictly smaller on every
+    // iteration of this loop.
+    if (data.size() >= last_length) {
+      LOG(DFATAL) << "Potential infinite loop.";
+      return false;
+    }
+    last_length = data.size();
+
+    // Process the data based on our current parser state.  Most of these
+    // methods receive a pointer to `data` and will mutate it as they consume
+    // bytes.  We continue looping until the whole input_data is consumed.
+    switch (state_) {
+      case STATUS_LINE:
+        if (!ProcessStatusLine(&data)) {
+          return false;
+        }
+        break;
+      case LEADING_HEADERS_CHECK_NEXT_LINE:
+        if (!CheckStartOfHeaderLine(data)) {
+          return false;
+        }
+        // fallthrough
+      case LEADING_HEADERS:
+        if (!ProcessLeadingHeaders(&data)) {
+          return false;
+        }
+        break;
+      case CHUNK_START:
+        if (!ProcessChunkStart(&data)) {
+          return false;
+        }
+        break;
+      case BODY_DATA:
+        if (!ProcessBodyData(&data)) {
+          return false;
+        }
+        break;
+      case CHUNK_ENDING:
+        if (!ProcessChunkEnding(&data)) {
+          return false;
+        }
+        break;
+      case COMPLETE:
+        DCHECK(buffer_.empty());
+        return true;
+      default:
+        LOG(DFATAL) << "Invalid parser state: " << state_;
+        return false;
+    }
+  }
+  return true;
+}
+
+bool HttpResponseParser::ProcessStatusLine(base::StringPiece* data) {
+  DCHECK(state_ == STATUS_LINE);
+  const size_t linebreak = data->find("\r\n");
+
+  // If we haven't reached the end of the line yet, buffer the data and quit.
+  if (linebreak == base::StringPiece::npos) {
+    data->AppendToString(&buffer_);
+    *data = base::StringPiece();
+    return true;
+  }
+
+  // Combine the data up to the linebreak with what we've buffered, and parse
+  // the status line out of it.
+  data->substr(0, linebreak).AppendToString(&buffer_);
+  if (!ParseStatusLine(buffer_)) {
+    return false;
+  }
+  buffer_.clear();
+
+  // Chop off the linebreak and all data before it, and move on to parsing the
+  // leading headers.
+  *data = data->substr(linebreak + 2);
+  state_ = LEADING_HEADERS;
+  return true;
+}
+
+bool HttpResponseParser::CheckStartOfHeaderLine(const base::StringPiece& data){
+  // This state is for when we have a complete header line buffered, and we
+  // need to check the next line to see if it starts with leading whitespace.
+  DCHECK(state_ == LEADING_HEADERS_CHECK_NEXT_LINE);
+  DCHECK(!buffer_.empty());
+  DCHECK(!data.empty());
+
+  // If this line _doesn't_ start with whitespace, then the buffered line is a
+  // complete header line, so we should parse it and clear the buffer.
+  // Otherwise, this next line is a continuation of the header, so we need to
+  // buffer more data.
+  const char first = data[0];
+  if (first != ' ' && first != '\t') {
+    if (!ParseLeadingHeader(buffer_)) {
+      return false;
+    }
+    buffer_.clear();
+  }
+
+  // Either way, we're ready to continuing parsing headers.
+  state_ = LEADING_HEADERS;
+  return true;
+}
+
+bool HttpResponseParser::ProcessLeadingHeaders(base::StringPiece* data) {
+  DCHECK(state_ == LEADING_HEADERS);
+  const size_t linebreak = data->find("\r\n");
+
+  // If we haven't reached the end of the line yet, buffer the data and quit.
+  if (linebreak == base::StringPiece::npos) {
+    data->AppendToString(&buffer_);
+    *data = base::StringPiece();
+    return true;
+  }
+
+  // If we're not in the middle of a header line (buffer is empty) and the
+  // linebreak comes at the very beginning, this must be the blank line that
+  // signals the end of the leading headers.  Skip the linebreak, switch states
+  // depending on what headers we saw (Is there body data?  Is it chunked?),
+  // and return.
+  if (linebreak == 0 && buffer_.empty()) {
+    switch (body_type_) {
+      case CHUNKED_BODY:
+        state_ = CHUNK_START;
+        break;
+      case UNCHUNKED_BODY:
+        state_ = BODY_DATA;
+        break;
+      case NO_BODY:
+        state_ = COMPLETE;
+        break;
+      default:
+        LOG(DFATAL) << "Invalid body type: " << body_type_;
+        return false;
+    }
+    visitor_->OnLeadingHeadersComplete(state_ == COMPLETE);
+    *data = data->substr(linebreak + 2);
+    return true;
+  }
+
+  // We've reached the end of the line, but we need to check the next line to
+  // see if it's a continuation of this header.  Buffer up to the linebreak,
+  // skip the linebreak itself, and set our state to check the next line.
+  data->substr(0, linebreak).AppendToString(&buffer_);
+  *data = data->substr(linebreak + 2);
+  state_ = LEADING_HEADERS_CHECK_NEXT_LINE;
+  return true;
+}
+
+bool HttpResponseParser::ProcessChunkStart(base::StringPiece* data) {
+  DCHECK(state_ == CHUNK_START);
+  const size_t linebreak = data->find("\r\n");
+
+  // If we haven't reached the end of the line yet, buffer the data and quit.
+  if (linebreak == base::StringPiece::npos) {
+    data->AppendToString(&buffer_);
+    *data = base::StringPiece();
+    return true;
+  }
+
+  // Combine the data up to the linebreak with what we've buffered, and parse
+  // the chunk length out of it.
+  data->substr(0, linebreak).AppendToString(&buffer_);
+  if (!ParseChunkStart(buffer_)) {
+    return false;
+  }
+  buffer_.clear();
+
+  // Skip the linebreak.
+  *data = data->substr(linebreak + 2);
+
+  // ParseChunkStart will put the size of the chunk into remaining_bytes_.  If
+  // the chunk size is zero, that means we've reached the end of the body data.
+  // Otherwise, we now need to read the data in this chunk.
+  if (remaining_bytes_ == 0) {
+    state_ = COMPLETE;
+    visitor_->OnData(base::StringPiece(), true);
+  } else {
+    state_ = BODY_DATA;
+  }
+  return true;
+}
+
+bool HttpResponseParser::ProcessBodyData(base::StringPiece* data) {
+  DCHECK(state_ == BODY_DATA);
+
+  // We never buffer anything when reading the body data.  This minimizes how
+  // much string copying we need to do for most responses.
+  DCHECK(buffer_.empty());
+
+  // If the available data is less that what remains of this chunk (if the data
+  // is chunked) or of the whole body (if there was instead an explicit
+  // content-length), then read in all the data we have and subtract from
+  // remaining_bytes_.
+  if (data->size() < remaining_bytes_) {
+    visitor_->OnData(*data, false);
+    remaining_bytes_ -= data->size();
+    *data = base::StringPiece();
+  }
+  // Otherwise, we have enough data here to fulfill remaining_bytes_, so read
+  // in that much data, and then switch states depending on whether we're using
+  // chunking or not.
+  else {
+    if (body_type_ == CHUNKED_BODY) {
+      state_ = CHUNK_ENDING;
+    } else {
+      DCHECK(body_type_ == UNCHUNKED_BODY);
+      state_ = COMPLETE;
+    }
+    visitor_->OnData(data->substr(0, remaining_bytes_), state_ == COMPLETE);
+    *data = data->substr(remaining_bytes_);
+    remaining_bytes_ = 0;
+  }
+  return true;
+}
+
+bool HttpResponseParser::ProcessChunkEnding(base::StringPiece* data) {
+  DCHECK(state_ == CHUNK_ENDING);
+  // For whatever reason, HTTP requires each chunk to end with a CRLF.  So,
+  // make sure it's there, and then skip it, before moving on to read the next
+  // chunk.
+  if (!data->starts_with("\r\n")) {
+    VLOG(1) << "Expected CRLF at end of chunk.";
+    return false;
+  }
+  *data = data->substr(2);
+  state_ = CHUNK_START;
+  return true;
+}
+
+bool HttpResponseParser::ParseStatusLine(const base::StringPiece& text) {
+  // An HTTP status line should look like:
+  // <HTTP version> <single space> <status code> <single space> <status phrase>
+  // For example, "HTTP/1.1 301 Moved permenantly".
+  // We'll be a little more lenient just in case, and allow multiple spaces
+  // between each part, and allow the phrase to be omitted.
+  const size_t first_space = text.find(' ');
+  if (first_space == base::StringPiece::npos) {
+    VLOG(1) << "Bad status line: " << text;
+    return false;
+  }
+  const size_t start_of_code = text.find_first_not_of(' ', first_space);
+  if (start_of_code == base::StringPiece::npos) {
+    VLOG(1) << "Bad status line: " << text;
+    return false;
+  }
+  const size_t second_space = NposToEnd(text, text.find(' ', start_of_code));
+  const size_t start_of_phrase =
+      NposToEnd(text, text.find_first_not_of(' ', second_space));
+
+  visitor_->OnStatusLine(
+      text.substr(0, first_space),
+      text.substr(start_of_code, second_space - start_of_code),
+      text.substr(start_of_phrase));
+  return true;
+}
+
+bool HttpResponseParser::ParseLeadingHeader(const base::StringPiece& text) {
+  // Even for multiline headers, we strip out the CRLFs, so there shouldn't be
+  // any left in the text that we're parsing.
+  DCHECK(text.find("\r\n") == base::StringPiece::npos);
+
+  // Find the colon separating the key from the value, and skip any leading
+  // whitespace between the colon and the value.
+  const size_t colon = text.find(':');
+  if (colon == base::StringPiece::npos) {
+    VLOG(1) << "Bad header line: " << text;
+    return false;
+  }
+  const size_t value_start =
+      NposToEnd(text, text.find_first_not_of(" \t", colon + 1));
+
+  const base::StringPiece key = text.substr(0, colon);
+  const base::StringPiece value = text.substr(value_start);
+
+  // We need to check the Content-Length and Transfer-Encoding headers to know
+  // if we're using chunking, and if not, how long the body is.
+  if (LowerCaseEqualsASCII(key.begin(), key.end(), http::kTransferEncoding)) {
+    if (value == http::kChunked) {
+      body_type_ = CHUNKED_BODY;
+    }
+  } else if (body_type_ != CHUNKED_BODY &&
+             LowerCaseEqualsASCII(key.begin(), key.end(),
+                                  http::kContentLength)) {
+    uint64 uint_value = 0u;
+    if (base::StringToUint64(value, &uint_value) && uint_value > 0u) {
+      remaining_bytes_ = uint_value;
+      body_type_ = UNCHUNKED_BODY;
+    } else {
+      VLOG(1) << "Bad content-length: " << value;
+    }
+  }
+
+  visitor_->OnLeadingHeader(key, value);
+  return true;
+}
+
+bool HttpResponseParser::ParseChunkStart(const base::StringPiece& text) {
+  // The line at the start of each chunk consists of the chunk length in
+  // hexadecimal, potentially followed by chunk-extension metadata that we
+  // don't care about anyway.  So just parse out the hex number and ignore the
+  // rest.
+  const size_t length =
+      NposToEnd(text, text.find_first_not_of("0123456789abcdefABCDEF"));
+  int int_value = 0;
+  if (!base::HexStringToInt(text.substr(0, length), &int_value) ||
+      int_value < 0) {
+    VLOG(1) << "Bad chunk line: " << text;
+    return false;
+  }
+  remaining_bytes_ = static_cast<size_t>(int_value);
+  return true;
+}
+
+}  // namespace mod_spdy

Added: httpd/httpd/trunk/modules/spdy/common/http_response_parser.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/spdy/common/http_response_parser.h?rev=1590597&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/spdy/common/http_response_parser.h (added)
+++ httpd/httpd/trunk/modules/spdy/common/http_response_parser.h Mon Apr 28 10:55:17 2014
@@ -0,0 +1,89 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef MOD_SPDY_COMMON_HTTP_RESPONSE_PARSER_H_
+#define MOD_SPDY_COMMON_HTTP_RESPONSE_PARSER_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/strings/string_piece.h"
+
+namespace mod_spdy {
+
+class HttpResponseVisitorInterface;
+
+// Parses incoming HTTP response data.  Data is fed in piece by piece with the
+// ProcessInput method, and appropriate methods are called on the visitor.
+// There is no need to indicate the end of the input, as this is inferred from
+// the Content-Length or Transfer-Encoding headers.  If the response uses
+// chunked encoding, the parser will de-chunk it.  Note that all data after the
+// end of the response body, including trailing headers, will be completely
+// ignored.
+class HttpResponseParser {
+ public:
+  explicit HttpResponseParser(HttpResponseVisitorInterface* visitor);
+  ~HttpResponseParser();
+
+  // Return true on success, false on failure.
+  bool ProcessInput(const base::StringPiece& input_data);
+  bool ProcessInput(const char* data, size_t size) {
+    return ProcessInput(base::StringPiece(data, size));
+  }
+
+  // For unit testing only: Get the remaining number of bytes expected (in the
+  // whole response, if we used Content-Length, or just in the current chunk,
+  // if we used Transfer-Encoding: chunked).
+  uint64 GetRemainingBytesForTest() const { return remaining_bytes_; }
+
+ private:
+  enum ParserState {
+    STATUS_LINE,
+    LEADING_HEADERS,
+    LEADING_HEADERS_CHECK_NEXT_LINE,
+    CHUNK_START,
+    BODY_DATA,
+    CHUNK_ENDING,
+    COMPLETE
+  };
+
+  enum BodyType {
+    NO_BODY,
+    UNCHUNKED_BODY,
+    CHUNKED_BODY
+  };
+
+  bool ProcessStatusLine(base::StringPiece* data);
+  bool CheckStartOfHeaderLine(const base::StringPiece& data);
+  bool ProcessLeadingHeaders(base::StringPiece* data);
+  bool ProcessChunkStart(base::StringPiece* data);
+  bool ProcessBodyData(base::StringPiece* data);
+  bool ProcessChunkEnding(base::StringPiece* data);
+
+  bool ParseStatusLine(const base::StringPiece& text);
+  bool ParseLeadingHeader(const base::StringPiece& text);
+  bool ParseChunkStart(const base::StringPiece& text);
+
+  HttpResponseVisitorInterface* const visitor_;
+  ParserState state_;
+  BodyType body_type_;
+  uint64 remaining_bytes_;
+  std::string buffer_;
+
+  DISALLOW_COPY_AND_ASSIGN(HttpResponseParser);
+};
+
+}  // namespace mod_spdy
+
+#endif  // MOD_SPDY_COMMON_HTTP_RESPONSE_PARSER_H_

Propchange: httpd/httpd/trunk/modules/spdy/common/http_response_parser.h
------------------------------------------------------------------------------
    svn:eol-style = native

Added: httpd/httpd/trunk/modules/spdy/common/http_response_parser_test.cc
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/spdy/common/http_response_parser_test.cc?rev=1590597&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/spdy/common/http_response_parser_test.cc (added)
+++ httpd/httpd/trunk/modules/spdy/common/http_response_parser_test.cc Mon Apr 28 10:55:17 2014
@@ -0,0 +1,273 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "mod_spdy/common/http_response_parser.h"
+
+#include "base/strings/string_piece.h"
+#include "mod_spdy/common/http_response_visitor_interface.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using mod_spdy::HttpResponseParser;
+using testing::Eq;
+using testing::InSequence;
+
+namespace {
+
+class MockHttpResponseVisitor: public mod_spdy::HttpResponseVisitorInterface {
+ public:
+  MOCK_METHOD3(OnStatusLine, void(const base::StringPiece&,
+                                  const base::StringPiece&,
+                                  const base::StringPiece&));
+  MOCK_METHOD2(OnLeadingHeader, void(const base::StringPiece&,
+                                     const base::StringPiece&));
+  MOCK_METHOD1(OnLeadingHeadersComplete, void(bool));
+  MOCK_METHOD2(OnData, void(const base::StringPiece&, bool));
+};
+
+class HttpResponseParserTest : public testing::Test {
+ public:
+  HttpResponseParserTest() : parser_(&visitor_) {}
+
+ protected:
+  MockHttpResponseVisitor visitor_;
+  HttpResponseParser parser_;
+};
+
+TEST_F(HttpResponseParserTest, SimpleWithContentLength) {
+  InSequence seq;
+  EXPECT_CALL(visitor_, OnStatusLine(Eq("HTTP/1.1"), Eq("200"), Eq("OK")));
+  EXPECT_CALL(visitor_, OnLeadingHeader(Eq("Content-Length"), Eq("14")));
+  EXPECT_CALL(visitor_, OnLeadingHeader(Eq("Content-Type"), Eq("text/plain")));
+  EXPECT_CALL(visitor_, OnLeadingHeader(Eq("X-Whatever"), Eq("foobar")));
+  EXPECT_CALL(visitor_, OnLeadingHeadersComplete(Eq(false)));
+  EXPECT_CALL(visitor_, OnData(Eq("Hello, world!\n"), Eq(true)));
+
+  ASSERT_TRUE(parser_.ProcessInput(
+      "HTTP/1.1 200 OK\r\n"
+      "Content-Length: 14\r\n"
+      "Content-Type:      text/plain\r\n"
+      "X-Whatever:foobar\r\n"
+      "\r\n"
+      "Hello, world!\n"
+      "\r\n"));
+}
+
+TEST_F(HttpResponseParserTest, SimpleWithChunkedData) {
+  InSequence seq;
+  EXPECT_CALL(visitor_, OnStatusLine(Eq("HTTP/1.1"), Eq("200"), Eq("OK")));
+  EXPECT_CALL(visitor_, OnLeadingHeader(
+      Eq("Transfer-Encoding"), Eq("chunked")));
+  EXPECT_CALL(visitor_, OnLeadingHeader(Eq("Content-Type"), Eq("text/plain")));
+  EXPECT_CALL(visitor_, OnLeadingHeadersComplete(Eq(false)));
+  EXPECT_CALL(visitor_, OnData(Eq("Hello, world!\n"), Eq(false)));
+  EXPECT_CALL(visitor_, OnData(Eq("It sure is good to see you today."),
+                               Eq(false)));
+  EXPECT_CALL(visitor_, OnData(Eq(""), Eq(true)));
+
+  ASSERT_TRUE(parser_.ProcessInput(
+      "HTTP/1.1 200 OK\r\n"
+      "Transfer-Encoding: chunked\r\n"
+      "Content-Type: text/plain\r\n"
+      "\r\n"
+      "E\r\n"
+      "Hello, world!\n\r\n"
+      "21; some-random-chunk-extension\r\n"
+      "It sure is good to see you today.\r\n"
+      "0\r\n"
+      "\r\n"));
+}
+
+// Check that Transfer-Encoding: chunked supersedes Content-Length.
+TEST_F(HttpResponseParserTest, ContentLengthAndTransferEncoding) {
+  InSequence seq;
+  EXPECT_CALL(visitor_, OnStatusLine(Eq("HTTP/1.1"), Eq("200"), Eq("OK")));
+  EXPECT_CALL(visitor_, OnLeadingHeader(Eq("Content-Length"), Eq("3")));
+  EXPECT_CALL(visitor_, OnLeadingHeader(
+      Eq("Transfer-Encoding"), Eq("chunked")));
+  EXPECT_CALL(visitor_, OnLeadingHeadersComplete(Eq(false)));
+  EXPECT_CALL(visitor_, OnData(Eq("Hello,"), Eq(false)));
+  EXPECT_CALL(visitor_, OnData(Eq(" world!\n"), Eq(false)));
+  EXPECT_CALL(visitor_, OnData(Eq(""), Eq(true)));
+
+  ASSERT_TRUE(parser_.ProcessInput(
+      "HTTP/1.1 200 OK\r\n"
+      "Content-Length: 3\r\n"
+      "Transfer-Encoding: chunked\r\n"
+      "\r\n"
+      "6\r\n"
+      "Hello,\r\n"
+      "8\r\n"
+      " world!\n\r\n"
+      "0\r\n"
+      "\r\n"));
+}
+
+// Check that Transfer-Encoding: chunked supersedes Content-Length even if
+// Content-Length comes later.
+TEST_F(HttpResponseParserTest, TransferEncodingAndContentLength) {
+  InSequence seq;
+  EXPECT_CALL(visitor_, OnStatusLine(Eq("HTTP/1.1"), Eq("200"), Eq("OK")));
+  EXPECT_CALL(visitor_, OnLeadingHeader(
+      Eq("Transfer-Encoding"), Eq("chunked")));
+  EXPECT_CALL(visitor_, OnLeadingHeader(Eq("Content-Length"), Eq("3")));
+  EXPECT_CALL(visitor_, OnLeadingHeadersComplete(Eq(false)));
+  EXPECT_CALL(visitor_, OnData(Eq("Hello,"), Eq(false)));
+  EXPECT_CALL(visitor_, OnData(Eq(" world!\n"), Eq(false)));
+  EXPECT_CALL(visitor_, OnData(Eq(""), Eq(true)));
+
+  ASSERT_TRUE(parser_.ProcessInput(
+      "HTTP/1.1 200 OK\r\n"
+      "Transfer-Encoding: chunked\r\n"
+      "Content-Length: 3\r\n"
+      "\r\n"
+      "6\r\n"
+      "Hello,\r\n"
+      "8\r\n"
+      " world!\n\r\n"
+      "0\r\n"
+      "\r\n"));
+}
+
+TEST_F(HttpResponseParserTest, NoBodyData) {
+  InSequence seq;
+  EXPECT_CALL(visitor_, OnStatusLine(Eq("HTTP/1.1"), Eq("301"),
+                                     Eq("Moved permenantly")));
+  EXPECT_CALL(visitor_, OnLeadingHeader(Eq("X-Empty"), Eq("")));
+  EXPECT_CALL(visitor_, OnLeadingHeader(Eq("Location"), Eq("/foo/bar.html")));
+  EXPECT_CALL(visitor_, OnLeadingHeadersComplete(Eq(true)));
+
+  ASSERT_TRUE(parser_.ProcessInput(
+      "HTTP/1.1  301  Moved permenantly\r\n"
+      "X-Empty:\r\n"
+      "Location: /foo/bar.html\r\n"
+      "\r\n"));
+}
+
+TEST_F(HttpResponseParserTest, NoStatusPhrase) {
+  InSequence seq;
+  EXPECT_CALL(visitor_, OnStatusLine(Eq("HTTP/1.1"), Eq("123"), Eq("")));
+  EXPECT_CALL(visitor_, OnLeadingHeadersComplete(Eq(true)));
+
+  ASSERT_TRUE(parser_.ProcessInput(
+      "HTTP/1.1 123\r\n"
+      "\r\n"));
+}
+
+TEST_F(HttpResponseParserTest, HeadersBrokenAcrossLines) {
+  InSequence seq;
+  EXPECT_CALL(visitor_, OnStatusLine(Eq("HTTP/1.1"), Eq("301"),
+                                     Eq("Moved permenantly")));
+  EXPECT_CALL(visitor_, OnLeadingHeader(
+      Eq("X-NextLine"), Eq("Alas, this is legal HTTP.")));
+  EXPECT_CALL(visitor_, OnLeadingHeader(Eq("Location"), Eq("/foo/bar.html")));
+  EXPECT_CALL(visitor_, OnLeadingHeader(
+      Eq("X-ThreeLines"), Eq("foo    bar  baz     quux")));
+  EXPECT_CALL(visitor_, OnLeadingHeadersComplete(Eq(true)));
+
+  ASSERT_TRUE(parser_.ProcessInput(
+      "HTTP/1.1 301 Moved permenantly\r\n"
+      "X-NextLine:\r\n"
+      "\tAlas, this is legal HTTP.\r\n"
+      "Location: /foo/bar.html\r\n"
+      "X-ThreeLines: foo\r\n"
+      "    bar  baz \r\n"
+      "    quux\r\n"
+      "\r\n"));
+}
+
+TEST_F(HttpResponseParserTest, DividedUpIntoPieces) {
+  InSequence seq;
+  EXPECT_CALL(visitor_, OnStatusLine(Eq("HTTP/1.1"), Eq("418"),
+                                     Eq("I'm a teapot")));
+  EXPECT_CALL(visitor_, OnLeadingHeader(
+      Eq("tRaNsFeR-EnCoDiNg"), Eq("chunked")));
+  EXPECT_CALL(visitor_, OnLeadingHeader(Eq("X-TwoLines"), Eq("foo  bar")));
+  EXPECT_CALL(visitor_, OnLeadingHeader(Eq("Content-Type"), Eq("text/plain")));
+  EXPECT_CALL(visitor_, OnLeadingHeadersComplete(Eq(false)));
+  EXPECT_CALL(visitor_, OnData(Eq("Hello,"), Eq(false)));
+  EXPECT_CALL(visitor_, OnData(Eq(" world!\n"), Eq(false)));
+  EXPECT_CALL(visitor_, OnData(Eq("It sure"), Eq(false)));
+  EXPECT_CALL(visitor_, OnData(Eq(" is good to see you today."), Eq(false)));
+  EXPECT_CALL(visitor_, OnData(Eq(""), Eq(true)));
+
+  ASSERT_TRUE(parser_.ProcessInput("HTTP/1.1 418 I'm"));
+  ASSERT_TRUE(parser_.ProcessInput(" a teapot\r\ntRaNsFeR-EnCoDiNg:"));
+  ASSERT_TRUE(parser_.ProcessInput("chunked\r\n"));
+  ASSERT_TRUE(parser_.ProcessInput("X-TwoLines:\tfoo "));
+  ASSERT_TRUE(parser_.ProcessInput("\r\n"));
+  ASSERT_TRUE(parser_.ProcessInput(" bar"));
+  ASSERT_TRUE(parser_.ProcessInput("\r\nContent-Type: text/plain"));
+  ASSERT_TRUE(parser_.ProcessInput("\r\n\r\nE"));
+  ASSERT_TRUE(parser_.ProcessInput("\r\n"));
+  ASSERT_TRUE(parser_.ProcessInput("Hello,"));
+  ASSERT_TRUE(parser_.ProcessInput(" world!\n"));
+  ASSERT_TRUE(parser_.ProcessInput("\r\n"));
+  ASSERT_TRUE(parser_.ProcessInput("21; some-random-"));
+  ASSERT_TRUE(parser_.ProcessInput("chunk-extension\r\nIt sure"));
+  ASSERT_TRUE(parser_.ProcessInput(" is good to see you today.\r\n0"));
+  ASSERT_TRUE(parser_.ProcessInput("0\r\n\r\n"));
+}
+
+// Test that we gracefully handle bogus content-lengths.  We should effectively
+// ignore the header, assume that the response has no content, and ignore what
+// follows the headers.
+TEST_F(HttpResponseParserTest, BogusContentLength) {
+  EXPECT_CALL(visitor_, OnStatusLine(Eq("HTTP/1.1"), Eq("200"), Eq("OK")));
+  EXPECT_CALL(visitor_, OnLeadingHeader(Eq("Content-Length"), Eq("bogus")));
+  EXPECT_CALL(visitor_, OnLeadingHeadersComplete(Eq(true)));
+
+  ASSERT_TRUE(parser_.ProcessInput(
+      "HTTP/1.1 200 OK\r\n"
+      "Content-Length: bogus\r\n"
+      "\r\n"
+      "bogus bogus bogus\r\n"));
+}
+
+// Test that we gracefully handle over-large content-lengths.  As with
+// unparsable Content-Length values, an overflow of the Content-Length value
+// should result in us ignoring it.
+TEST_F(HttpResponseParserTest, ContentLengthOverflow) {
+  EXPECT_CALL(visitor_, OnStatusLine(Eq("HTTP/1.1"), Eq("200"), Eq("OK")));
+  EXPECT_CALL(visitor_, OnLeadingHeader(
+      Eq("Content-Length"), Eq("9999999999999999999999999999999999999")));
+  EXPECT_CALL(visitor_, OnLeadingHeadersComplete(Eq(true)));
+
+  ASSERT_TRUE(parser_.ProcessInput(
+      "HTTP/1.1 200 OK\r\n"
+      "Content-Length: 9999999999999999999999999999999999999\r\n"
+      "\r\n"
+      "bogus bogus bogus\r\n"));
+}
+
+// We should be able to handle pretty big content lengths without overflowing,
+// however!  Otherwise, downloads of extremely large files may fail.  Here, we
+// test (the beginning of) a response that will contain a 5TB of data.
+TEST_F(HttpResponseParserTest, LargeContentLength) {
+  EXPECT_CALL(visitor_, OnStatusLine(Eq("HTTP/1.1"), Eq("200"), Eq("OK")));
+  EXPECT_CALL(visitor_, OnLeadingHeader(
+      Eq("Content-Length"), Eq("5497558138880")));
+  EXPECT_CALL(visitor_, OnLeadingHeadersComplete(Eq(false)));
+  EXPECT_CALL(visitor_, OnData(Eq("This is the beginning of 5TB of data."),
+                               Eq(false)));
+
+  ASSERT_TRUE(parser_.ProcessInput(
+      "HTTP/1.1 200 OK\r\n"
+      "Content-Length: 5497558138880\r\n"
+      "\r\n"
+      "This is the beginning of 5TB of data."));
+  ASSERT_EQ(5497558138843uLL, parser_.GetRemainingBytesForTest());
+}
+
+}  // namespace

Added: httpd/httpd/trunk/modules/spdy/common/http_response_visitor_interface.cc
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/spdy/common/http_response_visitor_interface.cc?rev=1590597&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/spdy/common/http_response_visitor_interface.cc (added)
+++ httpd/httpd/trunk/modules/spdy/common/http_response_visitor_interface.cc Mon Apr 28 10:55:17 2014
@@ -0,0 +1,22 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "mod_spdy/common/http_response_visitor_interface.h"
+
+namespace mod_spdy {
+
+HttpResponseVisitorInterface::HttpResponseVisitorInterface() {}
+HttpResponseVisitorInterface::~HttpResponseVisitorInterface() {}
+
+}  // namespace mod_spdy

Added: httpd/httpd/trunk/modules/spdy/common/http_response_visitor_interface.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/spdy/common/http_response_visitor_interface.h?rev=1590597&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/spdy/common/http_response_visitor_interface.h (added)
+++ httpd/httpd/trunk/modules/spdy/common/http_response_visitor_interface.h Mon Apr 28 10:55:17 2014
@@ -0,0 +1,58 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef MOD_SPDY_COMMON_HTTP_RESPONSE_VISITOR_INTERFACE_H_
+#define MOD_SPDY_COMMON_HTTP_RESPONSE_VISITOR_INTERFACE_H_
+
+#include "base/basictypes.h"
+#include "base/strings/string_piece.h"
+
+namespace mod_spdy {
+
+// Interface that gets called back as an HTTP response is visited.
+class HttpResponseVisitorInterface {
+ public:
+  HttpResponseVisitorInterface();
+  virtual ~HttpResponseVisitorInterface();
+
+  // Called when an HTTP response status line is visited.  Indicates that a new
+  // HTTP response is being visited.
+  virtual void OnStatusLine(const base::StringPiece& version,
+                            const base::StringPiece& status_code,
+                            const base::StringPiece& status_phrase) = 0;
+
+  // Called zero or more times, once for each leading (i.e. normal, not
+  // trailing) HTTP header.  This is called after OnStatusLine but before
+  // OnLeadingHeadersComplete.
+  virtual void OnLeadingHeader(const base::StringPiece& key,
+                               const base::StringPiece& value) = 0;
+
+  // Called after the leading HTTP headers have been visited.  This will be
+  // called exactly once when the leading headers are done (even if there were
+  // no leading headers).  If the `fin` argument is true, the response is now
+  // complete (i.e. it has no body) and no more methods will be called.
+  virtual void OnLeadingHeadersComplete(bool fin) = 0;
+
+  // Called zero or more times, after OnLeadingHeadersComplete.  If the `fin`
+  // argument is true, the response is now complete and no more methods will be
+  // called.
+  virtual void OnData(const base::StringPiece& data, bool fin) = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HttpResponseVisitorInterface);
+};
+
+}  // namespace mod_spdy
+
+#endif  // MOD_SPDY_COMMON_HTTP_RESPONSE_VISITOR_INTERFACE_H_

Propchange: httpd/httpd/trunk/modules/spdy/common/http_response_visitor_interface.h
------------------------------------------------------------------------------
    svn:eol-style = native

Added: httpd/httpd/trunk/modules/spdy/common/http_string_builder.cc
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/spdy/common/http_string_builder.cc?rev=1590597&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/spdy/common/http_string_builder.cc (added)
+++ httpd/httpd/trunk/modules/spdy/common/http_string_builder.cc Mon Apr 28 10:55:17 2014
@@ -0,0 +1,122 @@
+// Copyright 2012 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "mod_spdy/common/http_string_builder.h"
+
+#include <string>
+
+#include "base/logging.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/stringprintf.h"
+
+namespace {
+
+void OnHeader(const base::StringPiece& key,
+              const base::StringPiece& value,
+              std::string* output) {
+  key.AppendToString(output);
+  output->append(": ");
+  value.AppendToString(output);
+  output->append("\r\n");
+}
+
+}  // namespace
+
+namespace mod_spdy {
+
+HttpStringBuilder::HttpStringBuilder(std::string* str)
+    : string_(str), state_(REQUEST_LINE) {
+  CHECK(string_);
+}
+
+HttpStringBuilder::~HttpStringBuilder() {}
+
+void HttpStringBuilder::OnRequestLine(const base::StringPiece& method,
+                                      const base::StringPiece& path,
+                                      const base::StringPiece& version) {
+  DCHECK(state_ == REQUEST_LINE);
+  state_ = LEADING_HEADERS;
+  method.AppendToString(string_);
+  string_->push_back(' ');
+  path.AppendToString(string_);
+  string_->push_back(' ');
+  version.AppendToString(string_);
+  string_->append("\r\n");
+}
+
+void HttpStringBuilder::OnLeadingHeader(const base::StringPiece& key,
+                                        const base::StringPiece& value) {
+  DCHECK(state_ == LEADING_HEADERS);
+  OnHeader(key, value, string_);
+}
+
+void HttpStringBuilder::OnLeadingHeadersComplete() {
+  DCHECK(state_ == LEADING_HEADERS);
+  state_ = LEADING_HEADERS_COMPLETE;
+  string_->append("\r\n");
+}
+
+void HttpStringBuilder::OnRawData(const base::StringPiece& data) {
+  DCHECK(state_ == LEADING_HEADERS_COMPLETE || state_ == RAW_DATA);
+  state_ = RAW_DATA;
+  data.AppendToString(string_);
+}
+
+void HttpStringBuilder::OnDataChunk(const base::StringPiece& data) {
+  DCHECK(state_ == LEADING_HEADERS_COMPLETE || state_ == DATA_CHUNKS);
+  state_ = DATA_CHUNKS;
+  // Encode the data as an HTTP data chunk.  See RFC 2616 section 3.6.1 for
+  // details.
+  base::StringAppendF(string_, "%lX\r\n",
+                      static_cast<unsigned long>(data.size()));
+  data.AppendToString(string_);
+  string_->append("\r\n");
+}
+
+void HttpStringBuilder::OnDataChunksComplete() {
+  DCHECK(state_ == DATA_CHUNKS);
+  state_ = DATA_CHUNKS_COMPLETE;
+  // Indicate that there are no more HTTP data chunks coming.  See RFC 2616
+  // section 3.6.1 for details.
+  string_->append("0\r\n");
+}
+
+void HttpStringBuilder::OnTrailingHeader(const base::StringPiece& key,
+                                         const base::StringPiece& value) {
+  DCHECK(state_ == DATA_CHUNKS_COMPLETE || state_ == TRAILING_HEADERS);
+  state_ = TRAILING_HEADERS;
+  OnHeader(key, value, string_);
+}
+
+void HttpStringBuilder::OnTrailingHeadersComplete() {
+  DCHECK(state_ == TRAILING_HEADERS);
+  state_ = TRAILING_HEADERS_COMPLETE;
+  string_->append("\r\n");
+}
+
+void HttpStringBuilder::OnComplete() {
+  DCHECK(state_ == LEADING_HEADERS_COMPLETE ||
+         state_ == RAW_DATA ||
+         state_ == DATA_CHUNKS_COMPLETE ||
+         state_ == TRAILING_HEADERS_COMPLETE);
+  if (state_ == DATA_CHUNKS_COMPLETE) {
+    // In this case, there have been data chunks, but we haven't called
+    // OnTrailingHeadersComplete because there were no trailing headers.  We
+    // still need an empty line to indicate the end of the request.
+    string_->append("\r\n");
+  }
+  state_ = COMPLETE;
+}
+
+}  // namespace mod_spdy

Added: httpd/httpd/trunk/modules/spdy/common/http_string_builder.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/spdy/common/http_string_builder.h?rev=1590597&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/spdy/common/http_string_builder.h (added)
+++ httpd/httpd/trunk/modules/spdy/common/http_string_builder.h Mon Apr 28 10:55:17 2014
@@ -0,0 +1,69 @@
+// Copyright 2012 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef MOD_SPDY_COMMON_HTTP_STRING_BUILDER_H_
+#define MOD_SPDY_COMMON_HTTP_STRING_BUILDER_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "mod_spdy/common/http_request_visitor_interface.h"
+
+namespace mod_spdy {
+
+// An HttpRequestVisitorInterface class that appends to a std::string.
+class HttpStringBuilder : public HttpRequestVisitorInterface {
+ public:
+  explicit HttpStringBuilder(std::string* str);
+  virtual ~HttpStringBuilder();
+
+  bool is_complete() const { return state_ == COMPLETE; }
+
+  // HttpRequestVisitorInterface methods:
+  virtual void OnRequestLine(const base::StringPiece& method,
+                             const base::StringPiece& path,
+                             const base::StringPiece& version);
+  virtual void OnLeadingHeader(const base::StringPiece& key,
+                            const base::StringPiece& value);
+  virtual void OnLeadingHeadersComplete();
+  virtual void OnRawData(const base::StringPiece& data);
+  virtual void OnDataChunk(const base::StringPiece& data);
+  virtual void OnDataChunksComplete();
+  virtual void OnTrailingHeader(const base::StringPiece& key,
+                                const base::StringPiece& value);
+  virtual void OnTrailingHeadersComplete();
+  virtual void OnComplete();
+
+ private:
+  enum State {
+    REQUEST_LINE,
+    LEADING_HEADERS,
+    LEADING_HEADERS_COMPLETE,
+    RAW_DATA,
+    DATA_CHUNKS,
+    DATA_CHUNKS_COMPLETE,
+    TRAILING_HEADERS,
+    TRAILING_HEADERS_COMPLETE,
+    COMPLETE
+  };
+
+  std::string* const string_;
+  State state_;
+
+  DISALLOW_COPY_AND_ASSIGN(HttpStringBuilder);
+};
+
+}  // namespace mod_spdy
+
+#endif  // MOD_SPDY_COMMON_HTTP_STRING_BUILDER_H_

Propchange: httpd/httpd/trunk/modules/spdy/common/http_string_builder.h
------------------------------------------------------------------------------
    svn:eol-style = native

Added: httpd/httpd/trunk/modules/spdy/common/http_to_spdy_converter.cc
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/spdy/common/http_to_spdy_converter.cc?rev=1590597&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/spdy/common/http_to_spdy_converter.cc (added)
+++ httpd/httpd/trunk/modules/spdy/common/http_to_spdy_converter.cc Mon Apr 28 10:55:17 2014
@@ -0,0 +1,192 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "mod_spdy/common/http_to_spdy_converter.h"
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/strings/string_piece.h"
+#include "mod_spdy/common/http_response_visitor_interface.h"
+#include "mod_spdy/common/protocol_util.h"
+#include "net/spdy/spdy_protocol.h"
+
+namespace {
+
+// This is the number of bytes we want to send per data frame.  We never send
+// data frames larger than this, but we might send smaller ones if we have to
+// flush early.
+// TODO The SPDY folks say that smallish (~4kB) data frames are good; however,
+//      we should experiment later on to see what value here performs the best.
+const size_t kTargetDataFrameBytes = 4096;
+
+}  // namespace
+
+namespace mod_spdy {
+
+class HttpToSpdyConverter::ConverterImpl : public HttpResponseVisitorInterface{
+ public:
+  ConverterImpl(spdy::SpdyVersion spdy_version, SpdyReceiver* receiver);
+  virtual ~ConverterImpl();
+
+  void Flush();
+
+  // HttpResponseVisitorInterface methods:
+  virtual void OnStatusLine(const base::StringPiece& version,
+                            const base::StringPiece& status_code,
+                            const base::StringPiece& status_phrase);
+  virtual void OnLeadingHeader(const base::StringPiece& key,
+                               const base::StringPiece& value);
+  virtual void OnLeadingHeadersComplete(bool fin);
+  virtual void OnData(const base::StringPiece& data, bool fin);
+
+ private:
+  void SendDataIfNecessary(bool flush, bool fin);
+  void SendDataFrame(const char* data, size_t size, bool flag_fin);
+
+  const spdy::SpdyVersion spdy_version_;
+  SpdyReceiver* const receiver_;
+  net::SpdyHeaderBlock headers_;
+  std::string data_buffer_;
+  bool sent_flag_fin_;
+
+  DISALLOW_COPY_AND_ASSIGN(ConverterImpl);
+};
+
+HttpToSpdyConverter::SpdyReceiver::SpdyReceiver() {}
+
+HttpToSpdyConverter::SpdyReceiver::~SpdyReceiver() {}
+
+HttpToSpdyConverter::HttpToSpdyConverter(spdy::SpdyVersion spdy_version,
+                                         SpdyReceiver* receiver)
+    : impl_(new ConverterImpl(spdy_version, receiver)),
+      parser_(impl_.get()) {}
+
+HttpToSpdyConverter::~HttpToSpdyConverter() {}
+
+bool HttpToSpdyConverter::ProcessInput(base::StringPiece input_data) {
+  return parser_.ProcessInput(input_data);
+}
+
+void HttpToSpdyConverter::Flush() {
+  impl_->Flush();
+}
+
+HttpToSpdyConverter::ConverterImpl::ConverterImpl(
+    spdy::SpdyVersion spdy_version, SpdyReceiver* receiver)
+    : spdy_version_(spdy_version),
+      receiver_(receiver),
+      sent_flag_fin_(false) {
+  DCHECK_NE(spdy::SPDY_VERSION_NONE, spdy_version);
+  CHECK(receiver_);
+}
+
+HttpToSpdyConverter::ConverterImpl::~ConverterImpl() {}
+
+void HttpToSpdyConverter::ConverterImpl::Flush() {
+  SendDataIfNecessary(true,  // true = do flush
+                      false);  // false = not fin yet
+}
+
+void HttpToSpdyConverter::ConverterImpl::OnStatusLine(
+    const base::StringPiece& version,
+    const base::StringPiece& status_code,
+    const base::StringPiece& status_phrase) {
+  DCHECK(headers_.empty());
+  const bool spdy2 = spdy_version_ < spdy::SPDY_VERSION_3;
+  headers_[spdy2 ? spdy::kSpdy2Version : spdy::kSpdy3Version] =
+      version.as_string();
+  headers_[spdy2 ? spdy::kSpdy2Status : spdy::kSpdy3Status] =
+      status_code.as_string();
+}
+
+void HttpToSpdyConverter::ConverterImpl::OnLeadingHeader(
+    const base::StringPiece& key,
+    const base::StringPiece& value) {
+  // Filter out headers that are invalid in SPDY.
+  if (IsInvalidSpdyResponseHeader(key)) {
+    return;
+  }
+  MergeInHeader(key, value, &headers_);
+}
+
+void HttpToSpdyConverter::ConverterImpl::OnLeadingHeadersComplete(bool fin) {
+  if (sent_flag_fin_) {
+    LOG(DFATAL) << "Trying to send headers after sending FLAG_FIN";
+    return;
+  }
+  if (fin) {
+    sent_flag_fin_ = true;
+  }
+  receiver_->ReceiveSynReply(&headers_, fin);
+  headers_.clear();
+}
+
+void HttpToSpdyConverter::ConverterImpl::OnData(const base::StringPiece& data,
+                                                bool fin) {
+  data.AppendToString(&data_buffer_);
+  SendDataIfNecessary(false, fin);  // false = don't flush
+}
+
+void HttpToSpdyConverter::ConverterImpl::SendDataIfNecessary(bool flush,
+                                                             bool fin) {
+  // If we have (strictly) more than one frame's worth of data waiting, send it
+  // down the filter chain, kTargetDataFrameBytes bytes at a time.  If we are
+  // left with _exactly_ kTargetDataFrameBytes bytes of data, we'll deal with
+  // that in the next code block (see the comment there to explain why).
+  if (data_buffer_.size() > kTargetDataFrameBytes) {
+    const char* start = data_buffer_.data();
+    size_t size = data_buffer_.size();
+    while (size > kTargetDataFrameBytes) {
+      SendDataFrame(start, kTargetDataFrameBytes, false);
+      start += kTargetDataFrameBytes;
+      size -= kTargetDataFrameBytes;
+    }
+    data_buffer_.erase(0, data_buffer_.size() - size);
+  }
+  DCHECK(data_buffer_.size() <= kTargetDataFrameBytes);
+
+  // We may still have some leftover data.  We need to send another data frame
+  // now (rather than waiting for a full kTargetDataFrameBytes) if:
+  //   1) This is the end of the response,
+  //   2) we're supposed to flush and the buffer is nonempty, or
+  //   3) we still have a full data frame's worth in the buffer.
+  //
+  // Note that because of the previous code block, condition (3) will only be
+  // true if we have exactly kTargetDataFrameBytes of data.  However, dealing
+  // with that case here instead of in the above block makes it easier to make
+  // sure we correctly set FLAG_FIN on the final data frame, which is why the
+  // above block uses a strict, > comparison rather than a non-strict, >=
+  // comparison.
+  if (fin || (flush && !data_buffer_.empty()) ||
+      data_buffer_.size() >= kTargetDataFrameBytes) {
+    SendDataFrame(data_buffer_.data(), data_buffer_.size(), fin);
+    data_buffer_.clear();
+  }
+}
+
+void HttpToSpdyConverter::ConverterImpl::SendDataFrame(
+    const char* data, size_t size, bool flag_fin) {
+  if (sent_flag_fin_) {
+    LOG(DFATAL) << "Trying to send data after sending FLAG_FIN";
+    return;
+  }
+  if (flag_fin) {
+    sent_flag_fin_ = true;
+  }
+  receiver_->ReceiveData(base::StringPiece(data, size), flag_fin);
+}
+
+}  // namespace mod_spdy

Added: httpd/httpd/trunk/modules/spdy/common/http_to_spdy_converter.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/spdy/common/http_to_spdy_converter.h?rev=1590597&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/spdy/common/http_to_spdy_converter.h (added)
+++ httpd/httpd/trunk/modules/spdy/common/http_to_spdy_converter.h Mon Apr 28 10:55:17 2014
@@ -0,0 +1,78 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef MOD_SPDY_COMMON_HTTP_TO_SPDY_CONVERTER_H_
+#define MOD_SPDY_COMMON_HTTP_TO_SPDY_CONVERTER_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_piece.h"
+#include "mod_spdy/common/http_response_parser.h"
+#include "mod_spdy/common/protocol_util.h"
+#include "net/spdy/spdy_framer.h"  // for SpdyHeaderBlock
+
+namespace mod_spdy {
+
+// Parses incoming HTTP response data and converts it into equivalent SPDY
+// frame data.
+class HttpToSpdyConverter {
+ public:
+  // Interface for the class that will receive frame data from the converter.
+  class SpdyReceiver {
+   public:
+    SpdyReceiver();
+    virtual ~SpdyReceiver();
+
+    // Receive a SYN_REPLY frame with the given headers.  The callee is free to
+    // mutate the headers map (e.g. to add an extra header) before forwarding
+    // it on, but the pointer will not remain valid after this method returns.
+    virtual void ReceiveSynReply(net::SpdyHeaderBlock* headers,
+                                 bool flag_fin) = 0;
+
+    // Receive a DATA frame with the given payload.  The data pointer will not
+    // remain valid after this method returns.
+    virtual void ReceiveData(base::StringPiece data, bool flag_fin) = 0;
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(SpdyReceiver);
+  };
+
+  // Create a converter that will send frame data to the given receiver.  The
+  // converter does *not* gain ownership of the receiver.
+  HttpToSpdyConverter(spdy::SpdyVersion spdy_version, SpdyReceiver* receiver);
+  ~HttpToSpdyConverter();
+
+  // Parse and process the next chunk of input; return true on success, false
+  // on failure.
+  bool ProcessInput(base::StringPiece input_data);
+  bool ProcessInput(const char* data, size_t size) {
+    return ProcessInput(base::StringPiece(data, size));
+  }
+
+  // Flush out any buffered data.
+  void Flush();
+
+ private:
+  class ConverterImpl;
+  scoped_ptr<ConverterImpl> impl_;
+  HttpResponseParser parser_;
+
+  DISALLOW_COPY_AND_ASSIGN(HttpToSpdyConverter);
+};
+
+}  // namespace mod_spdy
+
+#endif  // MOD_SPDY_COMMON_HTTP_TO_SPDY_CONVERTER_H_

Propchange: httpd/httpd/trunk/modules/spdy/common/http_to_spdy_converter.h
------------------------------------------------------------------------------
    svn:eol-style = native

Added: httpd/httpd/trunk/modules/spdy/common/http_to_spdy_converter_test.cc
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/spdy/common/http_to_spdy_converter_test.cc?rev=1590597&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/spdy/common/http_to_spdy_converter_test.cc (added)
+++ httpd/httpd/trunk/modules/spdy/common/http_to_spdy_converter_test.cc Mon Apr 28 10:55:17 2014
@@ -0,0 +1,259 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "mod_spdy/common/http_to_spdy_converter.h"
+
+#include "base/strings/string_piece.h"
+#include "mod_spdy/common/protocol_util.h"
+#include "net/spdy/spdy_protocol.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::DeleteArg;
+using testing::Eq;
+using testing::InSequence;
+using testing::Pointee;
+
+namespace {
+
+class MockSpdyReceiver : public mod_spdy::HttpToSpdyConverter::SpdyReceiver {
+ public:
+  MOCK_METHOD2(ReceiveSynReply, void(net::SpdyHeaderBlock* headers,
+                                     bool flag_fin));
+  MOCK_METHOD2(ReceiveData, void(base::StringPiece data, bool flag_fin));
+};
+
+class HttpToSpdyConverterTest :
+      public testing::TestWithParam<mod_spdy::spdy::SpdyVersion> {
+ public:
+  HttpToSpdyConverterTest() : converter_(GetParam(), &receiver_) {}
+
+ protected:
+  const char* status_header_name() const {
+    return (GetParam() < mod_spdy::spdy::SPDY_VERSION_3 ?
+            mod_spdy::spdy::kSpdy2Status :
+            mod_spdy::spdy::kSpdy3Status);
+  }
+  const char* version_header_name() const {
+    return (GetParam() < mod_spdy::spdy::SPDY_VERSION_3 ?
+            mod_spdy::spdy::kSpdy2Version :
+            mod_spdy::spdy::kSpdy3Version);
+  }
+
+  MockSpdyReceiver receiver_;
+  mod_spdy::HttpToSpdyConverter converter_;
+  net::SpdyHeaderBlock expected_headers_;
+};
+
+// Simple response with a small payload.  We should get a SYN_REPLY and a DATA
+// frame.
+TEST_P(HttpToSpdyConverterTest, SimpleWithContentLength) {
+  expected_headers_[status_header_name()] = "200";
+  expected_headers_[version_header_name()] = "HTTP/1.1";
+  expected_headers_[mod_spdy::http::kContentLength] = "14";
+  expected_headers_[mod_spdy::http::kContentType] = "text/plain";
+  expected_headers_["x-whatever"] = "foobar";
+
+  InSequence seq;
+  EXPECT_CALL(receiver_, ReceiveSynReply(Pointee(Eq(expected_headers_)),
+                                         Eq(false)));
+  EXPECT_CALL(receiver_, ReceiveData(Eq("Hello, world!\n"), Eq(true)));
+
+  ASSERT_TRUE(converter_.ProcessInput(
+      "HTTP/1.1 200 OK\r\n"
+      "Content-Length: 14\r\n"
+      "Content-Type:      text/plain\r\n"
+      "X-Whatever:foobar\r\n"
+      "\r\n"
+      "Hello, world!\n"
+      "\r\n"));
+}
+
+// The data arrives in two chunks, but they're small, so we should consolidate
+// them into a single DATA frame.
+TEST_P(HttpToSpdyConverterTest, SimpleWithChunking) {
+  expected_headers_[status_header_name()] = "200";
+  expected_headers_[version_header_name()] = "HTTP/1.1";
+  expected_headers_[mod_spdy::http::kContentType] = "text/plain";
+
+  InSequence seq;
+  EXPECT_CALL(receiver_, ReceiveSynReply(Pointee(Eq(expected_headers_)),
+                                         Eq(false)));
+  EXPECT_CALL(receiver_, ReceiveData(Eq("Hello, world!\n"), Eq(true)));
+
+  ASSERT_TRUE(converter_.ProcessInput(
+      "HTTP/1.1 200 OK\r\n"
+      "Connection: Keep-Alive\r\n"
+      "Content-Type: text/plain\r\n"
+      "Keep-Alive: timeout=10, max=5\r\n"
+      "Transfer-Encoding: chunked\r\n"
+      "\r\n"
+      "6\r\n"
+      "Hello,\r\n"
+      "8\r\n"
+      " world!\n\r\n"
+      "0\r\n"
+      "\r\n"));
+}
+
+// Test that we don't get tripped up if there is garbage after the end of
+// a chunked message.
+TEST_P(HttpToSpdyConverterTest, ChunkedEncodingWithTrailingGarbage) {
+  expected_headers_[status_header_name()] = "200";
+  expected_headers_[version_header_name()] = "HTTP/1.1";
+  expected_headers_[mod_spdy::http::kContentType] = "text/plain";
+
+  InSequence seq;
+  EXPECT_CALL(receiver_, ReceiveSynReply(Pointee(Eq(expected_headers_)),
+                                         Eq(false)));
+  EXPECT_CALL(receiver_, ReceiveData(Eq("Hello, world!\n"), Eq(true)));
+
+  ASSERT_TRUE(converter_.ProcessInput(
+      "HTTP/1.1 200 OK\r\n"
+      "Content-Type: text/plain\r\n"
+      "Transfer-Encoding: chunked\r\n"
+      "\r\n"
+      "E\r\n"
+      "Hello, world!\n\r\n"
+      "0\r\n"
+      "0\r\n" // multiple last-chunks
+      "\r\n\x1bGaRbAgE"));  // and also some garbage bytes
+}
+
+// No response body, so we should get the FLAG_FIN on the SYN_REPLY, and no
+// DATA frames.
+TEST_P(HttpToSpdyConverterTest, NoResponseBody) {
+  expected_headers_[status_header_name()] = "301";
+  expected_headers_[version_header_name()] = "HTTP/1.1";
+  expected_headers_["location"] = "https://www.example.com/";
+
+  InSequence seq;
+  EXPECT_CALL(receiver_, ReceiveSynReply(Pointee(Eq(expected_headers_)),
+                                         Eq(true)));
+
+  ASSERT_TRUE(converter_.ProcessInput(
+      "HTTP/1.1 301 Moved permenantly\r\n"
+      "Location: https://www.example.com/\r\n"
+      "\r\n"));
+}
+
+// Simple response with a large payload.  We should get a SYN_REPLY and
+// multiple DATA frames.
+TEST_P(HttpToSpdyConverterTest, BreakUpLargeDataIntoMultipleFrames) {
+  expected_headers_[status_header_name()] = "200";
+  expected_headers_[version_header_name()] = "HTTP/1.1";
+  expected_headers_[mod_spdy::http::kContentLength] = "10000";
+  expected_headers_[mod_spdy::http::kContentType] = "text/plain";
+
+  InSequence seq;
+  EXPECT_CALL(receiver_, ReceiveSynReply(Pointee(Eq(expected_headers_)),
+                                         Eq(false)));
+  EXPECT_CALL(receiver_, ReceiveData(Eq(std::string(4096, 'x')), Eq(false)));
+  EXPECT_CALL(receiver_, ReceiveData(Eq(std::string(4096, 'x')), Eq(false)));
+  EXPECT_CALL(receiver_, ReceiveData(Eq(std::string(1808, 'x')), Eq(true)));
+
+  ASSERT_TRUE(converter_.ProcessInput(
+      "HTTP/1.1 200 OK\r\n"
+      "Content-Length: 10000\r\n"
+      "Content-Type: text/plain\r\n"
+      "\r\n" +
+      std::string(10000, 'x') +
+      "\r\n"));
+}
+
+// Test that we buffer data until we get the full frame.
+TEST_P(HttpToSpdyConverterTest, BufferUntilWeHaveACompleteFrame) {
+  expected_headers_[status_header_name()] = "200";
+  expected_headers_[version_header_name()] = "HTTP/1.1";
+  expected_headers_[mod_spdy::http::kContentLength] = "4096";
+  expected_headers_[mod_spdy::http::kContentType] = "text/plain";
+
+  InSequence seq;
+  // Send some of the headers.  We shouldn't get anything out yet.
+  ASSERT_TRUE(converter_.ProcessInput(
+      "HTTP/1.1 200 OK\r\n"
+      "Content-Length: 4096\r\n"));
+  // Send the rest of the headers, and some of the data.  We should get the
+  // SYN_REPLY now, but no data yet.
+  EXPECT_CALL(receiver_, ReceiveSynReply(Pointee(Eq(expected_headers_)),
+                                         Eq(false)));
+  ASSERT_TRUE(converter_.ProcessInput(
+      "Content-Type: text/plain\r\n"
+      "\r\n" +
+      std::string(2000, 'x')));
+  // Send some more data, but still not enough for a full frame.
+  ASSERT_TRUE(converter_.ProcessInput(std::string(2000, 'x')));
+  // Send the last of the data.  We should finally get the one DATA frame.
+  EXPECT_CALL(receiver_, ReceiveData(Eq(std::string(4096, 'x')), Eq(true)));
+  ASSERT_TRUE(converter_.ProcessInput(std::string(96, 'x')));
+}
+
+// Test that we flush the buffer when told.
+TEST_P(HttpToSpdyConverterTest, RespectFlushes) {
+  expected_headers_[status_header_name()] = "200";
+  expected_headers_[version_header_name()] = "HTTP/1.1";
+  expected_headers_[mod_spdy::http::kContentLength] = "4096";
+  expected_headers_[mod_spdy::http::kContentType] = "text/plain";
+
+  InSequence seq;
+  // Send the headers and some of the data (not enough for a full frame).  We
+  // should get the headers out, but no data yet.
+  EXPECT_CALL(receiver_, ReceiveSynReply(Pointee(Eq(expected_headers_)),
+                                         Eq(false)));
+  ASSERT_TRUE(converter_.ProcessInput(
+      "HTTP/1.1 200 OK\r\n"
+      "Content-Length: 4096\r\n"
+      "Content-Type: text/plain\r\n"
+      "\r\n" +
+      std::string(2000, 'x')));
+  // Perform a flush.  We should get the data sent so far.
+  EXPECT_CALL(receiver_, ReceiveData(Eq(std::string(2000, 'x')), Eq(false)));
+  converter_.Flush();
+  // Send the rest of the data.  We should get out a second DATA frame, with
+  // FLAG_FIN set.
+  EXPECT_CALL(receiver_, ReceiveData(Eq(std::string(2096, 'y')), Eq(true)));
+  ASSERT_TRUE(converter_.ProcessInput(std::string(2096, 'y')));
+}
+
+// Test that we flush the buffer when told.
+TEST_P(HttpToSpdyConverterTest, FlushAfterEndDoesNothing) {
+  expected_headers_[status_header_name()] = "200";
+  expected_headers_[version_header_name()] = "HTTP/1.1";
+  expected_headers_[mod_spdy::http::kContentLength] = "6";
+  expected_headers_[mod_spdy::http::kContentType] = "text/plain";
+
+  InSequence seq;
+  EXPECT_CALL(receiver_, ReceiveSynReply(Pointee(Eq(expected_headers_)),
+                                         Eq(false)));
+  EXPECT_CALL(receiver_, ReceiveData(Eq("foobar"), Eq(true)));
+  ASSERT_TRUE(converter_.ProcessInput(
+      "HTTP/1.1 200 OK\r\n"
+      "Content-Length: 6\r\n"
+      "Content-Type: text/plain\r\n"
+      "\r\n"
+      "foobar"));
+  // Flushing after we're done (even multiple times) should be permitted, but
+  // should do nothing.
+  converter_.Flush();
+  converter_.Flush();
+  converter_.Flush();
+}
+
+// Run each test over both SPDY v2 and SPDY v3.
+INSTANTIATE_TEST_CASE_P(Spdy2And3, HttpToSpdyConverterTest, testing::Values(
+    mod_spdy::spdy::SPDY_VERSION_2, mod_spdy::spdy::SPDY_VERSION_3,
+    mod_spdy::spdy::SPDY_VERSION_3_1));
+
+}  // namespace



Mime
View raw message