Search code examples
swifthttp2

Unable to do streamed request with bounded stream pair


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

  1. Record sound
  2. Prepare the request(This time, the needNewBodyStream method will be called)
  3. Prepare the body data
  4. Pass in the input stream(initialized from the body data)to the completion handler

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:

  1. Record sound
  2. Prepare the body data
  3. Get stream pair
  4. Prepare the request(This time, the needNewBodyStream method will be called)
  5. Pass in the input stream to the completion handler
  6. Write data with output stream

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?


Solution

  • 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.