1 /* 2 * Copyright 2015 The gRPC Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package io.grpc.netty; 18 19 import static com.google.common.truth.Truth.assertThat; 20 import static com.google.common.truth.TruthJUnit.assume; 21 import static org.junit.Assert.assertEquals; 22 import static org.junit.Assert.assertNull; 23 import static org.junit.Assert.assertSame; 24 25 import com.google.common.base.MoreObjects; 26 import io.grpc.InternalChannelz; 27 import io.grpc.InternalChannelz.SocketOptions; 28 import io.grpc.Metadata; 29 import io.grpc.Status; 30 import io.grpc.internal.GrpcUtil; 31 import io.netty.channel.Channel; 32 import io.netty.channel.ChannelFactory; 33 import io.netty.channel.ChannelOption; 34 import io.netty.channel.ConnectTimeoutException; 35 import io.netty.channel.EventLoopGroup; 36 import io.netty.channel.ServerChannel; 37 import io.netty.channel.WriteBufferWaterMark; 38 import io.netty.channel.embedded.EmbeddedChannel; 39 import io.netty.channel.socket.nio.NioSocketChannel; 40 import io.netty.handler.codec.http2.DefaultHttp2Headers; 41 import io.netty.handler.codec.http2.Http2Error; 42 import io.netty.handler.codec.http2.Http2Exception; 43 import io.netty.handler.codec.http2.Http2Headers; 44 import io.netty.util.AsciiString; 45 import java.nio.channels.UnresolvedAddressException; 46 import java.util.Map; 47 import org.junit.Test; 48 import org.junit.runner.RunWith; 49 import org.junit.runners.JUnit4; 50 51 /** Unit tests for {@link Utils}. */ 52 @RunWith(JUnit4.class) 53 public class UtilsTest { 54 private final Metadata.Key<String> userKey = 55 Metadata.Key.of("user-key", Metadata.ASCII_STRING_MARSHALLER); 56 private final String userValue = "user-value"; 57 58 @Test testStatusFromThrowable()59 public void testStatusFromThrowable() { 60 Status s = Status.CANCELLED.withDescription("msg"); 61 assertSame(s, Utils.statusFromThrowable(new Exception(s.asException()))); 62 Throwable t; 63 t = new ConnectTimeoutException("msg"); 64 assertStatusEquals(Status.UNAVAILABLE.withCause(t), Utils.statusFromThrowable(t)); 65 t = new UnresolvedAddressException(); 66 assertStatusEquals(Status.UNAVAILABLE.withCause(t), Utils.statusFromThrowable(t)); 67 t = new Http2Exception(Http2Error.INTERNAL_ERROR, "msg"); 68 assertStatusEquals(Status.INTERNAL.withCause(t), Utils.statusFromThrowable(t)); 69 t = new Exception("msg"); 70 assertStatusEquals(Status.UNKNOWN.withCause(t), Utils.statusFromThrowable(t)); 71 } 72 73 @Test convertClientHeaders_sanitizes()74 public void convertClientHeaders_sanitizes() { 75 Metadata metaData = new Metadata(); 76 77 // Intentionally being explicit here rather than relying on any pre-defined lists of headers, 78 // since the goal of this test is to validate the correctness of such lists in the first place. 79 metaData.put(GrpcUtil.CONTENT_TYPE_KEY, "to-be-removed"); 80 metaData.put(GrpcUtil.USER_AGENT_KEY, "to-be-removed"); 81 metaData.put(GrpcUtil.TE_HEADER, "to-be-removed"); 82 metaData.put(userKey, userValue); 83 84 String scheme = "https"; 85 String userAgent = "user-agent"; 86 String method = "POST"; 87 String authority = "authority"; 88 String path = "//testService/test"; 89 90 Http2Headers output = 91 Utils.convertClientHeaders( 92 metaData, 93 new AsciiString(scheme), 94 new AsciiString(path), 95 new AsciiString(authority), 96 new AsciiString(method), 97 new AsciiString(userAgent)); 98 DefaultHttp2Headers headers = new DefaultHttp2Headers(); 99 for (Map.Entry<CharSequence, CharSequence> entry : output) { 100 headers.add(entry.getKey(), entry.getValue()); 101 } 102 103 // 7 reserved headers, 1 user header 104 assertEquals(7 + 1, headers.size()); 105 // Check the 3 reserved headers that are non pseudo 106 // Users can not create pseudo headers keys so no need to check for them here 107 assertEquals(GrpcUtil.CONTENT_TYPE_GRPC, 108 headers.get(GrpcUtil.CONTENT_TYPE_KEY.name()).toString()); 109 assertEquals(userAgent, headers.get(GrpcUtil.USER_AGENT_KEY.name()).toString()); 110 assertEquals(GrpcUtil.TE_TRAILERS, headers.get(GrpcUtil.TE_HEADER.name()).toString()); 111 // Check the user header is in tact 112 assertEquals(userValue, headers.get(userKey.name()).toString()); 113 } 114 115 @Test 116 @SuppressWarnings("UndefinedEquals") // AsciiString.equals convertServerHeaders_sanitizes()117 public void convertServerHeaders_sanitizes() { 118 Metadata metaData = new Metadata(); 119 120 // Intentionally being explicit here rather than relying on any pre-defined lists of headers, 121 // since the goal of this test is to validate the correctness of such lists in the first place. 122 metaData.put(GrpcUtil.CONTENT_TYPE_KEY, "to-be-removed"); 123 metaData.put(GrpcUtil.TE_HEADER, "to-be-removed"); 124 metaData.put(GrpcUtil.USER_AGENT_KEY, "to-be-removed"); 125 metaData.put(userKey, userValue); 126 127 Http2Headers output = Utils.convertServerHeaders(metaData); 128 DefaultHttp2Headers headers = new DefaultHttp2Headers(); 129 for (Map.Entry<CharSequence, CharSequence> entry : output) { 130 headers.add(entry.getKey(), entry.getValue()); 131 } 132 // 2 reserved headers, 1 user header 133 assertEquals(2 + 1, headers.size()); 134 assertEquals(Utils.CONTENT_TYPE_GRPC, headers.get(GrpcUtil.CONTENT_TYPE_KEY.name())); 135 } 136 137 @Test channelOptionsTest_noLinger()138 public void channelOptionsTest_noLinger() { 139 Channel channel = new EmbeddedChannel(); 140 assertNull(channel.config().getOption(ChannelOption.SO_LINGER)); 141 InternalChannelz.SocketOptions socketOptions = Utils.getSocketOptions(channel); 142 assertNull(socketOptions.lingerSeconds); 143 } 144 145 @Test 146 @SuppressWarnings("deprecation") channelOptionsTest_oio()147 public void channelOptionsTest_oio() { 148 Channel channel = new io.netty.channel.socket.oio.OioSocketChannel(); 149 SocketOptions socketOptions = setAndValidateGeneric(channel); 150 assertEquals(250, (int) socketOptions.soTimeoutMillis); 151 } 152 153 @Test channelOptionsTest_nio()154 public void channelOptionsTest_nio() { 155 Channel channel = new NioSocketChannel(); 156 SocketOptions socketOptions = setAndValidateGeneric(channel); 157 assertNull(socketOptions.soTimeoutMillis); 158 } 159 setAndValidateGeneric(Channel channel)160 private static InternalChannelz.SocketOptions setAndValidateGeneric(Channel channel) { 161 channel.config().setOption(ChannelOption.SO_LINGER, 3); 162 // only applicable for OIO channels: 163 channel.config().setOption(ChannelOption.SO_TIMEOUT, 250); 164 // Test some arbitrarily chosen options with a non numeric values 165 channel.config().setOption(ChannelOption.SO_KEEPALIVE, true); 166 WriteBufferWaterMark writeBufWaterMark = new WriteBufferWaterMark(10, 20); 167 channel.config().setOption(ChannelOption.WRITE_BUFFER_WATER_MARK, writeBufWaterMark); 168 169 InternalChannelz.SocketOptions socketOptions = Utils.getSocketOptions(channel); 170 assertEquals(3, (int) socketOptions.lingerSeconds); 171 assertEquals("true", socketOptions.others.get("SO_KEEPALIVE")); 172 assertEquals( 173 writeBufWaterMark.toString(), 174 socketOptions.others.get(ChannelOption.WRITE_BUFFER_WATER_MARK.toString())); 175 return socketOptions; 176 } 177 assertStatusEquals(Status expected, Status actual)178 private static void assertStatusEquals(Status expected, Status actual) { 179 assertEquals(expected.getCode(), actual.getCode()); 180 assertThat(MoreObjects.firstNonNull(actual.getDescription(), "")) 181 .contains(MoreObjects.firstNonNull(expected.getDescription(), "")); 182 assertEquals(expected.getCause(), actual.getCause()); 183 } 184 185 @Test defaultEventLoopGroup_whenEpollIsAvailable()186 public void defaultEventLoopGroup_whenEpollIsAvailable() { 187 assume().that(Utils.isEpollAvailable()).isTrue(); 188 189 EventLoopGroup defaultBossGroup = Utils.DEFAULT_BOSS_EVENT_LOOP_GROUP.create(); 190 EventLoopGroup defaultWorkerGroup = Utils.DEFAULT_WORKER_EVENT_LOOP_GROUP.create(); 191 192 assertThat(defaultBossGroup.getClass().getName()) 193 .isEqualTo("io.netty.channel.epoll.EpollEventLoopGroup"); 194 assertThat(defaultWorkerGroup.getClass().getName()) 195 .isEqualTo("io.netty.channel.epoll.EpollEventLoopGroup"); 196 197 defaultBossGroup.shutdownGracefully(); 198 defaultWorkerGroup.shutdownGracefully(); 199 } 200 201 @Test defaultClientChannelType_whenEpollIsAvailable()202 public void defaultClientChannelType_whenEpollIsAvailable() { 203 assume().that(Utils.isEpollAvailable()).isTrue(); 204 205 Class<? extends Channel> clientChannelType = Utils.DEFAULT_CLIENT_CHANNEL_TYPE; 206 207 assertThat(clientChannelType.getName()) 208 .isEqualTo("io.netty.channel.epoll.EpollSocketChannel"); 209 } 210 211 @Test defaultServerChannelFactory_whenEpollIsAvailable()212 public void defaultServerChannelFactory_whenEpollIsAvailable() { 213 assume().that(Utils.isEpollAvailable()).isTrue(); 214 215 ChannelFactory<? extends ServerChannel> channelFactory = Utils.DEFAULT_SERVER_CHANNEL_FACTORY; 216 217 assertThat(channelFactory.toString()) 218 .isEqualTo("ReflectiveChannelFactory(EpollServerSocketChannel.class)"); 219 } 220 221 @Test maybeGetTcpUserTimeoutOption()222 public void maybeGetTcpUserTimeoutOption() { 223 assume().that(Utils.isEpollAvailable()).isTrue(); 224 225 assertThat(Utils.maybeGetTcpUserTimeoutOption()).isNotNull(); 226 } 227 } 228