• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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