I am trying to make the streamed request work by using stream pairs. For test purpose, I prerecorded a sound and use a stream pair and write the contents with the output stream and pass the input stream to the completion handler of this method
func urlSession(_ session: URLSession, task: URLSessionTask, needNewBodyStream completionHandler: @escaping (InputStream?) -> Void)
The request is successful with no stream pairs. Just prepare the data first and initialize an Input Stream with it and pass it to the completion handler of the method above. Here's the steps that worked for me
Code:
func urlSession(_ session: URLSession, task: URLSessionTask, needNewBodyStream completionHandler: @escaping (InputStream?) -> Void)
{
NSLog("need new body stream")
var bodyData = Data()
bodyData.append(getBoundaryBeginData())
bodyData.append(getJsonMessageHeaderData())
bodyData.append(AVSRecognizeRequest().jsonData())
bodyData.append("\r\n".data(using: String.Encoding.utf8)!)
bodyData.append(getBoundaryBeginData())
bodyData.append("Content-Disposition: form-data; name=\"audio\"\r\n".data(using: String.Encoding.utf8)!)
bodyData.append("Content-Type: application/octet-stream\r\n\r\n".data(using: String.Encoding.utf8)!)
try! bodyData.append(AudioManager.shared.getRecording()!)
bodyData.append("\r\n".data(using: String.Encoding.utf8)!)
bodyData.append(getBoundaryEndData())
NSLog("Total data length is \(bodyData.count)")
completionHandler(InputStream.init(data: bodyData))
}
However, if I pass the input stream (from the stream pair) to the completion handler then write the contents after that, the request fails with a 400 error. The ff. are the steps that fails:
When I check the delegate method func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64)
the total bytes sent was only 8192. But my total data length was about 80k.
Code:
public func stopRecognizing()
{
//*** 1. RECORD A SOUND
AudioManager.shared.stopRecording()
//*** 2. PREPARE BODY DATA
var bodyData = Data()
bodyData.append(getBoundaryBeginData())
bodyData.append(getJsonMessageHeaderData())
bodyData.append(AVSRecognizeRequest().jsonData())
bodyData.append("\r\n".data(using: String.Encoding.utf8)!)
bodyData.append(getBoundaryBeginData())
bodyData.append("Content-Disposition: form-data; name=\"audio\"\r\n".data(using: String.Encoding.utf8)!)
bodyData.append("Content-Type: application/octet-stream\r\n\r\n".data(using: String.Encoding.utf8)!)
try! bodyData.append(AudioManager.shared.getRecording()!)
bodyData.append("\r\n".data(using: String.Encoding.utf8)!)
bodyData.append(getBoundaryEndData())
//*** 3. GET STREAM PAIR
Stream.getBoundStreams(withBufferSize: bodyData.count, inputStream: &inputStream, outputStream: &outputStream)
//*** 4. PREPARES THE REQUEST (AFTER THIS IS CALLED, the needNewBodyStream delegate method will be called)
sendRecognizeRequest()
//*** 6. WRITE THE DATA WITH OUTPUT STREAM
outputStream?.open()
bodyData.withUnsafeBytes(
{(bytes: UnsafePointer<UInt8>) -> Void in
outputStream?.write(bytes, maxLength: bodyData.count)
})
}
func urlSession(_ session: URLSession, task: URLSessionTask, needNewBodyStream completionHandler: @escaping (InputStream?) -> Void)
{
NSLog("need new body stream")
//*** 5. PASS IN THE INPUT STREAM
completionHandler(inputStream)
}
I tested a stream pair my self by running a thread that continuously reads from the input stream and on the other hand, an output stream that writes data continuously. I can clearly that the data written to other stream can be read from the input stream given that they are already bound.
So, why didn't my streamed request work?
I found the problem. I have to close the output stream after I am done writing. When I close the output stream, the total sent bytes from this callback now gives me the total data number func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64)
and not 8192.