Merge pull request #2991 from murgatroid99/core_shutdown_idempotency

Short-circuit shutdown when it is already published (core)
diff --git a/INSTALL b/INSTALL
index 808166d..d183fce 100644
--- a/INSTALL
+++ b/INSTALL
@@ -9,25 +9,40 @@
 * If you are in a hurry *
 *************************
 
+On Linux (Debian):
+
+ Note: you will need to add the Debian 'unstable' distribution to your sources
+ file first.
+
+ Add the following line to your `/etc/apt/sources.list` file:
+
+   deb http://ftp.us.debian.org/debian unstable main contrib non-free
+
+ Install the gRPC library:
+
+ $ [sudo] apt-get install libgrpc-dev
+
+OR
+
  $ git clone https://github.com/grpc/grpc.git
  $ cd grpc
  $ git submodule update --init
  $ make 
- $ sudo make install
+ $ [sudo] make install
 
 You don't need anything else than GNU Make, gcc and autotools. Under a Debian
 or Ubuntu system, this should boil down to the following packages:
 
-  $ apt-get install build-essential autoconf libtool
+ $ [sudo] apt-get install build-essential autoconf libtool
 
 Building the python wrapper requires the following:
 
-  # apt-get install python-all-dev python-virtualenv
+ $ [sudo] apt-get install python-all-dev python-virtualenv
 
 If you want to install in a different directory than the default /usr/lib, you can
 override it on the command line:
 
-  # make install prefix=/opt
+ $ [sudo] make install prefix=/opt
 
 
 *******************************
diff --git a/src/cpp/client/channel.cc b/src/cpp/client/channel.cc
index 9695a0f..17f31c2 100644
--- a/src/cpp/client/channel.cc
+++ b/src/cpp/client/channel.cc
@@ -71,7 +71,7 @@
   } else {
     const char* host_str = NULL;
     if (!context->authority().empty()) {
-      host_str = context->authority().c_str();
+      host_str = context->authority_.c_str();
     } else if (!host_.empty()) {
       host_str = host_.c_str();
     }
diff --git a/src/csharp/Grpc.Auth/AuthInterceptors.cs b/src/csharp/Grpc.Auth/AuthInterceptors.cs
new file mode 100644
index 0000000..61338f7
--- /dev/null
+++ b/src/csharp/Grpc.Auth/AuthInterceptors.cs
@@ -0,0 +1,84 @@
+#region Copyright notice and license
+
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#endregion
+
+using System;
+using System.Threading;
+
+using Google.Apis.Auth.OAuth2;
+using Grpc.Core;
+using Grpc.Core.Utils;
+
+namespace Grpc.Auth
+{
+    /// <summary>
+    /// Factory methods to create authorization interceptors.
+    /// </summary>
+    public static class AuthInterceptors
+    {
+        private const string AuthorizationHeader = "Authorization";
+        private const string Schema = "Bearer";
+
+        /// <summary>
+        /// Creates interceptor that will obtain access token from any credential type that implements
+        /// <c>ITokenAccess</c>. (e.g. <c>GoogleCredential</c>).
+        /// </summary>
+        public static HeaderInterceptor FromCredential(ITokenAccess credential)
+        {
+            return new HeaderInterceptor((method, authUri, metadata) =>
+            {
+                // TODO(jtattermusch): Rethink synchronous wait to obtain the result.
+                var accessToken = credential.GetAccessTokenForRequestAsync(authUri, CancellationToken.None)
+                        .ConfigureAwait(false).GetAwaiter().GetResult();
+                metadata.Add(CreateBearerTokenHeader(accessToken));
+            });
+        }
+
+        /// <summary>
+        /// Creates OAuth2 interceptor that will use given access token as authorization.
+        /// </summary>
+        /// <param name="accessToken">OAuth2 access token.</param>
+        public static HeaderInterceptor FromAccessToken(string accessToken)
+        {
+            Preconditions.CheckNotNull(accessToken);
+            return new HeaderInterceptor((method, authUri, metadata) =>
+            {
+                metadata.Add(CreateBearerTokenHeader(accessToken));
+            });
+        }
+
+        private static Metadata.Entry CreateBearerTokenHeader(string accessToken)
+        {
+            return new Metadata.Entry(AuthorizationHeader, Schema + " " + accessToken);
+        }
+    }
+}
diff --git a/src/csharp/Grpc.Auth/Grpc.Auth.csproj b/src/csharp/Grpc.Auth/Grpc.Auth.csproj
index 930a34b..4fb087d 100644
--- a/src/csharp/Grpc.Auth/Grpc.Auth.csproj
+++ b/src/csharp/Grpc.Auth/Grpc.Auth.csproj
@@ -3,8 +3,6 @@
   <PropertyGroup>
     <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
     <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
-    <ProductVersion>10.0.0</ProductVersion>
-    <SchemaVersion>2.0</SchemaVersion>
     <ProjectGuid>{AE21D0EE-9A2C-4C15-AB7F-5224EED5B0EA}</ProjectGuid>
     <OutputType>Library</OutputType>
     <RootNamespace>Grpc.Auth</RootNamespace>
@@ -41,57 +39,47 @@
     <AssemblyOriginatorKeyFile>C:\keys\Grpc.snk</AssemblyOriginatorKeyFile>
   </PropertyGroup>
   <ItemGroup>
-    <Reference Include="BouncyCastle.Crypto, Version=1.7.4137.9688, Culture=neutral, PublicKeyToken=a4292a325f69b123, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\BouncyCastle.1.7.0\lib\Net40-Client\BouncyCastle.Crypto.dll</HintPath>
-    </Reference>
-    <Reference Include="Google.Apis.Auth, Version=1.9.3.19379, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\Google.Apis.Auth.1.9.3\lib\net40\Google.Apis.Auth.dll</HintPath>
-    </Reference>
-    <Reference Include="Google.Apis.Auth.PlatformServices, Version=1.9.3.19383, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\Google.Apis.Auth.1.9.3\lib\net40\Google.Apis.Auth.PlatformServices.dll</HintPath>
-    </Reference>
-    <Reference Include="Google.Apis.Core, Version=1.9.3.19379, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\Google.Apis.Core.1.9.3\lib\portable-net40+sl50+win+wpa81+wp80\Google.Apis.Core.dll</HintPath>
-    </Reference>
-    <Reference Include="Microsoft.Threading.Tasks, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll</HintPath>
-    </Reference>
-    <Reference Include="Microsoft.Threading.Tasks.Extensions, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll</HintPath>
-    </Reference>
-    <Reference Include="Microsoft.Threading.Tasks.Extensions.Desktop, Version=1.0.168.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll</HintPath>
-    </Reference>
-    <Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
-    </Reference>
     <Reference Include="System" />
     <Reference Include="System.Net" />
     <Reference Include="System.Net.Http" />
-    <Reference Include="System.Net.Http.Extensions, Version=2.2.29.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
+    <Reference Include="System.Net.Http.WebRequest" />
+    <Reference Include="BouncyCastle.Crypto">
+      <HintPath>..\packages\BouncyCastle.1.7.0\lib\Net40-Client\BouncyCastle.Crypto.dll</HintPath>
+    </Reference>
+    <Reference Include="Google.Apis.Auth">
+      <HintPath>..\packages\Google.Apis.Auth.1.9.3\lib\net40\Google.Apis.Auth.dll</HintPath>
+    </Reference>
+    <Reference Include="Google.Apis.Auth.PlatformServices">
+      <HintPath>..\packages\Google.Apis.Auth.1.9.3\lib\net40\Google.Apis.Auth.PlatformServices.dll</HintPath>
+    </Reference>
+    <Reference Include="Google.Apis.Core">
+      <HintPath>..\packages\Google.Apis.Core.1.9.3\lib\portable-net40+sl50+win+wpa81+wp80\Google.Apis.Core.dll</HintPath>
+    </Reference>
+    <Reference Include="Microsoft.Threading.Tasks">
+      <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll</HintPath>
+    </Reference>
+    <Reference Include="Microsoft.Threading.Tasks.Extensions">
+      <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll</HintPath>
+    </Reference>
+    <Reference Include="Microsoft.Threading.Tasks.Extensions.Desktop">
+      <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll</HintPath>
+    </Reference>
+    <Reference Include="Newtonsoft.Json">
+      <HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
+    </Reference>
+    <Reference Include="System.Net.Http.Extensions">
       <HintPath>..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Extensions.dll</HintPath>
     </Reference>
-    <Reference Include="System.Net.Http.Primitives, Version=4.2.29.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
+    <Reference Include="System.Net.Http.Primitives">
       <HintPath>..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Primitives.dll</HintPath>
     </Reference>
-    <Reference Include="System.Net.Http.WebRequest" />
   </ItemGroup>
   <ItemGroup>
     <Compile Include="..\Grpc.Core\Version.cs">
       <Link>Version.cs</Link>
     </Compile>
     <Compile Include="Properties\AssemblyInfo.cs" />
-    <Compile Include="OAuth2Interceptors.cs" />
+    <Compile Include="AuthInterceptors.cs" />
   </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   <ItemGroup>
diff --git a/src/csharp/Grpc.Auth/OAuth2Interceptors.cs b/src/csharp/Grpc.Auth/OAuth2Interceptors.cs
deleted file mode 100644
index d628a83..0000000
--- a/src/csharp/Grpc.Auth/OAuth2Interceptors.cs
+++ /dev/null
@@ -1,115 +0,0 @@
-#region Copyright notice and license
-
-// Copyright 2015, Google Inc.
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-//     * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-//     * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-//     * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#endregion
-
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.IO;
-using System.Security.Cryptography.X509Certificates;
-using System.Text.RegularExpressions;
-using System.Threading;
-using System.Threading.Tasks;
-
-using Google.Apis.Auth.OAuth2;
-using Google.Apis.Util;
-using Grpc.Core;
-using Grpc.Core.Utils;
-
-namespace Grpc.Auth
-{
-    public static class OAuth2Interceptors
-    {
-        /// <summary>
-        /// Creates OAuth2 interceptor that will obtain access token from GoogleCredentials.
-        /// </summary>
-        public static MetadataInterceptorDelegate FromCredential(GoogleCredential googleCredential)
-        {
-            var interceptor = new OAuth2Interceptor(googleCredential, SystemClock.Default);
-            return new MetadataInterceptorDelegate(interceptor.InterceptHeaders);
-        }
-
-        /// <summary>
-        /// Creates OAuth2 interceptor that will use given OAuth2 token.
-        /// </summary>
-        /// <param name="oauth2Token"></param>
-        /// <returns></returns>
-        public static MetadataInterceptorDelegate FromAccessToken(string oauth2Token)
-        {
-            Preconditions.CheckNotNull(oauth2Token);
-            return new MetadataInterceptorDelegate((authUri, metadata) =>
-            {
-                metadata.Add(OAuth2Interceptor.CreateBearerTokenHeader(oauth2Token));
-            });
-        }
-
-        /// <summary>
-        /// Injects OAuth2 authorization header into initial metadata (= request headers).
-        /// </summary>
-        private class OAuth2Interceptor
-        {
-            private const string AuthorizationHeader = "Authorization";
-            private const string Schema = "Bearer";
-
-            private ITokenAccess credential;
-            private IClock clock;
-
-            public OAuth2Interceptor(ITokenAccess credential, IClock clock)
-            {
-                this.credential = credential;
-                this.clock = clock;
-            }
-
-            /// <summary>
-            /// Gets access token and requests refreshing it if is going to expire soon.
-            /// </summary>
-            /// <param name="cancellationToken"></param>
-            /// <returns></returns>
-            public string GetAccessToken(string authUri, CancellationToken cancellationToken)
-            {
-                // TODO(jtattermusch): Rethink synchronous wait to obtain the result.
-                return credential.GetAccessTokenForRequestAsync(authUri, cancellationToken: cancellationToken).GetAwaiter().GetResult();
-            }
-
-            public void InterceptHeaders(string authUri, Metadata metadata)
-            {
-                var accessToken = GetAccessToken(authUri, CancellationToken.None);
-                metadata.Add(CreateBearerTokenHeader(accessToken));
-            }
-
-            public static Metadata.Entry CreateBearerTokenHeader(string accessToken)
-            {
-                return new Metadata.Entry(AuthorizationHeader, Schema + " " + accessToken);
-            }
-        }
-    }
-}
diff --git a/src/csharp/Grpc.Core/AsyncClientStreamingCall.cs b/src/csharp/Grpc.Core/AsyncClientStreamingCall.cs
index bf020cd..fb9b562 100644
--- a/src/csharp/Grpc.Core/AsyncClientStreamingCall.cs
+++ b/src/csharp/Grpc.Core/AsyncClientStreamingCall.cs
@@ -89,6 +89,24 @@
         }
 
         /// <summary>
+        /// Gets the call status if the call has already finished.
+        /// Throws InvalidOperationException otherwise.
+        /// </summary>
+        public Status GetStatus()
+        {
+            return getStatusFunc();
+        }
+
+        /// <summary>
+        /// Gets the call trailing metadata if the call has already finished.
+        /// Throws InvalidOperationException otherwise.
+        /// </summary>
+        public Metadata GetTrailers()
+        {
+            return getTrailersFunc();
+        }
+
+        /// <summary>
         /// Provides means to cleanup after the call.
         /// If the call has already finished normally (request stream has been completed and call result has been received), doesn't do anything.
         /// Otherwise, requests cancellation of the call which should terminate all pending async operations associated with the call.
diff --git a/src/csharp/Grpc.Core/AsyncDuplexStreamingCall.cs b/src/csharp/Grpc.Core/AsyncDuplexStreamingCall.cs
index 0979de6..183c842 100644
--- a/src/csharp/Grpc.Core/AsyncDuplexStreamingCall.cs
+++ b/src/csharp/Grpc.Core/AsyncDuplexStreamingCall.cs
@@ -32,8 +32,6 @@
 #endregion
 
 using System;
-using System.Runtime.CompilerServices;
-using System.Threading.Tasks;
 
 namespace Grpc.Core
 {
diff --git a/src/csharp/Grpc.Core/AsyncServerStreamingCall.cs b/src/csharp/Grpc.Core/AsyncServerStreamingCall.cs
index 380efcd..ab2049f 100644
--- a/src/csharp/Grpc.Core/AsyncServerStreamingCall.cs
+++ b/src/csharp/Grpc.Core/AsyncServerStreamingCall.cs
@@ -32,8 +32,6 @@
 #endregion
 
 using System;
-using System.Runtime.CompilerServices;
-using System.Threading.Tasks;
 
 namespace Grpc.Core
 {
diff --git a/src/csharp/Grpc.Core/ChannelOptions.cs b/src/csharp/Grpc.Core/ChannelOptions.cs
index 0cb2953..ad54b46 100644
--- a/src/csharp/Grpc.Core/ChannelOptions.cs
+++ b/src/csharp/Grpc.Core/ChannelOptions.cs
@@ -71,7 +71,7 @@
         /// Creates a channel option with an integer value.
         /// </summary>
         /// <param name="name">Name.</param>
-        /// <param name="stringValue">String value.</param>
+        /// <param name="intValue">Integer value.</param>
         public ChannelOption(string name, int intValue)
         {
             this.type = OptionType.Integer;
diff --git a/src/csharp/Grpc.Core/ClientBase.cs b/src/csharp/Grpc.Core/ClientBase.cs
index f240d77..7bc100c 100644
--- a/src/csharp/Grpc.Core/ClientBase.cs
+++ b/src/csharp/Grpc.Core/ClientBase.cs
@@ -32,15 +32,15 @@
 #endregion
 
 using System;
-using System.Collections.Generic;
 using System.Text.RegularExpressions;
-
-using Grpc.Core.Internal;
-using Grpc.Core.Utils;
+using System.Threading.Tasks;
 
 namespace Grpc.Core
 {
-    public delegate void MetadataInterceptorDelegate(string authUri, Metadata metadata);
+    /// <summary>
+    /// Interceptor for call headers.
+    /// </summary>
+    public delegate void HeaderInterceptor(IMethod method, string authUri, Metadata metadata);
 
     /// <summary>
     /// Base class for client-side stubs.
@@ -60,10 +60,10 @@
         }
 
         /// <summary>
-        /// Can be used to register a custom header (initial metadata) interceptor.
-        /// The delegate each time before a new call on this client is started.
+        /// Can be used to register a custom header (request metadata) interceptor.
+        /// The interceptor is invoked each time a new call on this client is started.
         /// </summary>
-        public MetadataInterceptorDelegate HeaderInterceptor
+        public HeaderInterceptor HeaderInterceptor
         {
             get;
             set;
@@ -107,7 +107,7 @@
                     options = options.WithHeaders(new Metadata());
                 }
                 var authUri = authUriBase != null ? authUriBase + method.ServiceName : null;
-                interceptor(authUri, options.Headers);
+                interceptor(method, authUri, options.Headers);
             }
             return new CallInvocationDetails<TRequest, TResponse>(channel, method, Host, options);
         }
diff --git a/src/csharp/Grpc.Core/Method.cs b/src/csharp/Grpc.Core/Method.cs
index 4c208b4..4c53285 100644
--- a/src/csharp/Grpc.Core/Method.cs
+++ b/src/csharp/Grpc.Core/Method.cs
@@ -55,9 +55,36 @@
     }
 
     /// <summary>
+    /// A non-generic representation of a remote method.
+    /// </summary>
+    public interface IMethod
+    {
+        /// <summary>
+        /// Gets the type of the method.
+        /// </summary>
+        MethodType Type { get; }
+
+        /// <summary>
+        /// Gets the name of the service to which this method belongs.
+        /// </summary>
+        string ServiceName { get; }
+
+        /// <summary>
+        /// Gets the unqualified name of the method.
+        /// </summary>
+        string Name { get; }
+
+        /// <summary>
+        /// Gets the fully qualified name of the method. On the server side, methods are dispatched
+        /// based on this name.
+        /// </summary>
+        string FullName { get; }
+    }
+
+    /// <summary>
     /// A description of a remote method.
     /// </summary>
-    public class Method<TRequest, TResponse>
+    public class Method<TRequest, TResponse> : IMethod
     {
         readonly MethodType type;
         readonly string serviceName;
diff --git a/src/csharp/Grpc.IntegrationTesting/InteropClient.cs b/src/csharp/Grpc.IntegrationTesting/InteropClient.cs
index 385ca92..f4b0a10 100644
--- a/src/csharp/Grpc.IntegrationTesting/InteropClient.cs
+++ b/src/csharp/Grpc.IntegrationTesting/InteropClient.cs
@@ -308,7 +308,7 @@
             Console.WriteLine("running service_account_creds");
             var credential = await GoogleCredential.GetApplicationDefaultAsync();
             credential = credential.CreateScoped(new[] { AuthScope });
-            client.HeaderInterceptor = OAuth2Interceptors.FromCredential(credential);
+            client.HeaderInterceptor = AuthInterceptors.FromCredential(credential);
 
             var request = SimpleRequest.CreateBuilder()
                 .SetResponseType(PayloadType.COMPRESSABLE)
@@ -332,7 +332,7 @@
             Console.WriteLine("running compute_engine_creds");
             var credential = await GoogleCredential.GetApplicationDefaultAsync();
             Assert.IsFalse(credential.IsCreateScopedRequired);
-            client.HeaderInterceptor = OAuth2Interceptors.FromCredential(credential);
+            client.HeaderInterceptor = AuthInterceptors.FromCredential(credential);
             
             var request = SimpleRequest.CreateBuilder()
                 .SetResponseType(PayloadType.COMPRESSABLE)
@@ -357,7 +357,7 @@
             var credential = await GoogleCredential.GetApplicationDefaultAsync();
             // check this a credential with scope support, but don't add the scope.
             Assert.IsTrue(credential.IsCreateScopedRequired);
-            client.HeaderInterceptor = OAuth2Interceptors.FromCredential(credential);
+            client.HeaderInterceptor = AuthInterceptors.FromCredential(credential);
 
             var request = SimpleRequest.CreateBuilder()
                 .SetResponseType(PayloadType.COMPRESSABLE)
@@ -381,7 +381,7 @@
             ITokenAccess credential = (await GoogleCredential.GetApplicationDefaultAsync()).CreateScoped(new[] { AuthScope });
             string oauth2Token = await credential.GetAccessTokenForRequestAsync();
 
-            client.HeaderInterceptor = OAuth2Interceptors.FromAccessToken(oauth2Token);
+            client.HeaderInterceptor = AuthInterceptors.FromAccessToken(oauth2Token);
 
             var request = SimpleRequest.CreateBuilder()
                 .SetFillUsername(true)
@@ -401,7 +401,7 @@
 
             ITokenAccess credential = (await GoogleCredential.GetApplicationDefaultAsync()).CreateScoped(new[] { AuthScope });
             string oauth2Token = await credential.GetAccessTokenForRequestAsync();
-            var headerInterceptor = OAuth2Interceptors.FromAccessToken(oauth2Token);
+            var headerInterceptor = AuthInterceptors.FromAccessToken(oauth2Token);
 
             var request = SimpleRequest.CreateBuilder()
                 .SetFillUsername(true)
@@ -409,7 +409,7 @@
                 .Build();
 
             var headers = new Metadata();
-            headerInterceptor("", headers);
+            headerInterceptor(null, "", headers);
             var response = client.UnaryCall(request, headers: headers);
 
             Assert.AreEqual(AuthScopeResponse, response.OauthScope);
diff --git a/src/node/README.md b/src/node/README.md
index 7d3d8c7..b641153 100644
--- a/src/node/README.md
+++ b/src/node/README.md
@@ -5,11 +5,35 @@
 
 ## PREREQUISITES
 - `node`: This requires `node` to be installed. If you instead have the `nodejs` executable on Debian, you should install the [`nodejs-legacy`](https://packages.debian.org/sid/nodejs-legacy) package.
-- [homebrew][] on Mac OS X, [linuxbrew][] on Linux.  These simplify the installation of the gRPC C core.
+- [homebrew][] on Mac OS X.  These simplify the installation of the gRPC C core.
 
 ## INSTALLATION
-On Mac OS X, install [homebrew][]. On Linux, install [linuxbrew][].
-Run the following command to install gRPC Node.js.
+
+**Linux (Debian):**
+
+Add [Debian unstable][] to your `sources.list` file. Example:
+
+```sh
+echo "deb http://ftp.us.debian.org/debian unstable main contrib non-free" | \
+sudo tee -a /etc/apt/sources.list
+```
+
+Install the gRPC Debian package
+
+```sh
+sudo apt-get update
+sudo apt-get install libgrpc-dev
+```
+
+Install the gRPC NPM package
+
+```sh
+npm install grpc
+```
+
+**Mac OS X**
+
+Install [homebrew][]. Run the following command to install gRPC Node.js.
 ```sh
 $ curl -fsSL https://goo.gl/getgrpc | bash -s nodejs
 ```
@@ -88,5 +112,5 @@
 An object with factory methods for creating credential objects for servers.
 
 [homebrew]:http://brew.sh
-[linuxbrew]:https://github.com/Homebrew/linuxbrew#installation
 [gRPC install script]:https://raw.githubusercontent.com/grpc/homebrew-grpc/master/scripts/install
+[Debian unstable]:https://www.debian.org/releases/sid/
diff --git a/src/node/ext/call.cc b/src/node/ext/call.cc
index a79a474..18858fa 100644
--- a/src/node/ext/call.cc
+++ b/src/node/ext/call.cc
@@ -207,6 +207,13 @@
     if (!::node::Buffer::HasInstance(value)) {
       return false;
     }
+    Handle<Object> object_value = value->ToObject();
+    if (object_value->HasOwnProperty(NanNew("grpcWriteFlags"))) {
+      Handle<Value> flag_value = object_value->Get(NanNew("grpcWriteFlags"));
+      if (flag_value->IsUint32()) {
+        out->flags = flag_value->Uint32Value() & GRPC_WRITE_USED_MASK;
+      }
+    }
     out->data.send_message = BufferToByteBuffer(value);
     Persistent<Value> *handle = new Persistent<Value>();
     NanAssignPersistent(*handle, value);
@@ -457,10 +464,6 @@
                           NanNew<FunctionTemplate>(GetPeer)->GetFunction());
   NanAssignPersistent(fun_tpl, tpl);
   Handle<Function> ctr = tpl->GetFunction();
-  ctr->Set(NanNew("WRITE_BUFFER_HINT"),
-           NanNew<Uint32, uint32_t>(GRPC_WRITE_BUFFER_HINT));
-  ctr->Set(NanNew("WRITE_NO_COMPRESS"),
-           NanNew<Uint32, uint32_t>(GRPC_WRITE_NO_COMPRESS));
   exports->Set(NanNew("Call"), ctr);
   constructor = new NanCallback(ctr);
 }
@@ -620,7 +623,7 @@
       call->wrapped_call, &ops[0], nops, new struct tag(
           callback, op_vector.release(), resources), NULL);
   if (error != GRPC_CALL_OK) {
-    return NanThrowError("startBatch failed", error);
+    return NanThrowError(nanErrorWithCode("startBatch failed", error));
   }
   CompletionQueueAsyncWorker::Next();
   NanReturnUndefined();
@@ -634,7 +637,7 @@
   Call *call = ObjectWrap::Unwrap<Call>(args.This());
   grpc_call_error error = grpc_call_cancel(call->wrapped_call, NULL);
   if (error != GRPC_CALL_OK) {
-    return NanThrowError("cancel failed", error);
+    return NanThrowError(nanErrorWithCode("cancel failed", error));
   }
   NanReturnUndefined();
 }
diff --git a/src/node/ext/call.h b/src/node/ext/call.h
index 6acda76..ef6e5fc 100644
--- a/src/node/ext/call.h
+++ b/src/node/ext/call.h
@@ -51,6 +51,19 @@
 using std::unique_ptr;
 using std::shared_ptr;
 
+/**
+ * Helper function for throwing errors with a grpc_call_error value.
+ * Modified from the answer by Gus Goose to
+ * http://stackoverflow.com/questions/31794200.
+ */
+inline v8::Local<v8::Value> nanErrorWithCode(const char *msg,
+                                             grpc_call_error code) {
+    NanEscapableScope();
+    v8::Local<v8::Object> err = NanError(msg).As<v8::Object>();
+    err->Set(NanNew("code"), NanNew<v8::Uint32>(code));
+    return NanEscapeScope(err);
+}
+
 v8::Handle<v8::Value> ParseMetadata(const grpc_metadata_array *metadata_array);
 
 class PersistentHolder {
diff --git a/src/node/ext/node_grpc.cc b/src/node/ext/node_grpc.cc
index d93dafd..0cf30da 100644
--- a/src/node/ext/node_grpc.cc
+++ b/src/node/ext/node_grpc.cc
@@ -196,6 +196,16 @@
   channel_state->Set(NanNew("FATAL_FAILURE"), FATAL_FAILURE);
 }
 
+void InitWriteFlags(Handle<Object> exports) {
+  NanScope();
+  Handle<Object> write_flags = NanNew<Object>();
+  exports->Set(NanNew("writeFlags"), write_flags);
+  Handle<Value> BUFFER_HINT(NanNew<Uint32, uint32_t>(GRPC_WRITE_BUFFER_HINT));
+  write_flags->Set(NanNew("BUFFER_HINT"), BUFFER_HINT);
+  Handle<Value> NO_COMPRESS(NanNew<Uint32, uint32_t>(GRPC_WRITE_NO_COMPRESS));
+  write_flags->Set(NanNew("NO_COMPRESS"), NO_COMPRESS);
+}
+
 void init(Handle<Object> exports) {
   NanScope();
   grpc_init();
@@ -204,6 +214,7 @@
   InitOpTypeConstants(exports);
   InitPropagateConstants(exports);
   InitConnectivityStateConstants(exports);
+  InitWriteFlags(exports);
 
   grpc::node::Call::Init(exports);
   grpc::node::Channel::Init(exports);
diff --git a/src/node/ext/server.cc b/src/node/ext/server.cc
index 8e39644..01217bc 100644
--- a/src/node/ext/server.cc
+++ b/src/node/ext/server.cc
@@ -235,7 +235,7 @@
       new struct tag(new NanCallback(args[0].As<Function>()), ops.release(),
                      shared_ptr<Resources>(nullptr)));
   if (error != GRPC_CALL_OK) {
-    return NanThrowError("requestCall failed", error);
+    return NanThrowError(nanErrorWithCode("requestCall failed", error));
   }
   CompletionQueueAsyncWorker::Next();
   NanReturnUndefined();
diff --git a/src/node/index.js b/src/node/index.js
index 93c65ac..889b0ac 100644
--- a/src/node/index.js
+++ b/src/node/index.js
@@ -145,6 +145,11 @@
 exports.callError = grpc.callError;
 
 /**
+ * Write flag name to code number mapping
+ */
+exports.writeFlags = grpc.writeFlags;
+
+/**
  * Credentials factories
  */
 exports.Credentials = grpc.Credentials;
diff --git a/src/node/src/client.js b/src/node/src/client.js
index 50cbf4a..48fe0dd 100644
--- a/src/node/src/client.js
+++ b/src/node/src/client.js
@@ -79,13 +79,19 @@
  * implementation of a method needed for implementing stream.Writable.
  * @access private
  * @param {Buffer} chunk The chunk to write
- * @param {string} encoding Ignored
+ * @param {string} encoding Used to pass write flags
  * @param {function(Error=)} callback Called when the write is complete
  */
 function _write(chunk, encoding, callback) {
   /* jshint validthis: true */
   var batch = {};
-  batch[grpc.opType.SEND_MESSAGE] = this.serialize(chunk);
+  var message = this.serialize(chunk);
+  if (_.isFinite(encoding)) {
+    /* Attach the encoding if it is a finite number. This is the closest we
+     * can get to checking that it is valid flags */
+    message.grpcWriteFlags = encoding;
+  }
+  batch[grpc.opType.SEND_MESSAGE] = message;
   this.call.startBatch(batch, function(err, event) {
     if (err) {
       // Something has gone wrong. Stop writing by failing to call callback
@@ -273,8 +279,10 @@
         return;
       }
       var client_batch = {};
+      var message = serialize(argument);
+      message.grpcWriteFlags = options.flags;
       client_batch[grpc.opType.SEND_INITIAL_METADATA] = metadata;
-      client_batch[grpc.opType.SEND_MESSAGE] = serialize(argument);
+      client_batch[grpc.opType.SEND_MESSAGE] = message;
       client_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true;
       client_batch[grpc.opType.RECV_INITIAL_METADATA] = true;
       client_batch[grpc.opType.RECV_MESSAGE] = true;
@@ -407,9 +415,11 @@
         return;
       }
       var start_batch = {};
+      var message = serialize(argument);
+      message.grpcWriteFlags = options.flags;
       start_batch[grpc.opType.SEND_INITIAL_METADATA] = metadata;
       start_batch[grpc.opType.RECV_INITIAL_METADATA] = true;
-      start_batch[grpc.opType.SEND_MESSAGE] = serialize(argument);
+      start_batch[grpc.opType.SEND_MESSAGE] = message;
       start_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true;
       call.startBatch(start_batch, function(err, response) {
         if (err) {
diff --git a/src/node/src/server.js b/src/node/src/server.js
index 8b86173..5037aba 100644
--- a/src/node/src/server.js
+++ b/src/node/src/server.js
@@ -115,8 +115,10 @@
  * @param {function(*):Buffer=} serialize Serialization function for the
  *     response
  * @param {Object=} metadata Optional trailing metadata to send with status
+ * @param {number=} flags Flags for modifying how the message is sent.
+ *     Defaults to 0.
  */
-function sendUnaryResponse(call, value, serialize, metadata) {
+function sendUnaryResponse(call, value, serialize, metadata, flags) {
   var end_batch = {};
   var status = {
     code: grpc.status.OK,
@@ -130,7 +132,9 @@
     end_batch[grpc.opType.SEND_INITIAL_METADATA] = {};
     call.metadataSent = true;
   }
-  end_batch[grpc.opType.SEND_MESSAGE] = serialize(value);
+  var message = serialize(value);
+  message.grpcWriteFlags = flags;
+  end_batch[grpc.opType.SEND_MESSAGE] = message;
   end_batch[grpc.opType.SEND_STATUS_FROM_SERVER] = status;
   call.startBatch(end_batch, function (){});
 }
@@ -254,7 +258,7 @@
  * for implementing stream.Writable.
  * @access private
  * @param {Buffer} chunk The chunk of data to write
- * @param {string} encoding Ignored
+ * @param {string} encoding Used to pass write flags
  * @param {function(Error=)} callback Callback to indicate that the write is
  *     complete
  */
@@ -265,7 +269,13 @@
     batch[grpc.opType.SEND_INITIAL_METADATA] = {};
     this.call.metadataSent = true;
   }
-  batch[grpc.opType.SEND_MESSAGE] = this.serialize(chunk);
+  var message = this.serialize(chunk);
+  if (_.isFinite(encoding)) {
+    /* Attach the encoding if it is a finite number. This is the closest we
+     * can get to checking that it is valid flags */
+    message.grpcWriteFlags = encoding;
+  }
+  batch[grpc.opType.SEND_MESSAGE] = message;
   this.call.startBatch(batch, function(err, value) {
     if (err) {
       this.emit('error', err);
@@ -450,14 +460,14 @@
     if (emitter.cancelled) {
       return;
     }
-    handler.func(emitter, function sendUnaryData(err, value, trailer) {
+    handler.func(emitter, function sendUnaryData(err, value, trailer, flags) {
       if (err) {
         if (trailer) {
           err.metadata = trailer;
         }
         handleError(call, err);
       } else {
-        sendUnaryResponse(call, value, handler.serialize, trailer);
+        sendUnaryResponse(call, value, handler.serialize, trailer, flags);
       }
     });
   });
@@ -514,7 +524,7 @@
   });
   waitForCancel(call, stream);
   stream.metadata = metadata;
-  handler.func(stream, function(err, value, trailer) {
+  handler.func(stream, function(err, value, trailer, flags) {
     stream.terminate();
     if (err) {
       if (trailer) {
@@ -522,7 +532,7 @@
       }
       handleError(call, err);
     } else {
-      sendUnaryResponse(call, value, handler.serialize, trailer);
+      sendUnaryResponse(call, value, handler.serialize, trailer, flags);
     }
   });
 }
diff --git a/src/php/README.md b/src/php/README.md
index 1804606..f432935 100644
--- a/src/php/README.md
+++ b/src/php/README.md
@@ -7,17 +7,17 @@
 
 Alpha : Ready for early adopters
 
-## ENVIRONMENT
+## Environment
 
 Prerequisite: PHP 5.5 or later, `phpunit`, `pecl`
 
-Linux:
+**Linux:**
 
 ```sh
 $ sudo apt-get install php5 php5-dev phpunit php-pear
 ```
 
-OS X:
+**Mac OS X:**
 
 ```sh
 $ curl https://phar.phpunit.de/phpunit.phar -o phpunit.phar
@@ -28,10 +28,39 @@
 $ sudo php -d detect_unicode=0 go-pear.phar
 ```
 
-## Build from Homebrew
+## Quick Install
 
-On Mac OS X, install [homebrew][]. On Linux, install [linuxbrew][]. Run the following command to
-install gRPC.
+**Linux (Debian):**
+
+Add [Debian unstable][] to your `sources.list` file. Example:
+
+```sh
+echo "deb http://ftp.us.debian.org/debian unstable main contrib non-free" | \
+sudo tee -a /etc/apt/sources.list
+```
+
+Install the gRPC Debian package
+
+```sh
+sudo apt-get update
+sudo apt-get install libgrpc-dev
+```
+
+Install the gRPC PHP extension
+
+```sh
+sudo pecl install grpc-alpha
+```
+
+**Mac OS X:**
+
+Install [homebrew][]. Example:
+
+```sh
+ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
+```
+
+Install the gRPC core library and the PHP extension in one step
 
 ```sh
 $ curl -fsSL https://goo.gl/getgrpc | bash -s php
@@ -39,6 +68,7 @@
 
 This will download and run the [gRPC install script][] and compile the gRPC PHP extension.
 
+
 ## Build from Source
 
 Clone this repository
@@ -71,7 +101,7 @@
 Install the gRPC PHP extension
 
 ```sh
-$ sudo pecl install grpc
+$ sudo pecl install grpc-alpha
 ```
 
 OR
@@ -140,6 +170,6 @@
 ```
 
 [homebrew]:http://brew.sh
-[linuxbrew]:https://github.com/Homebrew/linuxbrew#installation
 [gRPC install script]:https://raw.githubusercontent.com/grpc/homebrew-grpc/master/scripts/install
 [Node]:https://github.com/grpc/grpc/tree/master/src/node/examples
+[Debian unstable]:https://www.debian.org/releases/sid/
diff --git a/src/php/ext/grpc/call.c b/src/php/ext/grpc/call.c
index 4e40dc4..6009a8d 100644
--- a/src/php/ext/grpc/call.c
+++ b/src/php/ext/grpc/call.c
@@ -273,7 +273,6 @@
   grpc_byte_buffer *message;
   int cancelled;
   grpc_call_error error;
-  grpc_event event;
   zval *result;
   char *message_str;
   size_t message_len;
@@ -409,14 +408,8 @@
                          (long)error TSRMLS_CC);
     goto cleanup;
   }
-  event = grpc_completion_queue_pluck(completion_queue, call->wrapped,
-                                      gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
-  if (!event.success) {
-    zend_throw_exception(spl_ce_LogicException,
-                         "The batch failed for some reason",
-                         1 TSRMLS_CC);
-    goto cleanup;
-  }
+  grpc_completion_queue_pluck(completion_queue, call->wrapped,
+                              gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
   for (int i = 0; i < op_num; i++) {
     switch(ops[i].op) {
       case GRPC_OP_SEND_INITIAL_METADATA:
diff --git a/src/php/tests/interop/interop_client.php b/src/php/tests/interop/interop_client.php
index 44e6242..376d306 100755
--- a/src/php/tests/interop/interop_client.php
+++ b/src/php/tests/interop/interop_client.php
@@ -271,7 +271,7 @@
 }
 
 function timeoutOnSleepingServer($stub) {
-  $call = $stub->FullDuplexCall(array('timeout' => 500000));
+  $call = $stub->FullDuplexCall(array('timeout' => 1000));
   $request = new grpc\testing\StreamingOutputCallRequest();
   $request->setResponseType(grpc\testing\PayloadType::COMPRESSABLE);
   $response_parameters = new grpc\testing\ResponseParameters();
diff --git a/src/python/README.md b/src/python/README.md
index 2beb3a9..de0142d 100644
--- a/src/python/README.md
+++ b/src/python/README.md
@@ -9,12 +9,36 @@
 PREREQUISITES
 -------------
 - Python 2.7, virtualenv, pip
-- [homebrew][] on Mac OS X, [linuxbrew][] on Linux.  These simplify the installation of the gRPC C core.
+- [homebrew][] on Mac OS X.  These simplify the installation of the gRPC C core.
 
 INSTALLATION
 -------------
-On Mac OS X, install [homebrew][]. On Linux, install [linuxbrew][].
-Run the following command to install gRPC Python.
+
+**Linux (Debian):**
+
+Add [Debian unstable][] to your `sources.list` file. Example:
+
+```sh
+echo "deb http://ftp.us.debian.org/debian unstable main contrib non-free" | \
+sudo tee -a /etc/apt/sources.list
+```
+
+Install the gRPC Debian package
+
+```sh
+sudo apt-get update
+sudo apt-get install libgrpc-dev
+```
+
+Install the gRPC Python module
+
+```sh
+sudo pip install grpcio
+```
+
+**Mac OS X**
+
+Install [homebrew][]. Run the following command to install gRPC Python.
 ```sh
 $ curl -fsSL https://goo.gl/getgrpc | bash -s python
 ```
@@ -27,11 +51,6 @@
 BUILDING FROM SOURCE
 ---------------------
 - Clone this repository
-- Build the gRPC core from the root of the
-  [gRPC Git repository](https://github.com/grpc/grpc)
-```
-$ make shared_c static_c
-```
 
 - Use build_python.sh to build the Python code and install it into a virtual environment
 ```
@@ -60,7 +79,7 @@
 ```
 
 [homebrew]:http://brew.sh
-[linuxbrew]:https://github.com/Homebrew/linuxbrew#installation
 [gRPC install script]:https://raw.githubusercontent.com/grpc/homebrew-grpc/master/scripts/install
 [Quick Start]:http://www.grpc.io/docs/tutorials/basic/python.html
 [detailed example]:http://www.grpc.io/docs/installation/python.html
+[Debian unstable]:https://www.debian.org/releases/sid/
diff --git a/src/python/grpcio/README.rst b/src/python/grpcio/README.rst
index 00bdecf..c7b5a3b 100644
--- a/src/python/grpcio/README.rst
+++ b/src/python/grpcio/README.rst
@@ -6,7 +6,7 @@
 Dependencies
 ------------
 
-Ensure you have installed the gRPC core.  On Mac OS X, install homebrew_. On Linux, install linuxbrew_.
+Ensure you have installed the gRPC core.  On Mac OS X, install homebrew_.
 Run the following command to install gRPC Python.
 
 ::
@@ -19,5 +19,4 @@
 
 .. _`install from source`: https://github.com/grpc/grpc/blob/master/src/python/README.md#building-from-source
 .. _homebrew: http://brew.sh
-.. _linuxbrew: https://github.com/Homebrew/linuxbrew#installation
 .. _`gRPC install script`: https://raw.githubusercontent.com/grpc/homebrew-grpc/master/scripts/install
diff --git a/src/ruby/README.md b/src/ruby/README.md
index 4b657c0..f8902e3 100644
--- a/src/ruby/README.md
+++ b/src/ruby/README.md
@@ -12,12 +12,36 @@
 -------------
 
 - Ruby 2.x. The gRPC API uses keyword args.
-- [homebrew][] on Mac OS X, [linuxbrew][] on Linux.  These simplify the installation of the gRPC C core.
+- [homebrew][] on Mac OS X.  These simplify the installation of the gRPC C core.
 
 INSTALLATION
 ---------------
-On Mac OS X, install [homebrew][]. On Linux, install [linuxbrew][].
-Run the following command to install gRPC Ruby.
+
+**Linux (Debian):**
+
+Add [Debian unstable][] to your `sources.list` file. Example:
+
+```sh
+echo "deb http://ftp.us.debian.org/debian unstable main contrib non-free" | \
+sudo tee -a /etc/apt/sources.list
+```
+
+Install the gRPC Debian package
+
+```sh
+sudo apt-get update
+sudo apt-get install libgrpc-dev
+```
+
+Install the gRPC Ruby package
+
+```sh
+gem install grpc
+```
+
+**Mac OS X**
+
+Install [homebrew][]. Run the following command to install gRPC Ruby.
 ```sh
 $ curl -fsSL https://goo.gl/getgrpc | bash -s ruby
 ```
@@ -26,12 +50,6 @@
 BUILD FROM SOURCE
 ---------------------
 - Clone this repository
-- Build the gRPC C core
-E.g, from the root of the gRPC [Git repository](https://github.com/google/grpc)
-```sh
-$ cd ../..
-$ make && sudo make install
-```
 
 - Install Ruby 2.x. Consider doing this with [RVM](http://rvm.io), it's a nice way of controlling
   the exact ruby version that's used.
@@ -77,8 +95,8 @@
   GRPC.logger.info("Answer: #{resp.inspect}")
   ```
 [homebrew]:http://brew.sh
-[linuxbrew]:https://github.com/Homebrew/linuxbrew#installation
 [gRPC install script]:https://raw.githubusercontent.com/grpc/homebrew-grpc/master/scripts/install
 [ruby extensions]:http://guides.rubygems.org/gems-with-extensions/
 [rubydoc]: http://www.rubydoc.info/gems/grpc
 [grpc.io]: http://www.grpc.io/docs/installation/ruby.html
+[Debian unstable]:https://www.debian.org/releases/sid/
diff --git a/src/ruby/lib/grpc/logconfig.rb b/src/ruby/lib/grpc/logconfig.rb
index 2bb7c86..6b442fe 100644
--- a/src/ruby/lib/grpc/logconfig.rb
+++ b/src/ruby/lib/grpc/logconfig.rb
@@ -54,5 +54,6 @@
     LOGGER = NoopLogger.new
   end
 
-  include DefaultLogger unless method_defined?(:logger)
+  # Inject the noop #logger if no module-level logger method has been injected.
+  extend DefaultLogger unless methods.include?(:logger)
 end