mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-03-14 06:56:05 +09:00
a working internet modem that only reads
This commit is contained in:
177
tsvm_core/src/net/torvald/tsvm/peripheral/HttpModem.kt
Normal file
177
tsvm_core/src/net/torvald/tsvm/peripheral/HttpModem.kt
Normal file
@@ -0,0 +1,177 @@
|
||||
package net.torvald.tsvm.peripheral
|
||||
|
||||
import net.torvald.tsvm.VM
|
||||
import net.torvald.tsvm.peripheral.TestDiskDrive.Companion.STATE_CODE_NO_SUCH_FILE_EXISTS
|
||||
import net.torvald.tsvm.peripheral.TestDiskDrive.Companion.STATE_CODE_OPERATION_FAILED
|
||||
import net.torvald.tsvm.peripheral.TestDiskDrive.Companion.STATE_CODE_SYSTEM_IO_ERROR
|
||||
import net.torvald.tsvm.peripheral.TestDiskDrive.Companion.composePositiveAns
|
||||
import java.io.*
|
||||
import java.net.MalformedURLException
|
||||
import java.net.URL
|
||||
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 2022-09-22.
|
||||
*/
|
||||
class HttpModem(private val vm: VM) : BlockTransferInterface(false, true) {
|
||||
|
||||
private val DBGPRN = true
|
||||
|
||||
private fun printdbg(msg: Any) {
|
||||
if (DBGPRN) println("[WgetModem] $msg")
|
||||
}
|
||||
|
||||
private var cnxOpen = false
|
||||
private var cnxUrl: String? = null
|
||||
|
||||
private val messageComposeBuffer = ByteArrayOutputStream(BLOCK_SIZE) // always use this and don't alter blockSendBuffer please
|
||||
private var blockSendBuffer = ByteArray(1)
|
||||
private var blockSendCount = 0
|
||||
|
||||
private var writeMode = false
|
||||
private var writeModeLength = -1
|
||||
|
||||
|
||||
init {
|
||||
statusCode = TestDiskDrive.STATE_CODE_STANDBY
|
||||
}
|
||||
|
||||
override fun hasNext(): Boolean {
|
||||
return (blockSendCount * BLOCK_SIZE < blockSendBuffer.size)
|
||||
}
|
||||
|
||||
override fun startSendImpl(recipient: BlockTransferInterface): Int {
|
||||
if (blockSendCount == 0) {
|
||||
blockSendBuffer = messageComposeBuffer.toByteArray()
|
||||
}
|
||||
|
||||
val sendSize = if (blockSendBuffer.size - (blockSendCount * BLOCK_SIZE) < BLOCK_SIZE)
|
||||
blockSendBuffer.size % BLOCK_SIZE
|
||||
else BLOCK_SIZE
|
||||
|
||||
recipient.writeout(ByteArray(sendSize) {
|
||||
blockSendBuffer[blockSendCount * BLOCK_SIZE + it]
|
||||
})
|
||||
|
||||
blockSendCount += 1
|
||||
|
||||
return sendSize
|
||||
}
|
||||
|
||||
private fun resetBuf() {
|
||||
blockSendCount = 0
|
||||
messageComposeBuffer.reset()
|
||||
}
|
||||
|
||||
private fun selfReset() {
|
||||
//readModeLength = -1
|
||||
cnxOpen = false
|
||||
cnxUrl = null
|
||||
blockSendCount = 0
|
||||
writeMode = false
|
||||
writeModeLength = -1
|
||||
}
|
||||
|
||||
private lateinit var writeBuffer: ByteArray
|
||||
private var writeBufferUsage = 0
|
||||
|
||||
override fun writeoutImpl(inputData: ByteArray) {
|
||||
if (writeMode) {
|
||||
//println("[DiskDrive] writeout with inputdata length of ${inputData.size}")
|
||||
//println("[DiskDriveMsg] ${inputData.toString(Charsets.UTF_8)}")
|
||||
|
||||
if (!cnxOpen) throw InternalError("Connection is not established but the modem is in write mode")
|
||||
|
||||
System.arraycopy(inputData, 0, writeBuffer, writeBufferUsage, minOf(writeModeLength - writeBufferUsage, inputData.size, BLOCK_SIZE))
|
||||
writeBufferUsage += inputData.size
|
||||
|
||||
if (writeBufferUsage >= writeModeLength) {
|
||||
// commit to the disk
|
||||
TODO("do something with writeBuffer")
|
||||
|
||||
writeMode = false
|
||||
}
|
||||
}
|
||||
else {
|
||||
val inputString = inputData.trimNull().toString(VM.CHARSET)
|
||||
|
||||
if (inputString.startsWith("DEVRST\u0017")) {
|
||||
printdbg("Device Reset")
|
||||
selfReset()
|
||||
statusCode = TestDiskDrive.STATE_CODE_STANDBY
|
||||
}
|
||||
else if (inputString.startsWith("DEVSTU\u0017"))
|
||||
recipient?.writeout(composePositiveAns("${statusCode.toChar()}", TestDiskDrive.errorMsgs[statusCode]))
|
||||
else if (inputString.startsWith("DEVTYP\u0017"))
|
||||
recipient?.writeout(composePositiveAns("HTTP"))
|
||||
else if (inputString.startsWith("DEVNAM\u0017"))
|
||||
recipient?.writeout(composePositiveAns("Wget Company HTTP Modem"))
|
||||
else if (inputString.startsWith("GET ")) {
|
||||
if (cnxUrl != null) {
|
||||
statusCode = TestDiskDrive.STATE_CODE_FILE_ALREADY_OPENED
|
||||
return
|
||||
}
|
||||
|
||||
printdbg("msg: $inputString, lastIndex: ${inputString.lastIndex}")
|
||||
|
||||
cnxUrl = inputString.substring(4).filter { it in '!'..'~' }
|
||||
|
||||
printdbg("URL: $cnxUrl")
|
||||
|
||||
this.ready = false
|
||||
this.busy = true
|
||||
|
||||
var httpIn: InputStream? = null
|
||||
var bufferedOut: OutputStream? = null
|
||||
resetBuf()
|
||||
try {
|
||||
// check the http connection before we do anything to the fs
|
||||
httpIn = BufferedInputStream(URL(cnxUrl).openStream())
|
||||
messageComposeBuffer.reset()
|
||||
bufferedOut = BufferedOutputStream(messageComposeBuffer, 1024)
|
||||
val data = ByteArray(1024)
|
||||
var fileComplete = false
|
||||
var count = 0
|
||||
while (!fileComplete) {
|
||||
count = httpIn.read(data, 0, 1024)
|
||||
if (count <= 0) {
|
||||
fileComplete = true
|
||||
} else {
|
||||
bufferedOut.write(data, 0, count)
|
||||
}
|
||||
}
|
||||
statusCode = TestDiskDrive.STATE_CODE_STANDBY
|
||||
}
|
||||
catch (e: MalformedURLException) {
|
||||
statusCode = STATE_CODE_NO_SUCH_FILE_EXISTS // MalformedUrl
|
||||
printdbg("Malformed URL: $cnxUrl")
|
||||
}
|
||||
catch (e: IOException) {
|
||||
statusCode = STATE_CODE_SYSTEM_IO_ERROR // IoException
|
||||
printdbg("IOException: $cnxUrl")
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
bufferedOut?.close()
|
||||
messageComposeBuffer.close()
|
||||
httpIn?.close()
|
||||
}
|
||||
catch (e: IOException) {
|
||||
statusCode = STATE_CODE_OPERATION_FAILED // UnableToCloseOutputStream
|
||||
printdbg("Unable to close: $cnxUrl")
|
||||
}
|
||||
finally {
|
||||
printdbg("Data in the URL: ${messageComposeBuffer.toString(VM.CHARSET)}")
|
||||
selfReset()
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
statusCode = TestDiskDrive.STATE_CODE_ILLEGAL_COMMAND
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -7,6 +7,9 @@ import java.io.FileInputStream
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* @param driveNum 0 for COM drive number 1, but the file path will still be zero-based
|
||||
*/
|
||||
class TestDiskDrive(private val vm: VM, private val driveNum: Int, theRootPath: File? = null) : BlockTransferInterface(false, true) {
|
||||
|
||||
companion object {
|
||||
@@ -41,6 +44,17 @@ class TestDiskDrive(private val vm: VM, private val driveNum: Int, theRootPath:
|
||||
errorMsgs[STATE_CODE_NOT_A_DIRECTORY] = "NOT A DIRECTORY"
|
||||
errorMsgs[STATE_CODE_NO_FILE_OPENED] = "NO FILE OPENED"
|
||||
}
|
||||
|
||||
fun composePositiveAns(vararg msg: String): ByteArray {
|
||||
val sb = ArrayList<Byte>()
|
||||
sb.addAll(msg[0].toByteArray().toTypedArray())
|
||||
for (k in 1 until msg.size) {
|
||||
sb.add(UNIT_SEP)
|
||||
sb.addAll(msg[k].toByteArray().toTypedArray())
|
||||
}
|
||||
sb.add(END_OF_SEND_BLOCK)
|
||||
return sb.toByteArray()
|
||||
}
|
||||
}
|
||||
|
||||
private val DBGPRN = true
|
||||
@@ -49,17 +63,6 @@ class TestDiskDrive(private val vm: VM, private val driveNum: Int, theRootPath:
|
||||
if (DBGPRN) println("[TestDiskDrive] $msg")
|
||||
}
|
||||
|
||||
fun composePositiveAns(vararg msg: String): ByteArray {
|
||||
val sb = ArrayList<Byte>()
|
||||
sb.addAll(msg[0].toByteArray().toTypedArray())
|
||||
for (k in 1 until msg.size) {
|
||||
sb.add(UNIT_SEP)
|
||||
sb.addAll(msg[k].toByteArray().toTypedArray())
|
||||
}
|
||||
sb.add(END_OF_SEND_BLOCK)
|
||||
return sb.toByteArray()
|
||||
}
|
||||
|
||||
private val rootPath = theRootPath ?: File("test_assets/test_drive_$driveNum")
|
||||
|
||||
private var fileOpen = false
|
||||
|
||||
Reference in New Issue
Block a user