httpd-cvs mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From j..@apache.org
Subject svn commit: r1590597 [3/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/filters/spdy_to_http_filter_test.cc
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/spdy/apache/filters/spdy_to_http_filter_test.cc?rev=1590597&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/spdy/apache/filters/spdy_to_http_filter_test.cc (added)
+++ httpd/httpd/trunk/modules/spdy/apache/filters/spdy_to_http_filter_test.cc Mon Apr 28 10:55:17 2014
@@ -0,0 +1,663 @@
+// 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/apache/filters/spdy_to_http_filter.h"
+
+#include <string>
+
+#include "httpd.h"
+#include "apr_buckets.h"
+#include "apr_tables.h"
+#include "util_filter.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_piece.h"
+#include "mod_spdy/apache/pool_util.h"
+#include "mod_spdy/common/protocol_util.h"
+#include "mod_spdy/common/shared_flow_control_window.h"
+#include "mod_spdy/common/spdy_frame_priority_queue.h"
+#include "mod_spdy/common/spdy_stream.h"
+#include "mod_spdy/common/testing/spdy_frame_matchers.h"
+#include "net/spdy/spdy_protocol.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class MockSpdyServerPushInterface : public mod_spdy::SpdyServerPushInterface {
+ public:
+    MOCK_METHOD4(StartServerPush,
+                 mod_spdy::SpdyServerPushInterface::PushStatus(
+                     net::SpdyStreamId associated_stream_id,
+                     int32 server_push_depth,
+                     net::SpdyPriority priority,
+                     const net::SpdyNameValueBlock& request_headers));
+};
+
+class SpdyToHttpFilterTest :
+      public testing::TestWithParam<mod_spdy::spdy::SpdyVersion> {
+ public:
+  SpdyToHttpFilterTest()
+      : spdy_version_(GetParam()),
+        stream_id_(1),
+        priority_(0u),
+        shared_window_(net::kSpdyStreamInitialWindowSize,
+                       net::kSpdyStreamInitialWindowSize),
+        stream_(spdy_version_, stream_id_, 0, 0, priority_,
+                net::kSpdyStreamInitialWindowSize, &output_queue_,
+                &shared_window_, &pusher_),
+        spdy_to_http_filter_(&stream_) {
+    bucket_alloc_ = apr_bucket_alloc_create(local_.pool());
+    connection_ = static_cast<conn_rec*>(
+        apr_pcalloc(local_.pool(), sizeof(conn_rec)));
+    connection_->pool = local_.pool();
+    connection_->bucket_alloc = bucket_alloc_;
+    ap_filter_ = static_cast<ap_filter_t*>(
+        apr_pcalloc(local_.pool(), sizeof(ap_filter_t)));
+    ap_filter_->c = connection_;
+    brigade_ = apr_brigade_create(local_.pool(), bucket_alloc_);
+  }
+
+ protected:
+  void PostSynStreamFrame(bool fin, const net::SpdyNameValueBlock& headers) {
+    scoped_ptr<net::SpdySynStreamIR> frame(
+        new net::SpdySynStreamIR(stream_id_));
+    frame->set_priority(priority_);
+    frame->set_fin(fin);
+    frame->GetMutableNameValueBlock()->insert(headers.begin(), headers.end());
+    stream_.PostInputFrame(frame.release());
+  }
+
+  void PostHeadersFrame(bool fin, const net::SpdyNameValueBlock& headers) {
+    scoped_ptr<net::SpdyHeadersIR> frame(new net::SpdyHeadersIR(stream_id_));
+    frame->set_fin(fin);
+    frame->GetMutableNameValueBlock()->insert(headers.begin(), headers.end());
+    stream_.PostInputFrame(frame.release());
+  }
+
+  void PostDataFrame(bool fin, const base::StringPiece& payload) {
+    scoped_ptr<net::SpdyDataIR> frame(
+        new net::SpdyDataIR(stream_id_, payload));
+    frame->set_fin(fin);
+    EXPECT_TRUE(shared_window_.OnReceiveInputData(payload.size()));
+    stream_.PostInputFrame(frame.release());
+  }
+
+  apr_status_t Read(ap_input_mode_t mode, apr_read_type_e block,
+                    apr_off_t readbytes) {
+    return spdy_to_http_filter_.Read(ap_filter_, brigade_,
+                                     mode, block, readbytes);
+  }
+
+  void ExpectTransientBucket(const std::string& expected) {
+    ASSERT_FALSE(APR_BRIGADE_EMPTY(brigade_))
+        << "Expected TRANSIENT bucket, but brigade is empty.";
+    apr_bucket* bucket = APR_BRIGADE_FIRST(brigade_);
+    ASSERT_TRUE(APR_BUCKET_IS_TRANSIENT(bucket))
+        << "Expected TRANSIENT bucket, but found " << bucket->type->name
+        << " bucket.";
+    const char* data = NULL;
+    apr_size_t size = 0;
+    ASSERT_EQ(APR_SUCCESS, apr_bucket_read(
+        bucket, &data, &size, APR_NONBLOCK_READ));
+    EXPECT_EQ(expected, std::string(data, size));
+    apr_bucket_delete(bucket);
+  }
+
+  void ExpectEosBucket() {
+    ASSERT_FALSE(APR_BRIGADE_EMPTY(brigade_))
+        << "Expected EOS bucket, but brigade is empty.";
+    apr_bucket* bucket = APR_BRIGADE_FIRST(brigade_);
+    ASSERT_TRUE(APR_BUCKET_IS_EOS(bucket))
+        << "Expected EOS bucket, but found " << bucket->type->name
+        << " bucket.";
+    apr_bucket_delete(bucket);
+  }
+
+  void ExpectEndOfBrigade() {
+    ASSERT_TRUE(APR_BRIGADE_EMPTY(brigade_))
+        << "Expected brigade to be empty, but found "
+        << APR_BRIGADE_FIRST(brigade_)->type->name << " bucket.";
+    ASSERT_EQ(APR_SUCCESS, apr_brigade_cleanup(brigade_));
+  }
+
+  void ExpectRstStream(net::SpdyRstStreamStatus status) {
+    net::SpdyFrameIR* raw_frame;
+    ASSERT_TRUE(output_queue_.Pop(&raw_frame))
+        << "Expected RST_STREAM frame, but output queue is empty.";
+    scoped_ptr<net::SpdyFrameIR> frame(raw_frame);
+    EXPECT_THAT(*frame, mod_spdy::testing::IsRstStream(stream_id_, status));
+  }
+
+  void ExpectNoMoreOutputFrames() {
+    EXPECT_TRUE(output_queue_.IsEmpty());
+  }
+
+  bool is_spdy2() const { return GetParam() < mod_spdy::spdy::SPDY_VERSION_3; }
+
+  const char* host_header_name() const {
+    return is_spdy2() ? mod_spdy::http::kHost : mod_spdy::spdy::kSpdy3Host;
+  }
+  const char* method_header_name() const {
+    return (is_spdy2() ? mod_spdy::spdy::kSpdy2Method :
+            mod_spdy::spdy::kSpdy3Method);
+  }
+  const char* path_header_name() const {
+    return (is_spdy2() ? mod_spdy::spdy::kSpdy2Url :
+            mod_spdy::spdy::kSpdy3Path);
+  }
+  const char* scheme_header_name() const {
+    return (is_spdy2() ? mod_spdy::spdy::kSpdy2Scheme :
+            mod_spdy::spdy::kSpdy3Scheme);
+  }
+  const char* version_header_name() const {
+    return (is_spdy2() ? mod_spdy::spdy::kSpdy2Version :
+            mod_spdy::spdy::kSpdy3Version);
+  }
+
+  const mod_spdy::spdy::SpdyVersion spdy_version_;
+  const net::SpdyStreamId stream_id_;
+  const net::SpdyPriority priority_;
+  mod_spdy::SpdyFramePriorityQueue output_queue_;
+  mod_spdy::SharedFlowControlWindow shared_window_;
+  MockSpdyServerPushInterface pusher_;
+  mod_spdy::SpdyStream stream_;
+  mod_spdy::SpdyToHttpFilter spdy_to_http_filter_;
+
+  mod_spdy::LocalPool local_;
+  apr_bucket_alloc_t* bucket_alloc_;
+  conn_rec* connection_;
+  ap_filter_t* ap_filter_;
+  apr_bucket_brigade* brigade_;
+};
+
+TEST_P(SpdyToHttpFilterTest, SimpleGetRequest) {
+  // Perform an INIT.  It should succeed, with no effect.
+  ASSERT_EQ(APR_SUCCESS, Read(AP_MODE_INIT, APR_BLOCK_READ, 1337));
+  ExpectEndOfBrigade();
+
+  // Invoke the fitler in non-blocking GETLINE mode.  We shouldn't get anything
+  // yet, because we haven't sent any frames from the client yet.
+  ASSERT_TRUE(APR_STATUS_IS_EAGAIN(
+      Read(AP_MODE_GETLINE, APR_NONBLOCK_READ, 0)));
+  ExpectEndOfBrigade();
+
+  // Send a SYN_STREAM frame from the client, with FLAG_FIN set.
+  net::SpdyNameValueBlock headers;
+  headers[host_header_name()] = "www.example.com";
+  headers[method_header_name()] = "GET";
+  headers["referer"] = "https://www.example.com/index.html";
+  headers[scheme_header_name()] = "https";
+  headers[path_header_name()] = "/foo/bar/index.html";
+  headers["user-agent"] = "ModSpdyUnitTest/1.0";
+  headers[version_header_name()] = "HTTP/1.1";
+  headers["x-do-not-track"] = "1";
+  PostSynStreamFrame(true, headers);
+
+  // Invoke the filter in blocking GETLINE mode.  We should get back just the
+  // HTTP request line.
+  ASSERT_EQ(APR_SUCCESS, Read(AP_MODE_GETLINE, APR_BLOCK_READ, 0));
+  ExpectTransientBucket("GET /foo/bar/index.html HTTP/1.1\r\n");
+  ExpectEndOfBrigade();
+
+  // Now do a SPECULATIVE read.  We should get back a few bytes.
+  ASSERT_EQ(APR_SUCCESS, Read(AP_MODE_SPECULATIVE, APR_NONBLOCK_READ, 8));
+  ExpectTransientBucket("host: ww");
+  ExpectEndOfBrigade();
+
+  // Now do another GETLINE read.  We should get back the first header line,
+  // including the data we just read speculatively.
+  ASSERT_EQ(APR_SUCCESS, Read(AP_MODE_GETLINE, APR_NONBLOCK_READ, 0));
+  ExpectTransientBucket("host: www.example.com\r\n");
+  ExpectEndOfBrigade();
+
+  // Do a READBYTES read.  We should get back a few bytes.
+  ASSERT_EQ(APR_SUCCESS, Read(AP_MODE_READBYTES, APR_NONBLOCK_READ, 12));
+  ExpectTransientBucket("referer: htt");
+  ExpectEndOfBrigade();
+
+  // Do another GETLINE read.  We should get back the rest of the header line,
+  // *not* including the data we just read.
+  ASSERT_EQ(APR_SUCCESS, Read(AP_MODE_GETLINE, APR_NONBLOCK_READ, 0));
+  ExpectTransientBucket("ps://www.example.com/index.html\r\n");
+  ExpectEndOfBrigade();
+
+  // Finally, do an EXHAUSTIVE read.  We should get back everything that
+  // remains, terminating with an EOS bucket.
+  ASSERT_EQ(APR_SUCCESS, Read(AP_MODE_EXHAUSTIVE, APR_NONBLOCK_READ, 0));
+  ExpectTransientBucket("user-agent: ModSpdyUnitTest/1.0\r\n"
+                        "x-do-not-track: 1\r\n"
+                        "accept-encoding: gzip,deflate\r\n"
+                        "\r\n");
+  ExpectEosBucket();
+  ExpectEndOfBrigade();
+  ExpectNoMoreOutputFrames();
+
+  // There's no more data left; attempting another read should result in EOF.
+  ASSERT_TRUE(APR_STATUS_IS_EOF(
+      Read(AP_MODE_READBYTES, APR_NONBLOCK_READ, 4)));
+}
+
+TEST_P(SpdyToHttpFilterTest, SimplePostRequest) {
+  // Send a SYN_STREAM frame from the client.
+  net::SpdyNameValueBlock headers;
+  headers[host_header_name()] = "www.example.com";
+  headers[method_header_name()] = "POST";
+  headers["referer"] = "https://www.example.com/index.html";
+  headers[scheme_header_name()] = "https";
+  headers[path_header_name()] = "/erase/the/whole/database.cgi";
+  headers["user-agent"] = "ModSpdyUnitTest/1.0";
+  headers[version_header_name()] = "HTTP/1.1";
+  PostSynStreamFrame(false, headers);
+
+  // Do a nonblocking READBYTES read.  We ask for lots of bytes, but since it's
+  // nonblocking we should immediately get back what's available so far.
+  ASSERT_EQ(APR_SUCCESS, Read(AP_MODE_READBYTES, APR_NONBLOCK_READ, 4096));
+  ExpectTransientBucket("POST /erase/the/whole/database.cgi HTTP/1.1\r\n"
+                        "host: www.example.com\r\n"
+                        "referer: https://www.example.com/index.html\r\n"
+                        "user-agent: ModSpdyUnitTest/1.0\r\n");
+  ExpectEndOfBrigade();
+
+  // There's nothing more available yet, so a nonblocking read should fail.
+  ASSERT_TRUE(APR_STATUS_IS_EAGAIN(
+      Read(AP_MODE_READBYTES, APR_NONBLOCK_READ, 4)));
+  ExpectEndOfBrigade();
+  ExpectNoMoreOutputFrames();
+
+  // Send some DATA frames.
+  PostDataFrame(false, "Hello, world!\nPlease erase ");
+  PostDataFrame(false, "the whole database ");
+  PostDataFrame(true, "immediately.\nThanks!\n");
+
+  // Now read in the data a bit at a time.
+  ASSERT_EQ(APR_SUCCESS, Read(AP_MODE_GETLINE, APR_NONBLOCK_READ, 0));
+  ExpectTransientBucket("transfer-encoding: chunked\r\n");
+  ExpectEndOfBrigade();
+  ASSERT_EQ(APR_SUCCESS, Read(AP_MODE_GETLINE, APR_NONBLOCK_READ, 0));
+  ExpectTransientBucket("accept-encoding: gzip,deflate\r\n");
+  ExpectEndOfBrigade();
+  ASSERT_EQ(APR_SUCCESS, Read(AP_MODE_GETLINE, APR_NONBLOCK_READ, 0));
+  ExpectTransientBucket("\r\n");
+  ExpectEndOfBrigade();
+  ASSERT_EQ(APR_SUCCESS, Read(AP_MODE_GETLINE, APR_NONBLOCK_READ, 0));
+  ExpectTransientBucket("1B\r\n");
+  ExpectEndOfBrigade();
+  ASSERT_EQ(APR_SUCCESS, Read(AP_MODE_READBYTES, APR_NONBLOCK_READ, 24));
+  ExpectTransientBucket("Hello, world!\nPlease era");
+  ExpectEndOfBrigade();
+  ExpectNoMoreOutputFrames();
+  ASSERT_EQ(APR_SUCCESS, Read(AP_MODE_SPECULATIVE, APR_NONBLOCK_READ, 15));
+  ExpectTransientBucket("se \r\n13\r\nthe wh");
+  ExpectEndOfBrigade();
+  ExpectNoMoreOutputFrames();
+  ASSERT_EQ(APR_SUCCESS, Read(AP_MODE_READBYTES, APR_NONBLOCK_READ, 36));
+  ExpectTransientBucket("se \r\n13\r\nthe whole database \r\n15\r\nim");
+  ExpectEndOfBrigade();
+  ExpectNoMoreOutputFrames();
+  ASSERT_EQ(APR_SUCCESS, Read(AP_MODE_READBYTES, APR_NONBLOCK_READ, 21));
+  ExpectTransientBucket("mediately.\nThanks!\n\r\n");
+  ExpectEndOfBrigade();
+  ASSERT_EQ(APR_SUCCESS, Read(AP_MODE_GETLINE, APR_NONBLOCK_READ, 0));
+  ExpectTransientBucket("0\r\n");
+  ExpectEndOfBrigade();
+  ASSERT_EQ(APR_SUCCESS, Read(AP_MODE_GETLINE, APR_NONBLOCK_READ, 0));
+  ExpectTransientBucket("\r\n");
+  ExpectEosBucket();
+  ExpectEndOfBrigade();
+  ExpectNoMoreOutputFrames();
+
+  // There's no more data left; attempting another read should result in EOF.
+  ASSERT_TRUE(APR_STATUS_IS_EOF(Read(AP_MODE_GETLINE, APR_BLOCK_READ, 0)));
+}
+
+TEST_P(SpdyToHttpFilterTest, PostRequestWithHeadersFrames) {
+  // Send a SYN_STREAM frame from the client.
+  net::SpdyNameValueBlock headers;
+  headers[host_header_name()] = "www.example.net";
+  headers[method_header_name()] = "POST";
+  headers["referer"] = "https://www.example.net/index.html";
+  headers[scheme_header_name()] = "https";
+  headers[path_header_name()] = "/erase/the/whole/database.cgi";
+  headers["user-agent"] = "ModSpdyUnitTest/1.0";
+  headers[version_header_name()] = "HTTP/1.1";
+  PostSynStreamFrame(false, headers);
+
+  // Send some DATA and HEADERS frames.  The HEADERS frames should get buffered
+  // and placed at the end of the HTTP request body as trailing headers.
+  PostDataFrame(false, "Please erase ");
+  net::SpdyNameValueBlock headers2;
+  headers2["x-super-cool"] = "foo";
+  PostHeadersFrame(false, headers2);
+  PostDataFrame(false, "everything ");
+  net::SpdyNameValueBlock headers3;
+  headers3["x-awesome"] = "quux";
+  PostHeadersFrame(false, headers3);
+  PostDataFrame(true, "immediately!!\n");
+
+  // Read in all the data.
+  ASSERT_EQ(APR_SUCCESS, Read(AP_MODE_EXHAUSTIVE, APR_NONBLOCK_READ, 0));
+  ExpectTransientBucket("POST /erase/the/whole/database.cgi HTTP/1.1\r\n"
+                        "host: www.example.net\r\n"
+                        "referer: https://www.example.net/index.html\r\n"
+                        "user-agent: ModSpdyUnitTest/1.0\r\n"
+                        "transfer-encoding: chunked\r\n"
+                        "accept-encoding: gzip,deflate\r\n"
+                        "\r\n"
+                        "D\r\n"
+                        "Please erase \r\n"
+                        "B\r\n"
+                        "everything \r\n"
+                        "E\r\n"
+                        "immediately!!\n\r\n"
+                        "0\r\n"
+                        "x-awesome: quux\r\n"
+                        "x-super-cool: foo\r\n"
+                        "\r\n");
+  ExpectEosBucket();
+  ExpectEndOfBrigade();
+  ExpectNoMoreOutputFrames();
+}
+
+TEST_P(SpdyToHttpFilterTest, GetRequestWithHeadersRightAfterSynStream) {
+  // Send a SYN_STREAM frame with some of the headers.
+  net::SpdyNameValueBlock headers;
+  headers[host_header_name()] = "www.example.org";
+  headers[method_header_name()] = "GET";
+  headers["referer"] = "https://www.example.org/foo/bar.html";
+  headers[scheme_header_name()] = "https";
+  headers[path_header_name()] = "/index.html";
+  headers[version_header_name()] = "HTTP/1.1";
+  PostSynStreamFrame(false, headers);
+
+  // Read in everything that's available so far.
+  ASSERT_EQ(APR_SUCCESS, Read(AP_MODE_EXHAUSTIVE, APR_NONBLOCK_READ, 0));
+  ExpectTransientBucket("GET /index.html HTTP/1.1\r\n"
+                        "host: www.example.org\r\n"
+                        "referer: https://www.example.org/foo/bar.html\r\n");
+  ExpectEndOfBrigade();
+
+  // Send a HEADERS frame with the rest of the headers.
+  net::SpdyNameValueBlock headers2;
+  headers2["accept-encoding"] = "deflate, gzip";
+  headers2["user-agent"] = "ModSpdyUnitTest/1.0";
+  PostHeadersFrame(true, headers2);
+
+  // Read in the rest of the request.
+  ASSERT_EQ(APR_SUCCESS, Read(AP_MODE_EXHAUSTIVE, APR_NONBLOCK_READ, 0));
+  ExpectTransientBucket("accept-encoding: deflate, gzip\r\n"
+                        "user-agent: ModSpdyUnitTest/1.0\r\n"
+                        "\r\n");
+  ExpectEosBucket();
+  ExpectEndOfBrigade();
+  ExpectNoMoreOutputFrames();
+}
+
+TEST_P(SpdyToHttpFilterTest, PostRequestWithHeadersRightAfterSynStream) {
+  // Send a SYN_STREAM frame from the client.
+  net::SpdyNameValueBlock headers;
+  headers[host_header_name()] = "www.example.org";
+  headers[method_header_name()] = "POST";
+  headers["referer"] = "https://www.example.org/index.html";
+  headers[scheme_header_name()] = "https";
+  headers[path_header_name()] = "/delete/everything.py";
+  headers[version_header_name()] = "HTTP/1.1";
+  headers["x-zzzz"] = "4Z";
+  PostSynStreamFrame(false, headers);
+
+  // Send a HEADERS frame before sending any data frames.
+  net::SpdyNameValueBlock headers2;
+  headers2["user-agent"] = "ModSpdyUnitTest/1.0";
+  PostHeadersFrame(false, headers2);
+
+  // Now send a couple DATA frames and a final HEADERS frame.
+  PostDataFrame(false, "Please erase everything immediately");
+  PostDataFrame(false, ", thanks!\n");
+  net::SpdyNameValueBlock headers3;
+  headers3["x-qqq"] = "3Q";
+  PostHeadersFrame(true, headers3);
+
+  // Read in all the data.  The first HEADERS frame should get put in before
+  // the data, and the last HEADERS frame should get put in after the data.
+  ASSERT_EQ(APR_SUCCESS, Read(AP_MODE_EXHAUSTIVE, APR_NONBLOCK_READ, 0));
+  ExpectTransientBucket("POST /delete/everything.py HTTP/1.1\r\n"
+                        "host: www.example.org\r\n"
+                        "referer: https://www.example.org/index.html\r\n"
+                        "x-zzzz: 4Z\r\n"
+                        "user-agent: ModSpdyUnitTest/1.0\r\n"
+                        "transfer-encoding: chunked\r\n"
+                        "accept-encoding: gzip,deflate\r\n"
+                        "\r\n"
+                        "23\r\n"
+                        "Please erase everything immediately\r\n"
+                        "A\r\n"
+                        ", thanks!\n\r\n"
+                        "0\r\n"
+                        "x-qqq: 3Q\r\n"
+                        "\r\n");
+  ExpectEosBucket();
+  ExpectEndOfBrigade();
+  ExpectNoMoreOutputFrames();
+}
+
+TEST_P(SpdyToHttpFilterTest, PostRequestWithEmptyDataFrameInMiddle) {
+  // Send a SYN_STREAM frame from the client.
+  net::SpdyNameValueBlock headers;
+  headers[host_header_name()] = "www.example.org";
+  headers[method_header_name()] = "POST";
+  headers["referer"] = "https://www.example.org/index.html";
+  headers[scheme_header_name()] = "https";
+  headers[path_header_name()] = "/do/some/stuff.py";
+  headers[version_header_name()] = "HTTP/1.1";
+  PostSynStreamFrame(false, headers);
+
+  // Now send a few DATA frames, with a zero-length data frame in the middle.
+  PostDataFrame(false, "Please do");
+  PostDataFrame(false, " some ");
+  PostDataFrame(false, "");
+  PostDataFrame(true, "stuff.\n");
+
+  // Read in all the data.  The empty data frame should be ignored.
+  ASSERT_EQ(APR_SUCCESS, Read(AP_MODE_EXHAUSTIVE, APR_NONBLOCK_READ, 0));
+  ExpectTransientBucket("POST /do/some/stuff.py HTTP/1.1\r\n"
+                        "host: www.example.org\r\n"
+                        "referer: https://www.example.org/index.html\r\n"
+                        "transfer-encoding: chunked\r\n"
+                        "accept-encoding: gzip,deflate\r\n"
+                        "\r\n"
+                        "9\r\n"
+                        "Please do\r\n"
+                        "6\r\n"
+                        " some \r\n"
+                        "7\r\n"
+                        "stuff.\n\r\n"
+                        "0\r\n"
+                        "\r\n");
+  ExpectEosBucket();
+  ExpectEndOfBrigade();
+  ExpectNoMoreOutputFrames();
+}
+
+TEST_P(SpdyToHttpFilterTest, PostRequestWithEmptyDataFrameAtEnd) {
+  // Send a SYN_STREAM frame from the client.
+  net::SpdyNameValueBlock headers;
+  headers[host_header_name()] = "www.example.org";
+  headers[method_header_name()] = "POST";
+  headers["referer"] = "https://www.example.org/index.html";
+  headers[scheme_header_name()] = "https";
+  headers[path_header_name()] = "/do/some/stuff.py";
+  headers[version_header_name()] = "HTTP/1.1";
+  PostSynStreamFrame(false, headers);
+
+  // Now send a few DATA frames, with a zero-length data frame at the end.
+  PostDataFrame(false, "Please do");
+  PostDataFrame(false, " some ");
+  PostDataFrame(false, "stuff.\n");
+  PostDataFrame(true, "");
+
+  // Read in all the data.  The empty data frame should be ignored (except for
+  // its FLAG_FIN).
+  ASSERT_EQ(APR_SUCCESS, Read(AP_MODE_EXHAUSTIVE, APR_NONBLOCK_READ, 0));
+  ExpectTransientBucket("POST /do/some/stuff.py HTTP/1.1\r\n"
+                        "host: www.example.org\r\n"
+                        "referer: https://www.example.org/index.html\r\n"
+                        "transfer-encoding: chunked\r\n"
+                        "accept-encoding: gzip,deflate\r\n"
+                        "\r\n"
+                        "9\r\n"
+                        "Please do\r\n"
+                        "6\r\n"
+                        " some \r\n"
+                        "7\r\n"
+                        "stuff.\n\r\n"
+                        "0\r\n"
+                        "\r\n");
+  ExpectEosBucket();
+  ExpectEndOfBrigade();
+  ExpectNoMoreOutputFrames();
+}
+
+TEST_P(SpdyToHttpFilterTest, PostRequestWithContentLength) {
+  // Send a SYN_STREAM frame from the client.
+  net::SpdyNameValueBlock headers;
+  headers[host_header_name()] = "www.example.org";
+  headers[method_header_name()] = "POST";
+  headers["referer"] = "https://www.example.org/index.html";
+  headers[scheme_header_name()] = "https";
+  headers[path_header_name()] = "/do/some/stuff.py";
+  headers[version_header_name()] = "HTTP/1.1";
+  PostSynStreamFrame(false, headers);
+
+  // Send a few more headers before sending data, including a content-length.
+  net::SpdyNameValueBlock headers2;
+  headers2["content-length"] = "22";
+  headers2["user-agent"] = "ModSpdyUnitTest/1.0";
+  PostHeadersFrame(false, headers2);
+
+  // Now send a few DATA frames.
+  PostDataFrame(false, "Please do");
+  PostDataFrame(false, " some ");
+  PostDataFrame(true, "stuff.\n");
+
+  // Read in all the data.  Because we supplied a content-length, chunked
+  // encoding should not be used (to support modules that don't work with
+  // chunked requests).
+  ASSERT_EQ(APR_SUCCESS, Read(AP_MODE_EXHAUSTIVE, APR_NONBLOCK_READ, 0));
+  ExpectTransientBucket("POST /do/some/stuff.py HTTP/1.1\r\n"
+                        "host: www.example.org\r\n"
+                        "referer: https://www.example.org/index.html\r\n"
+                        "content-length: 22\r\n"
+                        "user-agent: ModSpdyUnitTest/1.0\r\n"
+                        "accept-encoding: gzip,deflate\r\n"
+                        "\r\n"
+                        "Please do some stuff.\n");
+  ExpectEosBucket();
+  ExpectEndOfBrigade();
+  ExpectNoMoreOutputFrames();
+}
+
+TEST_P(SpdyToHttpFilterTest, PostRequestWithContentLengthAndTrailingHeaders) {
+  // Send a SYN_STREAM frame from the client, including a content-length.
+  net::SpdyNameValueBlock headers;
+  headers["content-length"] = "22";
+  headers[host_header_name()] = "www.example.org";
+  headers[method_header_name()] = "POST";
+  headers["referer"] = "https://www.example.org/index.html";
+  headers[scheme_header_name()] = "https";
+  headers[path_header_name()] = "/do/some/stuff.py";
+  headers[version_header_name()] = "HTTP/1.1";
+  PostSynStreamFrame(false, headers);
+
+  // Now send a few DATA frames.
+  PostDataFrame(false, "Please do");
+  PostDataFrame(false, " some ");
+  PostDataFrame(false, "stuff.\n");
+
+  // Finish with a HEADERS frame.
+  net::SpdyNameValueBlock headers2;
+  headers2["x-metadata"] = "foobar";
+  headers2["x-whatever"] = "quux";
+  PostHeadersFrame(true, headers2);
+
+  // Read in all the data.  Because we supplied a content-length, chunked
+  // encoding should not be used, and as an unfortunate consequence, we must
+  // therefore ignore the trailing headers (justified in that, at least in
+  // HTTP, they're generally only used for ignorable metadata; in fact, they're
+  // not generally used at all).
+  ASSERT_EQ(APR_SUCCESS, Read(AP_MODE_EXHAUSTIVE, APR_NONBLOCK_READ, 0));
+  // One (usually irrelevant) quirk of our implementation is that the host
+  // header appears in a slightly different place for SPDY v2 and SPDY v3.
+  // This is beacuse in SPDY v3 the host header is ":host", which sorts
+  // earlier, and which we transform into the HTTP header "host".
+  if (is_spdy2()) {
+    ExpectTransientBucket("POST /do/some/stuff.py HTTP/1.1\r\n"
+                          "content-length: 22\r\n"
+                          "host: www.example.org\r\n"
+                          "referer: https://www.example.org/index.html\r\n"
+                          "accept-encoding: gzip,deflate\r\n"
+                          "\r\n"
+                          "Please do some stuff.\n");
+  } else {
+    ExpectTransientBucket("POST /do/some/stuff.py HTTP/1.1\r\n"
+                          "host: www.example.org\r\n"
+                          "content-length: 22\r\n"
+                          "referer: https://www.example.org/index.html\r\n"
+                          "accept-encoding: gzip,deflate\r\n"
+                          "\r\n"
+                          "Please do some stuff.\n");
+  }
+  ExpectEosBucket();
+  ExpectEndOfBrigade();
+  ExpectNoMoreOutputFrames();
+}
+
+TEST_P(SpdyToHttpFilterTest, ExtraSynStream) {
+  // Send a SYN_STREAM frame from the client.
+  net::SpdyNameValueBlock headers;
+  headers[host_header_name()] = "www.example.com";
+  headers[method_header_name()] = "POST";
+  headers["referer"] = "https://www.example.com/index.html";
+  headers[scheme_header_name()] = "https";
+  headers[path_header_name()] = "/erase/the/whole/database.cgi";
+  headers["user-agent"] = "ModSpdyUnitTest/1.0";
+  headers[version_header_name()] = "HTTP/1.1";
+  PostSynStreamFrame(false, headers);
+
+  // Read in all available data.
+  ASSERT_EQ(APR_SUCCESS, Read(AP_MODE_EXHAUSTIVE, APR_NONBLOCK_READ, 0));
+  ExpectTransientBucket("POST /erase/the/whole/database.cgi HTTP/1.1\r\n"
+                        "host: www.example.com\r\n"
+                        "referer: https://www.example.com/index.html\r\n"
+                        "user-agent: ModSpdyUnitTest/1.0\r\n");
+  ExpectEndOfBrigade();
+
+  // Now send another SYN_STREAM for the same stream_id, which is illegal.
+  PostSynStreamFrame(false, headers);
+  // If we try to read more data, we'll get nothing.
+  ASSERT_TRUE(APR_STATUS_IS_ECONNABORTED(
+      Read(AP_MODE_EXHAUSTIVE, APR_NONBLOCK_READ, 0)));
+  ExpectEosBucket();
+  ExpectEndOfBrigade();
+  // The stream should have been aborted.
+  ExpectRstStream(net::RST_STREAM_PROTOCOL_ERROR);
+  ExpectNoMoreOutputFrames();
+  EXPECT_TRUE(stream_.is_aborted());
+}
+
+// Run each test over both SPDY v2 and SPDY v3.
+INSTANTIATE_TEST_CASE_P(Spdy2And3, SpdyToHttpFilterTest, testing::Values(
+    mod_spdy::spdy::SPDY_VERSION_2, mod_spdy::spdy::SPDY_VERSION_3,
+    mod_spdy::spdy::SPDY_VERSION_3_1));
+
+}  // namespace

Added: httpd/httpd/trunk/modules/spdy/apache/id_pool.cc
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/spdy/apache/id_pool.cc?rev=1590597&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/spdy/apache/id_pool.cc (added)
+++ httpd/httpd/trunk/modules/spdy/apache/id_pool.cc Mon Apr 28 10:55:17 2014
@@ -0,0 +1,82 @@
+/* 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.
+ */
+// Contains IdPool, a class for managing 16-bit process-global IDs.
+
+#include "mod_spdy/apache/id_pool.h"
+
+#include <vector>
+#include <set>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+namespace mod_spdy {
+
+IdPool* IdPool::g_instance = NULL;
+const uint16 IdPool::kOverFlowId;
+
+IdPool::IdPool()
+    : next_never_used_(0) /* So it gets incremented to 1 in ::Alloc */ {
+}
+
+IdPool::~IdPool() {
+}
+
+void IdPool::CreateInstance() {
+  DCHECK(g_instance == NULL);
+  g_instance = new IdPool();
+}
+
+void IdPool::DestroyInstance() {
+  DCHECK(g_instance != NULL);
+  delete g_instance;
+  g_instance = NULL;
+}
+
+uint16 IdPool::Alloc() {
+  base::AutoLock lock(mutex_);
+  if (!free_list_.empty()) {
+    uint16 id = free_list_.back();
+    free_list_.pop_back();
+    alloc_set_.insert(id);
+    return id;
+  }
+
+  // We do not use 0 or kOverFlowId normally..
+  if (alloc_set_.size() == (0x10000 - 2)) {
+    LOG(WARNING) << "Out of slave fetch IDs, things may break";
+    return kOverFlowId;
+  }
+
+  // Freelist is empty, but we haven't yet used some ID, so return it.
+  ++next_never_used_;
+  DCHECK(next_never_used_ != kOverFlowId);
+  DCHECK(alloc_set_.find(next_never_used_) == alloc_set_.end());
+  alloc_set_.insert(next_never_used_);
+  return next_never_used_;
+}
+
+void IdPool::Free(uint16 id) {
+  if (id == kOverFlowId) {
+    return;
+  }
+
+  base::AutoLock lock(mutex_);
+  DCHECK(alloc_set_.find(id) != alloc_set_.end());
+  alloc_set_.erase(id);
+  free_list_.push_back(id);
+}
+
+}  // namespace mod_spdy

Added: httpd/httpd/trunk/modules/spdy/apache/id_pool.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/spdy/apache/id_pool.h?rev=1590597&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/spdy/apache/id_pool.h (added)
+++ httpd/httpd/trunk/modules/spdy/apache/id_pool.h Mon Apr 28 10:55:17 2014
@@ -0,0 +1,68 @@
+/* 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_ID_POOL_H_
+#define MOD_SPDY_APACHE_ID_POOL_H_
+
+#include <vector>
+#include <set>
+
+#include "base/basictypes.h"
+#include "base/synchronization/lock.h"
+
+namespace mod_spdy {
+
+// A class for managing non-zero 16-bit process-global IDs.
+class IdPool {
+ public:
+  static const uint16 kOverFlowId = 0xFFFF;
+
+  // Returns the one and only instance of the IdPool. Note that one must
+  // be created with CreateInstance().
+  static IdPool* Instance() { return g_instance; }
+
+  // Call this before threading starts to initialize the instance pointer.
+  static void CreateInstance();
+
+  // Call this once you're done with the pool object to delete it.
+  static void DestroyInstance();
+
+  // Allocates a new, distinct, non-zero ID. 2^16-2 possible values may be
+  // returned; if more than that are needed simultaneously (without being
+  // Free()d) kOverFlowId will always be returned.
+  uint16 Alloc();
+
+  // Release an ID that's no longer in use, making it available for further
+  // calls to Alloc().
+  void Free(uint16 id);
+
+ private:
+  IdPool();
+  ~IdPool();
+
+  static IdPool* g_instance;
+
+  base::Lock mutex_;
+  std::vector<uint16> free_list_;  // IDs known to be free
+  std::set<uint16> alloc_set_;  // IDs currently in use
+  uint16 next_never_used_;  // Next ID we have never returned from Alloc,
+                            // for use when the free list is empty.
+
+  DISALLOW_COPY_AND_ASSIGN(IdPool);
+};
+
+}  // namespace mod_spdy
+
+#endif  /* MOD_SPDY_APACHE_ID_POOL_H_ */

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

Added: httpd/httpd/trunk/modules/spdy/apache/id_pool_test.cc
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/spdy/apache/id_pool_test.cc?rev=1590597&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/spdy/apache/id_pool_test.cc (added)
+++ httpd/httpd/trunk/modules/spdy/apache/id_pool_test.cc Mon Apr 28 10:55:17 2014
@@ -0,0 +1,97 @@
+// 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/apache/id_pool.h"
+
+#include <set>
+
+#include "base/basictypes.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+using mod_spdy::IdPool;
+
+TEST(IdPoolTest, Lifetime) {
+  EXPECT_EQ(NULL, IdPool::Instance());
+  IdPool::CreateInstance();
+  EXPECT_TRUE(IdPool::Instance() != NULL);
+  IdPool::DestroyInstance();
+  EXPECT_EQ(NULL, IdPool::Instance());
+}
+
+TEST(IdPoolTest, BasicAllocation) {
+  IdPool::CreateInstance();
+  IdPool* instance = IdPool::Instance();
+  uint16 id_1 = instance->Alloc();
+  uint16 id_2 = instance->Alloc();
+  uint16 id_3 = instance->Alloc();
+  EXPECT_NE(0, id_1);
+  EXPECT_NE(0, id_2);
+  EXPECT_NE(0, id_3);
+  EXPECT_NE(id_1, id_2);
+  EXPECT_NE(id_1, id_3);
+  EXPECT_NE(id_2, id_3);
+  instance->Free(id_1);
+  instance->Free(id_2);
+  instance->Free(id_3);
+  IdPool::DestroyInstance();
+}
+
+TEST(IdPoolTest, AllocatingMany) {
+  // We should be able to allocate 2^16-2 unique ids.
+  IdPool::CreateInstance();
+  IdPool* instance = IdPool::Instance();
+
+  std::set<uint16> in_use;
+  for (int run = 0; run < 0xFFFE; ++run) {
+    uint16 new_id = instance->Alloc();
+    EXPECT_NE(0, new_id);
+    EXPECT_NE(IdPool::kOverFlowId, new_id);
+    EXPECT_TRUE(in_use.find(new_id) == in_use.end());
+    in_use.insert(new_id);
+  }
+
+  // All attempts after this point should return kOverFlowId.
+  for (int run = 0; run < 100; ++run) {
+    EXPECT_EQ(IdPool::kOverFlowId, instance->Alloc());
+  }
+
+  // Trying to free the overflow ID is harmless.
+  instance->Free(IdPool::kOverFlowId);
+
+  // Now delete half of them.
+  int deleted = 0;
+  std::set<uint16>::iterator i = in_use.begin();
+  while (deleted != 0xFFFE / 2) {
+    ASSERT_TRUE(i != in_use.end());
+    instance->Free(*i);
+    ++deleted;
+    in_use.erase(i);
+    i = in_use.begin();
+  }
+
+  // Should now be able to allocate that many again.
+  for (int run = 0; run < 0xFFFE / 2; ++run) {
+    uint16 new_id = instance->Alloc();
+    EXPECT_NE(0, new_id);
+    EXPECT_NE(IdPool::kOverFlowId, new_id);
+    EXPECT_TRUE(in_use.find(new_id) == in_use.end());
+    in_use.insert(new_id);
+  }
+
+  IdPool::DestroyInstance();
+}
+
+}  // namespace

Added: httpd/httpd/trunk/modules/spdy/apache/log_message_handler.cc
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/spdy/apache/log_message_handler.cc?rev=1590597&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/spdy/apache/log_message_handler.cc (added)
+++ httpd/httpd/trunk/modules/spdy/apache/log_message_handler.cc Mon Apr 28 10:55:17 2014
@@ -0,0 +1,264 @@
+// 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/apache/log_message_handler.h"
+
+#include <limits>
+#include <string>
+
+#include "httpd.h"
+// When HAVE_SYSLOG is defined, apache http_log.h will include syslog.h, which
+// #defined LOG_* as numbers. This conflicts with what we are using those here.
+#undef HAVE_SYSLOG
+#include "http_log.h"
+
+#include "base/debug/debugger.h"
+#include "base/debug/stack_trace.h"
+#include "base/logging.h"
+#include "base/threading/thread_local.h"
+#include "mod_spdy/apache/pool_util.h"
+#include "mod_spdy/common/spdy_stream.h"
+#include "mod_spdy/common/version.h"
+
+// Make sure we don't attempt to use LOG macros here, since doing so
+// would cause us to go into an infinite log loop.
+#undef LOG
+#define LOG USING_LOG_HERE_WOULD_CAUSE_INFINITE_RECURSION
+
+namespace {
+
+class LogHandler;
+
+const char* const kLogMessagePrefix =
+    "[mod_spdy/" MOD_SPDY_VERSION_STRING "-" LASTCHANGE_STRING "] ";
+
+apr_pool_t* log_pool = NULL;
+base::ThreadLocalPointer<LogHandler>* gThreadLocalLogHandler = NULL;
+
+const int kMaxInt = std::numeric_limits<int>::max();
+int log_level_cutoff = kMaxInt;
+
+class LogHandler {
+ public:
+  explicit LogHandler(LogHandler* parent) : parent_(parent) {}
+  virtual ~LogHandler() {}
+  virtual void Log(int log_level, const std::string& message) = 0;
+  LogHandler* parent() const { return parent_; }
+ private:
+  LogHandler* parent_;
+  DISALLOW_COPY_AND_ASSIGN(LogHandler);
+};
+
+// Log a message with the given LogHandler; if the LogHandler is NULL, fall
+// back to using ap_log_perror.
+void LogWithHandler(LogHandler* handler, int log_level,
+                    const std::string& message) {
+  if (handler != NULL) {
+    handler->Log(log_level, message);
+  } else {
+    // ap_log_perror only prints messages with a severity of at least NOTICE,
+    // so if we're falling back to ap_log_perror (which should be rare) then
+    // force the log_level to a verbosity of NOTICE or lower.
+    COMPILE_ASSERT(APLOG_DEBUG > APLOG_NOTICE,
+                   higher_verbosity_is_higher_number);
+    ap_log_perror(APLOG_MARK, std::min(log_level, APLOG_NOTICE), APR_SUCCESS,
+                  log_pool, "%s", message.c_str());
+  }
+}
+
+void PopLogHandler() {
+  CHECK(gThreadLocalLogHandler);
+  LogHandler* handler = gThreadLocalLogHandler->Get();
+  CHECK(handler);
+  gThreadLocalLogHandler->Set(handler->parent());
+  delete handler;
+}
+
+class ServerLogHandler : public LogHandler {
+ public:
+  ServerLogHandler(LogHandler* parent, server_rec* server)
+      : LogHandler(parent), server_(server) {}
+  virtual void Log(int log_level, const std::string& message) {
+    ap_log_error(APLOG_MARK, log_level, APR_SUCCESS, server_,
+                 "%s", message.c_str());
+  }
+ private:
+  server_rec* const server_;
+  DISALLOW_COPY_AND_ASSIGN(ServerLogHandler);
+};
+
+class ConnectionLogHandler : public LogHandler {
+ public:
+  ConnectionLogHandler(LogHandler* parent, conn_rec* connection)
+      : LogHandler(parent), connection_(connection) {}
+  virtual void Log(int log_level, const std::string& message) {
+    ap_log_cerror(APLOG_MARK, log_level, APR_SUCCESS, connection_,
+                  "%s", message.c_str());
+  }
+ private:
+  conn_rec* const connection_;
+  DISALLOW_COPY_AND_ASSIGN(ConnectionLogHandler);
+};
+
+class StreamLogHandler : public LogHandler {
+ public:
+  StreamLogHandler(LogHandler* parent, conn_rec* connection,
+                   const mod_spdy::SpdyStream* stream)
+      : LogHandler(parent), connection_(connection), stream_(stream) {}
+  virtual void Log(int log_level, const std::string& message) {
+    ap_log_cerror(APLOG_MARK, log_level, APR_SUCCESS, connection_,
+                  "[stream %d] %s", static_cast<int>(stream_->stream_id()),
+                  message.c_str());
+  }
+ private:
+  conn_rec* const connection_;
+  const mod_spdy::SpdyStream* const stream_;
+  DISALLOW_COPY_AND_ASSIGN(StreamLogHandler);
+};
+
+int GetApacheLogLevel(int severity) {
+  switch (severity) {
+    case logging::LOG_INFO:
+      return APLOG_INFO;
+    case logging::LOG_WARNING:
+      return APLOG_WARNING;
+    case logging::LOG_ERROR:
+      return APLOG_ERR;
+    case logging::LOG_ERROR_REPORT:
+      return APLOG_CRIT;
+    case logging::LOG_FATAL:
+      return APLOG_ALERT;
+    default:  // For VLOG()s
+      return APLOG_DEBUG;
+  }
+}
+
+bool LogMessageHandler(int severity, const char* file, int line,
+                       size_t message_start, const std::string& str) {
+  const int this_log_level = GetApacheLogLevel(severity);
+
+  std::string message(kLogMessagePrefix);
+  message.append(str);
+  if (severity == logging::LOG_FATAL) {
+    if (base::debug::BeingDebugged()) {
+      base::debug::BreakDebugger();
+    } else {
+      base::debug::StackTrace trace;
+      std::ostringstream stream;
+      trace.OutputToStream(&stream);
+      message.append(stream.str());
+    }
+  }
+
+  // Trim the newline off the end of the message string.
+  size_t last_msg_character_index = message.length() - 1;
+  if (message[last_msg_character_index] == '\n') {
+    message.resize(last_msg_character_index);
+  }
+
+  if (this_log_level <= log_level_cutoff || log_level_cutoff == kMaxInt) {
+    LogWithHandler(gThreadLocalLogHandler->Get(), this_log_level, message);
+  }
+
+  if (severity == logging::LOG_FATAL) {
+    // Crash the process to generate a dump.
+    base::debug::BreakDebugger();
+  }
+
+  return true;
+}
+
+// Include PID and TID in each log message.
+bool kShowProcessId = true;
+bool kShowThreadId = true;
+
+// Disabled since this information is already included in the apache
+// log line.
+bool kShowTimestamp = false;
+
+// Disabled by default due to CPU cost. Enable to see high-resolution
+// timestamps in the logs.
+bool kShowTickcount = false;
+
+}  // namespace
+
+namespace mod_spdy {
+
+ScopedServerLogHandler::ScopedServerLogHandler(server_rec* server) {
+  CHECK(gThreadLocalLogHandler);
+  gThreadLocalLogHandler->Set(new ServerLogHandler(
+      gThreadLocalLogHandler->Get(), server));
+}
+
+ScopedServerLogHandler::~ScopedServerLogHandler() {
+  PopLogHandler();
+}
+
+ScopedConnectionLogHandler::ScopedConnectionLogHandler(conn_rec* connection) {
+  CHECK(gThreadLocalLogHandler);
+  gThreadLocalLogHandler->Set(new ConnectionLogHandler(
+      gThreadLocalLogHandler->Get(), connection));
+}
+
+ScopedConnectionLogHandler::~ScopedConnectionLogHandler() {
+  PopLogHandler();
+}
+
+ScopedStreamLogHandler::ScopedStreamLogHandler(conn_rec* slave_connection,
+                                               const SpdyStream* stream) {
+  CHECK(gThreadLocalLogHandler);
+  gThreadLocalLogHandler->Set(new StreamLogHandler(
+      gThreadLocalLogHandler->Get(), slave_connection, stream));
+}
+
+ScopedStreamLogHandler::~ScopedStreamLogHandler() {
+  PopLogHandler();
+}
+
+void InstallLogMessageHandler(apr_pool_t* pool) {
+  log_pool = pool;
+  gThreadLocalLogHandler = new base::ThreadLocalPointer<LogHandler>();
+  PoolRegisterDelete(pool, gThreadLocalLogHandler);
+  logging::SetLogItems(kShowProcessId,
+                       kShowThreadId,
+                       kShowTimestamp,
+                       kShowTickcount);
+  logging::SetLogMessageHandler(&LogMessageHandler);
+}
+
+void SetLoggingLevel(int apache_log_level, int vlog_level) {
+  switch (apache_log_level) {
+    case APLOG_EMERG:
+    case APLOG_ALERT:
+      logging::SetMinLogLevel(logging::LOG_FATAL);
+      break;
+    case APLOG_CRIT:
+      logging::SetMinLogLevel(logging::LOG_ERROR_REPORT);
+      break;
+    case APLOG_ERR:
+      logging::SetMinLogLevel(logging::LOG_ERROR);
+      break;
+    case APLOG_WARNING:
+      logging::SetMinLogLevel(logging::LOG_WARNING);
+      break;
+    case APLOG_NOTICE:
+    case APLOG_INFO:
+    case APLOG_DEBUG:
+    default:
+      logging::SetMinLogLevel(std::min(logging::LOG_INFO, -vlog_level));
+      break;
+  }
+}
+
+}  // namespace mod_spdy

Added: httpd/httpd/trunk/modules/spdy/apache/log_message_handler.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/spdy/apache/log_message_handler.h?rev=1590597&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/spdy/apache/log_message_handler.h (added)
+++ httpd/httpd/trunk/modules/spdy/apache/log_message_handler.h Mon Apr 28 10:55:17 2014
@@ -0,0 +1,84 @@
+// 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_APACHE_LOG_MESSAGE_HANDLER_H_
+#define MOD_SPDY_APACHE_LOG_MESSAGE_HANDLER_H_
+
+#include <string>
+
+#include "httpd.h"
+#include "apr_pools.h"
+
+#include "base/basictypes.h"
+
+namespace mod_spdy {
+
+class SpdyStream;
+
+// Stack-allocate to install a server-specific log handler for the duration of
+// the current scope (on the current thread only).  For example:
+//
+//   void SomeApacheHookFunction(server_rec* server) {
+//     ScopedServerLogHandler handler(server);
+//     ...call various other functions here...
+//   }
+//
+// The log handler will be in effect until the end of the block, but only for
+// this thread (even if this thread spawns other threads in the meantime).
+// Establishing this server-specific log handler allows LOG() macros within
+// called functions to produce better messages.
+class ScopedServerLogHandler {
+ public:
+  explicit ScopedServerLogHandler(server_rec* server);
+  ~ScopedServerLogHandler();
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ScopedServerLogHandler);
+};
+
+// Stack-allocate to install a connection-specific log handler for the duration
+// of the current scope (on the current thread only).  See the doc comment for
+// ScopedServerLogHandler above for an example.
+class ScopedConnectionLogHandler {
+ public:
+  explicit ScopedConnectionLogHandler(conn_rec* connection);
+  ~ScopedConnectionLogHandler();
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ScopedConnectionLogHandler);
+};
+
+// Stack-allocate to install a stream-specific log handler for the duration of
+// the current scope (on the current thread only).  See the doc comment for
+// ScopedServerLogHandler above for an example.
+class ScopedStreamLogHandler {
+ public:
+  explicit ScopedStreamLogHandler(conn_rec* slave_connection,
+                                  const SpdyStream* stream);
+  ~ScopedStreamLogHandler();
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ScopedStreamLogHandler);
+};
+
+// Install a log message handler that routes LOG() messages to the
+// apache error log.  Should be called once, at server startup.
+void InstallLogMessageHandler(apr_pool_t* pool);
+
+// Set the logging level for LOG() messages, based on the Apache log level and
+// the VLOG-level specified in the server config.  Note that the VLOG level
+// will be ignored unless the Apache log verbosity is at NOTICE or higher.
+// Should be called once for each child process, at process startup.
+void SetLoggingLevel(int apache_log_level, int vlog_level);
+
+}  // namespace mod_spdy
+
+#endif  // MOD_SPDY_APACHE_LOG_MESSAGE_HANDLER_H_

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

Added: httpd/httpd/trunk/modules/spdy/apache/master_connection_context.cc
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/spdy/apache/master_connection_context.cc?rev=1590597&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/spdy/apache/master_connection_context.cc (added)
+++ httpd/httpd/trunk/modules/spdy/apache/master_connection_context.cc Mon Apr 28 10:55:17 2014
@@ -0,0 +1,66 @@
+// 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/apache/master_connection_context.h"
+
+#include "base/logging.h"
+#include "mod_spdy/common/protocol_util.h"
+#include "mod_spdy/common/spdy_stream.h"
+
+namespace mod_spdy {
+
+MasterConnectionContext::MasterConnectionContext(bool using_ssl)
+    : using_ssl_(using_ssl),
+      npn_state_(NOT_DONE_YET),
+      assume_spdy_(false),
+      spdy_version_(spdy::SPDY_VERSION_NONE) {}
+
+MasterConnectionContext::~MasterConnectionContext() {}
+
+bool MasterConnectionContext::is_using_spdy() const {
+  const bool using_spdy = (npn_state_ == USING_SPDY || assume_spdy_);
+  return using_spdy;
+}
+
+MasterConnectionContext::NpnState MasterConnectionContext::npn_state() const {
+  return npn_state_;
+}
+
+void MasterConnectionContext::set_npn_state(NpnState state) {
+  npn_state_ = state;
+}
+
+bool MasterConnectionContext::is_assuming_spdy() const {
+  return assume_spdy_;
+}
+
+void MasterConnectionContext::set_assume_spdy(bool assume) {
+  assume_spdy_ = assume;
+}
+
+spdy::SpdyVersion MasterConnectionContext::spdy_version() const {
+  DCHECK(is_using_spdy());
+  DCHECK_NE(spdy::SPDY_VERSION_NONE, spdy_version_);
+  return spdy_version_;
+}
+
+void MasterConnectionContext::set_spdy_version(
+    spdy::SpdyVersion spdy_version) {
+  DCHECK(is_using_spdy());
+  DCHECK_EQ(spdy::SPDY_VERSION_NONE, spdy_version_);
+  DCHECK_NE(spdy::SPDY_VERSION_NONE, spdy_version);
+  spdy_version_ = spdy_version;
+}
+
+}  // namespace mod_spdy

Added: httpd/httpd/trunk/modules/spdy/apache/master_connection_context.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/spdy/apache/master_connection_context.h?rev=1590597&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/spdy/apache/master_connection_context.h (added)
+++ httpd/httpd/trunk/modules/spdy/apache/master_connection_context.h Mon Apr 28 10:55:17 2014
@@ -0,0 +1,89 @@
+// 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_APACHE_MASTER_CONNECTION_CONTEXT_H_
+#define MOD_SPDY_APACHE_MASTER_CONNECTION_CONTEXT_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "mod_spdy/common/protocol_util.h"
+
+namespace mod_spdy {
+
+class SpdyStream;
+
+// Shared context object for a SPDY connection to the outside world.
+class MasterConnectionContext {
+ public:
+  // Create a context object for a master connection (one to the outside world,
+  // not for talking to Apache).
+  explicit MasterConnectionContext(bool using_ssl);
+  ~MasterConnectionContext();
+
+  // Return true if the connection to the user is over SSL.  This is almost
+  // always true, but may be false if we've been set to use SPDY for non-SSL
+  // connections (for debugging).
+  bool is_using_ssl() const { return using_ssl_; }
+
+  // Return true if we are using SPDY for this connection, which is the case if
+  // either 1) SPDY was chosen by NPN, or 2) we are assuming SPDY regardless of
+  // NPN.
+  bool is_using_spdy() const;
+
+  enum NpnState {
+    // NOT_DONE_YET: NPN has not yet completed.
+    NOT_DONE_YET,
+    // USING_SPDY: We have agreed with the client to use SPDY for this
+    // connection.
+    USING_SPDY,
+    // NOT_USING_SPDY: We have decided not to use SPDY for this connection.
+    NOT_USING_SPDY
+  };
+
+  // Get the NPN state of this connection.  Unless you actually care about NPN
+  // itself, you probably don't want to use this method to check if SPDY is
+  // being used; instead, use is_using_spdy().
+  NpnState npn_state() const;
+
+  // Set the NPN state of this connection.
+  void set_npn_state(NpnState state);
+
+  // If true, we are simply _assuming_ SPDY, regardless of the outcome of NPN.
+  bool is_assuming_spdy() const;
+
+  // Set whether we are assuming SPDY for this connection (regardless of NPN).
+  void set_assume_spdy(bool assume);
+
+  // Return the SPDY version number we will be using.  Requires that
+  // is_using_spdy() is true and that the version number has already been set.
+  spdy::SpdyVersion spdy_version() const;
+
+  // Set the SPDY version number we will be using.  Requires that
+  // is_using_spdy() is true, and set_spdy_version hasn't already been called.
+  void set_spdy_version(spdy::SpdyVersion spdy_version);
+
+ private:
+  const bool using_ssl_;
+  NpnState npn_state_;
+  bool assume_spdy_;
+  spdy::SpdyVersion spdy_version_;
+
+  DISALLOW_COPY_AND_ASSIGN(MasterConnectionContext);
+};
+
+}  // namespace mod_spdy
+
+#endif  // MOD_SPDY_APACHE_MASTER_CONNECTION_CONTEXT_H_

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

Added: httpd/httpd/trunk/modules/spdy/apache/pool_util.cc
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/spdy/apache/pool_util.cc?rev=1590597&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/spdy/apache/pool_util.cc (added)
+++ httpd/httpd/trunk/modules/spdy/apache/pool_util.cc Mon Apr 28 10:55:17 2014
@@ -0,0 +1,31 @@
+// 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/pool_util.h"
+
+#include <string>
+
+#include "apr_errno.h"
+
+#include "base/basictypes.h"
+
+namespace mod_spdy {
+
+std::string AprStatusString(apr_status_t status) {
+  char buffer[120];
+  apr_strerror(status, buffer, arraysize(buffer));
+  return std::string(buffer);
+}
+
+}  // namespace mod_spdy

Added: httpd/httpd/trunk/modules/spdy/apache/pool_util.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/spdy/apache/pool_util.h?rev=1590597&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/spdy/apache/pool_util.h (added)
+++ httpd/httpd/trunk/modules/spdy/apache/pool_util.h Mon Apr 28 10:55:17 2014
@@ -0,0 +1,91 @@
+// 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_APACHE_POOL_UTIL_H_
+#define MOD_SPDY_APACHE_POOL_UTIL_H_
+
+#include <string>
+
+#include "apr_pools.h"
+#include "base/logging.h"
+
+namespace mod_spdy {
+
+/**
+ * Wrapper object that creates a new apr_pool_t and then destroys it when
+ * deleted (handy for creating a local apr_pool_t on the stack).
+ *
+ * Example usage:
+ *
+ *   apr_status_t SomeFunction() {
+ *     LocalPool local;
+ *     char* buffer = apr_palloc(local.pool(), 1024);
+ *     // Do stuff with buffer; it will dealloc when we leave this scope.
+ *     return APR_SUCCESS;
+ *   }
+ */
+class LocalPool {
+ public:
+  LocalPool() : pool_(NULL) {
+    // apr_pool_create() only fails if we run out of memory.  However, we make
+    // no effort elsewhere in this codebase to deal with running out of memory,
+    // so there's no sense in dealing with it here.  Instead, just assert that
+    // pool creation succeeds.
+    const apr_status_t status = apr_pool_create(&pool_, NULL);
+    CHECK(status == APR_SUCCESS);
+    CHECK(pool_ != NULL);
+  }
+
+  ~LocalPool() {
+    apr_pool_destroy(pool_);
+  }
+
+  apr_pool_t* pool() const { return pool_; }
+
+ private:
+  apr_pool_t* pool_;
+
+  DISALLOW_COPY_AND_ASSIGN(LocalPool);
+};
+
+// Helper function for PoolRegisterDelete.
+template <class T>
+apr_status_t DeletionFunction(void* object) {
+  delete static_cast<T*>(object);
+  return APR_SUCCESS;
+}
+
+// Register a C++ object to be deleted with a pool.
+template <class T>
+void PoolRegisterDelete(apr_pool_t* pool, T* object) {
+  // Note that the "child cleanup" argument below doesn't apply to us, so we
+  // use apr_pool_cleanup_null, which is a no-op cleanup function.
+  apr_pool_cleanup_register(pool, object,
+                            DeletionFunction<T>,  // cleanup function
+                            apr_pool_cleanup_null);  // child cleanup
+}
+
+// Un-register a C++ object from deletion with a pool.  Essentially, this
+// undoes a previous call to PoolRegisterDelete with the same pool and object.
+template <class T>
+void PoolUnregisterDelete(apr_pool_t* pool, T* object) {
+  apr_pool_cleanup_kill(pool, object, DeletionFunction<T>);
+}
+
+// Return a string describing the given APR status code.
+std::string AprStatusString(apr_status_t status);
+
+}  // namespace mod_spdy
+
+#endif  // MOD_SPDY_APACHE_POOL_UTIL_H_

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

Added: httpd/httpd/trunk/modules/spdy/apache/pool_util_test.cc
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/spdy/apache/pool_util_test.cc?rev=1590597&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/spdy/apache/pool_util_test.cc (added)
+++ httpd/httpd/trunk/modules/spdy/apache/pool_util_test.cc Mon Apr 28 10:55:17 2014
@@ -0,0 +1,59 @@
+// 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/apache/pool_util.h"
+
+#include "base/basictypes.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+// Class to help us detect when it is deleted.
+class SetOnDelete {
+ public:
+  SetOnDelete(int value, int* ptr) : value_(value), ptr_(ptr) {}
+  ~SetOnDelete() { *ptr_ = value_; }
+ private:
+  const int value_;
+  int* const ptr_;
+  DISALLOW_COPY_AND_ASSIGN(SetOnDelete);
+};
+
+TEST(PoolUtilTest, LocalPoolRegisterDelete) {
+  int value = 3;
+  {
+    mod_spdy::LocalPool local;
+    SetOnDelete* setter = new SetOnDelete(5, &value);
+    mod_spdy::PoolRegisterDelete(local.pool(), setter);
+    ASSERT_EQ(3, value);
+  }
+  ASSERT_EQ(5, value);
+}
+
+TEST(PoolUtilTest, LocalPoolUnregisterDelete) {
+  int value = 2;
+  SetOnDelete* setter = new SetOnDelete(7, &value);
+  {
+    mod_spdy::LocalPool local;
+    mod_spdy::PoolRegisterDelete(local.pool(), setter);
+    ASSERT_EQ(2, value);
+    mod_spdy::PoolUnregisterDelete(local.pool(), setter);
+    ASSERT_EQ(2, value);
+  }
+  ASSERT_EQ(2, value);
+  delete setter;
+  ASSERT_EQ(7, value);
+}
+
+}  // namespace

Added: httpd/httpd/trunk/modules/spdy/apache/slave_connection.cc
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/spdy/apache/slave_connection.cc?rev=1590597&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/spdy/apache/slave_connection.cc (added)
+++ httpd/httpd/trunk/modules/spdy/apache/slave_connection.cc Mon Apr 28 10:55:17 2014
@@ -0,0 +1,190 @@
+// 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/apache/slave_connection.h"
+
+#include "apr_strings.h"
+// Temporarily define CORE_PRIVATE so we can see the declarations for
+// ap_create_conn_config (in http_config.h), ap_process_connection (in
+// http_connection.h), and core_module (in http_core.h).
+#define CORE_PRIVATE
+#include "httpd.h"
+#include "http_config.h"
+#include "http_connection.h"
+#include "http_core.h"
+#undef CORE_PRIVATE
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "mod_spdy/apache/config_util.h"
+#include "mod_spdy/apache/id_pool.h"
+#include "mod_spdy/apache/log_message_handler.h"
+#include "mod_spdy/apache/master_connection_context.h"
+#include "mod_spdy/apache/slave_connection_context.h"
+#include "mod_spdy/apache/sockaddr_util.h"
+#include "mod_spdy/apache/ssl_util.h"
+
+namespace mod_spdy {
+
+SlaveConnectionFactory::SlaveConnectionFactory(conn_rec* master_connection) {
+  // If the parent connection is using mod_spdy, we can extract relevant info
+  // on whether we're using it there.
+  if (HasMasterConnectionContext(master_connection)) {
+    MasterConnectionContext* master_context =
+        GetMasterConnectionContext(master_connection);
+    is_using_ssl_ = master_context->is_using_ssl();
+    spdy_version_ = (master_context->is_using_spdy() ?
+                     master_context->spdy_version() :
+                     spdy::SPDY_VERSION_NONE);
+  } else {
+    is_using_ssl_ = IsUsingSslForConnection(master_connection);
+    spdy_version_ = spdy::SPDY_VERSION_NONE;
+  }
+
+  base_server_ = master_connection->base_server;
+  local_addr_ = DeepCopySockAddr(master_connection->local_addr, pool_.pool());
+  local_ip_ = apr_pstrdup(pool_.pool(), master_connection->local_ip);
+  remote_addr_ = DeepCopySockAddr(master_connection->remote_addr, pool_.pool());
+  remote_ip_ = apr_pstrdup(pool_.pool(), master_connection->remote_ip);
+  master_connection_id_ = master_connection->id;
+}
+
+SlaveConnectionFactory::~SlaveConnectionFactory() {
+  // Nothing to do --- pool_ dtor will clean everything up.
+}
+
+SlaveConnection* SlaveConnectionFactory::Create() {
+  return new SlaveConnection(this);
+}
+
+SlaveConnection::SlaveConnection(SlaveConnectionFactory* factory) {
+  apr_pool_t* pool = pool_.pool();
+
+  slave_connection_ =
+      static_cast<conn_rec*>(apr_pcalloc(pool, sizeof(conn_rec)));
+
+  // Initialize what fields of the connection object we can (the rest are
+  // zeroed out by apr_pcalloc).  In particular, we should set at least those
+  // fields set by core_create_conn() in core.c in Apache.
+  // -> id will be set once we are actually running the connection, in
+  // ::Run().
+  slave_connection_->clogging_input_filters = 0;
+  slave_connection_->sbh = NULL;
+  // We will manage this connection and all the associated resources with the
+  // pool we just created.
+  slave_connection_->pool = pool;
+  slave_connection_->bucket_alloc = apr_bucket_alloc_create(pool);
+  slave_connection_->conn_config = ap_create_conn_config(pool);
+  slave_connection_->notes = apr_table_make(pool, 5);
+  // Use the same server settings and client address for the slave connection
+  // as for the master connection --- the factory saved them for us.
+  slave_connection_->base_server = factory->base_server_;
+  slave_connection_->local_addr = factory->local_addr_;
+  slave_connection_->local_ip = factory->local_ip_;
+  slave_connection_->remote_addr = factory->remote_addr_;
+  slave_connection_->remote_ip = factory->remote_ip_;
+
+  // One of the other things we will need in slave_connection is a
+  // connection id. One of the bits of info we will need for it is the
+  // id of the master connection. We save it here, and use it inside ::Run().
+  master_connection_id_ = factory->master_connection_id_;
+
+  // We're supposed to pass a socket object to ap_process_connection below, but
+  // there's no meaningful object to pass for this slave connection, because
+  // we're not really talking to the network.  Our pre-connection hook will
+  // prevent the core filters, which talk to the socket, from being inserted,
+  // so they won't notice anyway; nonetheless, we can't pass NULL to
+  // ap_process_connection because that can cause some other modules to
+  // segfault if they try to muck with the socket's settings.  So, we'll just
+  // allocate our own socket object for those modules to mess with.  This is a
+  // kludge, but it seems to work.
+  slave_socket_ = NULL;
+  apr_status_t status = apr_socket_create(
+      &slave_socket_, APR_INET, SOCK_STREAM, APR_PROTO_TCP, pool);
+  DCHECK(status == APR_SUCCESS);
+  DCHECK(slave_socket_ != NULL);
+
+  // In our context object for this connection, mark this connection as being
+  // a slave.  Our pre-connection and process-connection hooks will notice
+  // this, and act accordingly, when they are called for the slave
+  // connection.
+  SlaveConnectionContext* slave_context =
+      CreateSlaveConnectionContext(slave_connection_);
+
+  // Now store the SSL and SPDY info.
+  slave_context->set_is_using_ssl(factory->is_using_ssl_);
+  slave_context->set_spdy_version(factory->spdy_version_);
+}
+
+SlaveConnection::~SlaveConnection() {
+  // pool_ destructor will take care of everything.
+}
+
+SlaveConnectionContext* SlaveConnection::GetSlaveConnectionContext() {
+  return mod_spdy::GetSlaveConnectionContext(slave_connection_);
+}
+
+void SlaveConnection::Run() {
+  // Pick a globally-unique ID for the slave connection; this must be unique
+  // at any given time.  Normally the MPM is responsible for assigning these,
+  // and each MPM does it differently, so we're cheating in a dangerous way by
+  // trying to assign one here.  However, most MPMs seem to do it in a similar
+  // way: for non-threaded MPMs (e.g. Prefork, WinNT), the ID is just the
+  // child ID, which is a small nonnegative integer (i.e. an array index into
+  // the list of active child processes); for threaded MPMs (e.g. Worker,
+  // Event) the ID is typically ((child_index * thread_limit) + thread_index),
+  // which will again be a positive integer, most likely (but not necessarily,
+  // if thread_limit is set absurdly high) smallish.
+  //
+  // Therefore, the approach that we take is to concatenate the Apache
+  // connection ID for the master connection with a small integer from IDPool
+  // that's unique within the process, and, to avoid conflicts with
+  // MPM-assigned connection IDs, we make our slave connection ID negative.
+  // We only have so many bits to work with
+  // (especially if long is only four bytes instead of eight), so we could
+  // potentially run into trouble if the master connection ID gets very large
+  // or we have too many active tasks simultaneously (i.e. more than 2^16).
+  // So, this approach definitely isn't any kind of robust; but it will
+  // probably usually work. It would, of course, be great to replace this
+  // with a better strategy, if we find one.
+  //
+  // TODO(mdsteele): We could also consider using an #if here to widen the
+  //   masks and the shift distance on systems where sizeof(long)==8.
+  //   We might as well use those extra bits if we have them.
+  COMPILE_ASSERT(sizeof(long) >= 4, long_is_at_least_32_bits);
+  const uint16 in_process_id = IdPool::Instance()->Alloc();
+  const long slave_connectionid =
+      -(((master_connection_id_ & 0x7fffL) << 16) | in_process_id);
+  slave_connection_->id = slave_connectionid;
+
+  // Normally, the core pre-connection hook sets the core module's connection
+  // context to the socket passed to ap_process_connection; certain other
+  // modules, such as mod_reqtimeout, read the core module's connection
+  // context directly so as to read this socket's settings.  However, we
+  // purposely don't allow the core pre-connection hook to run, because we
+  // don't want the core connection filters to be inserted.  So, to avoid
+  // breaking other modules, we take it upon oursevles to set the core
+  // module's connection context to the socket we are passing to
+  // ap_process_connection.  This is ugly, but seems to work.
+  ap_set_module_config(slave_connection_->conn_config,
+                       &core_module, slave_socket_);
+
+  // Invoke Apache's usual processing pipeline.  This will block until the
+  // connection is complete.
+  ap_process_connection(slave_connection_, slave_socket_);
+
+  IdPool::Instance()->Free(in_process_id);
+}
+
+}  // namespace mod_spdy

Added: httpd/httpd/trunk/modules/spdy/apache/slave_connection.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/spdy/apache/slave_connection.h?rev=1590597&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/spdy/apache/slave_connection.h (added)
+++ httpd/httpd/trunk/modules/spdy/apache/slave_connection.h Mon Apr 28 10:55:17 2014
@@ -0,0 +1,112 @@
+/* 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_SLAVE_CONNECTION_H_
+#define MOD_SPDY_APACHE_SLAVE_CONNECTION_H_
+
+#include "base/basictypes.h"
+#include "mod_spdy/apache/pool_util.h"
+#include "mod_spdy/common/protocol_util.h"
+
+struct apr_sockaddr_t;
+struct apr_socket_t;
+struct conn_rec;
+struct server_rec;
+
+namespace mod_spdy {
+
+class SlaveConnection;
+class SlaveConnectionContext;
+
+// SlaveConnectionFactory + SlaveConnection helps execute requests within
+// the current Apache process, with the request and response both going to
+// some other code and not an external client talking over TCP.
+//
+// SlaveConnectionFactory + SlaveConnection help create a fake Apache conn_rec
+// object and run it. That conn_rec will have a SlaveConnectionContext
+// attached to it, which various hooks in mod_spdy.cc will recognize and handle
+// specially. In particular, they will arrange to have the I/O for connection
+// routed to and from the input & output filters set on the
+// SlaveConnectionContext.
+class SlaveConnectionFactory {
+ public:
+  // Prepares the factory to create slave connections with endpoint, SPDY and
+  // SSL information matching that of the master_connection.
+  //
+  // Does not retain any pointers to data from master_connection, so may be
+  // used after master_connection is destroyed.
+  explicit SlaveConnectionFactory(conn_rec* master_connection);
+
+  ~SlaveConnectionFactory();
+
+  // Creates a slave connection matching the settings in the constructor.
+  // You should attach I/O filters on its GetSlaveConnectionContext() before
+  // calling Run().
+  //
+  // The resulted object lives on the C++ heap, and must be deleted.
+  SlaveConnection* Create();
+
+ private:
+  friend class SlaveConnection;
+
+  // Saved information from master_connection
+  bool is_using_ssl_;
+  spdy::SpdyVersion spdy_version_;
+  server_rec* base_server_;
+  LocalPool pool_;
+  // All of these are in pool_:
+  apr_sockaddr_t* local_addr_;
+  char* local_ip_;
+  apr_sockaddr_t* remote_addr_;
+  char* remote_ip_;
+  long master_connection_id_;
+
+  DISALLOW_COPY_AND_ASSIGN(SlaveConnectionFactory);
+};
+
+class SlaveConnection {
+ public:
+  ~SlaveConnection();
+
+  // Returns the Apache conn_rec object this manages.
+  conn_rec* apache_connection() { return slave_connection_; }
+
+  // Returns the underlying SlaveConnectionContext, which lets you query
+  // information about the connection and hook in I/O filters.
+  //
+  // This is the same as GetSlaveConnectionContext(apache_connection()), and
+  // can thus be accessed via the conn_rec* as well.
+  SlaveConnectionContext* GetSlaveConnectionContext();
+
+  // Executes the requests associated with this connection, taking a request
+  // from the input filter set on the SlaveConnectionContext(), and directing
+  // the response to the output filter. Note that this is a blocking operation.
+  void Run();
+
+ private:
+  SlaveConnection(SlaveConnectionFactory* factory);
+  friend class SlaveConnectionFactory;
+
+  LocalPool pool_;
+  conn_rec* slave_connection_;  // owned by pool_
+  apr_socket_t* slave_socket_;  // owned by pool_
+  long master_connection_id_;
+
+  DISALLOW_COPY_AND_ASSIGN(SlaveConnection);
+};
+
+}  // namespace mod_spdy
+
+#endif  /* MOD_SPDY_APACHE_SLAVE_CONNECTION_H_ */

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

Added: httpd/httpd/trunk/modules/spdy/apache/slave_connection_api.cc
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/spdy/apache/slave_connection_api.cc?rev=1590597&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/spdy/apache/slave_connection_api.cc (added)
+++ httpd/httpd/trunk/modules/spdy/apache/slave_connection_api.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/slave_connection_api.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "mod_spdy/apache/slave_connection.h"
+#include "mod_spdy/apache/slave_connection_context.h"
+
+using mod_spdy::SlaveConnection;
+using mod_spdy::SlaveConnectionContext;
+using mod_spdy::SlaveConnectionFactory;
+
+struct spdy_slave_connection_factory {
+  explicit spdy_slave_connection_factory(SlaveConnectionFactory* impl)
+      : impl(impl) {}
+  scoped_ptr<SlaveConnectionFactory> impl;
+};
+
+struct spdy_slave_connection {
+  explicit spdy_slave_connection(SlaveConnection* impl)
+      : impl(impl) {}
+  scoped_ptr<SlaveConnection> impl;
+};
+
+spdy_slave_connection_factory* spdy_create_slave_connection_factory(
+    conn_rec* master_connection) {
+  return new spdy_slave_connection_factory(
+      new SlaveConnectionFactory(master_connection));
+}
+
+void spdy_destroy_slave_connection_factory(
+    spdy_slave_connection_factory* factory) {
+  delete factory;
+}
+
+spdy_slave_connection* spdy_create_slave_connection(
+    spdy_slave_connection_factory* factory,
+    ap_filter_rec_t* input_filter,
+    void* input_filter_ctx,
+    ap_filter_rec_t* output_filter,
+    void* output_filter_ctx) {
+  spdy_slave_connection* wrapper =
+      new spdy_slave_connection(factory->impl->Create());
+
+  SlaveConnectionContext* ctx = wrapper->impl->GetSlaveConnectionContext();
+  ctx->SetInputFilter(input_filter, input_filter_ctx);
+  ctx->SetOutputFilter(output_filter, output_filter_ctx);
+
+  return wrapper;
+}
+
+void spdy_run_slave_connection(spdy_slave_connection* conn) {
+  conn->impl->Run();
+}
+
+void spdy_destroy_slave_connection(spdy_slave_connection* conn) {
+  delete conn;
+}
+
+void ModSpdyExportSlaveConnectionFunctions() {
+  APR_REGISTER_OPTIONAL_FN(spdy_create_slave_connection_factory);
+  APR_REGISTER_OPTIONAL_FN(spdy_destroy_slave_connection_factory);
+  APR_REGISTER_OPTIONAL_FN(spdy_create_slave_connection);
+  APR_REGISTER_OPTIONAL_FN(spdy_run_slave_connection);
+  APR_REGISTER_OPTIONAL_FN(spdy_destroy_slave_connection);
+}

Added: httpd/httpd/trunk/modules/spdy/apache/slave_connection_api.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/spdy/apache/slave_connection_api.h?rev=1590597&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/spdy/apache/slave_connection_api.h (added)
+++ httpd/httpd/trunk/modules/spdy/apache/slave_connection_api.h 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.
+ */
+
+/* This is a public header file, to be used by other Apache modules.  So,
+ * identifiers declared here should follow Apache module naming conventions
+ * (specifically, identifiers should be lowercase_with_underscores, and our
+ * identifiers should start with the spdy_ prefix), and this header file must
+ * be valid in old-school C (not just C++). */
+
+#ifndef MOD_SPDY_APACHE_SLAVE_CONNECTION_API_H_
+#define MOD_SPDY_APACHE_SLAVE_CONNECTION_API_H_
+
+#include "httpd.h"
+#include "apr_optional.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ap_filter_rec_t;
+
+struct spdy_slave_connection_factory;
+struct spdy_slave_connection;
+
+/** Creates a factory object that can be used to make in-process pseudo-fetches
+ * with the same origin and target hosts as in master_connection
+ */
+APR_DECLARE_OPTIONAL_FN(
+    struct spdy_slave_connection_factory*,
+    spdy_create_slave_connection_factory, (conn_rec* master_connection));
+
+/** Destroys a factory object. */
+APR_DECLARE_OPTIONAL_FN(
+    void, spdy_destroy_slave_connection_factory,
+        (struct spdy_slave_connection_factory* factory));
+
+/** Asks mod_spdy to help with fetching a request on a slave connection.
+ * The input_filter must produce the request, and output_filter must
+ * handle the response. May return NULL if functionality is not available.
+ * The request will not be run until spdy_run_slave_connection() is invoked.
+ */
+APR_DECLARE_OPTIONAL_FN(
+    struct spdy_slave_connection*,
+    spdy_create_slave_connection, (
+        struct spdy_slave_connection_factory* factory,
+        struct ap_filter_rec_t* input_filter,
+        void* input_filter_ctx,
+        struct ap_filter_rec_t* output_filter,
+        void* output_filter_ctx));
+
+/** Actually performs the fetch on the object. Blocks, perhaps for a significant
+ *  amount of time. */
+APR_DECLARE_OPTIONAL_FN(
+    void, spdy_run_slave_connection, (struct spdy_slave_connection* conn));
+
+/** Cleans up the connection object. Must not be in active use. */
+APR_DECLARE_OPTIONAL_FN(
+    void, spdy_destroy_slave_connection, (struct spdy_slave_connection*));
+
+/* Used by mod_spdy to setup the exports. Not exported itself */
+void ModSpdyExportSlaveConnectionFunctions(void);
+
+#ifdef __cplusplus
+}  /* extern "C" */
+#endif
+
+#endif  /* MOD_SPDY_APACHE_SLAVE_CONNECTION_API_H_ */

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

Added: httpd/httpd/trunk/modules/spdy/apache/slave_connection_context.cc
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/spdy/apache/slave_connection_context.cc?rev=1590597&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/spdy/apache/slave_connection_context.cc (added)
+++ httpd/httpd/trunk/modules/spdy/apache/slave_connection_context.cc Mon Apr 28 10:55:17 2014
@@ -0,0 +1,46 @@
+// 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/slave_connection_context.h"
+
+#include "base/logging.h"
+#include "mod_spdy/common/spdy_stream.h"
+
+namespace mod_spdy {
+
+SlaveConnectionContext::SlaveConnectionContext()
+    : using_ssl_(false),
+      spdy_version_(spdy::SPDY_VERSION_NONE),
+      slave_stream_(NULL),
+      output_filter_handle_(NULL),
+      output_filter_context_(NULL),
+      input_filter_handle_(NULL),
+      input_filter_context_(NULL) {
+}
+
+SlaveConnectionContext::~SlaveConnectionContext() {}
+
+void SlaveConnectionContext::SetOutputFilter(
+    ap_filter_rec_t* handle, void* context) {
+  output_filter_handle_ = handle;
+  output_filter_context_ = context;
+}
+
+void SlaveConnectionContext::SetInputFilter(
+    ap_filter_rec_t* handle, void* context) {
+  input_filter_handle_ = handle;
+  input_filter_context_ = context;
+}
+
+}  // namespace mod_spdy

Added: httpd/httpd/trunk/modules/spdy/apache/slave_connection_context.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/spdy/apache/slave_connection_context.h?rev=1590597&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/spdy/apache/slave_connection_context.h (added)
+++ httpd/httpd/trunk/modules/spdy/apache/slave_connection_context.h Mon Apr 28 10:55:17 2014
@@ -0,0 +1,99 @@
+// 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_SLAVE_CONNECTION_CONTEXT_H_
+#define MOD_SPDY_APACHE_SLAVE_CONNECTION_CONTEXT_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "mod_spdy/common/protocol_util.h"
+
+struct ap_filter_rec_t;
+
+namespace mod_spdy {
+
+class SpdyStream;
+
+// Context for a 'slave' connection in mod_spdy, used to represent a fetch
+// of a given URL from within Apache (as opposed to outgoing SPDY session to
+// the client, which has a ConnectionContext).
+class SlaveConnectionContext {
+ public:
+  SlaveConnectionContext();
+  ~SlaveConnectionContext();
+
+  // Return true if the connection to the user is over SSL.  This is almost
+  // always true, but may be false if we've been set to use SPDY for non-SSL
+  // connections (for debugging).  Note that for a slave connection, this
+  // refers to whether the master network connection is using SSL.
+  bool is_using_ssl() const { return using_ssl_; }
+  void set_is_using_ssl(bool ssl_on) { using_ssl_ = ssl_on; }
+
+  // Return the SpdyStream object associated with this slave connection.
+  // Note that this may be NULL in case mod_spdy is acting on behalf of
+  // another module. Not owned.
+  SpdyStream* slave_stream() const { return slave_stream_; }
+  void set_slave_stream(SpdyStream* stream) { slave_stream_ = stream; }
+
+  // Return the SPDY version will be using, or SPDY_VERSION_NONE if we're not
+  // using SPDY.
+  spdy::SpdyVersion spdy_version() const { return spdy_version_; }
+  void set_spdy_version(spdy::SpdyVersion version) { spdy_version_ = version; }
+
+  // See SlaveConnection documentation for description of these.
+  void SetOutputFilter(ap_filter_rec_t* handle, void* context);
+  void SetInputFilter(ap_filter_rec_t* handle, void* context);
+
+  ap_filter_rec_t* output_filter_handle() const {
+    return output_filter_handle_;
+  }
+
+  void* output_filter_context() const {
+    return output_filter_context_;
+  }
+
+  ap_filter_rec_t* input_filter_handle() const {
+    return input_filter_handle_;
+  }
+
+  void* input_filter_context() const {
+    return input_filter_context_;
+  }
+
+ private:
+  // These are used to properly inform modules running on slave connections
+  // on whether the connection should be treated as using SPDY and SSL.
+  bool using_ssl_;
+  spdy::SpdyVersion spdy_version_;
+
+  // Used for SPDY push.
+  SpdyStream* slave_stream_;
+
+  // Filters to attach. These are set by clients of SlaveConnection
+  // between creation and Run(), and read by mod_spdy's PreConnection hook,
+  // where they are installed in Apache's filter chains.
+  ap_filter_rec_t* output_filter_handle_;
+  void* output_filter_context_;
+
+  ap_filter_rec_t* input_filter_handle_;
+  void* input_filter_context_;
+
+  DISALLOW_COPY_AND_ASSIGN(SlaveConnectionContext);
+};
+
+}  // namespace mod_spdy
+
+#endif  // MOD_SPDY_APACHE_SLAVE_CONNECTION_CONTEXT_H_

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



Mime
View raw message