mirror of
https://github.com/square/okhttp.git
synced 2025-08-07 12:42:57 +03:00
Okio Filesystem (#6500)
* Testing okio * Working tests * Working tests * Working tests * Working tests * okio 3 * Fix dependencies * File system * Cleanup * Cleanup * Cleanup * Cache fixes * Cache fixes * Review comments * Cleanup * Cleanup * Build fixes
This commit is contained in:
@@ -1,116 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Square, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package okhttp3.internal.io
|
||||
|
||||
import java.io.File
|
||||
import java.io.FileNotFoundException
|
||||
import java.io.IOException
|
||||
import java.util.IdentityHashMap
|
||||
import okio.Buffer
|
||||
import okio.ForwardingSink
|
||||
import okio.ForwardingSource
|
||||
import okio.Sink
|
||||
import okio.Source
|
||||
import okhttp3.TestUtil.isDescendentOf
|
||||
import org.junit.jupiter.api.extension.AfterEachCallback
|
||||
import org.junit.jupiter.api.extension.ExtensionContext
|
||||
|
||||
/** A simple file system where all files are held in memory. Not safe for concurrent use. */
|
||||
class InMemoryFileSystem : FileSystem, AfterEachCallback {
|
||||
val files = mutableMapOf<File, Buffer>()
|
||||
private val openSources = IdentityHashMap<Source, File>()
|
||||
private val openSinks = IdentityHashMap<Sink, File>()
|
||||
|
||||
override fun afterEach(context: ExtensionContext?) {
|
||||
ensureResourcesClosed()
|
||||
}
|
||||
|
||||
fun ensureResourcesClosed() {
|
||||
val openResources = mutableListOf<String>()
|
||||
for (file in openSources.values) {
|
||||
openResources.add("Source for $file")
|
||||
}
|
||||
for (file in openSinks.values) {
|
||||
openResources.add("Sink for $file")
|
||||
}
|
||||
check(openResources.isEmpty()) {
|
||||
"Resources acquired but not closed:\n * ${openResources.joinToString(separator = "\n * ")}"
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(FileNotFoundException::class)
|
||||
override fun source(file: File): Source {
|
||||
val result = files[file] ?: throw FileNotFoundException()
|
||||
val source: Source = result.clone()
|
||||
openSources[source] = file
|
||||
return object : ForwardingSource(source) {
|
||||
override fun close() {
|
||||
openSources.remove(source)
|
||||
super.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(FileNotFoundException::class)
|
||||
override fun sink(file: File) = sink(file, false)
|
||||
|
||||
@Throws(FileNotFoundException::class)
|
||||
override fun appendingSink(file: File) = sink(file, true)
|
||||
|
||||
private fun sink(file: File, appending: Boolean): Sink {
|
||||
var result: Buffer? = null
|
||||
if (appending) {
|
||||
result = files[file]
|
||||
}
|
||||
if (result == null) {
|
||||
result = Buffer()
|
||||
}
|
||||
files[file] = result
|
||||
val sink: Sink = result
|
||||
openSinks[sink] = file
|
||||
return object : ForwardingSink(sink) {
|
||||
override fun close() {
|
||||
openSinks.remove(sink)
|
||||
super.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun delete(file: File) {
|
||||
files.remove(file)
|
||||
}
|
||||
|
||||
override fun exists(file: File) = files.containsKey(file)
|
||||
|
||||
override fun size(file: File) = files[file]?.size ?: 0L
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun rename(from: File, to: File) {
|
||||
files[to] = files.remove(from) ?: throw FileNotFoundException()
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun deleteContents(directory: File) {
|
||||
val i = files.keys.iterator()
|
||||
while (i.hasNext()) {
|
||||
val file = i.next()
|
||||
if (file.isDescendentOf(directory)) i.remove()
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString() = "InMemoryFileSystem"
|
||||
}
|
@@ -1,104 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Square, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package okhttp3.internal.io
|
||||
|
||||
import java.io.File
|
||||
import java.util.Collections
|
||||
import okhttp3.TestUtil.isDescendentOf
|
||||
import okio.ForwardingSink
|
||||
import okio.ForwardingSource
|
||||
import okio.IOException
|
||||
import okio.Sink
|
||||
import okio.Source
|
||||
|
||||
/**
|
||||
* Emulate Windows file system limitations on any file system. In particular, Windows will throw an
|
||||
* [IOException] when asked to delete or rename an open file.
|
||||
*/
|
||||
class WindowsFileSystem(val delegate: FileSystem) : FileSystem {
|
||||
/** Guarded by itself. */
|
||||
private val openFiles = Collections.synchronizedList(mutableListOf<File>())
|
||||
|
||||
override fun source(file: File): Source = FileSource(file, delegate.source(file))
|
||||
|
||||
override fun sink(file: File): Sink = FileSink(file, delegate.sink(file))
|
||||
|
||||
override fun appendingSink(file: File): Sink = FileSink(file, delegate.appendingSink(file))
|
||||
|
||||
override fun delete(file: File) {
|
||||
val fileOpen = file in openFiles
|
||||
if (fileOpen) throw IOException("file is open $file")
|
||||
delegate.delete(file)
|
||||
}
|
||||
|
||||
override fun exists(file: File) = delegate.exists(file)
|
||||
|
||||
override fun size(file: File) = delegate.size(file)
|
||||
|
||||
override fun rename(from: File, to: File) {
|
||||
val fromOpen = from in openFiles
|
||||
if (fromOpen) throw IOException("file is open $from")
|
||||
|
||||
val toOpen = to in openFiles
|
||||
if (toOpen) throw IOException("file is open $to")
|
||||
|
||||
delegate.rename(from, to)
|
||||
}
|
||||
|
||||
override fun deleteContents(directory: File) {
|
||||
val openChild = synchronized(openFiles) {
|
||||
openFiles.firstOrNull { it.isDescendentOf(directory) }
|
||||
}
|
||||
if (openChild != null) throw IOException("file is open $openChild")
|
||||
delegate.deleteContents(directory)
|
||||
}
|
||||
|
||||
private inner class FileSink(val file: File, delegate: Sink) : ForwardingSink(delegate) {
|
||||
var closed = false
|
||||
|
||||
init {
|
||||
openFiles += file
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
if (!closed) {
|
||||
closed = true
|
||||
val removed = openFiles.remove(file)
|
||||
check(removed)
|
||||
}
|
||||
delegate.close()
|
||||
}
|
||||
}
|
||||
|
||||
private inner class FileSource(val file: File, delegate: Source) : ForwardingSource(delegate) {
|
||||
var closed = false
|
||||
|
||||
init {
|
||||
openFiles += file
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
if (!closed) {
|
||||
closed = true
|
||||
val removed = openFiles.remove(file)
|
||||
check(removed)
|
||||
}
|
||||
delegate.close()
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString() = "$delegate for Windows™"
|
||||
}
|
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Square, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package okhttp3.okio
|
||||
|
||||
import okio.ExperimentalFileSystem
|
||||
import okio.FileSystem
|
||||
import okio.ForwardingFileSystem
|
||||
import okio.Path
|
||||
import okio.Sink
|
||||
import okio.Source
|
||||
|
||||
@OptIn(ExperimentalFileSystem::class)
|
||||
class LoggingFilesystem(fileSystem: FileSystem) : ForwardingFileSystem(fileSystem) {
|
||||
fun log(line: String) {
|
||||
println(line)
|
||||
}
|
||||
|
||||
override fun appendingSink(file: Path): Sink {
|
||||
log("appendingSink($file)")
|
||||
|
||||
return super.appendingSink(file)
|
||||
}
|
||||
|
||||
override fun atomicMove(source: Path, target: Path) {
|
||||
log("atomicMove($source, $target)")
|
||||
|
||||
super.atomicMove(source, target)
|
||||
}
|
||||
|
||||
override fun createDirectory(dir: Path) {
|
||||
log("createDirectory($dir)")
|
||||
|
||||
super.createDirectory(dir)
|
||||
}
|
||||
|
||||
override fun delete(path: Path) {
|
||||
log("delete($path)")
|
||||
|
||||
super.delete(path)
|
||||
}
|
||||
|
||||
override fun sink(path: Path): Sink {
|
||||
log("sink($path)")
|
||||
|
||||
return super.sink(path)
|
||||
}
|
||||
|
||||
override fun source(file: Path): Source {
|
||||
log("source($file)")
|
||||
|
||||
return super.source(file)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user