Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Basic working connection limiter #50

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions connection-limiter/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.DS_Store
/.build
/Packages
/*.xcodeproj
xcuserdata/
DerivedData/
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
23 changes: 23 additions & 0 deletions connection-limiter/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// swift-tools-version:5.2
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
name: "connection-limiter",
dependencies: [
.package(name: "swift-nio", url: "https://github.com/apple/swift-nio", from: "2.16.0")
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages which this package depends on.
.target(
name: "connection-limiter",
dependencies: [
.product(name: "NIO", package: "swift-nio")
]),
.testTarget(
name: "connection-limiterTests",
dependencies: ["connection-limiter"]),
]
)
3 changes: 3 additions & 0 deletions connection-limiter/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# connection-limiter

A description of this package.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wanna throw together some actual text here?

64 changes: 64 additions & 0 deletions connection-limiter/Sources/connection-limiter/main.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need the license header.

import NIO

class LimitHandler: ChannelDuplexHandler {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these examples are often helpful for people who know they want to do something but don't quite know how: as such I think the documentation and comments here should be comprehensive.  


typealias OutboundIn = Channel
typealias InboundIn = Channel

let connectionLimit: Int
var currentConnections = 0

init(connectionLimit: Int) {
self.connectionLimit = connectionLimit
}

func channelRead(context: ChannelHandlerContext, data: NIOAny) {
let channel = self.unwrapInboundIn(data)
context.fireChannelRead(data)
self.currentConnections += 1
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This value is never decremented. That doesn't seem likely to be right.

channel.closeFuture.whenSuccess {
context.read()
}
}

func read(context: ChannelHandlerContext) {
guard self.currentConnections < self.connectionLimit else {
return
}
context.read()
}

}

class EchoChannelHandler: ChannelInboundHandler {

typealias InboundIn = ByteBuffer
typealias InboundOut = ByteBuffer

func channelRead(context: ChannelHandlerContext, data: NIOAny) {
var input = self.unwrapInboundIn(data)
var buffer = context.channel.allocator.buffer(capacity: input.readableBytes + 6)
buffer.writeString("Echo: ")
buffer.writeBuffer(&input)
let output = self.wrapInboundOut(buffer)
context.writeAndFlush(output, promise: nil)
}

}

let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
try ServerBootstrap(group: group)
.serverChannelInitializer({ (channel) -> EventLoopFuture<Void> in
channel.pipeline.addHandler(LimitHandler(connectionLimit: 1))
})
.serverChannelOption(ChannelOptions.maxMessagesPerRead, value: 1)
.serverChannelOption(ChannelOptions.backlog, value: 1)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Setting this to 1 is not something we'd recommend in a real app and it doesn't affect the correctness of the example, so let's remove this.

.childChannelInitializer { (channel) -> EventLoopFuture<Void> in
channel.pipeline.addHandler(EchoChannelHandler())
}
.serverChannelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1)
.bind(host: "127.0.0.1", port: 4321)
.wait()
.closeFuture
.wait()
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import XCTest
import class Foundation.Bundle

final class connection_limiterTests: XCTestCase {

}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May as well just remove the tests entirely.