mirror of
https://github.com/google/nomulus.git
synced 2025-05-13 16:07:15 +02:00
Add QuotaHandler to GCP proxy
The quota handler terminates connections when quota is exceeded. The next CL will add instrumentation for quota related metrics. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=185042675
This commit is contained in:
parent
d38e29fd5e
commit
6ca523386a
10 changed files with 550 additions and 35 deletions
|
@ -0,0 +1,146 @@
|
|||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// 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.
|
||||
|
||||
package google.registry.proxy.handler;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.proxy.handler.ProxyProtocolHandler.REMOTE_ADDRESS_KEY;
|
||||
import static google.registry.testing.JUnitBackports.expectThrows;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import google.registry.proxy.handler.QuotaHandler.OverQuotaException;
|
||||
import google.registry.proxy.handler.QuotaHandler.WhoisQuotaHandler;
|
||||
import google.registry.proxy.quota.QuotaManager;
|
||||
import google.registry.proxy.quota.QuotaManager.QuotaRequest;
|
||||
import google.registry.proxy.quota.QuotaManager.QuotaResponse;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.embedded.EmbeddedChannel;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeZone;
|
||||
import org.joda.time.Duration;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
/** Unit tests for {@link WhoisQuotaHandler} */
|
||||
@RunWith(JUnit4.class)
|
||||
public class WhoisQuotaHandlerTest {
|
||||
|
||||
private final QuotaManager quotaManager = mock(QuotaManager.class);
|
||||
private final WhoisQuotaHandler handler = new WhoisQuotaHandler(quotaManager);
|
||||
private final EmbeddedChannel channel = new EmbeddedChannel(handler);
|
||||
private final DateTime now = DateTime.now(DateTimeZone.UTC);
|
||||
private final String remoteAddress = "127.0.0.1";
|
||||
private final Object message = new Object();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
channel.attr(REMOTE_ADDRESS_KEY).set(remoteAddress);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccess_quotaGranted() {
|
||||
when(quotaManager.acquireQuota(QuotaRequest.create(remoteAddress)))
|
||||
.thenReturn(QuotaResponse.create(true, remoteAddress, now));
|
||||
|
||||
// First read, acquire quota.
|
||||
assertThat(channel.writeInbound(message)).isTrue();
|
||||
assertThat((Object) channel.readInbound()).isEqualTo(message);
|
||||
assertThat(channel.isActive()).isTrue();
|
||||
verify(quotaManager).acquireQuota(QuotaRequest.create(remoteAddress));
|
||||
|
||||
// Second read, should not acquire quota again.
|
||||
assertThat(channel.writeInbound(message)).isTrue();
|
||||
assertThat((Object) channel.readInbound()).isEqualTo(message);
|
||||
|
||||
// Channel closed, release quota.
|
||||
ChannelFuture unusedFuture = channel.close();
|
||||
verifyNoMoreInteractions(quotaManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailure_quotaNotGranted() {
|
||||
when(quotaManager.acquireQuota(QuotaRequest.create(remoteAddress)))
|
||||
.thenReturn(QuotaResponse.create(false, remoteAddress, now));
|
||||
OverQuotaException e =
|
||||
expectThrows(OverQuotaException.class, () -> channel.writeInbound(message));
|
||||
assertThat(e).hasMessageThat().contains("none");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccess_twoChannels_twoUserIds() {
|
||||
// Set up another user.
|
||||
final WhoisQuotaHandler otherHandler = new WhoisQuotaHandler(quotaManager);
|
||||
final EmbeddedChannel otherChannel = new EmbeddedChannel(otherHandler);
|
||||
final String otherRemoteAddress = "192.168.0.1";
|
||||
otherChannel.attr(REMOTE_ADDRESS_KEY).set(otherRemoteAddress);
|
||||
final DateTime later = now.plus(Duration.standardSeconds(1));
|
||||
|
||||
when(quotaManager.acquireQuota(QuotaRequest.create(remoteAddress)))
|
||||
.thenReturn(QuotaResponse.create(true, remoteAddress, now));
|
||||
when(quotaManager.acquireQuota(QuotaRequest.create(otherRemoteAddress)))
|
||||
.thenReturn(QuotaResponse.create(false, otherRemoteAddress, later));
|
||||
|
||||
// Allows the first user.
|
||||
assertThat(channel.writeInbound(message)).isTrue();
|
||||
assertThat((Object) channel.readInbound()).isEqualTo(message);
|
||||
assertThat(channel.isActive()).isTrue();
|
||||
|
||||
// Blocks the second user.
|
||||
OverQuotaException e =
|
||||
expectThrows(OverQuotaException.class, () -> otherChannel.writeInbound(message));
|
||||
assertThat(e).hasMessageThat().contains("none");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccess_oneUser_rateLimited() {
|
||||
// Set up another channel for the same user.
|
||||
final WhoisQuotaHandler otherHandler = new WhoisQuotaHandler(quotaManager);
|
||||
final EmbeddedChannel otherChannel = new EmbeddedChannel(otherHandler);
|
||||
otherChannel.attr(REMOTE_ADDRESS_KEY).set(remoteAddress);
|
||||
final DateTime later = now.plus(Duration.standardSeconds(1));
|
||||
|
||||
// Set up the third channel for the same user
|
||||
final WhoisQuotaHandler thirdHandler = new WhoisQuotaHandler(quotaManager);
|
||||
final EmbeddedChannel thirdChannel = new EmbeddedChannel(thirdHandler);
|
||||
thirdChannel.attr(REMOTE_ADDRESS_KEY).set(remoteAddress);
|
||||
final DateTime evenLater = now.plus(Duration.standardSeconds(60));
|
||||
|
||||
when(quotaManager.acquireQuota(QuotaRequest.create(remoteAddress)))
|
||||
.thenReturn(QuotaResponse.create(true, remoteAddress, now))
|
||||
// Throttles the second connection.
|
||||
.thenReturn(QuotaResponse.create(false, remoteAddress, later))
|
||||
// Allows the third connection because token refilled.
|
||||
.thenReturn(QuotaResponse.create(true, remoteAddress, evenLater));
|
||||
|
||||
// Allows the first channel.
|
||||
assertThat(channel.writeInbound(message)).isTrue();
|
||||
assertThat((Object) channel.readInbound()).isEqualTo(message);
|
||||
assertThat(channel.isActive()).isTrue();
|
||||
|
||||
// Blocks the second channel.
|
||||
OverQuotaException e =
|
||||
expectThrows(OverQuotaException.class, () -> otherChannel.writeInbound(message));
|
||||
assertThat(e).hasMessageThat().contains("none");
|
||||
|
||||
// Allows the third channel.
|
||||
assertThat(thirdChannel.writeInbound(message)).isTrue();
|
||||
assertThat((Object) thirdChannel.readInbound()).isEqualTo(message);
|
||||
assertThat(thirdChannel.isActive()).isTrue();
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue