1 #region Copyright notice and license 2 3 // Copyright 2019 The gRPC Authors. 4 // 5 // Licensed under the Apache License, Version 2.0 (the "License"); 6 // you may not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, software 12 // distributed under the License is distributed on an "AS IS" BASIS, 13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 // See the License for the specific language governing permissions and 15 // limitations under the License. 16 17 #endregion 18 19 using System; 20 using System.Collections.Generic; 21 using System.Net.Sockets; 22 using System.Linq; 23 using System.Threading; 24 using System.Threading.Tasks; 25 using Grpc.Core; 26 using Grpc.Core.Logging; 27 using Grpc.Core.Utils; 28 using Grpc.Core.Internal; 29 using Grpc.Testing; 30 using NUnit.Framework; 31 32 namespace Grpc.IntegrationTesting 33 { 34 /// <summary> 35 /// See https://github.com/grpc/grpc/issues/18074, this test is meant to 36 /// try to trigger the described bug. 37 /// Runs interop tests in-process, with that client using a target 38 /// name that using a target name that triggers interaction with 39 /// external DNS servers (even though it resolves to the in-proc server). 40 /// </summary> 41 public class ExternalDnsWithTracingClientServerTest 42 { 43 Server server; 44 Channel channel; 45 TestService.TestServiceClient client; 46 47 [OneTimeSetUp] Init()48 public void Init() 49 { 50 // We only care about running this test on Windows (see #18074) 51 // TODO(jtattermusch): We could run it on Linux and Mac as well, 52 // but there are two issues. 53 // 1. Due to https://github.com/grpc/grpc/issues/14963, setting the 54 // enviroment variables actually has no effect on CoreCLR. 55 // 2. On mono the test with enabled tracing sometimes times out 56 // due to suspected mono-related issue on shutdown 57 // See https://github.com/grpc/grpc/issues/18126 58 if (PlatformApis.IsWindows) 59 { 60 Environment.SetEnvironmentVariable("GRPC_TRACE", "all"); 61 Environment.SetEnvironmentVariable("GRPC_VERBOSITY", "DEBUG"); 62 var newLogger = new SocketUsingLogger(GrpcEnvironment.Logger); 63 GrpcEnvironment.SetLogger(newLogger); 64 } 65 // Disable SO_REUSEPORT to prevent https://github.com/grpc/grpc/issues/10755 66 server = new Server(new[] { new ChannelOption(ChannelOptions.SoReuseport, 0) }) 67 { 68 Services = { TestService.BindService(new TestServiceImpl()) }, 69 Ports = { { "[::1]", ServerPort.PickUnused, ServerCredentials.Insecure } }, 70 // reduce the number of request call tokens to 71 // avoid flooding the logs with token-related messages 72 RequestCallTokensPerCompletionQueue = 3, 73 }; 74 server.Start(); 75 int port = server.Ports.Single().BoundPort; 76 channel = new Channel("loopback6.unittest.grpc.io", port, ChannelCredentials.Insecure); 77 client = new TestService.TestServiceClient(channel); 78 } 79 80 [OneTimeTearDown] Cleanup()81 public void Cleanup() 82 { 83 channel.ShutdownAsync().Wait(); 84 server.ShutdownAsync().Wait(); 85 } 86 87 [Test] EmptyUnary()88 public void EmptyUnary() 89 { 90 InteropClient.RunEmptyUnary(client); 91 } 92 } 93 94 /// <summary> 95 /// Logger which does some socket operation after delegating 96 /// actual logging to its delegate logger. The main goal is to 97 /// reset the current thread's WSA error status. 98 /// The only reason for the delegateLogger is to continue 99 /// to have this test display debug logs. 100 /// </summary> 101 internal sealed class SocketUsingLogger : ILogger 102 { 103 private ILogger delegateLogger; 104 SocketUsingLogger(ILogger delegateLogger)105 public SocketUsingLogger(ILogger delegateLogger) { 106 this.delegateLogger = delegateLogger; 107 } 108 Debug(string message)109 public void Debug(string message) 110 { 111 MyLog(() => delegateLogger.Debug(message)); 112 } 113 Debug(string format, params object[] formatArgs)114 public void Debug(string format, params object[] formatArgs) 115 { 116 MyLog(() => delegateLogger.Debug(format, formatArgs)); 117 } 118 Error(string message)119 public void Error(string message) 120 { 121 MyLog(() => delegateLogger.Error(message)); 122 } 123 Error(Exception exception, string message)124 public void Error(Exception exception, string message) 125 { 126 MyLog(() => delegateLogger.Error(exception, message)); 127 } 128 Error(string format, params object[] formatArgs)129 public void Error(string format, params object[] formatArgs) 130 { 131 MyLog(() => delegateLogger.Error(format, formatArgs)); 132 } 133 ForType()134 public ILogger ForType<T>() 135 { 136 return this; 137 } 138 Info(string message)139 public void Info(string message) 140 { 141 MyLog(() => delegateLogger.Info(message)); 142 } 143 Info(string format, params object[] formatArgs)144 public void Info(string format, params object[] formatArgs) 145 { 146 MyLog(() => delegateLogger.Info(format, formatArgs)); 147 } 148 Warning(string message)149 public void Warning(string message) 150 { 151 MyLog(() => delegateLogger.Warning(message)); 152 } 153 Warning(Exception exception, string message)154 public void Warning(Exception exception, string message) 155 { 156 MyLog(() => delegateLogger.Warning(exception, message)); 157 } 158 Warning(string format, params object[] formatArgs)159 public void Warning(string format, params object[] formatArgs) 160 { 161 MyLog(() => delegateLogger.Warning(format, formatArgs)); 162 } 163 MyLog(Action delegateLog)164 private void MyLog(Action delegateLog) 165 { 166 delegateLog(); 167 // Create and close a socket, just in order to affect 168 // the WSA (on Windows) error status of the current thread. 169 Socket s = new Socket(AddressFamily.InterNetwork, 170 SocketType.Stream, 171 ProtocolType.Tcp); 172 173 s.Dispose(); 174 } 175 } 176 } 177