Enqueueing into NSInputStream?
Asked Answered
G

1

11

I would like to add three "parts" to an NSInputStream: an NSString, an output from another stream and then another NSString. The idea is the following:

The first and last NSStrings represent the beginning and end of a SOAP request while the output from the stream is a result of loading a very large file and encoding it as Base64 string. So, in the end I would have the final NSInputStream hold the whole SOAP request like this:

< soap beginning > < Base64 encoded data > < soap ending >

The reason I want the whole request to be held in NSInputStream is two-fold:

  1. I don't what to load the very large data file into memory
  2. I think that this is the only way to enforce sending the final request in HTTP 1.1 chunks (which I need because otherwise, if the request becomes too big, the server won't accept it). So, I know that doing this:

    NSInputStream *dataStream = ....; 
    [request setHTTPBodyStream:dataStream];
    

ensures that the request will be sent as HTTP 1.1 chunks and not as one huge raw SOAP request.

So, I wonder how this can be achieved - namely, how do I "enqueue" things into an NSInputStream? Can it be even done? Is there an alternative way?

Just for reference, in Java this can be done as follows

 Vector<InputStream> streamVec = new Vector<InputStream>();
 BufferedInputStream fStream = new BufferedInputStream(fileData.getInputStream());
 Base64InputStream b64stream = new Base64InputStream(fStream, true);
 String[] SOAPBody = GenerateSOAPBody(fileInfo).split("CUT_HERE");  
 streamVec.add(new ByteArrayInputStream(SOAPBody[0].getBytes()));
 streamVec.add(b64stream);
 streamVec.add(new ByteArrayInputStream(SOAPBody[1].getBytes()));
 SequenceInputStream seqStream = new SequenceInputStream(streamVec.elements());

because Java has these objects available, but NSStreams in objective-c look like very low level objects and are very hard to work with.

Note: I completely re-wrote the original question as I asked it 2 days ago, since I think the new edit explains more clearly what the problem is. I hope it would help it be easier comprehended and maybe answered

UPDATE 2

Here is what I've been able to achieve so far: Instead of trying to enqueue into a stream, I am using a temp file to first write the < soap beginning >, then I set up an input stream to read from the file in chunks, encode each chunk as a Base64 string and write this to the same temp file, finally, when my stream closes, I write the < soap ending > to the temp file. Then I set up another input stream with the contents of this file which I pass to the NSMutableURLRequest:

        NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url];
        ...
        NSInputStream *dataStream = [NSInputStream inputStreamWithFileAtPath:_tempFilePath];
        [request setHTTPBodyStream:dataStream];

This ensures HTTP 1.1 chunked transfer of the contents of the file. After the connection finishes, delete the temp file.

This seems to work fine but of course this is an annoying work-about. I don't want to be writing to a temp file when it all could have been handled by streams (ideally.) If anybody still has better suggestions, let me know :)

UPDATE 3

OK, another update is in order. While my writing to file seems to work, I am now hitting an unexpected issue with some of my requests failing to upload to the server. Specifically, everything is going according to the plan, I am reading the contents of the temp file into a stream and set HTTP body of my request to be this stream and it starts transmitting the HTTP 1.1 chunks as I want it to - but for some reason some packets get dropped and the final request - this is my guess - gets malformed and thus fails. I think the issue of dropped packets is random, because I observe it on larger requests - that is, the issue just has more chance to show up - while my smaller requests usually go thru just fine. This is of course a separate issue from the original in this question. If anybody has a good idea what might be causing this, I asked about the problem here: Packets dropped during chunked HTTP 1.1 request sent by NSURLConnection

Gypsy answered 5/3, 2013 at 23:28 Comment(0)
O
0

Your solution is an ok option, but you can do it with a stream. It means subclassing NSInputStream, and that isn't trivial because there are a bunch of methods you need to implement.

Basically your subclass would initially return the header bytes, then it would return bytes from the 'internal' stream to the file content, then when that's used up it returns the footer bytes. It means maintaining a record of how big the header and footer are and how much has been processed so far, but that isn't a big issue.

There's an example of creating a subclass here which shows the tricky hidden methods you need to implement to get the stream subclass to work properly without throwing exceptions.

Overland answered 5/7, 2016 at 21:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.