Skip to content

Commit 3495155

Browse files
Nikita-Smirnov-ExactproDenis PlotnikovOleg Smirnov
authored
[th2-5092] Puts unique th2-request-id property to metadata of reque… (#28)
Co-authored-by: Denis Plotnikov <denis.plotnikov@exactprosystems.com> Co-authored-by: Oleg Smirnov <oleg.smirnov@exactprosystems.com>
1 parent 7cbb7a5 commit 3495155

File tree

9 files changed

+117
-30
lines changed

9 files changed

+117
-30
lines changed

.github/workflows/build.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
name: Build and publish Docker distributions to Github Container Registry ghcr.io
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
- version-*
8+
paths:
9+
- gradle.properties
10+
# - package_info.json
11+
12+
jobs:
13+
build-job:
14+
uses: th2-net/.github/.github/workflows/compound-java.yml@main
15+
with:
16+
build-target: 'Docker,Sonatype'
17+
docker-username: ${{ github.actor }}
18+
secrets:
19+
docker-password: ${{ secrets.GITHUB_TOKEN }}
20+
sonatypeUsername: ${{ secrets.SONATYPE_NEXUS_USERNAME }}
21+
sonatypePassword: ${{ secrets.SONATYPE_NEXUS_PASSWORD }}
22+
sonatypeSigningKey: ${{ secrets.SONATYPE_GPG_ARMORED_KEY }}
23+
sonatypeSigningPassword: ${{ secrets.SONATYPE_SIGNING_PASSWORD }}

.github/workflows/dev-build.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
name: Dev build and publish Docker distributions to Github Container Registry ghcr.io
2+
3+
on:
4+
push:
5+
branches-ignore:
6+
- master
7+
- version-*
8+
- dependabot**
9+
paths-ignore:
10+
- README.md
11+
12+
jobs:
13+
build-job:
14+
uses: th2-net/.github/.github/workflows/compound-java-dev.yml@main
15+
with:
16+
build-target: 'Docker,Sonatype'
17+
docker-username: ${{ github.actor }}
18+
secrets:
19+
docker-password: ${{ secrets.GITHUB_TOKEN }}
20+
sonatypeUsername: ${{ secrets.SONATYPE_NEXUS_USERNAME }}
21+
sonatypePassword: ${{ secrets.SONATYPE_NEXUS_PASSWORD }}
22+
sonatypeSigningKey: ${{ secrets.SONATYPE_GPG_ARMORED_KEY }}
23+
sonatypeSigningPassword: ${{ secrets.SONATYPE_SIGNING_PASSWORD }}

README.md

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# HTTP Client v2.1.0
1+
# HTTP Client v2.2.0
22

33
This microservice allows performing HTTP requests and receive HTTP responses. It also can perform basic authentication
44

@@ -131,7 +131,7 @@ metadata:
131131
name: http-client
132132
spec:
133133
image-name: ghcr.io/th2-net/th2-conn-http-client
134-
image-version: 0.7.2
134+
image-version: 2.2.0-dev
135135
custom-config:
136136
https: false
137137
host: 127.0.0.1
@@ -177,6 +177,11 @@ spec:
177177

178178
## Changelog
179179

180+
### v2.2.0
181+
182+
* Puts unique `th2-request-id` property to metadata of request/response messages
183+
* Merged `v0.8.1`
184+
180185
### v2.1.0
181186

182187
* Supports th2 transport protocol
@@ -193,8 +198,13 @@ spec:
193198
* th2-common upgrade to `5.2.0`
194199
* th2-bom upgrade to `4.2.0`
195200

196-
### v0.8.0
201+
### v0.8.1
197202

203+
#### Fixed:
204+
205+
+ Fix problem with exceeding the limit for sockets to acquire when and exception is thrown during that process.
206+
207+
### v0.8.0
198208
* Vulnerability check pipeline step
199209
* th2-common upgrade to `3.44.0`
200210
* th2-bom upgrade to `4.1.0`

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
kotlin.code.style=official
2-
release_version=2.1.0
2+
release_version=2.2.0
33
description='HTTP Client'
44
vcs_url=https://github.com/th2-net/th2-conn-http-client
55
app_main_class=com.exactpro.th2.http.client.Main

gradle/wrapper/gradle-wrapper.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#Mon May 25 11:22:24 MSK 2020
2-
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip
2+
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
33
distributionBase=GRADLE_USER_HOME
44
distributionPath=wrapper/dists
55
zipStorePath=wrapper/dists

src/main/kotlin/com/exactpro/th2/http/client/ClientOptions.kt

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -114,25 +114,30 @@ internal class ClientOptions(
114114

115115
fun acquire(): Socket {
116116
semaphore.acquire()
117-
118-
var socket = sockets.poll().let { socket ->
119-
when {
120-
socket == null || socket.isClosed || !socket.isConnected -> factory(host, port)
121-
else -> socket
117+
try {
118+
var socket = sockets.poll().let { socket ->
119+
when {
120+
socket == null || socket.isClosed || !socket.isConnected -> factory(host, port)
121+
else -> socket
122+
}
122123
}
123-
}
124124

125-
val currentTime = System.currentTimeMillis()
126-
val expirationTime = expirationTimes.getOrPut(socket) { currentTime + keepAliveTimeout }
125+
val currentTime = System.currentTimeMillis()
126+
val expirationTime = expirationTimes.getOrPut(socket) { currentTime + keepAliveTimeout }
127127

128-
if (expirationTime < currentTime) {
129-
expirationTimes -= socket
130-
socket = factory(host, port)
131-
}
128+
if (expirationTime < currentTime) {
129+
expirationTimes -= socket
130+
socket = factory(host, port)
131+
}
132132

133133

134-
return socket.apply {
135-
expirationTimes[socket] = currentTime + keepAliveTimeout
134+
return socket.apply {
135+
expirationTimes[socket] = currentTime + keepAliveTimeout
136+
}
137+
} catch (ex: Exception) {
138+
// release semaphore because the socket was not created
139+
semaphore.release()
140+
throw ex
136141
}
137142
}
138143

src/main/kotlin/com/exactpro/th2/http/client/api/decorators/Th2RequestDecorator.kt

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package com.exactpro.th2.http.client.api.decorators
1818

19+
import com.exactpro.th2.common.event.EventUtils.generateUUID
1920
import com.exactpro.th2.common.grpc.EventID
2021
import rawhttp.core.EagerHttpRequest
2122
import rawhttp.core.RawHttpHeaders
@@ -24,16 +25,22 @@ import rawhttp.core.RequestLine
2425
import rawhttp.core.body.BodyReader
2526
import rawhttp.core.body.HttpMessageBody
2627
import java.net.InetAddress
28+
import java.time.Instant
29+
import java.util.concurrent.TimeUnit.SECONDS
30+
import java.util.concurrent.atomic.AtomicLong
2731

2832
class Th2RawHttpRequest(
2933
requestLine: RequestLine,
3034
headers: RawHttpHeaders,
3135
bodyReader: BodyReader?,
3236
senderAddress: InetAddress?,
3337
val parentEventId: EventID?,
34-
val metadataProperties: Map<String, String>
38+
val metadataProperties: Map<String, String>,
39+
val th2RequestId: String = nextRequestId(),
3540
) : RawHttpRequest(requestLine, headers, bodyReader, senderAddress) {
3641

42+
43+
3744
override fun withBody(body: HttpMessageBody?, adjustHeaders: Boolean): Th2RawHttpRequest =
3845
withBody(body, adjustHeaders) { headers: RawHttpHeaders, bodyReader: BodyReader? ->
3946
Th2RawHttpRequest(
@@ -42,7 +49,8 @@ class Th2RawHttpRequest(
4249
bodyReader,
4350
senderAddress.orElse(null),
4451
parentEventId,
45-
metadataProperties
52+
metadataProperties,
53+
th2RequestId,
4654
)
4755
}
4856

@@ -61,7 +69,8 @@ class Th2RawHttpRequest(
6169
body.orElse(null),
6270
senderAddress.orElse(null),
6371
parentEventId,
64-
metadataProperties
72+
metadataProperties,
73+
th2RequestId,
6574
)
6675
}
6776

@@ -73,13 +82,22 @@ class Th2RawHttpRequest(
7382
body.orElse(null),
7483
senderAddress.orElse(null),
7584
parentEventId,
76-
metadataProperties
85+
metadataProperties,
86+
th2RequestId,
7787
)
7888

7989
override fun eagerly(): EagerHttpRequest {
8090
throw UnsupportedOperationException("Unsupported eagerly call of request. It's client side, no need to eager request")
8191
}
8292

93+
companion object {
94+
private val BASE_REQUEST_ID = generateUUID()
95+
private val REQUEST_ID_COUNTER = Instant.now().run {
96+
AtomicLong(epochSecond * SECONDS.toNanos(1) + nano)
97+
}
98+
99+
private fun nextRequestId() = "$BASE_REQUEST_ID-${REQUEST_ID_COUNTER.incrementAndGet()}"
100+
}
83101
}
84102

85103

src/main/kotlin/com/exactpro/th2/http/client/util/MessageUtil.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ private const val HEADER_NAME_FIELD = "name"
6666
private const val HEADER_VALUE_FIELD = "value"
6767
private const val HEADER_PREFIX = "header-"
6868

69+
internal const val TH2_REQUEST_ID_PROPERTY = "th2-request-id"
6970
private const val METHOD_PROPERTY = METHOD_FIELD
7071
private const val URI_PROPERTY = URI_FIELD
7172
private const val CONTENT_TYPE_PROPERTY = "contentType"
@@ -305,7 +306,7 @@ private fun HttpMessage.toByteArrayOutputStream(): ByteArrayOutputStream {
305306
}
306307

307308
private fun RawHttpRequest.toProperties(): Map<String, String> = when (this) {
308-
is Th2RawHttpRequest -> metadataProperties.toMutableMap()
309+
is Th2RawHttpRequest -> metadataProperties.toMutableMap().apply { put(TH2_REQUEST_ID_PROPERTY, th2RequestId) }
309310
else -> hashMapOf()
310311
}.apply {
311312
put(METHOD_PROPERTY, method)

src/test/kotlin/com/exactpro/th2/http/client/CommonTests.kt

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,14 @@ import com.exactpro.th2.common.grpc.ConnectionID
2020
import com.exactpro.th2.common.grpc.EventID
2121
import com.exactpro.th2.http.client.api.decorators.Th2RawHttpRequest
2222
import com.exactpro.th2.http.client.util.toProtoMessage
23-
import org.junit.jupiter.api.Assertions
23+
import org.junit.jupiter.api.Assertions.assertEquals
24+
import org.junit.jupiter.api.Assertions.assertFalse
2425
import org.junit.jupiter.api.Test
2526
import rawhttp.core.HttpVersion
2627
import rawhttp.core.RawHttpHeaders
2728
import rawhttp.core.RequestLine
2829
import java.net.URI
30+
import kotlin.test.assertContains
2931

3032
class CommonTests {
3133

@@ -39,11 +41,16 @@ class CommonTests {
3941

4042
val rawMessage = request.toProtoMessage(ConnectionID.getDefaultInstance(), 12345L)
4143
val newMetadata = rawMessage.metadata.propertiesMap
42-
Assertions.assertEquals(metadata.values.size + 2 /* method and uri */, newMetadata.values.size)
43-
metadata.forEach {
44-
Assertions.assertTrue(newMetadata.containsKey(it.key))
45-
Assertions.assertEquals(it.value, newMetadata[it.key])
44+
assertEquals(metadata.values.size + 2 /* method and uri */ + 1 /* th2-request-id */, newMetadata.values.size)
45+
assertFalse(newMetadata[PROPERTY].isNullOrBlank(), "The $PROPERTY message property is null or blank")
46+
metadata.forEach { (name, value) ->
47+
assertContains(newMetadata, name)
48+
assertEquals(value, newMetadata[name])
4649
}
47-
Assertions.assertEquals(parentEventID, rawMessage.parentEventId)
50+
assertEquals(parentEventID, rawMessage.parentEventId)
51+
}
52+
53+
companion object {
54+
private const val PROPERTY = "th2-request-id"
4855
}
4956
}

0 commit comments

Comments
 (0)