From 98b06c6237913ecd7158f1a7d06b815521639c04 Mon Sep 17 00:00:00 2001
From: blucas
Date: Fri, 18 May 2012 17:33:19 +0100
Subject: [PATCH 1/3] #337 back-port ipfilter and traffic to 3 branch
---
.../jboss/netty/handler/ipfilter/CIDR.java | 234 ++++++++
.../jboss/netty/handler/ipfilter/CIDR4.java | 159 ++++++
.../jboss/netty/handler/ipfilter/CIDR6.java | 164 ++++++
.../handler/ipfilter/IpFilterListener.java | 67 +++
.../netty/handler/ipfilter/IpFilterRule.java | 25 +
.../handler/ipfilter/IpFilterRuleHandler.java | 259 +++++++++
.../handler/ipfilter/IpFilterRuleList.java | 95 ++++
.../handler/ipfilter/IpFilteringHandler.java | 35 ++
.../ipfilter/IpFilteringHandlerImpl.java | 164 ++++++
.../jboss/netty/handler/ipfilter/IpSet.java | 30 ++
.../netty/handler/ipfilter/IpSubnet.java | 168 ++++++
.../handler/ipfilter/IpSubnetFilterRule.java | 68 +++
.../netty/handler/ipfilter/IpV4Subnet.java | 276 ++++++++++
.../ipfilter/IpV4SubnetFilterRule.java | 63 +++
.../handler/ipfilter/OneIpFilterHandler.java | 73 +++
.../netty/handler/ipfilter/PatternRule.java | 202 +++++++
.../netty/handler/ipfilter/package-info.java | 91 ++++
.../AbstractTrafficShapingHandler.java | 499 ++++++++++++++++++
.../traffic/ChannelTrafficShapingHandler.java | 177 +++++++
.../traffic/GlobalTrafficShapingHandler.java | 159 ++++++
.../netty/handler/traffic/TrafficCounter.java | 401 ++++++++++++++
.../netty/handler/traffic/package-info.java | 91 ++++
.../handler/ipfilter/IpFilterRuleTest.java | 347 ++++++++++++
23 files changed, 3847 insertions(+)
create mode 100644 src/main/java/org/jboss/netty/handler/ipfilter/CIDR.java
create mode 100644 src/main/java/org/jboss/netty/handler/ipfilter/CIDR4.java
create mode 100644 src/main/java/org/jboss/netty/handler/ipfilter/CIDR6.java
create mode 100644 src/main/java/org/jboss/netty/handler/ipfilter/IpFilterListener.java
create mode 100644 src/main/java/org/jboss/netty/handler/ipfilter/IpFilterRule.java
create mode 100644 src/main/java/org/jboss/netty/handler/ipfilter/IpFilterRuleHandler.java
create mode 100644 src/main/java/org/jboss/netty/handler/ipfilter/IpFilterRuleList.java
create mode 100644 src/main/java/org/jboss/netty/handler/ipfilter/IpFilteringHandler.java
create mode 100644 src/main/java/org/jboss/netty/handler/ipfilter/IpFilteringHandlerImpl.java
create mode 100644 src/main/java/org/jboss/netty/handler/ipfilter/IpSet.java
create mode 100644 src/main/java/org/jboss/netty/handler/ipfilter/IpSubnet.java
create mode 100644 src/main/java/org/jboss/netty/handler/ipfilter/IpSubnetFilterRule.java
create mode 100644 src/main/java/org/jboss/netty/handler/ipfilter/IpV4Subnet.java
create mode 100644 src/main/java/org/jboss/netty/handler/ipfilter/IpV4SubnetFilterRule.java
create mode 100644 src/main/java/org/jboss/netty/handler/ipfilter/OneIpFilterHandler.java
create mode 100644 src/main/java/org/jboss/netty/handler/ipfilter/PatternRule.java
create mode 100644 src/main/java/org/jboss/netty/handler/ipfilter/package-info.java
create mode 100644 src/main/java/org/jboss/netty/handler/traffic/AbstractTrafficShapingHandler.java
create mode 100644 src/main/java/org/jboss/netty/handler/traffic/ChannelTrafficShapingHandler.java
create mode 100644 src/main/java/org/jboss/netty/handler/traffic/GlobalTrafficShapingHandler.java
create mode 100644 src/main/java/org/jboss/netty/handler/traffic/TrafficCounter.java
create mode 100644 src/main/java/org/jboss/netty/handler/traffic/package-info.java
create mode 100644 src/test/java/org/jboss/netty/handler/ipfilter/IpFilterRuleTest.java
diff --git a/src/main/java/org/jboss/netty/handler/ipfilter/CIDR.java b/src/main/java/org/jboss/netty/handler/ipfilter/CIDR.java
new file mode 100644
index 0000000000..f0e881369f
--- /dev/null
+++ b/src/main/java/org/jboss/netty/handler/ipfilter/CIDR.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright 2011 The Netty Project
+ *
+ * The Netty Project licenses this file to you 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 org.jboss.netty.handler.ipfilter;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.StringTokenizer;
+
+/**
+ */
+public abstract class CIDR implements Comparable {
+ /** The base address of the CIDR notation */
+ protected InetAddress baseAddress;
+
+ /** The mask used in the CIDR notation */
+ protected int cidrMask;
+
+ /**
+ * Create CIDR using the CIDR Notation
+ *
+ * @return the generated CIDR
+ */
+ public static CIDR newCIDR(InetAddress baseAddress, int cidrMask) throws UnknownHostException {
+ if (cidrMask < 0) {
+ throw new UnknownHostException("Invalid mask length used: " + cidrMask);
+ }
+ if (baseAddress instanceof Inet4Address) {
+ if (cidrMask > 32) {
+ throw new UnknownHostException("Invalid mask length used: " + cidrMask);
+ }
+ return new CIDR4((Inet4Address) baseAddress, cidrMask);
+ }
+ // IPv6.
+ if (cidrMask > 128) {
+ throw new UnknownHostException("Invalid mask length used: " + cidrMask);
+ }
+ return new CIDR6((Inet6Address) baseAddress, cidrMask);
+ }
+
+ /**
+ * Create CIDR using the normal Notation
+ *
+ * @return the generated CIDR
+ */
+ public static CIDR newCIDR(InetAddress baseAddress, String scidrMask) throws UnknownHostException {
+ int cidrMask = getNetMask(scidrMask);
+ if (cidrMask < 0) {
+ throw new UnknownHostException("Invalid mask length used: " + cidrMask);
+ }
+ if (baseAddress instanceof Inet4Address) {
+ if (cidrMask > 32) {
+ throw new UnknownHostException("Invalid mask length used: " + cidrMask);
+ }
+ return new CIDR4((Inet4Address) baseAddress, cidrMask);
+ }
+ cidrMask += 96;
+ // IPv6.
+ if (cidrMask > 128) {
+ throw new UnknownHostException("Invalid mask length used: " + cidrMask);
+ }
+ return new CIDR6((Inet6Address) baseAddress, cidrMask);
+ }
+
+ /**
+ * Create CIDR using the CIDR or normal Notation
+ * i.e.:
+ * CIDR subnet = newCIDR ("10.10.10.0/24"); or
+ * CIDR subnet = newCIDR ("1fff:0:0a88:85a3:0:0:ac1f:8001/24"); or
+ * CIDR subnet = newCIDR ("10.10.10.0/255.255.255.0");
+ *
+ * @return the generated CIDR
+ */
+ public static CIDR newCIDR(String cidr) throws UnknownHostException {
+ int p = cidr.indexOf("/");
+ if (p < 0) {
+ throw new UnknownHostException("Invalid CIDR notation used: " + cidr);
+ }
+ String addrString = cidr.substring(0, p);
+ String maskString = cidr.substring(p + 1);
+ InetAddress addr = addressStringToInet(addrString);
+ int mask = 0;
+ if (maskString.indexOf(".") < 0) {
+ mask = parseInt(maskString, -1);
+ } else {
+ mask = getNetMask(maskString);
+ if (addr instanceof Inet6Address) {
+ mask += 96;
+ }
+ }
+ if (mask < 0) {
+ throw new UnknownHostException("Invalid mask length used: " + maskString);
+ }
+ return newCIDR(addr, mask);
+ }
+
+ /** @return the baseAddress of the CIDR block. */
+ public InetAddress getBaseAddress() {
+ return baseAddress;
+ }
+
+ /** @return the Mask length. */
+ public int getMask() {
+ return cidrMask;
+ }
+
+ /** @return the textual CIDR notation. */
+ @Override
+ public String toString() {
+ return baseAddress.getHostAddress() + "/" + cidrMask;
+ }
+
+ /** @return the end address of this block. */
+ public abstract InetAddress getEndAddress();
+
+ /**
+ * Compares the given InetAddress against the CIDR and returns true if
+ * the ip is in the subnet-ip-range and false if not.
+ *
+ * @return returns true if the given IP address is inside the currently
+ * set network.
+ */
+ public abstract boolean contains(InetAddress inetAddress);
+
+ @Override
+ public boolean equals(Object arg0) {
+ if (!(arg0 instanceof CIDR)) {
+ return false;
+ }
+ return this.compareTo((CIDR) arg0) == 0;
+ }
+
+ @Override
+ public int hashCode() {
+ return baseAddress.hashCode();
+ }
+
+ /**
+ * Convert an IPv4 or IPv6 textual representation into an
+ * InetAddress.
+ *
+ * @return the created InetAddress
+ */
+ private static InetAddress addressStringToInet(String addr) throws UnknownHostException {
+ return InetAddress.getByName(addr);
+ }
+
+ /**
+ * Get the Subnet's Netmask in Decimal format.
+ * i.e.: getNetMask("255.255.255.0") returns the integer CIDR mask
+ *
+ * @param netMask a network mask
+ * @return the integer CIDR mask
+ */
+ private static int getNetMask(String netMask) {
+ StringTokenizer nm = new StringTokenizer(netMask, ".");
+ int i = 0;
+ int[] netmask = new int[4];
+ while (nm.hasMoreTokens()) {
+ netmask[i] = Integer.parseInt(nm.nextToken());
+ i++;
+ }
+ int mask1 = 0;
+ for (i = 0; i < 4; i++) {
+ mask1 += Integer.bitCount(netmask[i]);
+ }
+ return mask1;
+ }
+
+ /**
+ * @param intstr a string containing an integer.
+ * @param def the default if the string does not contain a valid
+ * integer.
+ * @return the inetAddress from the integer
+ */
+ private static int parseInt(String intstr, int def) {
+ Integer res;
+ if (intstr == null) {
+ return def;
+ }
+ try {
+ res = Integer.decode(intstr);
+ } catch (Exception e) {
+ res = new Integer(def);
+ }
+ return res.intValue();
+ }
+
+ /**
+ * Compute a byte representation of IpV4 from a IpV6
+ *
+ * @return the byte representation
+ * @throws IllegalArgumentException if the IpV6 cannot be mapped to IpV4
+ */
+ public static byte[] getIpV4FromIpV6(Inet6Address address) {
+ byte[] baddr = address.getAddress();
+ for (int i = 0; i < 9; i++) {
+ if (baddr[i] != 0) {
+ throw new IllegalArgumentException("This IPv6 address cannot be used in IPv4 context");
+ }
+ }
+ if (baddr[10] != 0 && baddr[10] != 0xFF || baddr[11] != 0 && baddr[11] != 0xFF) {
+ throw new IllegalArgumentException("This IPv6 address cannot be used in IPv4 context");
+ }
+ return new byte[]
+ {baddr[12], baddr[13], baddr[14], baddr[15]};
+ }
+
+ /**
+ * Compute a byte representation of IpV6 from a IpV4
+ *
+ * @return the byte representation
+ * @throws IllegalArgumentException if the IpV6 cannot be mapped to IpV4
+ */
+ public static byte[] getIpV6FromIpV4(Inet4Address address) {
+ byte[] baddr = address.getAddress();
+ return new byte[]
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, baddr[0], baddr[1], baddr[2], baddr[3]};
+ }
+}
diff --git a/src/main/java/org/jboss/netty/handler/ipfilter/CIDR4.java b/src/main/java/org/jboss/netty/handler/ipfilter/CIDR4.java
new file mode 100644
index 0000000000..08b83d8b67
--- /dev/null
+++ b/src/main/java/org/jboss/netty/handler/ipfilter/CIDR4.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2011 The Netty Project
+ *
+ * The Netty Project licenses this file to you 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 org.jboss.netty.handler.ipfilter;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ */
+public class CIDR4 extends CIDR {
+ /** The integer for the base address */
+ private int addressInt;
+
+ /** The integer for the end address */
+ private final int addressEndInt;
+
+ /**
+ * @param newaddr
+ * @param mask
+ */
+ protected CIDR4(Inet4Address newaddr, int mask) {
+ cidrMask = mask;
+ addressInt = ipv4AddressToInt(newaddr);
+ int newmask = ipv4PrefixLengthToMask(mask);
+ addressInt &= newmask;
+ try {
+ baseAddress = intToIPv4Address(addressInt);
+ } catch (UnknownHostException e) {
+ // this should never happen
+ }
+ addressEndInt = addressInt + ipv4PrefixLengthToLength(cidrMask) - 1;
+ }
+
+ @Override
+ public InetAddress getEndAddress() {
+ try {
+ return intToIPv4Address(addressEndInt);
+ } catch (UnknownHostException e) {
+ // this should never happen
+ return null;
+ }
+ }
+
+ @Override
+ public int compareTo(CIDR arg) {
+ if (arg instanceof CIDR6) {
+ byte[] address = getIpV4FromIpV6((Inet6Address) arg.baseAddress);
+ int net = ipv4AddressToInt(address);
+ if (net == addressInt && arg.cidrMask == cidrMask) {
+ return 0;
+ }
+ if (net < addressInt) {
+ return 1;
+ } else if (net > addressInt) {
+ return -1;
+ } else if (arg.cidrMask < cidrMask) {
+ return -1;
+ }
+ return 1;
+ }
+ CIDR4 o = (CIDR4) arg;
+ if (o.addressInt == addressInt && o.cidrMask == cidrMask) {
+ return 0;
+ }
+ if (o.addressInt < addressInt) {
+ return 1;
+ } else if (o.addressInt > addressInt) {
+ return -1;
+ } else if (o.cidrMask < cidrMask) {
+ // greater Mask means less IpAddresses so -1
+ return -1;
+ }
+ return 1;
+ }
+
+ @Override
+ public boolean contains(InetAddress inetAddress) {
+ int search = ipv4AddressToInt(inetAddress);
+ return search >= addressInt && search <= addressEndInt;
+ }
+
+ /**
+ * Given an IPv4 baseAddress length, return the block length. I.e., a
+ * baseAddress length of 24 will return 256.
+ */
+ private static int ipv4PrefixLengthToLength(int prefix_length) {
+ return 1 << 32 - prefix_length;
+ }
+
+ /**
+ * Given a baseAddress length, return a netmask. I.e, a baseAddress length
+ * of 24 will return 0xFFFFFF00.
+ */
+ private static int ipv4PrefixLengthToMask(int prefix_length) {
+ return ~((1 << 32 - prefix_length) - 1);
+ }
+
+ /**
+ * Convert an integer into an (IPv4) InetAddress.
+ *
+ * @return the created InetAddress
+ */
+ private static InetAddress intToIPv4Address(int addr) throws UnknownHostException {
+ byte[] a = new byte[4];
+ a[0] = (byte) (addr >> 24 & 0xFF);
+ a[1] = (byte) (addr >> 16 & 0xFF);
+ a[2] = (byte) (addr >> 8 & 0xFF);
+ a[3] = (byte) (addr & 0xFF);
+ return InetAddress.getByAddress(a);
+ }
+
+ /**
+ * Given an IPv4 address, convert it into an integer.
+ *
+ * @return the integer representation of the InetAddress
+ * @throws IllegalArgumentException if the address is really an
+ * IPv6 address.
+ */
+ private static int ipv4AddressToInt(InetAddress addr) {
+ byte[] address = null;
+ if (addr instanceof Inet6Address) {
+ address = getIpV4FromIpV6((Inet6Address) addr);
+ } else {
+ address = addr.getAddress();
+ }
+ return ipv4AddressToInt(address);
+ }
+
+ /**
+ * Given an IPv4 address as array of bytes, convert it into an integer.
+ *
+ * @return the integer representation of the InetAddress
+ * @throws IllegalArgumentException if the address is really an
+ * IPv6 address.
+ */
+ private static int ipv4AddressToInt(byte[] address) {
+ int net = 0;
+ for (byte addres : address) {
+ net <<= 8;
+ net |= addres & 0xFF;
+ }
+ return net;
+ }
+}
diff --git a/src/main/java/org/jboss/netty/handler/ipfilter/CIDR6.java b/src/main/java/org/jboss/netty/handler/ipfilter/CIDR6.java
new file mode 100644
index 0000000000..7c7964d5e3
--- /dev/null
+++ b/src/main/java/org/jboss/netty/handler/ipfilter/CIDR6.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2011 The Netty Project
+ *
+ * The Netty Project licenses this file to you 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 org.jboss.netty.handler.ipfilter;
+
+import java.math.BigInteger;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+import org.jboss.netty.logging.InternalLogger;
+import org.jboss.netty.logging.InternalLoggerFactory;
+
+/**
+ */
+public class CIDR6 extends CIDR {
+
+ private static final InternalLogger logger = InternalLoggerFactory.getInstance(CIDR6.class);
+
+ /** The big integer for the base address */
+ private BigInteger addressBigInt;
+
+ /** The big integer for the end address */
+ private final BigInteger addressEndBigInt;
+
+ /**
+ * @param newaddress
+ * @param newmask
+ */
+ protected CIDR6(Inet6Address newaddress, int newmask) {
+ cidrMask = newmask;
+ addressBigInt = ipv6AddressToBigInteger(newaddress);
+ BigInteger mask = ipv6CidrMaskToMask(newmask);
+ try {
+ addressBigInt = addressBigInt.and(mask);
+ baseAddress = bigIntToIPv6Address(addressBigInt);
+ } catch (UnknownHostException e) {
+ // this should never happen.
+ }
+ addressEndBigInt = addressBigInt.add(ipv6CidrMaskToBaseAddress(cidrMask)).subtract(BigInteger.ONE);
+ }
+
+ @Override
+ public InetAddress getEndAddress() {
+ try {
+ return bigIntToIPv6Address(addressEndBigInt);
+ } catch (UnknownHostException e) {
+ if (logger.isErrorEnabled()) {
+ logger.error("invalid ip address calculated as an end address");
+ }
+ return null;
+ }
+ }
+
+ @Override
+ public int compareTo(CIDR arg) {
+ if (arg instanceof CIDR4) {
+ BigInteger net = ipv6AddressToBigInteger(arg.baseAddress);
+ int res = net.compareTo(addressBigInt);
+ if (res == 0) {
+ if (arg.cidrMask == cidrMask) {
+ return 0;
+ } else if (arg.cidrMask < cidrMask) {
+ return -1;
+ }
+ return 1;
+ }
+ return res;
+ }
+ CIDR6 o = (CIDR6) arg;
+ if (o.addressBigInt.equals(addressBigInt) && o.cidrMask == cidrMask) {
+ return 0;
+ }
+ int res = o.addressBigInt.compareTo(addressBigInt);
+ if (res == 0) {
+ if (o.cidrMask < cidrMask) {
+ // greater Mask means less IpAddresses so -1
+ return -1;
+ }
+ return 1;
+ }
+ return res;
+ }
+
+ @Override
+ public boolean contains(InetAddress inetAddress) {
+ BigInteger search = ipv6AddressToBigInteger(inetAddress);
+ return search.compareTo(addressBigInt) >= 0 && search.compareTo(addressEndBigInt) <= 0;
+ }
+
+ /**
+ * Given an IPv6 baseAddress length, return the block length. I.e., a
+ * baseAddress length of 96 will return 2**32.
+ */
+ private static BigInteger ipv6CidrMaskToBaseAddress(int cidrMask) {
+ return BigInteger.ONE.shiftLeft(128 - cidrMask);
+ }
+
+ private static BigInteger ipv6CidrMaskToMask(int cidrMask) {
+ return BigInteger.ONE.shiftLeft(128 - cidrMask).subtract(BigInteger.ONE).not();
+ }
+
+ /**
+ * Given an IPv6 address, convert it into a BigInteger.
+ *
+ * @return the integer representation of the InetAddress
+ * @throws IllegalArgumentException if the address is not an IPv6
+ * address.
+ */
+ private static BigInteger ipv6AddressToBigInteger(InetAddress addr) {
+ byte[] ipv6;
+ if (addr instanceof Inet4Address) {
+ ipv6 = getIpV6FromIpV4((Inet4Address) addr);
+ } else {
+ ipv6 = addr.getAddress();
+ }
+ if (ipv6[0] == -1) {
+ return new BigInteger(1, ipv6);
+ }
+ return new BigInteger(ipv6);
+ }
+
+ /**
+ * Convert a big integer into an IPv6 address.
+ *
+ * @return the inetAddress from the integer
+ * @throws UnknownHostException if the big integer is too large,
+ * and thus an invalid IPv6 address.
+ */
+ private static InetAddress bigIntToIPv6Address(BigInteger addr) throws UnknownHostException {
+ byte[] a = new byte[16];
+ byte[] b = addr.toByteArray();
+ if (b.length > 16 && !(b.length == 17 && b[0] == 0)) {
+ throw new UnknownHostException("invalid IPv6 address (too big)");
+ }
+ if (b.length == 16) {
+ return InetAddress.getByAddress(b);
+ }
+ // handle the case where the IPv6 address starts with "FF".
+ if (b.length == 17) {
+ System.arraycopy(b, 1, a, 0, 16);
+ } else {
+ // copy the address into a 16 byte array, zero-filled.
+ int p = 16 - b.length;
+ for (int i = 0; i < b.length; i++) {
+ a[p + i] = b[i];
+ }
+ }
+ return InetAddress.getByAddress(a);
+ }
+}
diff --git a/src/main/java/org/jboss/netty/handler/ipfilter/IpFilterListener.java b/src/main/java/org/jboss/netty/handler/ipfilter/IpFilterListener.java
new file mode 100644
index 0000000000..0ccda3c290
--- /dev/null
+++ b/src/main/java/org/jboss/netty/handler/ipfilter/IpFilterListener.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2011 The Netty Project
+ *
+ * The Netty Project licenses this file to you 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 org.jboss.netty.handler.ipfilter;
+
+import java.net.InetSocketAddress;
+
+import org.jboss.netty.channel.ChannelEvent;
+import org.jboss.netty.channel.ChannelFuture;
+import org.jboss.netty.channel.ChannelHandlerContext;
+
+/**
+ * The listener interface for receiving ipFilter events.
+ *
+ * @see IpFilteringHandler
+ */
+public interface IpFilterListener {
+
+ /**
+ * Called when the channel has the CONNECTED status and the channel was allowed by a previous call to accept().
+ * This method enables your implementation to send a message back to the client before closing
+ * or whatever you need. This method returns a ChannelFuture on which the implementation
+ * can wait uninterruptibly before continuing.
+ * For instance, If a message is sent back, the corresponding ChannelFuture has to be returned.
+ *
+ * @param inetSocketAddress the remote {@link InetSocketAddress} from client
+ * @return the associated ChannelFuture to be waited for before closing the channel. Null is allowed.
+ */
+ ChannelFuture allowed(ChannelHandlerContext ctx, ChannelEvent e, InetSocketAddress inetSocketAddress);
+
+ /**
+ * Called when the channel has the CONNECTED status and the channel was refused by a previous call to accept().
+ * This method enables your implementation to send a message back to the client before closing
+ * or whatever you need. This method returns a ChannelFuture on which the implementation
+ * will wait uninterruptibly before closing the channel.
+ * For instance, If a message is sent back, the corresponding ChannelFuture has to be returned.
+ *
+ * @param inetSocketAddress the remote {@link InetSocketAddress} from client
+ * @return the associated ChannelFuture to be waited for before closing the channel. Null is allowed.
+ */
+ ChannelFuture refused(ChannelHandlerContext ctx, ChannelEvent e, InetSocketAddress inetSocketAddress);
+
+ /**
+ * Called in handleUpstream, if this channel was previously blocked,
+ * to check if whatever the event, it should be passed to the next entry in the pipeline.
+ * If one wants to not block events, just overridden this method by returning always true.
+ * Note that OPENED and BOUND events are still passed to the next entry in the pipeline since
+ * those events come out before the CONNECTED event and so the possibility to filter the connection.
+ *
+ * @return True if the event should continue, False if the event should not continue
+ * since this channel was blocked by this filter
+ */
+ boolean continues(ChannelHandlerContext ctx, ChannelEvent e);
+
+}
diff --git a/src/main/java/org/jboss/netty/handler/ipfilter/IpFilterRule.java b/src/main/java/org/jboss/netty/handler/ipfilter/IpFilterRule.java
new file mode 100644
index 0000000000..50926886e6
--- /dev/null
+++ b/src/main/java/org/jboss/netty/handler/ipfilter/IpFilterRule.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2011 The Netty Project
+ *
+ * The Netty Project licenses this file to you 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 org.jboss.netty.handler.ipfilter;
+
+/** This Interface defines an Ip Filter Rule. */
+public interface IpFilterRule extends IpSet {
+ /** @return True if this Rule is an ALLOW rule */
+ boolean isAllowRule();
+
+ /** @return True if this Rule is a DENY rule */
+ boolean isDenyRule();
+}
diff --git a/src/main/java/org/jboss/netty/handler/ipfilter/IpFilterRuleHandler.java b/src/main/java/org/jboss/netty/handler/ipfilter/IpFilterRuleHandler.java
new file mode 100644
index 0000000000..04b133a5d3
--- /dev/null
+++ b/src/main/java/org/jboss/netty/handler/ipfilter/IpFilterRuleHandler.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright 2011 The Netty Project
+ *
+ * The Netty Project licenses this file to you 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 org.jboss.netty.handler.ipfilter;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.jboss.netty.channel.ChannelEvent;
+import org.jboss.netty.channel.ChannelHandler.Sharable;
+import org.jboss.netty.channel.ChannelHandlerContext;
+
+/**
+ * Implementation of Filter of IP based on ALLOW and DENY rules.
+ *
+ * This implementation could be changed by implementing a new {@link IpFilterRule} than default
+ * {@link IpV4SubnetFilterRule} (IPV4 support only), {@link IpSubnetFilterRule} (IPV4 and IPV6 support) or {@link IpFilterRule} (IP and host name string pattern support) .
+ *
+ * The check is done by going from step to step in the underlying array of IpFilterRule.
+ * Each {@link IpFilterRule} answers to the method accept if the {@link InetAddress} is accepted or not,
+ * according to its implementation. If an InetAddress arrives at the end of the list, as in Firewall
+ * usual rules, the InetAddress is therefore accepted by default.
+ *
+ *
If it was constructed with True as first argument,
+ * the IpFilterRule is an ALLOW rule (every InetAddress that fits in the rule will be accepted).
+ *
If it was constructed with False as first argument,
+ * the IpFilterRule is a DENY rule (every InetAddress that fits in the rule will be refused).
+ *
+ *
+ * An empty list means allow all (no limitation).
+ * For efficiency reason, you should not add/remove too frequently IpFilterRules to/from this handler.
+ * You should prefer to replace an entry (set method) with an ALLOW/DENY ALL IpFilterRule
+ * if possible.
+ * This handler should be created only once and reused on every pipeline since it handles
+ * a global status of what is allowed or blocked.
+ *
+ * Note that {@link IpSubnetFilterRule} which supports IPV4 and IPV6 should be used with as much as
+ * possible no mixed IP protocol. Both IPV4 and IPV6 are supported but a mix (IpFilter in IPV6 notation
+ * and the address from the channel in IPV4, or the reverse) can lead to wrong result.
+ */
+@Sharable
+public class IpFilterRuleHandler extends IpFilteringHandlerImpl {
+ /** List of {@link IpFilterRule} */
+ private final CopyOnWriteArrayList ipFilterRuleList = new CopyOnWriteArrayList();
+
+ /** Constructor from a new list of IpFilterRule */
+ public IpFilterRuleHandler(List newList) {
+ if (newList != null) {
+ ipFilterRuleList.addAll(newList);
+ }
+ }
+
+ /**
+ * Empty constructor (no IpFilterRule in the List at construction). In such a situation,
+ * empty list implies allow all.
+ */
+ public IpFilterRuleHandler() {
+ }
+
+ // Below are methods directly inspired from CopyOnWriteArrayList methods
+
+ /** Add an ipFilterRule in the list at the end */
+ public void add(IpFilterRule ipFilterRule) {
+ if (ipFilterRule == null) {
+ throw new NullPointerException("IpFilterRule can not be null");
+ }
+ ipFilterRuleList.add(ipFilterRule);
+ }
+
+ /** Add an ipFilterRule in the list at the specified position (shifting to the right other elements) */
+ public void add(int index, IpFilterRule ipFilterRule) {
+ if (ipFilterRule == null) {
+ throw new NullPointerException("IpFilterRule can not be null");
+ }
+ ipFilterRuleList.add(index, ipFilterRule);
+ }
+
+ /**
+ * Appends all of the elements in the specified collection to the end of this list,
+ * in the order that they are returned by the specified collection's iterator.
+ */
+ public void addAll(Collection c) {
+ if (c == null) {
+ throw new NullPointerException("Collection can not be null");
+ }
+ ipFilterRuleList.addAll(c);
+ }
+
+ /** Inserts all of the elements in the specified collection into this list, starting at the specified position. */
+ public void addAll(int index, Collection c) {
+ if (c == null) {
+ throw new NullPointerException("Collection can not be null");
+ }
+ ipFilterRuleList.addAll(index, c);
+ }
+
+ /**
+ * Append the element if not present.
+ *
+ * @return the number of elements added
+ */
+ public int addAllAbsent(Collection c) {
+ if (c == null) {
+ throw new NullPointerException("Collection can not be null");
+ }
+ return ipFilterRuleList.addAllAbsent(c);
+ }
+
+ /**
+ * Append the element if not present.
+ *
+ * @return true if the element was added
+ */
+ public boolean addIfAbsent(IpFilterRule ipFilterRule) {
+ if (ipFilterRule == null) {
+ throw new NullPointerException("IpFilterRule can not be null");
+ }
+ return ipFilterRuleList.addIfAbsent(ipFilterRule);
+ }
+
+ /** Clear the list */
+ public void clear() {
+ ipFilterRuleList.clear();
+ }
+
+ /**
+ * Returns true if this list contains the specified element
+ *
+ * @return true if this list contains the specified element
+ */
+ public boolean contains(IpFilterRule ipFilterRule) {
+ if (ipFilterRule == null) {
+ throw new NullPointerException("IpFilterRule can not be null");
+ }
+ return ipFilterRuleList.contains(ipFilterRule);
+ }
+
+ /**
+ * Returns true if this list contains all of the elements of the specified collection
+ *
+ * @return true if this list contains all of the elements of the specified collection
+ */
+ public boolean containsAll(Collection c) {
+ if (c == null) {
+ throw new NullPointerException("Collection can not be null");
+ }
+ return ipFilterRuleList.containsAll(c);
+ }
+
+ /**
+ * Returns the element at the specified position in this list
+ *
+ * @return the element at the specified position in this list
+ */
+ public IpFilterRule get(int index) {
+ return ipFilterRuleList.get(index);
+ }
+
+ /**
+ * Returns true if this list contains no elements
+ *
+ * @return true if this list contains no elements
+ */
+ public boolean isEmpty() {
+ return ipFilterRuleList.isEmpty();
+ }
+
+ /** Remove the ipFilterRule from the list */
+ public void remove(IpFilterRule ipFilterRule) {
+ if (ipFilterRule == null) {
+ throw new NullPointerException("IpFilterRule can not be null");
+ }
+ ipFilterRuleList.remove(ipFilterRule);
+ }
+
+ /**
+ * Removes the element at the specified position in this list
+ *
+ * @return the element previously at the specified position
+ */
+ public IpFilterRule remove(int index) {
+ return ipFilterRuleList.remove(index);
+ }
+
+ /** Removes from this list all of its elements that are contained in the specified collection */
+ public void removeAll(Collection c) {
+ if (c == null) {
+ throw new NullPointerException("Collection can not be null");
+ }
+ ipFilterRuleList.removeAll(c);
+ }
+
+ /** Retains only the elements in this list that are contained in the specified collection */
+ public void retainAll(Collection c) {
+ if (c == null) {
+ throw new NullPointerException("Collection can not be null");
+ }
+ ipFilterRuleList.retainAll(c);
+ }
+
+ /**
+ * Replaces the element at the specified position in this list with the specified element
+ *
+ * @return the element previously at the specified position
+ */
+ public IpFilterRule set(int index, IpFilterRule ipFilterRule) {
+ if (ipFilterRule == null) {
+ throw new NullPointerException("IpFilterRule can not be null");
+ }
+ return ipFilterRuleList.set(index, ipFilterRule);
+ }
+
+ /**
+ * Returns the number of elements in this list.
+ *
+ * @return the number of elements in this list.
+ */
+ public int size() {
+ return ipFilterRuleList.size();
+ }
+
+ @Override
+ protected boolean accept(ChannelHandlerContext ctx, ChannelEvent e, InetSocketAddress inetSocketAddress)
+ throws Exception {
+ if (ipFilterRuleList.isEmpty()) {
+ // No limitation neither in deny or allow, so accept
+ return true;
+ }
+ InetAddress inetAddress = inetSocketAddress.getAddress();
+ Iterator iterator = ipFilterRuleList.iterator();
+ IpFilterRule ipFilterRule = null;
+ while (iterator.hasNext()) {
+ ipFilterRule = iterator.next();
+ if (ipFilterRule.contains(inetAddress)) {
+ // Match founds, is it a ALLOW or DENY rule
+ return ipFilterRule.isAllowRule();
+ }
+ }
+ // No limitation founds and no allow either, but as it is like Firewall rules, it is therefore accepted
+ return true;
+ }
+
+}
diff --git a/src/main/java/org/jboss/netty/handler/ipfilter/IpFilterRuleList.java b/src/main/java/org/jboss/netty/handler/ipfilter/IpFilterRuleList.java
new file mode 100644
index 0000000000..eae1f7b755
--- /dev/null
+++ b/src/main/java/org/jboss/netty/handler/ipfilter/IpFilterRuleList.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2011 The Netty Project
+ *
+ * The Netty Project licenses this file to you 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 org.jboss.netty.handler.ipfilter;
+
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+
+import org.jboss.netty.logging.InternalLogger;
+import org.jboss.netty.logging.InternalLoggerFactory;
+
+/**
+ * The Class IpFilterRuleList is a helper class to generate a List of Rules from a string.
+ * In case of parse errors no exceptions are thrown. The error is logged.
+ *
+ * Rule List Syntax:
+ *
+ *
+ *
+ * Example: allow only localhost:
+ *
+ * new IPFilterRuleHandler().addAll(new IpFilterRuleList("+n:localhost, -n:*"));
+ *
+ */
+public class IpFilterRuleList extends ArrayList {
+ private static final long serialVersionUID = -6164162941749588780L;
+
+ private static final InternalLogger logger = InternalLoggerFactory.getInstance(IpFilterRuleList.class);
+
+ /**
+ * Instantiates a new ip filter rule list.
+ *
+ * @param rules the rules
+ */
+ public IpFilterRuleList(String rules) {
+ parseRules(rules);
+ }
+
+ private void parseRules(String rules) {
+ String[] ruless = rules.split(",");
+ for (String rule : ruless) {
+ parseRule(rule.trim());
+ }
+ }
+
+ private void parseRule(String rule) {
+ if (rule == null || rule.length() == 0) {
+ return;
+ }
+ if (!(rule.startsWith("+") || rule.startsWith("-"))) {
+ if (logger.isErrorEnabled()) {
+ logger.error("syntax error in ip filter rule:" + rule);
+ }
+ return;
+ }
+
+ boolean allow = rule.startsWith("+");
+ if (rule.charAt(1) == 'n' || rule.charAt(1) == 'i') {
+ this.add(new PatternRule(allow, rule.substring(1)));
+ } else if (rule.charAt(1) == 'c') {
+ try {
+ this.add(new IpSubnetFilterRule(allow, rule.substring(3)));
+ } catch (UnknownHostException e) {
+ if (logger.isErrorEnabled()) {
+ logger.error("error parsing ip filter " + rule, e);
+ }
+ }
+ } else {
+ if (logger.isErrorEnabled()) {
+ logger.error("syntax error in ip filter rule:" + rule);
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/jboss/netty/handler/ipfilter/IpFilteringHandler.java b/src/main/java/org/jboss/netty/handler/ipfilter/IpFilteringHandler.java
new file mode 100644
index 0000000000..c4c0e2d373
--- /dev/null
+++ b/src/main/java/org/jboss/netty/handler/ipfilter/IpFilteringHandler.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2011 The Netty Project
+ *
+ * The Netty Project licenses this file to you 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 org.jboss.netty.handler.ipfilter;
+
+/**
+ * The Interface IpFilteringHandler.
+ * A Filter of IP.
+ *
+ * Users can add an {@link IpFilterListener} to add specific actions in case a connection is allowed or refused.
+ */
+public interface IpFilteringHandler {
+
+ /**
+ * Sets the filter listener.
+ *
+ * @param listener the new ip filter listener
+ */
+ void setIpFilterListener(IpFilterListener listener);
+
+ /** Remove the filter listener. */
+ void removeIpFilterListener();
+}
diff --git a/src/main/java/org/jboss/netty/handler/ipfilter/IpFilteringHandlerImpl.java b/src/main/java/org/jboss/netty/handler/ipfilter/IpFilteringHandlerImpl.java
new file mode 100644
index 0000000000..7374dfa01a
--- /dev/null
+++ b/src/main/java/org/jboss/netty/handler/ipfilter/IpFilteringHandlerImpl.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2011 The Netty Project
+ *
+ * The Netty Project licenses this file to you 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 org.jboss.netty.handler.ipfilter;
+
+import java.net.InetSocketAddress;
+
+import org.jboss.netty.channel.ChannelEvent;
+import org.jboss.netty.channel.ChannelFuture;
+import org.jboss.netty.channel.ChannelFutureListener;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.jboss.netty.channel.ChannelStateEvent;
+import org.jboss.netty.channel.ChannelUpstreamHandler;
+import org.jboss.netty.channel.Channels;
+
+// TODO: Auto-generated Javadoc
+
+/** General class that handle Ip Filtering. */
+public abstract class IpFilteringHandlerImpl implements ChannelUpstreamHandler, IpFilteringHandler {
+
+ private IpFilterListener listener;
+
+ /**
+ * Called when the channel is connected. It returns True if the corresponding connection
+ * is to be allowed. Else it returns False.
+ *
+ * @param inetSocketAddress the remote {@link InetSocketAddress} from client
+ * @return True if the corresponding connection is allowed, else False.
+ */
+ protected abstract boolean accept(ChannelHandlerContext ctx, ChannelEvent e, InetSocketAddress inetSocketAddress)
+ throws Exception;
+
+ /**
+ * Called when the channel has the CONNECTED status and the channel was refused by a previous call to accept().
+ * This method enables your implementation to send a message back to the client before closing
+ * or whatever you need. This method returns a ChannelFuture on which the implementation
+ * will wait uninterruptibly before closing the channel.
+ * For instance, If a message is sent back, the corresponding ChannelFuture has to be returned.
+ *
+ * @param inetSocketAddress the remote {@link InetSocketAddress} from client
+ * @return the associated ChannelFuture to be waited for before closing the channel. Null is allowed.
+ */
+ protected ChannelFuture handleRefusedChannel(ChannelHandlerContext ctx, ChannelEvent e,
+ InetSocketAddress inetSocketAddress) throws Exception {
+ if (listener == null) {
+ return null;
+ }
+ return listener.refused(ctx, e, inetSocketAddress);
+ }
+
+ protected ChannelFuture handleAllowedChannel(ChannelHandlerContext ctx, ChannelEvent e,
+ InetSocketAddress inetSocketAddress) throws Exception {
+ if (listener == null) {
+ return null;
+ }
+ return listener.allowed(ctx, e, inetSocketAddress);
+ }
+
+ /**
+ * Internal method to test if the current channel is blocked. Should not be overridden.
+ *
+ * @return True if the current channel is blocked, else False
+ */
+ protected boolean isBlocked(ChannelHandlerContext ctx) {
+ return ctx.getAttachment() != null;
+ }
+
+ /**
+ * Called in handleUpstream, if this channel was previously blocked,
+ * to check if whatever the event, it should be passed to the next entry in the pipeline.
+ * If one wants to not block events, just overridden this method by returning always true.
+ * Note that OPENED and BOUND events are still passed to the next entry in the pipeline since
+ * those events come out before the CONNECTED event and so the possibility to filter the connection.
+ *
+ * @return True if the event should continue, False if the event should not continue
+ * since this channel was blocked by this filter
+ */
+ protected boolean continues(ChannelHandlerContext ctx, ChannelEvent e) throws Exception {
+ if (listener != null) {
+ return listener.continues(ctx, e);
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public void handleUpstream(ChannelHandlerContext ctx, ChannelEvent e) throws Exception {
+ if (e instanceof ChannelStateEvent) {
+ ChannelStateEvent evt = (ChannelStateEvent) e;
+ switch (evt.getState()) {
+ case OPEN:
+ case BOUND:
+ // Special case: OPEND and BOUND events are before CONNECTED,
+ // but CLOSED and UNBOUND events are after DISCONNECTED: should those events be blocked too?
+ if (isBlocked(ctx) && !continues(ctx, evt)) {
+ // don't pass to next level since channel was blocked early
+ return;
+ } else {
+ ctx.sendUpstream(e);
+ return;
+ }
+ case CONNECTED:
+ if (evt.getValue() != null) {
+ // CONNECTED
+ InetSocketAddress inetSocketAddress = (InetSocketAddress) e.getChannel().getRemoteAddress();
+ if (!accept(ctx, e, inetSocketAddress)) {
+ ctx.setAttachment(Boolean.TRUE);
+ ChannelFuture future = handleRefusedChannel(ctx, e, inetSocketAddress);
+ if (future != null) {
+ future.addListener(ChannelFutureListener.CLOSE);
+ } else {
+ Channels.close(e.getChannel());
+ }
+ if (isBlocked(ctx) && !continues(ctx, evt)) {
+ // don't pass to next level since channel was blocked early
+ return;
+ }
+ } else {
+ handleAllowedChannel(ctx, e, inetSocketAddress);
+ }
+ // This channel is not blocked
+ ctx.setAttachment(null);
+ } else {
+ // DISCONNECTED
+ if (isBlocked(ctx) && !continues(ctx, evt)) {
+ // don't pass to next level since channel was blocked early
+ return;
+ }
+ }
+ break;
+ }
+ }
+ if (isBlocked(ctx) && !continues(ctx, e)) {
+ // don't pass to next level since channel was blocked early
+ return;
+ }
+ // Whatever it is, if not blocked, goes to the next level
+ ctx.sendUpstream(e);
+ }
+
+ @Override
+ public void setIpFilterListener(IpFilterListener listener) {
+ this.listener = listener;
+ }
+
+ @Override
+ public void removeIpFilterListener() {
+ this.listener = null;
+
+ }
+
+}
diff --git a/src/main/java/org/jboss/netty/handler/ipfilter/IpSet.java b/src/main/java/org/jboss/netty/handler/ipfilter/IpSet.java
new file mode 100644
index 0000000000..c77c0ee6dd
--- /dev/null
+++ b/src/main/java/org/jboss/netty/handler/ipfilter/IpSet.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2011 The Netty Project
+ *
+ * The Netty Project licenses this file to you 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 org.jboss.netty.handler.ipfilter;
+
+import java.net.InetAddress;
+
+/** This Interface defines an IpSet object. */
+public interface IpSet {
+ /**
+ * Compares the given InetAddress against the IpSet and returns true if
+ * the InetAddress is contained in this Rule and false if not.
+ *
+ * @return returns true if the given IP address is contained in the current
+ * IpSet.
+ */
+ boolean contains(InetAddress inetAddress1);
+}
diff --git a/src/main/java/org/jboss/netty/handler/ipfilter/IpSubnet.java b/src/main/java/org/jboss/netty/handler/ipfilter/IpSubnet.java
new file mode 100644
index 0000000000..f62b16b72d
--- /dev/null
+++ b/src/main/java/org/jboss/netty/handler/ipfilter/IpSubnet.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2011 The Netty Project
+ *
+ * The Netty Project licenses this file to you 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 org.jboss.netty.handler.ipfilter;
+
+import org.jboss.netty.logging.InternalLogger;
+import org.jboss.netty.logging.InternalLoggerFactory;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ * This class allows to check if an IP V4 or V6 Address is contained in a subnet.
+ *
+ * Supported IP V4 Formats for the Subnets are: 1.1.1.1/255.255.255.255 or 1.1.1.1/32 (CIDR-Notation)
+ * and (InetAddress,Mask) where Mask is a integer for CIDR-notation or a String for Standard Mask notation.
+ *
Example1:
+ * IpV4Subnet ips = new IpV4Subnet("192.168.1.0/24");
+ * System.out.println("Result: "+ ips.contains("192.168.1.123"));
+ * System.out.println("Result: "+ ips.contains(inetAddress2));
+ * Example1 bis:
+ * IpV4Subnet ips = new IpV4Subnet(inetAddress, 24);
+ * where inetAddress is 192.168.1.0 and inetAddress2 is 192.168.1.123
+ *
Example2:
+ * IpV4Subnet ips = new IpV4Subnet("192.168.1.0/255.255.255.0");
+ * System.out.println("Result: "+ ips.contains("192.168.1.123"));
+ * System.out.println("Result: "+ ips.contains(inetAddress2));
+ * Example2 bis:
+ * IpV4Subnet ips = new IpV4Subnet(inetAddress, "255.255.255.0");
+ * where inetAddress is 192.168.1.0 and inetAddress2 is 192.168.1.123
+ *
+ * Supported IP V6 Formats for the Subnets are: a:b:c:d:e:f:g:h/NN (CIDR-Notation)
+ * or any IPV6 notations (like a:b:c:d::/NN, a:b:c:d:e:f:w.x.y.z/NN)
+ * and (InetAddress,Mask) where Mask is a integer for CIDR-notation
+ * and (InetAddress,subnet).
+ *
Example1:
+ * IpSubnet ips = new IpSubnet("1fff:0:0a88:85a3:0:0:0:0/24");
+ * IpSubnet ips = new IpSubnet("1fff:0:0a88:85a3::/24");
+ * System.out.println("Result: "+ ips.contains("1fff:0:0a88:85a3:0:0:ac1f:8001"));
+ * System.out.println("Result: "+ ips.contains(inetAddress2));
+ * Example1 bis:
+ * IpSubnet ips = new IpSubnet(inetAddress, 24);
+ * where inetAddress2 is 1fff:0:0a88:85a3:0:0:ac1f:8001
+ */
+public class IpSubnet implements IpSet, Comparable {
+
+ private static final InternalLogger logger =
+ InternalLoggerFactory.getInstance(IpSubnet.class);
+
+ /** Internal representation */
+ private CIDR cidr;
+
+ /** Create IpSubnet for ALL (used for ALLOW or DENY ALL) */
+ public IpSubnet() {
+ // ALLOW or DENY ALL
+ cidr = null;
+ }
+
+ /**
+ * Create IpSubnet using the CIDR or normal Notation
+ * i.e.:
+ * IpSubnet subnet = new IpSubnet("10.10.10.0/24"); or
+ * IpSubnet subnet = new IpSubnet("10.10.10.0/255.255.255.0"); or
+ * IpSubnet subnet = new IpSubnet("1fff:0:0a88:85a3:0:0:0:0/24");
+ *
+ * @param netAddress a network address as string.
+ */
+ public IpSubnet(String netAddress) throws UnknownHostException {
+ cidr = CIDR.newCIDR(netAddress);
+ }
+
+ /** Create IpSubnet using the CIDR Notation */
+ public IpSubnet(InetAddress inetAddress, int cidrNetMask) throws UnknownHostException {
+ cidr = CIDR.newCIDR(inetAddress, cidrNetMask);
+ }
+
+ /** Create IpSubnet using the normal Notation */
+ public IpSubnet(InetAddress inetAddress, String netMask) throws UnknownHostException {
+ cidr = CIDR.newCIDR(inetAddress, netMask);
+ }
+
+ /**
+ * Compares the given IP-Address against the Subnet and returns true if
+ * the ip is in the subnet-ip-range and false if not.
+ *
+ * @param ipAddr an ipaddress
+ * @return returns true if the given IP address is inside the currently
+ * set network.
+ */
+ public boolean contains(String ipAddr) throws UnknownHostException {
+ InetAddress inetAddress1 = InetAddress.getByName(ipAddr);
+ return this.contains(inetAddress1);
+ }
+
+ /**
+ * Compares the given InetAddress against the Subnet and returns true if
+ * the ip is in the subnet-ip-range and false if not.
+ *
+ * @return returns true if the given IP address is inside the currently
+ * set network.
+ */
+ @Override
+ public boolean contains(InetAddress inetAddress) {
+ if (cidr == null) {
+ // ANY
+ return true;
+ }
+ return cidr.contains(inetAddress);
+ }
+
+ @Override
+ public String toString() {
+ return cidr.toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof IpSubnet)) {
+ return false;
+ }
+ IpSubnet ipSubnet = (IpSubnet) o;
+ return ipSubnet.cidr.equals(cidr);
+ }
+
+ @Override
+ public int hashCode() {
+ return cidr.hashCode();
+ }
+
+ /** Compare two IpSubnet */
+ @Override
+ public int compareTo(IpSubnet o) {
+ return cidr.toString().compareTo(o.cidr.toString());
+ }
+
+ /**
+ * Simple test functions
+ *
+ * @param args where args[0] is the netmask (standard or CIDR notation) and optional args[1] is
+ * the inetAddress to test with this IpSubnet
+ */
+ public static void main(String[] args) throws Exception {
+ if (args.length != 0) {
+ IpSubnet ipSubnet = null;
+ try {
+ ipSubnet = new IpSubnet(args[0]);
+ } catch (UnknownHostException e) {
+ return;
+ }
+ logger.debug("IpSubnet: " + ipSubnet.toString() + " from " + ipSubnet.cidr.getBaseAddress() + " to "
+ + ipSubnet.cidr.getEndAddress() + " mask " + ipSubnet.cidr.getMask());
+ if (args.length > 1) {
+ logger.debug("Is IN: " + args[1] + " " + ipSubnet.contains(args[1]));
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/jboss/netty/handler/ipfilter/IpSubnetFilterRule.java b/src/main/java/org/jboss/netty/handler/ipfilter/IpSubnetFilterRule.java
new file mode 100644
index 0000000000..137a7f62fd
--- /dev/null
+++ b/src/main/java/org/jboss/netty/handler/ipfilter/IpSubnetFilterRule.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2011 The Netty Project
+ *
+ * The Netty Project licenses this file to you 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 org.jboss.netty.handler.ipfilter;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ * Ip V4 and Ip V6 filter rule.
+ *
+ * Note that mix of IPV4 and IPV6 is allowed but it is not recommended. So it is preferable to not
+ * mix IPV4 addresses with IPV6 rules, even if it should work.
+ */
+public class IpSubnetFilterRule extends IpSubnet implements IpFilterRule {
+ /** Is this IpV4Subnet an ALLOW or DENY rule */
+ private boolean isAllowRule = true;
+
+ /**
+ * Constructor for a ALLOW or DENY ALL
+ *
+ * @param allow True for ALLOW, False for DENY
+ */
+ public IpSubnetFilterRule(boolean allow) {
+ isAllowRule = allow;
+ }
+
+ /** @param allow True for ALLOW, False for DENY */
+ public IpSubnetFilterRule(boolean allow, InetAddress inetAddress, int cidrNetMask) throws UnknownHostException {
+ super(inetAddress, cidrNetMask);
+ isAllowRule = allow;
+ }
+
+ /** @param allow True for ALLOW, False for DENY */
+ public IpSubnetFilterRule(boolean allow, InetAddress inetAddress, String netMask) throws UnknownHostException {
+ super(inetAddress, netMask);
+ isAllowRule = allow;
+ }
+
+ /** @param allow True for ALLOW, False for DENY */
+ public IpSubnetFilterRule(boolean allow, String netAddress) throws UnknownHostException {
+ super(netAddress);
+ isAllowRule = allow;
+ }
+
+ @Override
+ public boolean isAllowRule() {
+ return isAllowRule;
+ }
+
+ @Override
+ public boolean isDenyRule() {
+ return !isAllowRule;
+ }
+
+}
diff --git a/src/main/java/org/jboss/netty/handler/ipfilter/IpV4Subnet.java b/src/main/java/org/jboss/netty/handler/ipfilter/IpV4Subnet.java
new file mode 100644
index 0000000000..ca6faf5250
--- /dev/null
+++ b/src/main/java/org/jboss/netty/handler/ipfilter/IpV4Subnet.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright 2011 The Netty Project
+ *
+ * The Netty Project licenses this file to you 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 org.jboss.netty.handler.ipfilter;
+
+import org.jboss.netty.logging.InternalLogger;
+import org.jboss.netty.logging.InternalLoggerFactory;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+/**
+ * This class allows to check if an IP-V4-Address is contained in a subnet.
+ * Supported Formats for the Subnets are: 1.1.1.1/255.255.255.255 or 1.1.1.1/32 (CIDR-Notation)
+ * and (InetAddress,Mask) where Mask is a integer for CIDR-notation or a String for Standard Mask notation.
+ *
Example1:
+ * IpV4Subnet ips = new IpV4Subnet("192.168.1.0/24");
+ * System.out.println("Result: "+ ips.contains("192.168.1.123"));
+ * System.out.println("Result: "+ ips.contains(inetAddress2));
+ * Example1 bis:
+ * IpV4Subnet ips = new IpV4Subnet(inetAddress, 24);
+ * where inetAddress is 192.168.1.0 and inetAddress2 is 192.168.1.123
+ *
Example2:
+ * IpV4Subnet ips = new IpV4Subnet("192.168.1.0/255.255.255.0");
+ * System.out.println("Result: "+ ips.contains("192.168.1.123"));
+ * System.out.println("Result: "+ ips.contains(inetAddress2));
+ * Example2 bis:
+ * IpV4Subnet ips = new IpV4Subnet(inetAddress, "255.255.255.0");
+ * where inetAddress is 192.168.1.0 and inetAddress2 is 192.168.1.123
+ */
+public class IpV4Subnet implements IpSet, Comparable {
+
+ private static final InternalLogger logger =
+ InternalLoggerFactory.getInstance(IpV4Subnet.class);
+
+ private static final int SUBNET_MASK = 0x80000000;
+
+ private static final int BYTE_ADDRESS_MASK = 0xFF;
+
+ private InetAddress inetAddress;
+
+ private int subnet;
+
+ private int mask;
+
+ private int cidrMask;
+
+ /** Create IpV4Subnet for ALL (used for ALLOW or DENY ALL) */
+ public IpV4Subnet() {
+ // ALLOW or DENY ALL
+ mask = -1;
+ // other will be ignored
+ inetAddress = null;
+ subnet = 0;
+ cidrMask = 0;
+ }
+
+ /**
+ * Create IpV4Subnet using the CIDR or normal Notation
+ * i.e.:
+ * IpV4Subnet subnet = new IpV4Subnet("10.10.10.0/24"); or
+ * IpV4Subnet subnet = new IpV4Subnet("10.10.10.0/255.255.255.0");
+ *
+ * @param netAddress a network address as string.
+ */
+ public IpV4Subnet(String netAddress) throws UnknownHostException {
+ setNetAddress(netAddress);
+ }
+
+ /** Create IpV4Subnet using the CIDR Notation */
+ public IpV4Subnet(InetAddress inetAddress, int cidrNetMask) {
+ setNetAddress(inetAddress, cidrNetMask);
+ }
+
+ /** Create IpV4Subnet using the normal Notation */
+ public IpV4Subnet(InetAddress inetAddress, String netMask) {
+ setNetAddress(inetAddress, netMask);
+ }
+
+ /**
+ * Sets the Network Address in either CIDR or Decimal Notation.
+ * i.e.: setNetAddress("1.1.1.1/24"); or
+ * setNetAddress("1.1.1.1/255.255.255.0");
+ *
+ * @param netAddress a network address as string.
+ */
+ private void setNetAddress(String netAddress) throws UnknownHostException {
+ Vector
+ *
+ *
Standard use could be as follow: The accept method must be overridden (of course you can
+ * override others).
+ *
+ *
+ *
accept method allows to specify your way of choosing if a new connection is
+ * to be allowed or not.
+ * In OneIpFilterHandler and IpFilterRuleHandler,
+ * this method is already implemented.
+ *
+ *
+ *
handleRefusedChannel method is executed when the accept method filters (blocks, so returning false)
+ * the new connection. This method allows you to implement specific actions to be taken before the channel is
+ * closed. After this method is called, the channel is immediately closed.
+ * So if you want to send back a message to the client, don't forget to return a respectful ChannelFuture,
+ * otherwise the message could be missed since the channel will be closed immediately after this
+ * call and the waiting on this channelFuture (at least with respect of asynchronous operations).
+ * Per default implementation this method invokes an {@link org.jboss.netty.handler.ipfilter.IpFilterListener} or returns null if no listener has been set.
+ *
+ *
+ *
continues is called when any event appears after CONNECTED event and only for
+ * blocked channels.
+ * It should return True if this new event has to go to next handlers
+ * in the pipeline if any, and False (default) if no events has to be passed to the next
+ * handlers when a channel is blocked. This is intend to prevent any unnecessary action since the connection is refused.
+ * However, you could change its behavior for instance because you don't want that any event
+ * will be blocked by this filter by returning always true or according to some events.
+ * Note that OPENED and BOUND events are still passed to the next entry in the pipeline since
+ * those events come out before the CONNECTED event, so there is no possibility to filter those two events
+ * before the CONNECTED event shows up. Therefore, you might want to let CLOSED and UNBOUND be passed
+ * to the next entry in the pipeline.
+ * Per default implementation this method invokes an {@link org.jboss.netty.handler.ipfilter.IpFilterListener} or returns false if no listener has been set.
+ *
+ *
+ *
Finally handleUpstream traps the CONNECTED and DISCONNECTED events.
+ * If in the CONNECTED events the channel is blocked (accept refused the channel),
+ * then any new events on this channel will be blocked.
+ * However, you could change its behavior for instance because you don't want that all events
+ * will be blocked by this filter by testing the result of isBlocked, and if so,
+ * calling ctx.sendUpstream(e); after calling the super method or by changing the continues method.
+
+ *
+ *
+ * A typical setup for ip filter for TCP/IP socket would be:
+ *
+ *
+ *
+ * @apiviz.exclude ^java\.lang\.
+ */
+package org.jboss.netty.handler.ipfilter;
+
+
diff --git a/src/main/java/org/jboss/netty/handler/traffic/AbstractTrafficShapingHandler.java b/src/main/java/org/jboss/netty/handler/traffic/AbstractTrafficShapingHandler.java
new file mode 100644
index 0000000000..f7fe35d60e
--- /dev/null
+++ b/src/main/java/org/jboss/netty/handler/traffic/AbstractTrafficShapingHandler.java
@@ -0,0 +1,499 @@
+/*
+ * Copyright 2011 The Netty Project
+ *
+ * The Netty Project licenses this file to you 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 org.jboss.netty.handler.traffic;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.jboss.netty.channel.Channel;
+import org.jboss.netty.channel.ChannelEvent;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.jboss.netty.channel.ChannelState;
+import org.jboss.netty.channel.ChannelStateEvent;
+import org.jboss.netty.channel.MessageEvent;
+import org.jboss.netty.channel.SimpleChannelHandler;
+import org.jboss.netty.logging.InternalLogger;
+import org.jboss.netty.logging.InternalLoggerFactory;
+import org.jboss.netty.util.DefaultObjectSizeEstimator;
+import org.jboss.netty.util.ExternalResourceReleasable;
+import org.jboss.netty.util.ObjectSizeEstimator;
+import org.jboss.netty.util.internal.ExecutorUtil;
+
+/**
+ * AbstractTrafficShapingHandler allows to limit the global bandwidth
+ * (see {@link GlobalTrafficShapingHandler}) or per session
+ * bandwidth (see {@link ChannelTrafficShapingHandler}), as traffic shaping.
+ * It allows too to implement an almost real time monitoring of the bandwidth using
+ * the monitors from {@link TrafficCounter} that will call back every checkInterval
+ * the method doAccounting of this handler.
+ *
+ *
+ * If you want for any particular reasons to stop the monitoring (accounting) or to change
+ * the read/write limit or the check interval, several methods allow that for you:
+ *
+ *
configure allows you to change read or write limits, or the checkInterval
+ *
getTrafficCounter allows you to have access to the TrafficCounter and so to stop
+ * or start the monitoring, to change the checkInterval directly, or to have access to its values.
+ *
+ *
+ */
+public abstract class AbstractTrafficShapingHandler extends
+ SimpleChannelHandler implements ExternalResourceReleasable {
+ /**
+ * Internal logger
+ */
+ static InternalLogger logger = InternalLoggerFactory
+ .getInstance(AbstractTrafficShapingHandler.class);
+
+ /**
+ * Default delay between two checks: 1s
+ */
+ public static final long DEFAULT_CHECK_INTERVAL = 1000;
+
+ /**
+ * Default minimal time to wait
+ */
+ private static final long MINIMAL_WAIT = 10;
+
+ /**
+ * Traffic Counter
+ */
+ protected TrafficCounter trafficCounter;
+
+ /**
+ * ObjectSizeEstimator
+ */
+ private ObjectSizeEstimator objectSizeEstimator;
+
+ /**
+ * Executor to associated to any TrafficCounter
+ */
+ protected Executor executor;
+
+ /**
+ * Limit in B/s to apply to write
+ */
+ private long writeLimit;
+
+ /**
+ * Limit in B/s to apply to read
+ */
+ private long readLimit;
+
+ /**
+ * Delay between two performance snapshots
+ */
+ protected long checkInterval = DEFAULT_CHECK_INTERVAL; // default 1 s
+
+ /**
+ * Boolean associated with the release of this TrafficShapingHandler.
+ * It will be true only once when the releaseExternalRessources is called
+ * to prevent waiting when shutdown.
+ */
+ final AtomicBoolean release = new AtomicBoolean(false);
+
+ private void init(ObjectSizeEstimator newObjectSizeEstimator,
+ Executor newExecutor, long newWriteLimit, long newReadLimit, long newCheckInterval) {
+ objectSizeEstimator = newObjectSizeEstimator;
+ executor = newExecutor;
+ writeLimit = newWriteLimit;
+ readLimit = newReadLimit;
+ checkInterval = newCheckInterval;
+ //logger.info("TSH: "+writeLimit+":"+readLimit+":"+checkInterval+":"+isPerChannel());
+ }
+
+ /**
+ *
+ * @param newTrafficCounter the TrafficCounter to set
+ */
+ void setTrafficCounter(TrafficCounter newTrafficCounter) {
+ trafficCounter = newTrafficCounter;
+ }
+
+ /**
+ * Constructor using default {@link ObjectSizeEstimator}
+ *
+ * @param executor
+ * created for instance like Executors.newCachedThreadPool
+ * @param writeLimit
+ * 0 or a limit in bytes/s
+ * @param readLimit
+ * 0 or a limit in bytes/s
+ * @param checkInterval
+ * The delay between two computations of performances for
+ * channels or 0 if no stats are to be computed
+ */
+ public AbstractTrafficShapingHandler(Executor executor, long writeLimit,
+ long readLimit, long checkInterval) {
+ init(new DefaultObjectSizeEstimator(), executor, writeLimit, readLimit,
+ checkInterval);
+ }
+
+ /**
+ * Constructor using the specified ObjectSizeEstimator
+ *
+ * @param objectSizeEstimator
+ * the {@link ObjectSizeEstimator} that will be used to compute
+ * the size of the message
+ * @param executor
+ * created for instance like Executors.newCachedThreadPool
+ * @param writeLimit
+ * 0 or a limit in bytes/s
+ * @param readLimit
+ * 0 or a limit in bytes/s
+ * @param checkInterval
+ * The delay between two computations of performances for
+ * channels or 0 if no stats are to be computed
+ */
+ public AbstractTrafficShapingHandler(
+ ObjectSizeEstimator objectSizeEstimator, Executor executor,
+ long writeLimit, long readLimit, long checkInterval) {
+ init(objectSizeEstimator, executor, writeLimit, readLimit,
+ checkInterval);
+ }
+
+ /**
+ * Constructor using default {@link ObjectSizeEstimator} and using default Check Interval
+ *
+ * @param executor
+ * created for instance like Executors.newCachedThreadPool
+ * @param writeLimit
+ * 0 or a limit in bytes/s
+ * @param readLimit
+ * 0 or a limit in bytes/s
+ */
+ public AbstractTrafficShapingHandler(Executor executor, long writeLimit,
+ long readLimit) {
+ init(new DefaultObjectSizeEstimator(), executor, writeLimit, readLimit,
+ DEFAULT_CHECK_INTERVAL);
+ }
+
+ /**
+ * Constructor using the specified ObjectSizeEstimator and using default Check Interval
+ *
+ * @param objectSizeEstimator
+ * the {@link ObjectSizeEstimator} that will be used to compute
+ * the size of the message
+ * @param executor
+ * created for instance like Executors.newCachedThreadPool
+ * @param writeLimit
+ * 0 or a limit in bytes/s
+ * @param readLimit
+ * 0 or a limit in bytes/s
+ */
+ public AbstractTrafficShapingHandler(
+ ObjectSizeEstimator objectSizeEstimator, Executor executor,
+ long writeLimit, long readLimit) {
+ init(objectSizeEstimator, executor, writeLimit, readLimit,
+ DEFAULT_CHECK_INTERVAL);
+ }
+
+ /**
+ * Constructor using default {@link ObjectSizeEstimator} and using NO LIMIT and default Check Interval
+ *
+ * @param executor
+ * created for instance like Executors.newCachedThreadPool
+ */
+ public AbstractTrafficShapingHandler(Executor executor) {
+ init(new DefaultObjectSizeEstimator(), executor, 0, 0,
+ DEFAULT_CHECK_INTERVAL);
+ }
+
+ /**
+ * Constructor using the specified ObjectSizeEstimator and using NO LIMIT and default Check Interval
+ *
+ * @param objectSizeEstimator
+ * the {@link ObjectSizeEstimator} that will be used to compute
+ * the size of the message
+ * @param executor
+ * created for instance like Executors.newCachedThreadPool
+ */
+ public AbstractTrafficShapingHandler(
+ ObjectSizeEstimator objectSizeEstimator, Executor executor) {
+ init(objectSizeEstimator, executor, 0, 0, DEFAULT_CHECK_INTERVAL);
+ }
+
+ /**
+ * Constructor using default {@link ObjectSizeEstimator} and using NO LIMIT
+ *
+ * @param executor
+ * created for instance like Executors.newCachedThreadPool
+ * @param checkInterval
+ * The delay between two computations of performances for
+ * channels or 0 if no stats are to be computed
+ */
+ public AbstractTrafficShapingHandler(Executor executor, long checkInterval) {
+ init(new DefaultObjectSizeEstimator(), executor, 0, 0, checkInterval);
+ }
+
+ /**
+ * Constructor using the specified ObjectSizeEstimator and using NO LIMIT
+ *
+ * @param objectSizeEstimator
+ * the {@link ObjectSizeEstimator} that will be used to compute
+ * the size of the message
+ * @param executor
+ * created for instance like Executors.newCachedThreadPool
+ * @param checkInterval
+ * The delay between two computations of performances for
+ * channels or 0 if no stats are to be computed
+ */
+ public AbstractTrafficShapingHandler(
+ ObjectSizeEstimator objectSizeEstimator, Executor executor,
+ long checkInterval) {
+ init(objectSizeEstimator, executor, 0, 0, checkInterval);
+ }
+
+ /**
+ * Change the underlying limitations and check interval.
+ */
+ public void configure(long newWriteLimit, long newReadLimit,
+ long newCheckInterval) {
+ this.configure(newWriteLimit, newReadLimit);
+ this.configure(newCheckInterval);
+ }
+
+ /**
+ * Change the underlying limitations.
+ */
+ public void configure(long newWriteLimit, long newReadLimit) {
+ writeLimit = newWriteLimit;
+ readLimit = newReadLimit;
+ if (trafficCounter != null) {
+ trafficCounter.resetAccounting(System.currentTimeMillis() + 1);
+ }
+ }
+
+ /**
+ * Change the check interval.
+ */
+ public void configure(long newCheckInterval) {
+ checkInterval = newCheckInterval;
+ if (trafficCounter != null) {
+ trafficCounter.configure(checkInterval);
+ }
+ }
+
+ /**
+ * Called each time the accounting is computed from the TrafficCounters.
+ * This method could be used for instance to implement almost real time accounting.
+ *
+ * @param counter
+ * the TrafficCounter that computes its performance
+ */
+ protected void doAccounting(TrafficCounter counter) {
+ // NOOP by default
+ }
+
+ /**
+ * Class to implement setReadable at fix time
+ */
+ private class ReopenRead implements Runnable {
+ /**
+ * Associated ChannelHandlerContext
+ */
+ private ChannelHandlerContext ctx;
+
+ /**
+ * Time to wait before clearing the channel
+ */
+ private long timeToWait;
+
+ /**
+ * @param ctx
+ * the associated channelHandlerContext
+ * @param timeToWait
+ */
+ protected ReopenRead(ChannelHandlerContext ctx, long timeToWait) {
+ this.ctx = ctx;
+ this.timeToWait = timeToWait;
+ }
+
+ /**
+ * Truly run the waken up of the channel
+ */
+ @Override
+ public void run() {
+ try {
+ if (release.get()) {
+ return;
+ }
+ Thread.sleep(timeToWait);
+ } catch (InterruptedException e) {
+ // interruption so exit
+ return;
+ }
+ // logger.info("WAKEUP!");
+ if (ctx != null && ctx.getChannel() != null &&
+ ctx.getChannel().isConnected()) {
+ //logger.info(" setReadable TRUE: "+timeToWait);
+ // readSuspended = false;
+ ctx.setAttachment(null);
+ ctx.getChannel().setReadable(true);
+ }
+ }
+ }
+
+ /**
+ *
+ * @return the time that should be necessary to wait to respect limit. Can
+ * be negative time
+ */
+ private long getTimeToWait(long limit, long bytes, long lastTime,
+ long curtime) {
+ long interval = curtime - lastTime;
+ if (interval == 0) {
+ // Time is too short, so just lets continue
+ return 0;
+ }
+ return bytes * 1000 / limit - interval;
+ }
+
+ @Override
+ public void messageReceived(ChannelHandlerContext arg0, MessageEvent arg1)
+ throws Exception {
+ try {
+ long curtime = System.currentTimeMillis();
+ long size = objectSizeEstimator.estimateSize(arg1.getMessage());
+ if (trafficCounter != null) {
+ trafficCounter.bytesRecvFlowControl(arg0, size);
+ if (readLimit == 0) {
+ // no action
+ return;
+ }
+ // compute the number of ms to wait before reopening the channel
+ long wait = getTimeToWait(readLimit, trafficCounter
+ .getCurrentReadBytes(), trafficCounter.getLastTime(),
+ curtime);
+ if (wait > MINIMAL_WAIT) { // At least 10ms seems a minimal time in order to
+ Channel channel = arg0.getChannel();
+ // try to limit the traffic
+ if (channel != null && channel.isConnected()) {
+ // Channel version
+ if (executor == null) {
+ // Sleep since no executor
+ //logger.info("Read sleep since no executor for "+wait+" ms for "+this);
+ if (release.get()) {
+ return;
+ }
+ Thread.sleep(wait);
+ return;
+ }
+ if (arg0.getAttachment() == null) {
+ // readSuspended = true;
+ arg0.setAttachment(Boolean.TRUE);
+ channel.setReadable(false);
+ //logger.info("Read will wakeup after "+wait+" ms "+this);
+ executor.execute(new ReopenRead(arg0, wait));
+ } else {
+ // should be waiting: but can occurs sometime so as a FIX
+ //logger.info("Read sleep ok but should not be here: "+wait+" "+this);
+ if (release.get()) {
+ return;
+ }
+ Thread.sleep(wait);
+ }
+ } else {
+ // Not connected or no channel
+ //logger.info("Read sleep "+wait+" ms for "+this);
+ if (release.get()) {
+ return;
+ }
+ Thread.sleep(wait);
+ }
+ }
+ }
+ } finally {
+ // The message is then just passed to the next handler
+ super.messageReceived(arg0, arg1);
+ }
+ }
+
+ @Override
+ public void writeRequested(ChannelHandlerContext arg0, MessageEvent arg1)
+ throws Exception {
+ try {
+ long curtime = System.currentTimeMillis();
+ long size = objectSizeEstimator.estimateSize(arg1.getMessage());
+ if (trafficCounter != null) {
+ trafficCounter.bytesWriteFlowControl(size);
+ if (writeLimit == 0) {
+ return;
+ }
+ // compute the number of ms to wait before continue with the channel
+ long wait = getTimeToWait(writeLimit, trafficCounter
+ .getCurrentWrittenBytes(), trafficCounter.getLastTime(),
+ curtime);
+ if (wait > MINIMAL_WAIT) {
+ // Global or Channel
+ if (release.get()) {
+ return;
+ }
+ Thread.sleep(wait);
+ }
+ }
+ } finally {
+ // The message is then just passed to the next handler
+ super.writeRequested(arg0, arg1);
+ }
+ }
+
+ @Override
+ public void handleDownstream(ChannelHandlerContext ctx, ChannelEvent e)
+ throws Exception {
+ if (e instanceof ChannelStateEvent) {
+ ChannelStateEvent cse = (ChannelStateEvent) e;
+ if (cse.getState() == ChannelState.INTEREST_OPS &&
+ (((Integer) cse.getValue()).intValue() & Channel.OP_READ) != 0) {
+
+ // setReadable(true) requested
+ boolean readSuspended = ctx.getAttachment() != null;
+ if (readSuspended) {
+ // Drop the request silently if this handler has
+ // set the flag.
+ e.getFuture().setSuccess();
+ return;
+ }
+ }
+ }
+ super.handleDownstream(ctx, e);
+ }
+
+ /**
+ *
+ * @return the current TrafficCounter (if
+ * channel is still connected)
+ */
+ public TrafficCounter getTrafficCounter() {
+ return trafficCounter;
+ }
+
+ @Override
+ public void releaseExternalResources() {
+ if (trafficCounter != null) {
+ trafficCounter.stop();
+ }
+ release.set(true);
+ ExecutorUtil.terminate(executor);
+ }
+
+ @Override
+ public String toString() {
+ return "TrafficShaping with Write Limit: " + writeLimit +
+ " Read Limit: " + readLimit + " and Counter: " +
+ (trafficCounter != null? trafficCounter.toString() : "none");
+ }
+}
diff --git a/src/main/java/org/jboss/netty/handler/traffic/ChannelTrafficShapingHandler.java b/src/main/java/org/jboss/netty/handler/traffic/ChannelTrafficShapingHandler.java
new file mode 100644
index 0000000000..9ec72afd5a
--- /dev/null
+++ b/src/main/java/org/jboss/netty/handler/traffic/ChannelTrafficShapingHandler.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2011 The Netty Project
+ *
+ * The Netty Project licenses this file to you 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 org.jboss.netty.handler.traffic;
+
+import java.util.concurrent.Executor;
+
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.jboss.netty.channel.ChannelPipelineFactory;
+import org.jboss.netty.channel.ChannelStateEvent;
+import org.jboss.netty.handler.execution.ExecutionHandler;
+import org.jboss.netty.handler.execution.MemoryAwareThreadPoolExecutor;
+import org.jboss.netty.handler.execution.OrderedMemoryAwareThreadPoolExecutor;
+import org.jboss.netty.util.ObjectSizeEstimator;
+
+/**
+ * This implementation of the {@link AbstractTrafficShapingHandler} is for channel
+ * traffic shaping, that is to say a per channel limitation of the bandwidth.
+ *
+ * The general use should be as follow:
+ *
+ *
Add in your pipeline a new ChannelTrafficShapingHandler, before a recommended {@link ExecutionHandler} (like
+ * {@link OrderedMemoryAwareThreadPoolExecutor} or {@link MemoryAwareThreadPoolExecutor}).
+ * ChannelTrafficShapingHandler myHandler = new ChannelTrafficShapingHandler(executor);
+ * executor could be created using Executors.newCachedThreadPool();
+ * pipeline.addLast("CHANNEL_TRAFFIC_SHAPING", myHandler);
+ *
+ * Note that this handler has a Pipeline Coverage of "one" which means a new handler must be created
+ * for each new channel as the counter cannot be shared among all channels. For instance, if you have a
+ * {@link ChannelPipelineFactory}, you should create a new ChannelTrafficShapingHandler in this
+ * {@link ChannelPipelineFactory} each time getPipeline() method is called.
+ *
+ * Other arguments can be passed like write or read limitation (in bytes/s where 0 means no limitation)
+ * or the check interval (in millisecond) that represents the delay between two computations of the
+ * bandwidth and so the call back of the doAccounting method (0 means no accounting at all).
+ *
+ * A value of 0 means no accounting for checkInterval. If you need traffic shaping but no such accounting,
+ * it is recommended to set a positive value, even if it is high since the precision of the
+ * Traffic Shaping depends on the period where the traffic is computed. The highest the interval,
+ * the less precise the traffic shaping will be. It is suggested as higher value something close
+ * to 5 or 10 minutes.
+ *
+ *
When you shutdown your application, release all the external resources like the executor
+ * by calling:
+ * myHandler.releaseExternalResources();
+ *
+ *
+ */
+public class ChannelTrafficShapingHandler extends AbstractTrafficShapingHandler {
+
+ /**
+ * @param executor
+ * @param writeLimit
+ * @param readLimit
+ * @param checkInterval
+ */
+ public ChannelTrafficShapingHandler(Executor executor, long writeLimit,
+ long readLimit, long checkInterval) {
+ super(executor, writeLimit, readLimit, checkInterval);
+ }
+
+ /**
+ * @param executor
+ * @param writeLimit
+ * @param readLimit
+ */
+ public ChannelTrafficShapingHandler(Executor executor, long writeLimit,
+ long readLimit) {
+ super(executor, writeLimit, readLimit);
+ }
+
+
+ /**
+ * @param executor
+ * @param checkInterval
+ */
+ public ChannelTrafficShapingHandler(Executor executor, long checkInterval) {
+ super(executor, checkInterval);
+ }
+
+ /**
+ * @param executor
+ */
+ public ChannelTrafficShapingHandler(Executor executor) {
+ super(executor);
+ }
+
+ /**
+ * @param objectSizeEstimator
+ * @param executor
+ * @param writeLimit
+ * @param readLimit
+ * @param checkInterval
+ */
+ public ChannelTrafficShapingHandler(
+ ObjectSizeEstimator objectSizeEstimator, Executor executor,
+ long writeLimit, long readLimit, long checkInterval) {
+ super(objectSizeEstimator, executor, writeLimit, readLimit,
+ checkInterval);
+ }
+
+ /**
+ * @param objectSizeEstimator
+ * @param executor
+ * @param writeLimit
+ * @param readLimit
+ */
+ public ChannelTrafficShapingHandler(
+ ObjectSizeEstimator objectSizeEstimator, Executor executor,
+ long writeLimit, long readLimit) {
+ super(objectSizeEstimator, executor, writeLimit, readLimit);
+ }
+
+ /**
+ * @param objectSizeEstimator
+ * @param executor
+ * @param checkInterval
+ */
+ public ChannelTrafficShapingHandler(
+ ObjectSizeEstimator objectSizeEstimator, Executor executor,
+ long checkInterval) {
+ super(objectSizeEstimator, executor, checkInterval);
+ }
+
+ /**
+ * @param objectSizeEstimator
+ * @param executor
+ */
+ public ChannelTrafficShapingHandler(
+ ObjectSizeEstimator objectSizeEstimator, Executor executor) {
+ super(objectSizeEstimator, executor);
+ }
+
+
+ @Override
+ public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e)
+ throws Exception {
+ if (trafficCounter != null) {
+ trafficCounter.stop();
+ trafficCounter = null;
+ }
+ super.channelClosed(ctx, e);
+ }
+
+ @Override
+ public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e)
+ throws Exception {
+ // readSuspended = true;
+ ctx.setAttachment(Boolean.TRUE);
+ ctx.getChannel().setReadable(false);
+ if (trafficCounter == null) {
+ // create a new counter now
+ trafficCounter = new TrafficCounter(this, executor, "ChannelTC" +
+ ctx.getChannel().getId(), checkInterval);
+ }
+ if (trafficCounter != null) {
+ trafficCounter.start();
+ }
+ super.channelConnected(ctx, e);
+ // readSuspended = false;
+ ctx.setAttachment(null);
+ ctx.getChannel().setReadable(true);
+ }
+
+}
diff --git a/src/main/java/org/jboss/netty/handler/traffic/GlobalTrafficShapingHandler.java b/src/main/java/org/jboss/netty/handler/traffic/GlobalTrafficShapingHandler.java
new file mode 100644
index 0000000000..7f54e28f29
--- /dev/null
+++ b/src/main/java/org/jboss/netty/handler/traffic/GlobalTrafficShapingHandler.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2011 The Netty Project
+ *
+ * The Netty Project licenses this file to you 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 org.jboss.netty.handler.traffic;
+
+import java.util.concurrent.Executor;
+
+import org.jboss.netty.channel.ChannelHandler.Sharable;
+import org.jboss.netty.handler.execution.ExecutionHandler;
+import org.jboss.netty.handler.execution.MemoryAwareThreadPoolExecutor;
+import org.jboss.netty.handler.execution.OrderedMemoryAwareThreadPoolExecutor;
+import org.jboss.netty.util.ObjectSizeEstimator;
+
+/**
+ * This implementation of the {@link AbstractTrafficShapingHandler} is for global
+ * traffic shaping, that is to say a global limitation of the bandwidth, whatever
+ * the number of opened channels.
+ *
+ * The general use should be as follow:
+ *
+ *
Create your unique GlobalTrafficShapingHandler like:
+ * GlobalTrafficShapingHandler myHandler = new GlobalTrafficShapingHandler(executor);
+ * executor could be created using Executors.newCachedThreadPool();
+ * pipeline.addLast("GLOBAL_TRAFFIC_SHAPING", myHandler);
+ *
+ * Note that this handler has a Pipeline Coverage of "all" which means only one such handler must be created
+ * and shared among all channels as the counter must be shared among all channels.
+ *
+ * Other arguments can be passed like write or read limitation (in bytes/s where 0 means no limitation)
+ * or the check interval (in millisecond) that represents the delay between two computations of the
+ * bandwidth and so the call back of the doAccounting method (0 means no accounting at all).
+ *
+ * A value of 0 means no accounting for checkInterval. If you need traffic shaping but no such accounting,
+ * it is recommended to set a positive value, even if it is high since the precision of the
+ * Traffic Shaping depends on the period where the traffic is computed. The highest the interval,
+ * the less precise the traffic shaping will be. It is suggested as higher value something close
+ * to 5 or 10 minutes.
+ *
+ *
Add it in your pipeline, before a recommended {@link ExecutionHandler} (like
+ * {@link OrderedMemoryAwareThreadPoolExecutor} or {@link MemoryAwareThreadPoolExecutor}).
+ * pipeline.addLast("GLOBAL_TRAFFIC_SHAPING", myHandler);
+ *
+ *
When you shutdown your application, release all the external resources like the executor
+ * by calling:
+ * myHandler.releaseExternalResources();
+ *
+ *
+ */
+@Sharable
+public class GlobalTrafficShapingHandler extends AbstractTrafficShapingHandler {
+ /**
+ * Create the global TrafficCounter
+ */
+ void createGlobalTrafficCounter() {
+ TrafficCounter tc = new TrafficCounter(this, executor, "GlobalTC",
+ checkInterval);
+ setTrafficCounter(tc);
+ tc.start();
+ }
+
+ /**
+ * @param executor
+ * @param writeLimit
+ * @param readLimit
+ * @param checkInterval
+ */
+ public GlobalTrafficShapingHandler(Executor executor, long writeLimit,
+ long readLimit, long checkInterval) {
+ super(executor, writeLimit, readLimit, checkInterval);
+ createGlobalTrafficCounter();
+ }
+
+ /**
+ * @param executor
+ * @param writeLimit
+ * @param readLimit
+ */
+ public GlobalTrafficShapingHandler(Executor executor, long writeLimit,
+ long readLimit) {
+ super(executor, writeLimit, readLimit);
+ createGlobalTrafficCounter();
+ }
+ /**
+ * @param executor
+ * @param checkInterval
+ */
+ public GlobalTrafficShapingHandler(Executor executor, long checkInterval) {
+ super(executor, checkInterval);
+ createGlobalTrafficCounter();
+ }
+
+ /**
+ * @param executor
+ */
+ public GlobalTrafficShapingHandler(Executor executor) {
+ super(executor);
+ createGlobalTrafficCounter();
+ }
+
+ /**
+ * @param objectSizeEstimator
+ * @param executor
+ * @param writeLimit
+ * @param readLimit
+ * @param checkInterval
+ */
+ public GlobalTrafficShapingHandler(ObjectSizeEstimator objectSizeEstimator,
+ Executor executor, long writeLimit, long readLimit,
+ long checkInterval) {
+ super(objectSizeEstimator, executor, writeLimit, readLimit,
+ checkInterval);
+ createGlobalTrafficCounter();
+ }
+
+ /**
+ * @param objectSizeEstimator
+ * @param executor
+ * @param writeLimit
+ * @param readLimit
+ */
+ public GlobalTrafficShapingHandler(ObjectSizeEstimator objectSizeEstimator,
+ Executor executor, long writeLimit, long readLimit) {
+ super(objectSizeEstimator, executor, writeLimit, readLimit);
+ createGlobalTrafficCounter();
+ }
+
+ /**
+ * @param objectSizeEstimator
+ * @param executor
+ * @param checkInterval
+ */
+ public GlobalTrafficShapingHandler(ObjectSizeEstimator objectSizeEstimator,
+ Executor executor, long checkInterval) {
+ super(objectSizeEstimator, executor, checkInterval);
+ createGlobalTrafficCounter();
+ }
+
+ /**
+ * @param objectSizeEstimator
+ * @param executor
+ */
+ public GlobalTrafficShapingHandler(ObjectSizeEstimator objectSizeEstimator,
+ Executor executor) {
+ super(objectSizeEstimator, executor);
+ createGlobalTrafficCounter();
+ }
+}
diff --git a/src/main/java/org/jboss/netty/handler/traffic/TrafficCounter.java b/src/main/java/org/jboss/netty/handler/traffic/TrafficCounter.java
new file mode 100644
index 0000000000..521664e173
--- /dev/null
+++ b/src/main/java/org/jboss/netty/handler/traffic/TrafficCounter.java
@@ -0,0 +1,401 @@
+/*
+ * Copyright 2011 The Netty Project
+ *
+ * The Netty Project licenses this file to you 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 org.jboss.netty.handler.traffic;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.jboss.netty.channel.ChannelHandlerContext;
+
+/**
+ * TrafficCounter is associated with {@link AbstractTrafficShapingHandler}.
+ *
+ * A TrafficCounter has for goal to count the traffic in order to enable to limit the traffic or not,
+ * globally or per channel. It compute statistics on read and written bytes at the specified
+ * interval and call back the {@link AbstractTrafficShapingHandler} doAccounting method at every
+ * specified interval. If this interval is set to 0, therefore no accounting will be done and only
+ * statistics will be computed at each receive or write operations.
+ */
+public class TrafficCounter {
+ /**
+ * Current written bytes
+ */
+ private final AtomicLong currentWrittenBytes = new AtomicLong();
+
+ /**
+ * Current read bytes
+ */
+ private final AtomicLong currentReadBytes = new AtomicLong();
+
+ /**
+ * Long life written bytes
+ */
+ private final AtomicLong cumulativeWrittenBytes = new AtomicLong();
+
+ /**
+ * Long life read bytes
+ */
+ private final AtomicLong cumulativeReadBytes = new AtomicLong();
+
+ /**
+ * Last Time where cumulative bytes where reset to zero
+ */
+ private long lastCumulativeTime;
+
+ /**
+ * Last writing bandwidth
+ */
+ private long lastWriteThroughput;
+
+ /**
+ * Last reading bandwidth
+ */
+ private long lastReadThroughput;
+
+ /**
+ * Last Time Check taken
+ */
+ private final AtomicLong lastTime = new AtomicLong();
+
+ /**
+ * Last written bytes number during last check interval
+ */
+ private long lastWrittenBytes;
+
+ /**
+ * Last read bytes number during last check interval
+ */
+ private long lastReadBytes;
+
+ /**
+ * Delay between two captures
+ */
+ AtomicLong checkInterval = new AtomicLong(
+ AbstractTrafficShapingHandler.DEFAULT_CHECK_INTERVAL);
+
+ // default 1 s
+
+ /**
+ * Name of this Monitor
+ */
+ final String name;
+
+ /**
+ * The associated TrafficShapingHandler
+ */
+ private AbstractTrafficShapingHandler trafficShapingHandler;
+
+ /**
+ * Default Executor
+ */
+ private Executor executor;
+
+ /**
+ * Is Monitor active
+ */
+ AtomicBoolean monitorActive = new AtomicBoolean();
+
+ /**
+ * Monitor
+ */
+ private TrafficMonitoring trafficMonitoring;
+
+ /**
+ * Class to implement monitoring at fix delay
+ */
+ private static class TrafficMonitoring implements Runnable {
+ /**
+ * The associated TrafficShapingHandler
+ */
+ private final AbstractTrafficShapingHandler trafficShapingHandler1;
+
+ /**
+ * The associated TrafficCounter
+ */
+ private final TrafficCounter counter;
+
+ /**
+ * @param trafficShapingHandler
+ * @param counter
+ */
+ protected TrafficMonitoring(
+ AbstractTrafficShapingHandler trafficShapingHandler,
+ TrafficCounter counter) {
+ trafficShapingHandler1 = trafficShapingHandler;
+ this.counter = counter;
+ }
+
+ /**
+ * Default run
+ */
+ @Override
+ public void run() {
+ try {
+ Thread.currentThread().setName(counter.name);
+ for (; counter.monitorActive.get();) {
+ long check = counter.checkInterval.get();
+ if (check > 0) {
+ Thread.sleep(check);
+ } else {
+ // Delay goes to 0, so exit
+ return;
+ }
+ long endTime = System.currentTimeMillis();
+ counter.resetAccounting(endTime);
+ if (trafficShapingHandler1 != null) {
+ trafficShapingHandler1.doAccounting(counter);
+ }
+ }
+ } catch (InterruptedException e) {
+ // End of computations
+ }
+ }
+ }
+
+ /**
+ * Start the monitoring process
+ */
+ public void start() {
+ synchronized (lastTime) {
+ if (monitorActive.get()) {
+ return;
+ }
+ lastTime.set(System.currentTimeMillis());
+ if (checkInterval.get() > 0) {
+ monitorActive.set(true);
+ trafficMonitoring = new TrafficMonitoring(
+ trafficShapingHandler, this);
+ executor.execute(trafficMonitoring);
+ }
+ }
+ }
+
+ /**
+ * Stop the monitoring process
+ */
+ public void stop() {
+ synchronized (lastTime) {
+ if (!monitorActive.get()) {
+ return;
+ }
+ monitorActive.set(false);
+ resetAccounting(System.currentTimeMillis());
+ if (trafficShapingHandler != null) {
+ trafficShapingHandler.doAccounting(this);
+ }
+ }
+ }
+
+ /**
+ * Reset the accounting on Read and Write
+ *
+ * @param newLastTime
+ */
+ void resetAccounting(long newLastTime) {
+ synchronized (lastTime) {
+ long interval = newLastTime - lastTime.getAndSet(newLastTime);
+ if (interval == 0) {
+ // nothing to do
+ return;
+ }
+ lastReadBytes = currentReadBytes.getAndSet(0);
+ lastWrittenBytes = currentWrittenBytes.getAndSet(0);
+ lastReadThroughput = lastReadBytes / interval * 1000;
+ // nb byte / checkInterval in ms * 1000 (1s)
+ lastWriteThroughput = lastWrittenBytes / interval * 1000;
+ // nb byte / checkInterval in ms * 1000 (1s)
+ }
+ }
+
+ /**
+ * Constructor with the {@link AbstractTrafficShapingHandler} that hosts it, the executorService to use, its
+ * name, the checkInterval between two computations in millisecond
+ * @param trafficShapingHandler the associated AbstractTrafficShapingHandler
+ * @param executor
+ * Should be a CachedThreadPool for efficiency
+ * @param name
+ * the name given to this monitor
+ * @param checkInterval
+ * the checkInterval in millisecond between two computations
+ */
+ public TrafficCounter(AbstractTrafficShapingHandler trafficShapingHandler,
+ Executor executor, String name, long checkInterval) {
+ this.trafficShapingHandler = trafficShapingHandler;
+ this.executor = executor;
+ this.name = name;
+ lastCumulativeTime = System.currentTimeMillis();
+ configure(checkInterval);
+ }
+
+ /**
+ * Change checkInterval between
+ * two computations in millisecond
+ *
+ * @param newcheckInterval
+ */
+ public void configure(long newcheckInterval) {
+ if (checkInterval.get() != newcheckInterval) {
+ checkInterval.set(newcheckInterval);
+ if (newcheckInterval <= 0) {
+ stop();
+ // No more active monitoring
+ lastTime.set(System.currentTimeMillis());
+ } else {
+ // Start if necessary
+ start();
+ }
+ }
+ }
+
+ /**
+ * Computes counters for Read.
+ *
+ * @param ctx
+ * the associated channelHandlerContext
+ * @param recv
+ * the size in bytes to read
+ */
+ void bytesRecvFlowControl(ChannelHandlerContext ctx, long recv) {
+ currentReadBytes.addAndGet(recv);
+ cumulativeReadBytes.addAndGet(recv);
+ }
+
+ /**
+ * Computes counters for Write.
+ *
+ * @param write
+ * the size in bytes to write
+ */
+ void bytesWriteFlowControl(long write) {
+ currentWrittenBytes.addAndGet(write);
+ cumulativeWrittenBytes.addAndGet(write);
+ }
+
+ /**
+ *
+ * @return the current checkInterval between two computations of traffic counter
+ * in millisecond
+ */
+ public long getCheckInterval() {
+ return checkInterval.get();
+ }
+
+ /**
+ *
+ * @return the Read Throughput in bytes/s computes in the last check interval
+ */
+ public long getLastReadThroughput() {
+ return lastReadThroughput;
+ }
+
+ /**
+ *
+ * @return the Write Throughput in bytes/s computes in the last check interval
+ */
+ public long getLastWriteThroughput() {
+ return lastWriteThroughput;
+ }
+
+ /**
+ *
+ * @return the number of bytes read during the last check Interval
+ */
+ public long getLastReadBytes() {
+ return lastReadBytes;
+ }
+
+ /**
+ *
+ * @return the number of bytes written during the last check Interval
+ */
+ public long getLastWrittenBytes() {
+ return lastWrittenBytes;
+ }
+
+ /**
+ *
+ * @return the current number of bytes read since the last checkInterval
+ */
+ public long getCurrentReadBytes() {
+ return currentReadBytes.get();
+ }
+
+ /**
+ *
+ * @return the current number of bytes written since the last check Interval
+ */
+ public long getCurrentWrittenBytes() {
+ return currentWrittenBytes.get();
+ }
+
+ /**
+ * @return the Time in millisecond of the last check as of System.currentTimeMillis()
+ */
+ public long getLastTime() {
+ return lastTime.get();
+ }
+
+ /**
+ * @return the cumulativeWrittenBytes
+ */
+ public long getCumulativeWrittenBytes() {
+ return cumulativeWrittenBytes.get();
+ }
+
+ /**
+ * @return the cumulativeReadBytes
+ */
+ public long getCumulativeReadBytes() {
+ return cumulativeReadBytes.get();
+ }
+
+ /**
+ * @return the lastCumulativeTime in millisecond as of System.currentTimeMillis()
+ * when the cumulative counters were reset to 0.
+ */
+ public long getLastCumulativeTime() {
+ return lastCumulativeTime;
+ }
+
+ /**
+ * Reset both read and written cumulative bytes counters and the associated time.
+ */
+ public void resetCumulativeTime() {
+ lastCumulativeTime = System.currentTimeMillis();
+ cumulativeReadBytes.set(0);
+ cumulativeWrittenBytes.set(0);
+ }
+
+ /**
+ * @return the name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * String information
+ */
+ @Override
+ public String toString() {
+ return "Monitor " + name + " Current Speed Read: " +
+ (lastReadThroughput >> 10) + " KB/s, Write: " +
+ (lastWriteThroughput >> 10) + " KB/s Current Read: " +
+ (currentReadBytes.get() >> 10) + " KB Current Write: " +
+ (currentWrittenBytes.get() >> 10) + " KB";
+ }
+}
diff --git a/src/main/java/org/jboss/netty/handler/traffic/package-info.java b/src/main/java/org/jboss/netty/handler/traffic/package-info.java
new file mode 100644
index 0000000000..25321804cb
--- /dev/null
+++ b/src/main/java/org/jboss/netty/handler/traffic/package-info.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2011 The Netty Project
+ *
+ * The Netty Project licenses this file to you 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.
+ */
+
+/**
+ * Implementation of a Traffic Shaping Handler and Dynamic Statistics.
+ *
+ *
The main goal of this package is to allow to shape the traffic (bandwidth limitation),
+ * but also to get statistics on how many bytes are read or written. Both functions can
+ * be active or inactive (traffic or statistics).
+ *
+ *
Two classes implement this behavior:
+ *
+ *
{@link org.jboss.netty.handler.traffic.TrafficCounter}: this class implements the counters needed by the handlers.
+ * It can be accessed to get some extra information like the read or write bytes since last check, the read and write
+ * bandwidth from last check...
+ *
+ *
{@link org.jboss.netty.handler.traffic.AbstractTrafficShapingHandler}: this abstract class implements the kernel
+ * of the traffic shaping. It could be extended to fit your needs. Two classes are proposed as default
+ * implementations: see {@link org.jboss.netty.handler.traffic.ChannelTrafficShapingHandler} and see {@link org.jboss.netty.handler.traffic.GlobalTrafficShapingHandler}
+ * respectively for Channel traffic shaping and Global traffic shaping.
+ *
+ * The insertion in the pipeline of one of those handlers can be wherever you want, but
+ * it must be placed before any {@link org.jboss.netty.handler.execution.MemoryAwareThreadPoolExecutor}
+ * in your pipeline.
+ * It is really recommended to have such a{@link org.jboss.netty.handler.execution.MemoryAwareThreadPoolExecutor}
+ * (either non ordered or {@link org.jboss.netty.handler.execution.OrderedMemoryAwareThreadPoolExecutor}
+ * ) in your pipeline
+ * when you want to use this feature with some real traffic shaping, since it will allow to relax the constraint on
+ * NioWorker to do other jobs if necessary.
+ * Instead, if you don't, you can have the following situation: if there are more clients
+ * connected and doing data transfer (either in read or write) than NioWorker, your global performance can be under
+ * your specifications or even sometimes it will block for a while which can turn to "timeout" operations.
+ * For instance, let says that you've got 2 NioWorkers, and 10 clients wants to send data to your server.
+ * If you set a bandwidth limitation of 100KB/s for each channel (client), you could have a final limitation of about
+ * 60KB/s for each channel since NioWorkers are stopping by this handler.
+ * When it is used as a read traffic shaper, the handler will set the channel as not readable, so as to relax the
+ * NioWorkers.
+ * An {@link org.jboss.netty.util.ObjectSizeEstimator} can be passed at construction to specify what
+ * is the size of the object to be read or write accordingly to the type of
+ * object. If not specified, it will used the {@link org.jboss.netty.util.DefaultObjectSizeEstimator} implementation.
+ *
+ *
+ *
Standard use could be as follow:
+ *
+ *
+ *
To activate or deactivate the traffic shaping, change the value corresponding to your desire as
+ * [Global or per Channel] [Write or Read] Limitation in byte/s.
+ * A value of 0
+ * stands for no limitation, so the traffic shaping is deactivate (on what you specified).
+ * You can either change those values with the method configure in {@link org.jboss.netty.handler.traffic.AbstractTrafficShapingHandler}.
+ *
+ *
+ *
To activate or deactivate the statistics, you can adjust the delay to a low (suggested not less than 200ms
+ * for efficiency reasons) or a high value (let say 24H in millisecond is huge enough to not get the problem)
+ * or even using 0 which means no computation will be done.
+ * If you want to do anything with this statistics, just override the doAccounting method.
+ * This interval can be changed either from the method configure in {@link org.jboss.netty.handler.traffic.AbstractTrafficShapingHandler}
+ * or directly using the method configure of {@link org.jboss.netty.handler.traffic.TrafficCounter}.
+ *
+ *
+ *
+ *
So in your application you will create your own TrafficShapingHandler and set the values to fit your needs.
+ * XXXXXTrafficShapingHandler myHandler = new XXXXXTrafficShapingHandler(executor);
+ * where executor could be created using Executors.newCachedThreadPool(); and XXXXX could be either
+ * Global or Channel
+ * pipeline.addLast("XXXXX_TRAFFIC_SHAPING", myHandler);
+ * ...
+ * pipeline.addLast("MemoryExecutor",new ExecutionHandler(memoryAwareThreadPoolExecutor));
+ *
Note that a new {@link org.jboss.netty.handler.traffic.ChannelTrafficShapingHandler} must be created for each new channel,
+ * but only one {@link org.jboss.netty.handler.traffic.GlobalTrafficShapingHandler} must be created for all channels.
+ *
+ *
Note also that you can create different GlobalTrafficShapingHandler if you want to separate classes of
+ * channels (for instance either from business point of view or from bind address point of view).
+ *
+ * @apiviz.exclude ^java\.lang\.
+ */
+package org.jboss.netty.handler.traffic;
+
diff --git a/src/test/java/org/jboss/netty/handler/ipfilter/IpFilterRuleTest.java b/src/test/java/org/jboss/netty/handler/ipfilter/IpFilterRuleTest.java
new file mode 100644
index 0000000000..cb2793ebae
--- /dev/null
+++ b/src/test/java/org/jboss/netty/handler/ipfilter/IpFilterRuleTest.java
@@ -0,0 +1,347 @@
+/*
+ * Copyright 2011 The Netty Project
+ *
+ * The Netty Project licenses this file to you 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 io.netty.handler.ipfilter;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+
+import junit.framework.TestCase;
+
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelConfig;
+import io.netty.channel.ChannelEvent;
+import io.netty.channel.ChannelFactory;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelPipeline;
+import io.netty.channel.UpstreamMessageEvent;
+import org.junit.Test;
+
+public class IpFilterRuleTest extends TestCase
+{
+ public static boolean accept(IpFilterRuleHandler h, InetSocketAddress addr) throws Exception
+ {
+ return h.accept(new ChannelHandlerContext()
+ {
+
+ @Override
+ public boolean canHandleDownstream()
+ {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public boolean canHandleUpstream()
+ {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public Object getAttachment()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Channel getChannel()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public ChannelHandler getHandler()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public String getName()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public ChannelPipeline getPipeline()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public void sendDownstream(ChannelEvent e)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void sendUpstream(ChannelEvent e)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void setAttachment(Object attachment)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ },
+ new UpstreamMessageEvent(new Channel()
+ {
+
+ @Override
+ public ChannelFuture bind(SocketAddress localAddress)
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public ChannelFuture close()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public ChannelFuture connect(SocketAddress remoteAddress)
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public ChannelFuture disconnect()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public ChannelFuture getCloseFuture()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public ChannelConfig getConfig()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public ChannelFactory getFactory()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Integer getId()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public int getInterestOps()
+ {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ @Override
+ public SocketAddress getLocalAddress()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Channel getParent()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public ChannelPipeline getPipeline()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public SocketAddress getRemoteAddress()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public boolean isBound()
+ {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public boolean isConnected()
+ {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public boolean isOpen()
+ {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public boolean isReadable()
+ {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public boolean isWritable()
+ {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public ChannelFuture setInterestOps(int interestOps)
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public ChannelFuture setReadable(boolean readable)
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public ChannelFuture unbind()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public ChannelFuture write(Object message)
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public ChannelFuture write(Object message, SocketAddress remoteAddress)
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public int compareTo(Channel o)
+ {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ @Override
+ public Object getAttachment() {
+ return null;
+ }
+
+ @Override
+ public void setAttachment(Object attachment) {
+
+ }
+
+ }, h, addr),
+ addr);
+ }
+
+ @Test
+ public void testIpFilterRule() throws Exception
+ {
+ IpFilterRuleHandler h = new IpFilterRuleHandler();
+ h.addAll(new IpFilterRuleList("+n:localhost, -n:*"));
+ InetSocketAddress addr = new InetSocketAddress(InetAddress.getLocalHost(), 8080);
+ assertTrue(accept(h, addr));
+ addr = new InetSocketAddress(InetAddress.getByName("127.0.0.2"), 8080);
+ assertFalse(accept(h, addr));
+ addr = new InetSocketAddress(InetAddress.getByName(InetAddress.getLocalHost().getHostName()), 8080);
+ assertTrue(accept(h, addr));
+
+ h.clear();
+ h.addAll(new IpFilterRuleList("+n:*"+InetAddress.getLocalHost().getHostName().substring(1)+", -n:*"));
+ addr = new InetSocketAddress(InetAddress.getLocalHost(), 8080);
+ assertTrue(accept(h, addr));
+ addr = new InetSocketAddress(InetAddress.getByName("127.0.0.2"), 8080);
+ assertFalse(accept(h, addr));
+ addr = new InetSocketAddress(InetAddress.getByName(InetAddress.getLocalHost().getHostName()), 8080);
+ assertTrue(accept(h, addr));
+
+ h.clear();
+ h.addAll(new IpFilterRuleList("+c:"+InetAddress.getLocalHost().getHostAddress()+"/32, -n:*"));
+ addr = new InetSocketAddress(InetAddress.getLocalHost(), 8080);
+ assertTrue(accept(h, addr));
+ addr = new InetSocketAddress(InetAddress.getByName("127.0.0.2"), 8080);
+ assertFalse(accept(h, addr));
+ addr = new InetSocketAddress(InetAddress.getByName(InetAddress.getLocalHost().getHostName()), 8080);
+ assertTrue(accept(h, addr));
+
+ h.clear();
+ h.addAll(new IpFilterRuleList(""));
+ addr = new InetSocketAddress(InetAddress.getLocalHost(), 8080);
+ assertTrue(accept(h, addr));
+ addr = new InetSocketAddress(InetAddress.getByName("127.0.0.2"), 8080);
+ assertTrue(accept(h, addr));
+ addr = new InetSocketAddress(InetAddress.getByName(InetAddress.getLocalHost().getHostName()), 8080);
+ assertTrue(accept(h, addr));
+
+ h.clear();
+ addr = new InetSocketAddress(InetAddress.getLocalHost(), 8080);
+ assertTrue(accept(h, addr));
+ addr = new InetSocketAddress(InetAddress.getByName("127.0.0.2"), 8080);
+ assertTrue(accept(h, addr));
+ addr = new InetSocketAddress(InetAddress.getByName(InetAddress.getLocalHost().getHostName()), 8080);
+ assertTrue(accept(h, addr));
+
+ }
+
+}
From 31eb8595bed60faa7bc263fa517542c57d6fac5a Mon Sep 17 00:00:00 2001
From: blucas
Date: Fri, 18 May 2012 17:57:30 +0100
Subject: [PATCH 2/3] #337 back-port ipfilter and traffic to 3 branch (Fix test
package)
---
.../handler/ipfilter/IpFilterRuleTest.java | 20 +++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/src/test/java/org/jboss/netty/handler/ipfilter/IpFilterRuleTest.java b/src/test/java/org/jboss/netty/handler/ipfilter/IpFilterRuleTest.java
index cb2793ebae..0256ab5f2b 100644
--- a/src/test/java/org/jboss/netty/handler/ipfilter/IpFilterRuleTest.java
+++ b/src/test/java/org/jboss/netty/handler/ipfilter/IpFilterRuleTest.java
@@ -13,7 +13,7 @@
* License for the specific language governing permissions and limitations
* under the License.
*/
-package io.netty.handler.ipfilter;
+package org.jboss.netty.handler.ipfilter;
import java.net.InetAddress;
import java.net.InetSocketAddress;
@@ -21,15 +21,15 @@ import java.net.SocketAddress;
import junit.framework.TestCase;
-import io.netty.channel.Channel;
-import io.netty.channel.ChannelConfig;
-import io.netty.channel.ChannelEvent;
-import io.netty.channel.ChannelFactory;
-import io.netty.channel.ChannelFuture;
-import io.netty.channel.ChannelHandler;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.ChannelPipeline;
-import io.netty.channel.UpstreamMessageEvent;
+import org.jboss.netty.channel.Channel;
+import org.jboss.netty.channel.ChannelConfig;
+import org.jboss.netty.channel.ChannelEvent;
+import org.jboss.netty.channel.ChannelFactory;
+import org.jboss.netty.channel.ChannelFuture;
+import org.jboss.netty.channel.ChannelHandler;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.jboss.netty.channel.ChannelPipeline;
+import org.jboss.netty.channel.UpstreamMessageEvent;
import org.junit.Test;
public class IpFilterRuleTest extends TestCase
From c052f904104cd0bb270ba5b8d96b2b7ada14fcb1 Mon Sep 17 00:00:00 2001
From: blucas
Date: Fri, 18 May 2012 18:00:16 +0100
Subject: [PATCH 3/3] #337 back-port ipfilter and traffic to 3 branch (Fix test
package method override)
---
.../jboss/netty/handler/ipfilter/IpFilterRuleTest.java | 10 ----------
1 file changed, 10 deletions(-)
diff --git a/src/test/java/org/jboss/netty/handler/ipfilter/IpFilterRuleTest.java b/src/test/java/org/jboss/netty/handler/ipfilter/IpFilterRuleTest.java
index 0256ab5f2b..5488de9ec2 100644
--- a/src/test/java/org/jboss/netty/handler/ipfilter/IpFilterRuleTest.java
+++ b/src/test/java/org/jboss/netty/handler/ipfilter/IpFilterRuleTest.java
@@ -280,16 +280,6 @@ public class IpFilterRuleTest extends TestCase
// TODO Auto-generated method stub
return 0;
}
-
- @Override
- public Object getAttachment() {
- return null;
- }
-
- @Override
- public void setAttachment(Object attachment) {
-
- }
}, h, addr),
addr);