|
@@ -3,7 +3,6 @@ package co.id.datacomsolusindo.ipphonebridge
|
|
import com.fasterxml.jackson.databind.ObjectMapper
|
|
import com.fasterxml.jackson.databind.ObjectMapper
|
|
import com.fasterxml.jackson.databind.ObjectWriter
|
|
import com.fasterxml.jackson.databind.ObjectWriter
|
|
import org.apache.logging.log4j.LogManager
|
|
import org.apache.logging.log4j.LogManager
|
|
-import org.springframework.context.annotation.Configuration
|
|
|
|
import org.springframework.core.Ordered
|
|
import org.springframework.core.Ordered
|
|
import org.springframework.core.annotation.Order
|
|
import org.springframework.core.annotation.Order
|
|
import org.springframework.core.io.ResourceLoader
|
|
import org.springframework.core.io.ResourceLoader
|
|
@@ -12,17 +11,10 @@ import org.springframework.http.HttpMethod
|
|
import org.springframework.http.HttpStatus
|
|
import org.springframework.http.HttpStatus
|
|
import org.springframework.messaging.simp.SimpMessagingTemplate
|
|
import org.springframework.messaging.simp.SimpMessagingTemplate
|
|
import org.springframework.stereotype.Component
|
|
import org.springframework.stereotype.Component
|
|
-import org.springframework.web.server.ResponseStatusException
|
|
|
|
-import org.springframework.web.servlet.config.annotation.EnableWebMvc
|
|
|
|
-import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry
|
|
|
|
-import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
|
|
|
|
-import org.springframework.web.servlet.resource.PathResourceResolver
|
|
|
|
import java.io.File
|
|
import java.io.File
|
|
import java.io.IOException
|
|
import java.io.IOException
|
|
import java.io.Serializable
|
|
import java.io.Serializable
|
|
import java.net.InetAddress
|
|
import java.net.InetAddress
|
|
-import java.time.Duration
|
|
|
|
-import java.time.LocalDateTime
|
|
|
|
import java.util.*
|
|
import java.util.*
|
|
import java.util.concurrent.ConcurrentHashMap
|
|
import java.util.concurrent.ConcurrentHashMap
|
|
import java.util.concurrent.ConcurrentMap
|
|
import java.util.concurrent.ConcurrentMap
|
|
@@ -30,7 +22,6 @@ import java.util.concurrent.TimeUnit
|
|
import javax.servlet.*
|
|
import javax.servlet.*
|
|
import javax.servlet.http.HttpServletRequest
|
|
import javax.servlet.http.HttpServletRequest
|
|
import javax.servlet.http.HttpServletResponse
|
|
import javax.servlet.http.HttpServletResponse
|
|
-import kotlin.concurrent.fixedRateTimer
|
|
|
|
import kotlin.streams.toList
|
|
import kotlin.streams.toList
|
|
|
|
|
|
|
|
|
|
@@ -76,7 +67,7 @@ class BridgeFilter(private val template: SimpMessagingTemplate, val resourceLoad
|
|
val client = splitPath[1]
|
|
val client = splitPath[1]
|
|
val toPath = "/" + splitPath.takeLast(splitPath.size - 1).joinToString("/")
|
|
val toPath = "/" + splitPath.takeLast(splitPath.size - 1).joinToString("/")
|
|
val clientNumber = client.toIntOrNull()
|
|
val clientNumber = client.toIntOrNull()
|
|
- if (clientNumber != null && clientNumber > 0 && clientNumber <= 999 && ClientHolder.get().containsKey(clientNumber.toString())) {
|
|
|
|
|
|
+ if (clientNumber != null && clientNumber > 0 && clientNumber <= 999) {
|
|
val search = isResourceAvailable(toPath)
|
|
val search = isResourceAvailable(toPath)
|
|
if (search != null && !search.endsWith("assets/bootstrap.js")) {
|
|
if (search != null && !search.endsWith("assets/bootstrap.js")) {
|
|
res.sendRedirect("/resources/$search")
|
|
res.sendRedirect("/resources/$search")
|
|
@@ -84,7 +75,6 @@ class BridgeFilter(private val template: SimpMessagingTemplate, val resourceLoad
|
|
LogManager.getLogger("client-$client")
|
|
LogManager.getLogger("client-$client")
|
|
.info("Request Start, UA: " + req.getHeader("User-Agent") + " From: " + InetAddress.getLocalHost().hostAddress + " To: " + req.requestURL.toString() + "?" + req.queryString)
|
|
.info("Request Start, UA: " + req.getHeader("User-Agent") + " From: " + InetAddress.getLocalHost().hostAddress + " To: " + req.requestURL.toString() + "?" + req.queryString)
|
|
|
|
|
|
-// if (req.getHeader("User-Agent").contains("(dart:io)") || req.requestURL.toString().endsWith("api/license")) {
|
|
|
|
val reqId = UUID.randomUUID().toString()
|
|
val reqId = UUID.randomUUID().toString()
|
|
try {
|
|
try {
|
|
val headerMap = req.headerNames.toList().associateBy({ it }, { req.getHeader(it) })
|
|
val headerMap = req.headerNames.toList().associateBy({ it }, { req.getHeader(it) })
|
|
@@ -107,38 +97,46 @@ class BridgeFilter(private val template: SimpMessagingTemplate, val resourceLoad
|
|
val body = req.reader.lines().toList().joinToString(System.lineSeparator())
|
|
val body = req.reader.lines().toList().joinToString(System.lineSeparator())
|
|
rb.body = body
|
|
rb.body = body
|
|
}
|
|
}
|
|
-
|
|
|
|
- Singletons.requestInstance.getOrPut(reqId){ RequestQue(reqId, rb, null)}
|
|
|
|
|
|
+ Singletons.requestInstance.getOrPut(reqId) { RequestQue(reqId, rb, null) }
|
|
val ow: ObjectWriter = ObjectMapper().writer().withDefaultPrettyPrinter()
|
|
val ow: ObjectWriter = ObjectMapper().writer().withDefaultPrettyPrinter()
|
|
- val partStr = ow.writeValueAsString(rb).chunked(1500)
|
|
|
|
|
|
+ val chunkSize = 2000
|
|
|
|
+ val partStr = ow.writeValueAsString(rb).chunked(chunkSize)
|
|
val partialID = UUID.randomUUID().toString()
|
|
val partialID = UUID.randomUUID().toString()
|
|
val st2 = System.nanoTime()
|
|
val st2 = System.nanoTime()
|
|
|
|
+ if (!ClientHolder.get().containsKey(clientNumber.toString())) {
|
|
|
|
+// subscribeGW /topic/request/reqID
|
|
|
|
+// sendGW "/app/partial/$client"
|
|
|
|
+ chain.doFilter(request, response)
|
|
|
|
+ return
|
|
|
|
+ } else {
|
|
|
|
+ partStr.forEachIndexed { idx, it ->
|
|
|
|
+ template.convertAndSend(
|
|
|
|
+ "/topic/partial/$client",
|
|
|
|
+ PartialData(partialID, partStr.size, idx + 1, it, st2)
|
|
|
|
+ )
|
|
|
|
+ }
|
|
|
|
|
|
-// println("req id $reqId")
|
|
|
|
- partStr.forEachIndexed { idx, it ->
|
|
|
|
- template.convertAndSend(
|
|
|
|
- "/topic/partial/$client",
|
|
|
|
- PartialData(partialID, partStr.size, idx + 1, it, st2)
|
|
|
|
- )
|
|
|
|
- }
|
|
|
|
|
|
|
|
|
|
+ }
|
|
|
|
|
|
var i = 0
|
|
var i = 0
|
|
|
|
|
|
while (Singletons.responseQue.getOrElse(reqId) { null } == null) {
|
|
while (Singletons.responseQue.getOrElse(reqId) { null } == null) {
|
|
- TimeUnit.MILLISECONDS.sleep(100)
|
|
|
|
|
|
+ TimeUnit.MILLISECONDS.sleep(50)
|
|
i++
|
|
i++
|
|
if (i >= 600 * partStr.size) {
|
|
if (i >= 600 * partStr.size) {
|
|
res.status = HttpStatus.REQUEST_TIMEOUT.value()
|
|
res.status = HttpStatus.REQUEST_TIMEOUT.value()
|
|
res.writer.write("{\"message\":\"Request timeout. Client not responding\"}")
|
|
res.writer.write("{\"message\":\"Request timeout. Client not responding\"}")
|
|
res.flushBuffer()
|
|
res.flushBuffer()
|
|
Singletons.requestInstance.remove(reqId)
|
|
Singletons.requestInstance.remove(reqId)
|
|
|
|
+ Singletons.buildSocketChunkData.remove(reqId)
|
|
|
|
+ Singletons.responseQue.remove(reqId)
|
|
val endTime = System.nanoTime()
|
|
val endTime = System.nanoTime()
|
|
val duration =
|
|
val duration =
|
|
(endTime - startTime).toDouble() / 1000000 //divide by 1000000 to get milliseconds.
|
|
(endTime - startTime).toDouble() / 1000000 //divide by 1000000 to get milliseconds.
|
|
LogManager.getLogger("client-$client")
|
|
LogManager.getLogger("client-$client")
|
|
.info("Request failed, No Response. Time: $duration UA: " + req.getHeader("User-Agent") + " From: " + InetAddress.getLocalHost().hostAddress + " To: " + req.requestURL.toString() + "?" + req.queryString)
|
|
.info("Request failed, No Response. Time: $duration UA: " + req.getHeader("User-Agent") + " From: " + InetAddress.getLocalHost().hostAddress + " To: " + req.requestURL.toString() + "?" + req.queryString)
|
|
-
|
|
|
|
|
|
+// unsubscribeGW /topic/request/reqID
|
|
ClientHolder.addFailedRequest(client)
|
|
ClientHolder.addFailedRequest(client)
|
|
return
|
|
return
|
|
}
|
|
}
|
|
@@ -162,9 +160,13 @@ class BridgeFilter(private val template: SimpMessagingTemplate, val resourceLoad
|
|
res.flushBuffer()
|
|
res.flushBuffer()
|
|
|
|
|
|
Singletons.requestInstance.remove(reqId)
|
|
Singletons.requestInstance.remove(reqId)
|
|
|
|
+ Singletons.buildSocketChunkData.remove(reqId)
|
|
|
|
+ Singletons.responseQue.remove(reqId)
|
|
|
|
+ // unsubscribeGW /topic/request/reqID
|
|
val endTime = System.nanoTime()
|
|
val endTime = System.nanoTime()
|
|
|
|
|
|
- val duration = (endTime - startTime).toDouble() / 1000000 //divide by 1000000 to get milliseconds.
|
|
|
|
|
|
+ val duration =
|
|
|
|
+ (endTime - startTime).toDouble() / 1000000 //divide by 1000000 to get milliseconds.
|
|
LogManager.getLogger("client-$client")
|
|
LogManager.getLogger("client-$client")
|
|
.info("Request Success. Time: $duration UA: " + req.getHeader("User-Agent") + " From: " + req.remoteAddr + " To: " + req.requestURL.toString() + "?" + req.queryString)
|
|
.info("Request Success. Time: $duration UA: " + req.getHeader("User-Agent") + " From: " + req.remoteAddr + " To: " + req.requestURL.toString() + "?" + req.queryString)
|
|
|
|
|
|
@@ -178,6 +180,8 @@ class BridgeFilter(private val template: SimpMessagingTemplate, val resourceLoad
|
|
res.writer.write("{\"message\":\"Request timeout. Unknown error\"}")
|
|
res.writer.write("{\"message\":\"Request timeout. Unknown error\"}")
|
|
res.flushBuffer()
|
|
res.flushBuffer()
|
|
Singletons.requestInstance.remove(reqId)
|
|
Singletons.requestInstance.remove(reqId)
|
|
|
|
+ Singletons.buildSocketChunkData.remove(reqId)
|
|
|
|
+ Singletons.responseQue.remove(reqId)
|
|
val endTime = System.nanoTime()
|
|
val endTime = System.nanoTime()
|
|
val duration = (endTime - startTime).toDouble() / 1000000 //divide by 1000000 to get milliseconds.
|
|
val duration = (endTime - startTime).toDouble() / 1000000 //divide by 1000000 to get milliseconds.
|
|
LogManager.getLogger("client-$client")
|
|
LogManager.getLogger("client-$client")
|
|
@@ -190,17 +194,6 @@ class BridgeFilter(private val template: SimpMessagingTemplate, val resourceLoad
|
|
chain.doFilter(request, response)
|
|
chain.doFilter(request, response)
|
|
return
|
|
return
|
|
}
|
|
}
|
|
-// if (
|
|
|
|
-// client.startsWith("_")
|
|
|
|
-// || client.startsWith("resources")
|
|
|
|
-// || client.startsWith("clientStat")
|
|
|
|
-// || client.startsWith("actuator")
|
|
|
|
-// ) {
|
|
|
|
-// chain.doFilter(request, response)
|
|
|
|
-// return
|
|
|
|
-// } else {
|
|
|
|
-//
|
|
|
|
-// }
|
|
|
|
}
|
|
}
|
|
|
|
|
|
fun isResourceAvailable(path: String, itr: Int = 1): String? {
|
|
fun isResourceAvailable(path: String, itr: Int = 1): String? {
|
|
@@ -217,9 +210,6 @@ class BridgeFilter(private val template: SimpMessagingTemplate, val resourceLoad
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-class RequestQue(val id: String, val requestBuilder: RequestBuilder, var responseObj: Resp?)
|
|
|
|
-class PartialData(val id: String, val total: Int, val idx: Int, val data: String, val startTime: Long)
|
|
|
|
-
|
|
|
|
class RequestBuilder(
|
|
class RequestBuilder(
|
|
val id: String,
|
|
val id: String,
|
|
val path: String,
|
|
val path: String,
|
|
@@ -231,6 +221,10 @@ class RequestBuilder(
|
|
var parts: MutableList<FilePart>? = null
|
|
var parts: MutableList<FilePart>? = null
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+class RequestQue(val id: String, val requestBuilder: RequestBuilder, var responseObj: Resp?)
|
|
|
|
+
|
|
|
|
+class PartialData(val id: String, val total: Int, val idx: Int, val data: String, val startTime: Long)
|
|
|
|
+
|
|
class FilePart(val name: String, val fileName: String, val data: String)
|
|
class FilePart(val name: String, val fileName: String, val data: String)
|
|
|
|
|
|
class Resp(val body: ByteArray?, val statusCode: Int, val headers: Map<String, Array<String>>? = null)
|
|
class Resp(val body: ByteArray?, val statusCode: Int, val headers: Map<String, Array<String>>? = null)
|
|
@@ -242,59 +236,5 @@ object Singletons {
|
|
val buildSocketChunkData: ConcurrentMap<String, ChunkCollector> by lazy { ConcurrentHashMap() }
|
|
val buildSocketChunkData: ConcurrentMap<String, ChunkCollector> by lazy { ConcurrentHashMap() }
|
|
}
|
|
}
|
|
|
|
|
|
-class SocketChunkData(
|
|
|
|
- val body: ByteArray?,
|
|
|
|
- val header: HttpHeaders?,
|
|
|
|
- val status: Int,
|
|
|
|
- val part: Int,
|
|
|
|
- val totalPart: Int
|
|
|
|
-)
|
|
|
|
-
|
|
|
|
-class ChunkCollector(private val id: String) {
|
|
|
|
- private val mapSocketChunk: ConcurrentMap<Int, SocketChunkData> by lazy { ConcurrentHashMap() }
|
|
|
|
-
|
|
|
|
- init {
|
|
|
|
- val tStart = LocalDateTime.now()
|
|
|
|
- fixedRateTimer("timer-$id", false, 20, 50) {
|
|
|
|
- val listSocketChunk = mapSocketChunk.entries.sortedBy { it.key }.map { it.value }
|
|
|
|
- if (listSocketChunk.size > 1 && listSocketChunk[0].totalPart == listSocketChunk.size) {
|
|
|
|
- val sortedChunk = listSocketChunk.sortedBy { it.part }
|
|
|
|
- val bodyList = sortedChunk.mapNotNull { it.body }
|
|
|
|
- var bd = ByteArray(0)
|
|
|
|
- bodyList.forEach { bd += it }
|
|
|
|
- Singletons.responseQue.getOrPut(id) {
|
|
|
|
- Resp(
|
|
|
|
- bd,
|
|
|
|
- sortedChunk.last().status,
|
|
|
|
- listSocketChunk.firstOrNull { it.header != null }?.header?.entries?.associate {
|
|
|
|
- Pair(it.key, it.value.toTypedArray())
|
|
|
|
- }
|
|
|
|
- )
|
|
|
|
- }
|
|
|
|
|
|
|
|
- this.cancel()
|
|
|
|
- }
|
|
|
|
- if (Duration.between(tStart, LocalDateTime.now()).toMillis() > 10 * 60 * 1000) {
|
|
|
|
- this.cancel()
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- fun add(dt: SocketChunkData) {
|
|
|
|
- mapSocketChunk.getOrPut(dt.part) { dt }
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-@Configuration
|
|
|
|
-@EnableWebMvc
|
|
|
|
-class MvcConfig : WebMvcConfigurer {
|
|
|
|
- override fun addResourceHandlers(registry: ResourceHandlerRegistry) {
|
|
|
|
- registry
|
|
|
|
- .addResourceHandler("resources/**")
|
|
|
|
- .addResourceLocations("file:web/")
|
|
|
|
- .setCachePeriod(3600 * 24 * 30)
|
|
|
|
- .resourceChain(true)
|
|
|
|
- .addResolver(PathResourceResolver())
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
|
|
|