Merge pull request #1467 from davotoula/prevent-resource-leaks-with-streams

Prevent resource leaks with streams
This commit is contained in:
Vitor Pamplona
2025-09-08 17:27:47 -04:00
committed by GitHub
7 changed files with 64 additions and 51 deletions

View File

@@ -1842,15 +1842,15 @@ object LocalCache : ILocalCache {
note.addRelay(relay) note.addRelay(relay)
} }
var isVerified = val isVerified =
try { try {
val cachePath = Amethyst.instance.nip95cache val cachePath = Amethyst.instance.nip95cache
cachePath.mkdirs() cachePath.mkdirs()
val file = File(cachePath, event.id) val file = File(cachePath, event.id)
if (!file.exists() && (wasVerified || justVerify(event))) { if (!file.exists() && (wasVerified || justVerify(event))) {
val stream = FileOutputStream(file) FileOutputStream(file).use { stream ->
stream.write(event.decode()) stream.write(event.decode())
stream.close() }
Log.i( Log.i(
"FileStorageEvent", "FileStorageEvent",
"NIP95 File received from $relay and saved to disk as $file", "NIP95 File received from $relay and saved to disk as $file",
@@ -2134,7 +2134,7 @@ object LocalCache : ILocalCache {
} }
} }
suspend fun findStatusesForUser(user: User): ImmutableList<AddressableNote> { fun findStatusesForUser(user: User): ImmutableList<AddressableNote> {
checkNotInMainThread() checkNotInMainThread()
return addressables return addressables
@@ -2151,7 +2151,7 @@ object LocalCache : ILocalCache {
.toImmutableList() .toImmutableList()
} }
suspend fun findEarliestOtsForNote( fun findEarliestOtsForNote(
note: Note, note: Note,
resolverBuilder: OtsResolverBuilder, resolverBuilder: OtsResolverBuilder,
): Long? { ): Long? {
@@ -2178,7 +2178,7 @@ object LocalCache : ILocalCache {
fun cachedModificationEventsForNote(note: Note): List<Note>? = modificationCache[note.idHex] fun cachedModificationEventsForNote(note: Note): List<Note>? = modificationCache[note.idHex]
suspend fun findLatestModificationForNote(note: Note): List<Note> { fun findLatestModificationForNote(note: Note): List<Note> {
checkNotInMainThread() checkNotInMainThread()
val noteAuthor = note.author ?: return emptyList() val noteAuthor = note.author ?: return emptyList()
@@ -2340,9 +2340,9 @@ object LocalCache : ILocalCache {
val children = val children =
if (noteEvent is WrappedEvent) { if (noteEvent is WrappedEvent) {
noteEvent.host?.id?.let { noteEvent.host?.id?.let {
getNoteIfExists(it)?.let { getNoteIfExists(it)?.let { it2 ->
removeFromCache(it) removeFromCache(it2)
it.removeAllChildNotes() it2.removeAllChildNotes()
} }
} }
} else { } else {

View File

@@ -41,7 +41,7 @@ class CrashReportCache(
null null
} }
suspend fun writeReport(report: String) { fun writeReport(report: String) {
val trace = outputStream() val trace = outputStream()
trace.write(report.toByteArray()) trace.write(report.toByteArray())
trace.close() trace.close()
@@ -50,8 +50,10 @@ class CrashReportCache(
suspend fun loadAndDelete(): String? = suspend fun loadAndDelete(): String? =
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
val stack = val stack =
inputStreamOrNull()?.let { inStream -> inputStreamOrNull()?.use { inStream ->
InputStreamReader(inStream).readText() InputStreamReader(inStream).use { reader ->
reader.readText()
}
} }
deleteReport() deleteReport()
stack stack

View File

@@ -96,19 +96,21 @@ class BlossomUploader {
checkNotNull(imageInputStream) { "Can't open the image input stream" } checkNotNull(imageInputStream) { "Can't open the image input stream" }
return upload( return imageInputStream.use { stream ->
imageInputStream, upload(
hash, stream,
payload.size, hash,
fileName, payload.size,
myContentType, fileName,
alt, myContentType,
sensitiveContent, alt,
serverBaseUrl, sensitiveContent,
okHttpClient, serverBaseUrl,
httpAuth, okHttpClient,
context, httpAuth,
) context,
)
}
} }
fun encodeAuth(event: BlossomAuthorizationEvent): String { fun encodeAuth(event: BlossomAuthorizationEvent): String {

View File

@@ -109,18 +109,20 @@ class Nip96Uploader {
checkNotNull(imageInputStream) { "Can't open the image input stream" } checkNotNull(imageInputStream) { "Can't open the image input stream" }
return upload( return imageInputStream.use { stream ->
imageInputStream, upload(
length, stream,
myContentType, length,
alt, myContentType,
sensitiveContent, alt,
server, sensitiveContent,
okHttpClient, server,
onProgress, okHttpClient,
httpAuth, onProgress,
context, httpAuth,
) context,
)
}
} }
suspend fun upload( suspend fun upload(

View File

@@ -50,8 +50,8 @@ class Request(
try { try {
val httpURLConnection = url.openConnection() as HttpURLConnection val httpURLConnection = url.openConnection() as HttpURLConnection
httpURLConnection.setReadTimeout(10000) httpURLConnection.readTimeout = 10000
httpURLConnection.setConnectTimeout(10000) httpURLConnection.connectTimeout = 10000
httpURLConnection.setRequestProperty("User-Agent", "OpenTimestamps Java") httpURLConnection.setRequestProperty("User-Agent", "OpenTimestamps Java")
httpURLConnection.setRequestProperty("Accept", "application/json") httpURLConnection.setRequestProperty("Accept", "application/json")
httpURLConnection.setRequestProperty("Accept-Encoding", "gzip") httpURLConnection.setRequestProperty("Accept-Encoding", "gzip")
@@ -62,17 +62,17 @@ class Request(
if (data != null) { if (data != null) {
httpURLConnection.setDoOutput(true) httpURLConnection.setDoOutput(true)
httpURLConnection.setRequestMethod("POST") httpURLConnection.requestMethod = "POST"
httpURLConnection.setRequestProperty( httpURLConnection.setRequestProperty(
"Content-Length", "Content-Length",
"" + this.data!!.size.toString(), "" + this.data!!.size.toString(),
) )
val wr = DataOutputStream(httpURLConnection.getOutputStream()) DataOutputStream(httpURLConnection.getOutputStream()).use { wr ->
wr.write(this.data, 0, this.data!!.size) wr.write(this.data, 0, this.data!!.size)
wr.flush() wr.flush()
wr.close() }
} else { } else {
httpURLConnection.setRequestMethod("GET") httpURLConnection.requestMethod = "GET"
} }
httpURLConnection.connect() httpURLConnection.connect()
@@ -84,7 +84,7 @@ class Request(
response.status = responseCode response.status = responseCode
response.fromUrl = url.toString() response.fromUrl = url.toString()
var `is` = httpURLConnection.getInputStream() var `is` = httpURLConnection.getInputStream()
if ("gzip" == httpURLConnection.getContentEncoding()) { if ("gzip" == httpURLConnection.contentEncoding) {
`is` = GZIPInputStream(`is`) `is` = GZIPInputStream(`is`)
} }
response.setStream(`is`) response.setStream(`is`)

View File

@@ -23,6 +23,7 @@ package com.vitorpamplona.quartz.nip03Timestamp.ots.http
import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.json.JsonMapper import com.fasterxml.jackson.databind.json.JsonMapper
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.Closeable
import java.io.IOException import java.io.IOException
import java.io.InputStream import java.io.InputStream
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
@@ -30,7 +31,7 @@ import java.nio.charset.StandardCharsets
/** /**
* Holds the response from an HTTP request. * Holds the response from an HTTP request.
*/ */
class Response { class Response : Closeable {
private var stream: InputStream? = null private var stream: InputStream? = null
var fromUrl: String? = null var fromUrl: String? = null
@@ -53,7 +54,7 @@ class Response {
@get:Throws(IOException::class) @get:Throws(IOException::class)
val string: String val string: String
get() = kotlin.text.String(this.bytes, StandardCharsets.UTF_8) get() = String(this.bytes, StandardCharsets.UTF_8)
@get:Throws(IOException::class) @get:Throws(IOException::class)
val bytes: ByteArray val bytes: ByteArray
@@ -79,4 +80,9 @@ class Response {
JsonMapper.builder().build() JsonMapper.builder().build()
return builder.readTree(jsonString) return builder.readTree(jsonString)
} }
override fun close() {
stream?.close()
stream = null
}
} }

View File

@@ -62,7 +62,7 @@ abstract class OpCrypto internal constructor() : OpUnary() {
val digest = MessageDigest.getInstance(this.hashLibName()) val digest = MessageDigest.getInstance(this.hashLibName())
var chunk = ctx.read(1048576) var chunk = ctx.read(1048576)
while (chunk != null && chunk.size > 0) { while (chunk.isNotEmpty()) {
digest.update(chunk) digest.update(chunk)
chunk = ctx.read(1048576) chunk = ctx.read(1048576)
} }
@@ -73,7 +73,7 @@ abstract class OpCrypto internal constructor() : OpUnary() {
} }
@Throws(IOException::class, NoSuchAlgorithmException::class) @Throws(IOException::class, NoSuchAlgorithmException::class)
fun hashFd(file: File?): ByteArray = hashFd(FileInputStream(file)) fun hashFd(file: File?): ByteArray = FileInputStream(file).use { inputStream -> hashFd(inputStream) }
@Throws(IOException::class, NoSuchAlgorithmException::class) @Throws(IOException::class, NoSuchAlgorithmException::class)
fun hashFd(bytes: ByteArray): ByteArray { fun hashFd(bytes: ByteArray): ByteArray {
@@ -93,6 +93,7 @@ abstract class OpCrypto internal constructor() : OpUnary() {
count = inputStream.read(chunk, 0, 1048576) count = inputStream.read(chunk, 0, 1048576)
} }
// TODO: Is this needed? Closing of stream should be callers responsibility?
inputStream.close() inputStream.close()
val hash = digest.digest() val hash = digest.digest()