Skip to content

Commit ac7fa8a

Browse files
committed
Migrate to Kotlin 1.8.20
Update Dockerfile to optimize subsequent runs Remove jcenter() from repositories since it is deprecated Update dependencies Fix some linter complains Add dependabot config file Add publish action to github actions
1 parent 61eb636 commit ac7fa8a

File tree

11 files changed

+144
-54
lines changed

11 files changed

+144
-54
lines changed

.github/dependabot.yml

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
version: 2
2+
updates:
3+
- package-ecosystem: "gradle"
4+
directory: "/"
5+
target-branch: "master"
6+
labels:
7+
- "dependency"
8+
schedule:
9+
interval: "daily"

.github/workflows/publish-gpr.yml

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
name: Publish to Github Packages
2+
3+
permissions:
4+
packages: write
5+
6+
on:
7+
push:
8+
branches: [ master ]
9+
pull_request:
10+
branches: [ master ]
11+
12+
jobs:
13+
build:
14+
runs-on: ubuntu-latest
15+
steps:
16+
- uses: actions/checkout@v2
17+
- name: Set up JDK 11
18+
uses: actions/setup-java@v3
19+
with:
20+
java-version: '11'
21+
distribution: 'temurin'
22+
- name: Validate Gradle wrapper
23+
uses: gradle/wrapper-validation-action@e6e38bacfdf1a337459f332974bb2327a31aaf4b
24+
- name: publishGprPublicationToGitHubPackagesRepository
25+
uses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1
26+
env:
27+
USERNAME: ${{ secrets.USERNAME }}
28+
TOKEN: ${{ secrets.GITHUB_TOKEN }}
29+
with:
30+
arguments: publishGprPublicationToGitHubPackagesRepository

Dockerfile

+14-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
1-
FROM ubuntu:18.04
1+
FROM ubuntu:latest
22

3-
RUN apt-get update && apt-get install -y openjdk-8-jdk python3 python3-pip
3+
RUN apt-get update && apt-get install -y openjdk-8-jdk python3 python3-pip
44
RUN python3 -m pip install pproxy
55

6-
COPY . /tmp
7-
WORKDIR /tmp
6+
WORKDIR /tests
7+
COPY gradle/ /tests/gradle
8+
COPY gradlew /tests/
9+
10+
RUN touch settings.gradle.kts && ./gradlew :wrapper
11+
12+
COPY settings.gradle.kts build.gradle.kts gradle.properties /tests/
13+
14+
RUN ./gradlew --refresh-dependencies
15+
16+
COPY test_proxy.sh run_tests.sh server.py /tests/
17+
COPY src /tests/src

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ is called with either success or failure method.
1717
But using callback based socket is inconvenient after all. It's commonly causes lots of lambdas, interfaces and
1818
indentation levels.
1919

20-
Here's out library comes in. Using a `suspendCoroutine` and `Continuation` mechanism in kotlin coroutines
20+
Here's our library comes in. Using a `suspendCoroutine` and `Continuation` mechanism in kotlin coroutines
2121
we're wrapped a callback based functions into coroutine-blocking function.
2222

2323
It's just blocking a current coroutine on connect/read/write call and resume it on callback functions -- success or failure.

build.gradle.kts

+4-5
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,19 @@ import org.jetbrains.kotlin.konan.properties.Properties
33

44
plugins {
55
`maven-publish`
6-
kotlin("jvm") version "1.5.21"
6+
kotlin("jvm") version "1.8.20"
77
}
88

99
group = "me.theevilroot"
10-
version = "1.1"
10+
version = "1.2"
1111

1212
repositories {
1313
mavenCentral()
14-
jcenter()
1514
}
1615

1716
dependencies {
18-
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.1")
19-
testImplementation("junit:junit:4.13")
17+
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4")
18+
testImplementation("junit:junit:4.13.2")
2019
}
2120

2221
val sourcesJar by tasks.registering(Jar::class) {
+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
distributionBase=GRADLE_USER_HOME
22
distributionPath=wrapper/dists
3-
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-bin.zip
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-bin.zip
44
zipStoreBase=GRADLE_USER_HOME
55
zipStorePath=wrapper/dists

src/main/kotlin/com/theevilroot/asyncsocket/CoroutineSocket.kt

+8-3
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ open class CoroutineSocket(
2121
get() = socket.isOpen
2222

2323
open suspend fun connect(isa: InetSocketAddress) {
24-
suspendCoroutine<Void> {
24+
suspendCoroutine {
2525
socket.connect(isa, it, ContinuationHandler<Void>())
2626
}
2727
isConnected = true
@@ -30,8 +30,13 @@ open class CoroutineSocket(
3030
open suspend fun read(buffer: ByteBuffer): Int {
3131
return suspendCoroutine {
3232
if (readTimeout != null) {
33-
socket.read(buffer, readTimeout.first,
34-
readTimeout.second, it, ContinuationHandler<Int>())
33+
socket.read(
34+
buffer,
35+
readTimeout.first,
36+
readTimeout.second,
37+
it,
38+
ContinuationHandler<Int>(),
39+
)
3540
} else {
3641
socket.read(buffer, it, ContinuationHandler<Int>())
3742
}

src/main/kotlin/com/theevilroot/asyncsocket/Socks4CoroutineSocket.kt

+20-11
Original file line numberDiff line numberDiff line change
@@ -10,31 +10,38 @@ class Socks4CoroutineSocket(
1010
val socksIsa: InetSocketAddress,
1111
channel: AsynchronousSocketChannel,
1212
val userId: String,
13-
readTimeout: Pair<Long, TimeUnit>? = null
13+
readTimeout: Pair<Long, TimeUnit>? = null,
1414
) : CoroutineSocket(channel, readTimeout) {
1515

1616
lateinit var remoteIsa: InetSocketAddress
1717

1818
private suspend fun socksConnect(isa: InetSocketAddress): InetSocketAddress {
19-
val message = byteArrayOf(0x04, 0x01,
19+
val message = byteArrayOf(
20+
0x04,
21+
0x01,
2022
(isa.port shr 8).toByte(),
2123
(isa.port and 0xff).toByte(),
2224
*isa.address.address,
2325
*userId.toByteArray(),
24-
0x00.toByte())
26+
0x00.toByte(),
27+
)
2528
ByteBuffer.wrap(message).let {
2629
val count = super.write(it)
27-
if (count < message.size)
30+
if (count < message.size) {
2831
throw SocksException("failed to write connect message. $count < ${message.size}")
32+
}
2933
}
3034
val response = ByteBuffer.allocate(8).also {
3135
val count = super.read(it)
32-
if (count < 8)
36+
if (count < 8) {
3337
throw SocksException("failed to read connect response. $count < 8")
38+
}
39+
}
40+
if (response[1] != 90.toByte()) {
41+
throw SocksException(
42+
"failed to connect to remote server through proxy. socks error: ${String.format("%02x", response[1])}",
43+
)
3444
}
35-
if (response[1] != 90.toByte())
36-
throw SocksException("failed to connect to remote server through proxy. " +
37-
"socks error: ${String.format("%02x", response[1])}")
3845

3946
val addr = ByteArray(4)
4047
val bPort = ByteArray(2)
@@ -59,18 +66,20 @@ class Socks4CoroutineSocket(
5966
}
6067

6168
override suspend fun read(buffer: ByteBuffer): Int {
62-
if (!this::remoteIsa.isInitialized)
69+
if (!this::remoteIsa.isInitialized) {
6370
throw IllegalStateException("remote address is not initialized. please, connect first")
71+
}
6472
return super.read(buffer).also {
6573
if (it < 0) close()
6674
}
6775
}
6876

6977
override suspend fun write(buffer: ByteBuffer): Int {
70-
if (!this::remoteIsa.isInitialized)
78+
if (!this::remoteIsa.isInitialized) {
7179
throw IllegalStateException("remote address is not initialized. please, connect first")
80+
}
7281
return super.write(buffer).also {
7382
if (it < 0) close()
7483
}
7584
}
76-
}
85+
}

src/main/kotlin/com/theevilroot/asyncsocket/SocksCoroutineSocket.kt

+50-23
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,25 @@ class SocksCoroutineSocket(
1111
val socksIsa: InetSocketAddress,
1212
channel: AsynchronousSocketChannel,
1313
val credentials: Pair<String, String>? = null,
14-
readTimeout: Pair<Long, TimeUnit>? = null
14+
readTimeout: Pair<Long, TimeUnit>? = null,
1515
) : CoroutineSocket(channel, readTimeout) {
1616

1717
enum class Method { NO_AUTH, USER_PASS }
1818

1919
enum class AddressType(val id: Byte) {
2020
IPv4(0x01.toByte()),
2121
DOMAIN(0x03.toByte()),
22-
IPv6(0x04.toByte());
22+
IPv6(0x04.toByte()),
23+
;
2324

2425
private suspend fun sizeOfAddress(read: suspend (ByteBuffer) -> Int): Int = when (this) {
2526
IPv4 -> 0x04
2627
IPv6 -> 0x10
2728
DOMAIN -> ByteBuffer.allocate(1).let {
2829
val count = read(it)
29-
if (count < 1)
30+
if (count < 1) {
3031
throw SocksException("failed to read domain name length for address. $count < 1")
32+
}
3133
it[0].toUByte().toInt()
3234
}
3335
}
@@ -36,8 +38,9 @@ class SocksCoroutineSocket(
3638
sizeOfAddress(read).let {
3739
val buffer = ByteBuffer.allocate(it + 2)
3840
val count = read(buffer)
39-
if (count < it + 2)
41+
if (count < it + 2) {
4042
throw SocksException("failed to read address. $count < ${it + 2}")
43+
}
4144
buffer
4245
}
4346
}
@@ -51,16 +54,19 @@ class SocksCoroutineSocket(
5154
val message = byteArrayOf(0x05, methods.size.toByte(), *methods)
5255
ByteBuffer.wrap(message).let {
5356
val count = super.write(it)
54-
if (count < message.size)
57+
if (count < message.size) {
5558
throw SocksException("failed to write methods message. $count < ${message.size}")
59+
}
5660
}
5761

5862
val method = ByteBuffer.allocate(2).let {
5963
val count = super.read(it)
60-
if (count < 2)
64+
if (count < 2) {
6165
throw SocksException("failed to read methods response. $count < 2")
62-
if (it[0] != 0x05.toByte())
66+
}
67+
if (it[0] != 0x05.toByte()) {
6368
throw SocksException("methods response: versions mismatch: ${it[0]}")
69+
}
6470
it[1]
6571
}
6672
return when (method) {
@@ -71,23 +77,32 @@ class SocksCoroutineSocket(
7177
}
7278

7379
private suspend fun socksConnect(isa: InetSocketAddress): InetSocketAddress {
74-
if (!this::method.isInitialized)
80+
if (!this::method.isInitialized) {
7581
throw IllegalStateException("socks auth method is not initialized. maybe you forget to call init()?")
76-
val message = byteArrayOf(0x05, 0x01, 0x00, 0x01,
82+
}
83+
val message = byteArrayOf(
84+
0x05,
85+
0x01,
86+
0x00,
87+
0x01,
7788
*isa.address.address,
7889
(isa.port shr 8).toByte(),
79-
(isa.port and 0xff).toByte())
90+
(isa.port and 0xff).toByte(),
91+
)
8092
ByteBuffer.wrap(message).let {
8193
val count = super.write(it)
82-
if (count < message.size)
94+
if (count < message.size) {
8395
throw SocksException("failed to write connect message. $count < ${message.size}")
96+
}
8497
}
8598
val header = ByteBuffer.allocate(4).also {
8699
val count = super.read(it)
87-
if (count < 4)
100+
if (count < 4) {
88101
throw SocksException("failed to read header of connect response. $count < 4")
89-
if (it[1] != 0x00.toByte())
102+
}
103+
if (it[1] != 0x00.toByte()) {
90104
throw ConnectException("failed to connect to ${isa.address.canonicalHostName}:${isa.port}. socks server respond ${it[1]} code")
105+
}
91106
}
92107
val addrType = AddressType.values().firstOrNull { it.id == header[3] }
93108
?: throw SocksException("unknown address type ${header[3]}")
@@ -102,26 +117,36 @@ class SocksCoroutineSocket(
102117
}
103118

104119
private suspend fun socksUserPassNegotiations() {
105-
if (credentials == null)
120+
if (credentials == null) {
106121
throw SocksException("server want to use user/pass authentication, but no credentials provided")
122+
}
107123
val (username, password) = credentials
108-
if (username.length > 255 || password.length > 255)
124+
if (username.length > 255 || password.length > 255) {
109125
throw SocksException("credentials is invalid. max length of username and password is 255")
110-
val message = byteArrayOf(0x01, username.length.toByte(), *username.toByteArray(),
111-
password.length.toByte(), *password.toByteArray())
126+
}
127+
val message = byteArrayOf(
128+
0x01,
129+
username.length.toByte(),
130+
*username.toByteArray(),
131+
password.length.toByte(),
132+
*password.toByteArray(),
133+
)
112134
ByteBuffer.wrap(message).also {
113135
val count = super.write(it)
114-
if (count < message.size)
136+
if (count < message.size) {
115137
throw SocksException("failed to write user/pass message. $count < ${message.size}")
138+
}
116139
}
117140
val response = ByteBuffer.allocate(2).let {
118141
val count = super.read(it)
119-
if (count < 2)
142+
if (count < 2) {
120143
throw SocksException("failed to read user/pass response. $count < 2")
144+
}
121145
it[1]
122146
}
123-
if (response != 0x00.toByte())
147+
if (response != 0x00.toByte()) {
124148
throw SocksException("authentication failure. server respond $response != 0x00")
149+
}
125150
}
126151

127152
lateinit var method: Method
@@ -163,18 +188,20 @@ class SocksCoroutineSocket(
163188
}
164189

165190
override suspend fun read(buffer: ByteBuffer): Int {
166-
if (!this::remoteIsa.isInitialized)
191+
if (!this::remoteIsa.isInitialized) {
167192
throw IllegalStateException("remote address is not initialized. please, connect first")
193+
}
168194
return super.read(buffer).also {
169195
if (it < 0) close()
170196
}
171197
}
172198

173199
override suspend fun write(buffer: ByteBuffer): Int {
174-
if (!this::remoteIsa.isInitialized)
200+
if (!this::remoteIsa.isInitialized) {
175201
throw IllegalStateException("remote address is not initialized. please, connect first")
202+
}
176203
return super.write(buffer).also {
177204
if (it < 0) close()
178205
}
179206
}
180-
}
207+
}

src/test/kotlin/com/theevilroot/asyncsocket/Socks4Test.kt

+3-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class Socks4Test {
2121
fun testSocks4Connect(): Unit = runBlocking {
2222
val socket = Socks4CoroutineSocket(InetSocketAddress("127.0.0.1", 1082), channel, "user")
2323
socket.init()
24-
socket.connect(InetSocketAddress("52.48.142.75", 80))
24+
socket.connect(InetSocketAddress("api.ipify.org", 80))
2525

2626
Assert.assertTrue(socket.isOpened)
2727
Assert.assertTrue(socket.isConnected)
@@ -31,7 +31,8 @@ class Socks4Test {
3131

3232
// read some data, we don't need much
3333
val response = ByteBuffer.allocate(32)
34-
Assert.assertTrue(socket.read(response) > 0)
34+
val count = socket.read(response)
35+
Assert.assertTrue("count was $count", count > 0)
3536

3637
val http = ByteArray(4)
3738
response.position(0)

0 commit comments

Comments
 (0)