XSLT存档  

不及格的程序员-八神

 查看分类:  ASP.NET XML/XSLT JavaScripT   我的MSN空间Blog
posts - 171,  comments - 1171,  trackbacks - 0
公告

'request body stream exhausted' after authentication challenge #661

 Closed
aburgel opened this issue on Nov 26, 2012 · 15 comments

Projects

None yet

12 participants

@aburgel@oiledCode@mattt@mythodeia@jefflab@Noroxs@ghousesgb@JoeSzymanski@jkrzemie@oddysee@msywensky@SimonAddicott
@aburgel
Contributor

When doing a multipart POST, if there's an authentication challenge, I'm getting a 'stream exhausted' error.

After a bit of research, I found that after a challenge, the request will be retried, which means that the NSInputStream needs to be read from the beginning again. The documentation on this is confusing. Comments in NSURLConnection.h imply that the data will be spooled to disk so the connection can be restarted, but the web docs say spooling only happens on OSX:

https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSURLConnectionDelegate_Protocol/Reference/Reference.html#//apple_ref/occ/intfm/NSURLConnectionDelegate/connection:needNewBodyStream

To fix this on iOS, AFNetworking should implement needNewBodyStream. There's a few posts on devforums.apple.com that recommend doing this:

https://devforums.apple.com/message/237388#237388
https://devforums.apple.com/message/344093#344093

 
@oiledCode

Hi, did you find a way to work around this?

 
@mattt
Contributor

Sorry for taking so long to respond to this. This is indeed a tricky problem, as the current implementation of the custom body stream doesn't seem to readily support anything like NSCopying. I'm looking into possible solutions here, but if anyone has any ideas, I'm all ears.

 
@oiledCode

I'm not an expert of AFNetworking, I'm just wondering how ASIHTTPRequest managed this scenario. Probably the architecture is completely different so a comparison it makes no sense.

 
@mythodeia

Experiencing the same issue as well at random times. However there is no authentication in the middle. weird issue and tricky to replicate/catch...

 
@aburgel
Contributor

I added a pull request #718 for this issue.

 
@mattt
Contributor
mattt commented on Jan 6, 2013

Fixed by #718.

 
@mattt mattt closed this on Jan 6, 2013
 
@jefflab

For future readers, what is the best way to confirm that you are getting an authentication challenge (and therefore potentially bumping into this bug)?

From the description above, it sounds like this error only occurs if there is an authentication challenge. Therefore, I set a breakpoint in AFURLConnectionOperation for the method:

- (void)connection:(NSURLConnection *)connection 
didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge

Is it correct to assume that this method will be called if there is an authentication challenge?

My goal is to find a server-side workaround for legacy clients, but I want to make sure this is really the cause of the problem before I go changing authentication settings.

 
@aburgel
Contributor

Yes that's a way to confirm. But this issue can also occur any time you need to restart a connection that uses an input stream, like via a redirect.

 
@Noroxs

I'm sorry to reopen this task again but I have still the "'request body stream exhausted' after authentication challenge" problem. I'm using AFNetworking 2.2.3 and every time when I run into the authentication challenge I get this error.

A possible workaround is to add the username and password into the basic authentication header. This works fine until the password is correct.

But I changed my authentication method to tokens. I'm adding the current token to the header and everything works fine until the token is not valid anymore. When this happens the authentication challenge is called and this will cause this error.

 
@ghousesgb

Facing same issue,
Noroxs, did u get any solutionn

 
@JoeSzymanski

I'm hitting this issues with the latest AFNetworking 2.* when trying to upload an image while handling authentication challenges. Is there any known workaround?
EDIT: In digging into the code, it looks like this functionality never made it from 1.0 to 2.0. Do we need someone to look into proving the copying functionality over?

 
@jkrzemie

+1

 
@oddysee

+1 Also facing this problem.

 
@msywensky

Just ran into the problem as of yesterday. Been working fine for a couple weeks now it occurs every time with a multpartformdata and challenge.

 
@SimonAddicott

Unless my understanding of the general issue is incorrect, i believe this piece of code achieves what others & myself are attempting todo.

AFHTTPRequestOperationManager *httpClient = [[AFHTTPRequestOperationManager alloc] init];
AFHTTPRequestSerializer *requestSerializer = [AFHTTPRequestSerializer serializer];
[requestSerializer setAuthorizationHeaderFieldWithUsername:user password:pass];
httpClient.requestSerializer = requestSerializer;

[httpClient POST:@"http://example.com/upload.php" parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
    [formData appendPartWithFileURL:filePath name:@"audio.m4a" error:nil];
} success:^(AFHTTPRequestOperation *operation, id responseObject) {
    NSLog(@"Success: %@", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"Error: %@", error);
}];

I'm using this to POST an audio file to an API with Basic Authentication. Hope it helps for new people experiencing this issue

 

 


https://devforums.apple.com/message/236248#236248

NSURLConnection request with file input stream results in "request body stream exhausted"

Jun 13, 2010 2:07 AM

Hi,

 

I have the following bit of code:

 

 

- (void)     uploadFileAtURL:(NSURL*)localURL toPath:(NSString*)inPath {

     

     NSURL                    *     remoteURL     =     [ [NSURL URLWithString:inPath relativeToURL:self.baseURL] absoluteURL ] ;

     NSInputStream               *     inputStream     =     [NSInputStream inputStreamWithURL:localURL] ;

     NSMutableURLRequest          *     request          =     [NSMutableURLRequest requestWithURL:remoteURL ] ;

     

     [request setHTTPMethod:@"PUT"] ;

     [request setHTTPBodyStream:inputStream] ;

     

     

     NSLog ( @"Stream: %@" , inputStream ) ;

     NSLog ( @"Local URL: %@" , localURL ) ;

     NSLog ( @"toPath: %@" , inPath ) ;

     NSLog ( @"Remote URL: %@" , remoteURL ) ;

     NSLog ( @"Request: %@" , request ) ;

     

     NSHTTPURLResponse          *     response     =     nil ;
     NSError                    *     error          =     nil ;



     [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error] ;

     
     NSInteger     _statusCode     =     [response statusCode] ;
     NSString*     _statusString     =     [NSHTTPURLResponse localizedStringForStatusCode:_statusCode] ;

     

     NSLog ( @"*** Status: %i - %@" , _statusCode , _statusString ) ;
     NSLog ( @"*** Error: %@" , error ) ;
}

 

 

 

This code, when executed, returns the following output to the console:

 

 

Stream: <__NSCFInputStream: 0x6324830>
Local URL: file://localhost/Temporary/IMG_0013.PNG
toPath: IMG_0013.PNG
Remote URL: http://idisk.me.com/myUserName/IMG_0013.PNG>
Request: <NSMutableURLRequest http://idisk.me.com/myUserName/IMG_0013.PNG>>
*** Status: 0 - server error
*** Error: Error Domain=NSURLErrorDomain Code=-1021 "request body stream exhausted" UserInfo=0x632b750 {NSErrorFailingURLStringKey=http://idisk.me.com/kemsleyc/IMG_0013.PNG>, NSErrorFailingURLKey=http://idisk.me.com/kemsleyc/IMG_0013.PNG>, NSLocalizedDescription=request body stream exhausted, NSUnderlyingError=0x632d600 "request body stream exhausted"}

 

 

I printed out the existence of the stream just to prove that it's there and not nil. The file *does* work when I use the stream for anything else - just not this.

 

I don't understand what it means by "request body stream exhausted" - duh? It hit the end of the file. It's supposed to handle that, right?

 

Any help would be greatly appreciated

CJKApps
 
Add Negative ScoreAdd Positive Score

I should note that I get the exact same error even if I use the following stream:

 

 

NSInputStream     *     inputStream     =     [NSInputStream inputStreamWithData:[@"Testing 123" dataUsingEncoding:NSASCIIStringEncoding]] ;

 

eskimo1
 
Add Negative ScoreAdd Positive Score

 

Ah the perils of using NSURLConnection synchronously (-:

 

The problem here is that NSURLConnection needs a new copy of your body stream.  This can happen, for example, when your first request triggers a redirect which causes NSURLConnection to retry at the new URL.  It's already read and sent your body stream in the first request, and there's no way it can rewind it to use for the second request (rewind is not part of the NSStream semantics), so it needs a new copy of the body stream.

 

In situations like this NSURLConnection sends its delegate the -connection:needNewBodyStream: callback.  The delegate should respond by returning an input stream that's equivalent to the one associated with the original request.

 

The problem here is that you're using NSURLConnection synchronously, and thus you don't get to set a delegate, and thus you can't respond to this callback.  To do so, you will have to switch to async.

 

Share and Enjoy

--

Quinn "The Eskimo!" <eskimo1@apple.com>>

Apple Developer Relations, Developer Technical Support, Core OS/Hardware

 

<http://www.apple.com/developer/>>

<http://www.lists.apple.com/macnetworkprog/>>

<http://www.lists.apple.com/mailman/listinfo/filesystem-dev>>


https://devforums.apple.com/message/344093

NSMutableURLRequest and "request body stream exhausted" error

Dec 5, 2010 6:41 AM

Hi all,

 

i have a problem with http PUT request and request body as stream from file.

 

no matter what the size of the file i get error "NSURLErrorDomain -1021 request body stream exhausted"

 

 

i know i can override this problem by implementing the method:

-(NSInputStream*)connection:(NSURLConnection *)connection needNewBodyStream:(NSURLRequest *)request

 

 

but this approche is not good as it will upload the whole file again, and 40 MB of file turns out to be 80 Mb of data transfer.

 

if i take the same file as NSData and set the request body it works fine.

 

i tried sending the request Async and sync same result in both.

 

here is my code, simple and similer to apples example:

 

NSURL *url = [NSURL URLWithString:[self concatenatedURLWithPath:path]];
NSMutableURLRequest *req = [[NSMutableURLRequest alloc] initWithURL:url];
[req setHTTPMethod:@"PUT"];
[req setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData];
[request setTimeoutInterval:DEFAULT_TIMEOUT];
[req setValue:_contentType forHTTPHeaderField:@"Content-Type"];
NSInputStream *fileStream = [NSInputStream inputStreamWithFileAtPath:_dataStreamLocation];

[req setHTTPBodyStream:fileStream];
_connection = [[NSURLConnection connectionWithRequest:req delegate:self] retain];
 

 

Am i doing something wrong?

Am i missing something?

 

 

Thanks!

 

Eitan

 

 

 

 

 

 

eskimo1
 
Add Negative ScoreAdd Positive Score

 

> if i take the same file as NSData and set the request body it works

> fine.

 

Indeed, but behind the scenes NSURLConnection is still sending the data twice, it's just that it can 'rewind' an NSData body without your help, but it can't do the same for an NSStream body.

 

Does your server support the "100 Continue" mechanism?  If so, this might help you out, at least on recent versions of iOS.  "100 Continue" is designed to solve exactly this sort of problem, where you want to get the HTTP authentication out of the way before you start transmitting a large body.  RFC 2616 has the details.

 

<http://www.ietf.org/rfc/rfc2616.txt>>

 

IIRC "100 Continue" support was very recently added to iOS.  If it works at all, I wouldn't expect it to work prior to iOS 4.  Also, to enable it you will have to set the "Expect: 100-continue" yourself.  Also, you still want to support the new body stream mechanism because, for example, there might be a proxy server between you and the origin server and that proxy might not support "100 Continue".

 

If the server does not support "100 Continue" (or if you need to work on earlier systems), you'll need a workaround.  I usually do this by sending a dummy command that triggers the authentication immediately prior to doing the PUT.  The correct dummy command depends on the server.  In some cases you can get away with a HEAD command, and in other cases I've used a WebDAV MOVE command.

 

Share and Enjoy

--

Quinn "The Eskimo!" <eskimo1@apple.com>>

Apple Developer Relations, Developer Technical Support, Core OS/Hardware

 

<http://www.apple.com/developer/>>

<http://www.lists.apple.com/macnetworkprog/>>

<http://www.lists.apple.com/mailman/listinfo/filesystem-dev>>

 

 

ullrich
 
Add Negative ScoreAdd Positive Score
In response to eskimo1 on Jan 9, 2011 6:30 AM

Hi Quinn,

 

We've got our own OAuth2 library (https://github.com/nxtbgthng/OAuth2Client) and are using a custom NSInputStream subclass to deal with Post requests and support uploading of large data within a multipart form.

We didn't implement the -connection:needNewBodyStream: delegate method of NSURLConnection (which btw isn't documented in the NSURLConnection documentation) and noticed that NSURLConnection in that case sometimes tries to reopen our stream.

 

According to the NSStream documentation of -open a stream can not be reopened once it has been closed. So is the behavior of NSURLConnection intended to be that way?

 

Thanks

-Ullrich

eskimo1
 
Add Negative ScoreAdd Positive Score

 

 

> (which btw isn't documented in the NSURLConnection documentation)

 

This is because it was recently added (for some definition of recently :-).  You should file a bug report about that.

 

<http://developer.apple.com/bugreporter/>>

 

> According to the NSStream documentation of -open a stream can not be

> reopened once it has been closed. So is the behavior of

> NSURLConnection intended to be that way?

 

I don't know.  However, before we too far down this path, how are you handling the scheduling on the run loop problem?  Last I checked there was no supported way to do that on a custom NSStream subclass, and you kinda need it when dealing with NSURLConnection.

 

Share and Enjoy

--

Quinn "The Eskimo!" <eskimo1@apple.com>>

Apple Developer Relations, Developer Technical Support, Core OS/Hardware

 

<http://www.apple.com/developer/>>

<http://www.lists.apple.com/macnetworkprog/>>

<http://www.lists.apple.com/mailman/listinfo/filesystem-dev>>

 

 

 

ullrich
 
Add Negative ScoreAdd Positive Score
In response to eskimo1 on Jan 12, 2011 1:42 AM

All-clear.

I found that it's not NSURLConnection that is reopening the stream, but rather our code that uses the NSURLRequest twice.

 

I don't know how familiar you are with OAuth2 but in a nutshell all requests are sent with a token that has been authorized by the server before.

Those tokens can expire (in our case after 1 hour). This results in a 401 unauthorized with some HTTP header information that an expired token has been used.

There is a mechanism for refreshing that token. What we did is, we remembered the request and if we get a 401 with the information that the token expired we trigger the token refresh and enqueue the request (or rather our own connection object that knows the request and the connection delegate) to be retried. All asynchronous.

 

This works fine as long as you remember to reset the post body stream on the URL request before reusing it. That's what I forgot to do.

 

For large Post or Put requests this might become an issue since in worst case the request has to run twice. We have an expiration time on the token so we can check beforehand if the token is likely to be expired. But there are other situations when the token may expire. For this it would be nice to use HTTP 100 (Continue) status code, but the last thing I heared was, that NSURLConnection not fully supports it. I'm going to play around with this today.

 

Thanks for your time Quinn! :)

 

BTW: subclassing NSStream hasn't been a problem. Our stream is basically an aggregate on sub streams (one for each HTTP Post parameter). On -scheduleInRunLoop I didn't call super but rather called the selector on all sub streams. I hope this is fine, we didn't have any problems with it yet.


 

Fix request stream exhaustion error #718

 Merged
 merged 1 commit into 

Projects

None yet

5 participants

@aburgel@mattt@kevinbarrett@libdx@cjwirth
@aburgel
Contributor

I fixed this by making AFMultipartBodyStream implement NSCopying and then implementing connection:needNewBodyStream: in AFURLConnectionOperation.

This fixes the issue but I haven't done other testing on it.

 
@mattt mattt merged commit ecf6899 into AFNetworking:master on Jan 6, 2013
 
@mattt
Contributor
mattt commented on Jan 6, 2013

Excellent, excellent work. Thanks for not only raising the issue in the first place, but providing the fix as well. I'm extremely happy to be able to include this fix.

You may want to take a look at some of the refactoring I did in 23d3bd5, while merging. Most notably, I replaced the inputData / inputURL properties with a single body property, of type id, to make it act more as a union than a struct, to put it into C terms. As far as I can tell, that works just the same, but let me know if I'm missing something here.

 
@aburgel
Contributor

Your changes look good to me.

The only other thing I'd consider is adding an assert in AFHTTPBodyPart when creating the inputStream to ensure that body is only NSData or NSURL, just to be extra defensive when using an id type.

 
@kevinbarrett

Returning nil from connection:needNewBodyStream: (in my case, when a multipart request receives a redirect to some HTML) seems to cause my request operation to fail without calling the completion block.

 
@aspcartman aspcartman referenced this pull request on Apr 19, 2013
 Closed

Resume upload never resume/restarts #887

@aburgel aburgel deleted the aburgel:stream_exhausted_fix branch on May 7, 2013
@ghost
ghost commented on Aug 8, 2013

I wonder, while both the AFMultipartBodyStream class and the AFHTTPBodyPart class conform to the NSCopying protocol, the body property of AFHTTPBodyPart may not. And in fact, the body property is never copied by copyWithZone:...

And, in case that it is an NSInputStream, once closed or exhausted, it will lead to the very same issue, or does it not?

See

- (id)copyWithZone:(NSZone *)zone {
    AFHTTPBodyPart *bodyPart = [[[self class] allocWithZone:zone] init];

    bodyPart.stringEncoding = self.stringEncoding;
    bodyPart.headers = self.headers;
    bodyPart.bodyContentLength = self.bodyContentLength;
    bodyPart.body = self.body;

    return bodyPart;
}
 
@libdx
libdx commented on Aug 9, 2013

I've also noticed one thing, it looks like whenever you set HTTPBodyStream for NSURLRequest it internally creates new instance of NSInputStream (__NSCFInputStream) class rather that retain object provided by you. So it seems that piece of code in connection:needNewBodyStream under the if is dead and never will be called, because NSInputStream itself doesn't conform to NSCopying protocol.

- (NSInputStream *)connection:(NSURLConnection __unused *)connection
            needNewBodyStream:(NSURLRequest *)request
{
    if ([request.HTTPBodyStream conformsToProtocol:@protocol(NSCopying)]) {
        return [request.HTTPBodyStream copy];
    } else {
        [self cancelConnection];

        return nil;
    }
}

So If I have understood correctly, HTTPBodyStream property will be always instance of__NSCFInputStream class and if so, we need to find new way to recreate stream object here.

 
@ghost
ghost commented on Aug 9, 2013

Just noticed the same behaviour of NSURLRequest/NSMutableURLRequest pointed out by libdx.

It seems that the behaviour of NSMutableURLRequest/NSURLRequest was changed from IOS Version 5.1 to IOS Version 6.1.

With IOS Version 5.1 NSMutableURLRequest/NSURLRequest will return the unaltered instance of the AFMultipartBodyStream.

 
@cjwirth

This bug is fixed in 1.3.2, but unfortunately is re-introduced in 1.3.3 due to issue #1096

 
 
 
@ioriwellings
 
 
 
 
 
 


 

request body stream exhausted #1713
 Closed	prathapkumar opened this issue on Dec 26, 2013 · 6 comments
Assignees

No one assigned
Labels

None yet
Projects

None yet
Milestone

No milestone
Notifications

  Subscribe
You’re not receiving notifications from this thread.
6 participants

@prathapkumar @msencenb @mattt @rcabamo @ghousesgb @BB9z
@prathapkumar
 
prathapkumar commented on Dec 26, 2013
Hi every one ,
i am trying to upload a video file using afnetworking 2.x
it shows an error like "'request body stream exhausted'" after searching a lot in web i got answer like 'upload again when you got this error' after uploading the second time it works , But uploading second time is not good

here is my code

-(void)uploadVideo
{

NSURLCredential nsCredential = [NSURLCredential credentialWithUser:@"*********_" password:@"_*********************" persistence:NSURLCredentialPersistenceForSession];

//    NSURLCredential *nsCredential = [NSURLCredential credentialWithUser:@"*********************" password:@"********************" persistence:NSURLCredentialPersistenceForSession];
//[client setDefaultCredential:nsCredential];
//[manager setCredential:nsCredential];

NSMutableDictionary *params = [[NSMutableDictionary alloc] initWithObjectsAndKeys:@"*****", @"userid", nil];
[params setObject:@"************" forKey:@"accesstoken"];


NSString *url = [ NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"Apple2" ofType:@".mp4"]];

NSData *videoData = [NSData dataWithContentsOfFile:url];

AFHTTPRequestSerializer *serializer = [AFHTTPRequestSerializer serializer];



NSMutableURLRequest *request =
[serializer multipartFormRequestWithMethod:@"POST" URLString:@"http://something.com/ios/upload/video"
                                parameters:params
                 constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
                     [formData appendPartWithFileData:videoData
                                                 name:@"file"
                                             fileName:@"Apple2.mp4"
                                             mimeType:@"video/mp4"];

                     [formData throttleBandwidthWithPacketSize:kAFUploadStream3GSuggestedPacketSize delay:kAFUploadStream3GSuggestedDelay];
                 }];


AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager setCredential:nsCredential];
AFHTTPRequestOperation *operation =
[manager HTTPRequestOperationWithRequest:request
                                 success:^(AFHTTPRequestOperation *operation, id responseObject) {
                                     NSLog(@"Success %@", responseObject);
                                 } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
                                     NSLog(@"Failure %@", error.description);
                                     [self uploadVideo];
                                 }];


[operation setUploadProgressBlock:^(NSUInteger __unused bytesWritten,
                                    long long totalBytesWritten,
                                    long long totalBytesExpectedToWrite) {
    NSLog(@"Wrote %lld/%lld", totalBytesWritten, totalBytesExpectedToWrite);
}];


[operation start];
Can you fix this error

Thank you

Regards
Prathap.M
@prathapkumar
 
prathapkumar commented on Dec 27, 2013
Is there any one ? "help to me" ...........
@msencenb
 
msencenb commented on Jan 1, 2014
I'm currently investigating similar behavior but only when I receive an authentication challenge. Here's a POST, with 'app.server' being a subclass of AFHTTPRequestOperationManager (2.0.1)

    CURAppDelegate *app = [[UIApplication sharedApplication] delegate];
    NSString *stringURL = @"http://example.com/myurlisactuallyhere";

    [app.server POST:stringURL parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData){
        [formData appendPartWithFileData:self.mediaData name:@"image_submission" fileName:@"iOS_image" mimeType:@"image/jpeg"];
        [formData throttleBandwidthWithPacketSize:kAFUploadStream3GSuggestedPacketSize delay:kAFUploadStream3GSuggestedDelay];
    } success:^(AFHTTPRequestOperation *operation, id responseObject){
        callback();
    } failure:^(AFHTTPRequestOperation *operation, NSError *error){
        errorCallback(@"Image failed to upload");
    }];
I hit the authentication challenge, and then if I add this nsurlconnectiondatadelegate method into AFHttpRequestOperation it gets tripped

- (NSInputStream *)connection:(NSURLConnection *)connection needNewBodyStream:(NSURLRequest *)request
{
    DebugLog(@"need new stream");
    return nil;
}
Have to go for the new years... but I will keep digging and looking at older issues
@mattt
 Contributor
mattt commented on Jan 17, 2014
In its current implementation, AFStreamingMultipartFormData cannot be copied and re-opened in the way that needNewBodyStream: mandates. Retuning nil in this method triggers automatic buffer rewind behavior that only takes effect with non-stream request bodies, which makes it a strong default.

Retrying the request is the recommended solution. A future version of AFNetworking may address this issue more directly.
 @mattt mattt closed this on Jan 17, 2014
@rcabamo
 
rcabamo commented on Feb 24, 2014
I have the same problem over Wifi connection and I receive the same error again and again. Any idea?

Thanks 😄
 @nicolamontini nicolamontini referenced this issue on May 5, 2014
 Closed
Request body stream exhausted when posting multipartFormRequest #2044
@ghousesgb
 
ghousesgb commented on Aug 7, 2014
Running into the same issue,
When we have NSURLCredential, might be an authentication challenge.
Any one looking into this updates plz
@BB9z
 Contributor
BB9z commented on Sep 29, 2014
Made a category may be the best way to fix this issue temporarily.

@interface AFURLConnectionOperation (AuthenticationChallengeUploadFix)
@end

@implementation AFURLConnectionOperation (AuthenticationChallengeUploadFix)

- (NSInputStream *)connection:(NSURLConnection __unused *)connection needNewBodyStream:(NSURLRequest *)request {
    if ([request.HTTPBodyStream conformsToProtocol:@protocol(NSCopying)]) {
        return [request.HTTPBodyStream copy];
    }
    return nil;
}

@end
Test on AFNetworking 2.4.1.

Ref: http://stackoverflow.com/questions/20143951/obj-c-afnetworking-2-0-post-request-does-not-work/

  

 


 

Fix request stream exhaustion error #718
 Merged	mattt  merged 1 commit into AFNetworking:master from aburgel:stream_exhausted_fix on Jan 6, 2013
+64 −7 
 Conversation 7   Commits 1   Files changed 2
You can now request reviews from specific people you work with.
Learn more.

 Got it!
Reviewers

No reviews
Assignees

No one assigned
Labels

None yet
Projects

None yet
Milestone

No milestone
Notifications

  Subscribe
You’re not receiving notifications from this thread.
5 participants

@aburgel @mattt @kevinbarrett @libdx @cjwirth
@aburgel
 Contributor
aburgel commented on Jan 6, 2013
I fixed this by making AFMultipartBodyStream implement NSCopying and then implementing connection:needNewBodyStream: in AFURLConnectionOperation.

This fixes the issue but I haven't done other testing on it.
 @aburgel	Fix request stream exhaustion error by copying the original NSInputSt…  …			ecf6899
 @aburgel aburgel referenced this pull request on Jan 6, 2013
 Closed
'request body stream exhausted' after authentication challenge #661
 @mattt mattt merged commit ecf6899 into AFNetworking:master on Jan 6, 2013
@mattt
 Contributor
mattt commented on Jan 6, 2013
Excellent, excellent work. Thanks for not only raising the issue in the first place, but providing the fix as well. I'm extremely happy to be able to include this fix.

You may want to take a look at some of the refactoring I did in 23d3bd5, while merging. Most notably, I replaced the inputData / inputURL properties with a single body property, of type id, to make it act more as a union than a struct, to put it into C terms. As far as I can tell, that works just the same, but let me know if I'm missing something here.
@aburgel
 Contributor
aburgel commented on Jan 6, 2013
Your changes look good to me.

The only other thing I'd consider is adding an assert in AFHTTPBodyPart when creating the inputStream to ensure that body is only NSData or NSURL, just to be extra defensive when using an id type.
@kevinbarrett
 
kevinbarrett commented on Jan 20, 2013
Returning nil from connection:needNewBodyStream: (in my case, when a multipart request receives a redirect to some HTML) seems to cause my request operation to fail without calling the completion block.
 @aspcartman aspcartman referenced this pull request on Apr 19, 2013
 Closed
Resume upload never resume/restarts #887
 @aburgel aburgel deleted the aburgel:stream_exhausted_fix branch on May 7, 2013
 @aburgel aburgel referenced this pull request on May 7, 2013
 Merged
Register error if new bodyStream cannot be created #972
@ghost
 
ghost commented on Aug 8, 2013
I wonder, while both the AFMultipartBodyStream class and the AFHTTPBodyPart class conform to the NSCopying protocol, the body property of AFHTTPBodyPart may not. And in fact, the body property is never copied by copyWithZone:...

And, in case that it is an NSInputStream, once closed or exhausted, it will lead to the very same issue, or does it not?

See

- (id)copyWithZone:(NSZone *)zone {
    AFHTTPBodyPart *bodyPart = [[[self class] allocWithZone:zone] init];

    bodyPart.stringEncoding = self.stringEncoding;
    bodyPart.headers = self.headers;
    bodyPart.bodyContentLength = self.bodyContentLength;
    bodyPart.body = self.body;

    return bodyPart;
}
@libdx
 
libdx commented on Aug 9, 2013
I've also noticed one thing, it looks like whenever you set HTTPBodyStream for NSURLRequest it internally creates new instance of NSInputStream (__NSCFInputStream) class rather that retain object provided by you. So it seems that piece of code in connection:needNewBodyStream under the if is dead and never will be called, because NSInputStream itself doesn't conform to NSCopying protocol.

- (NSInputStream *)connection:(NSURLConnection __unused *)connection
            needNewBodyStream:(NSURLRequest *)request
{
    if ([request.HTTPBodyStream conformsToProtocol:@protocol(NSCopying)]) {
        return [request.HTTPBodyStream copy];
    } else {
        [self cancelConnection];

        return nil;
    }
}
So If I have understood correctly, HTTPBodyStream property will be always instance of __NSCFInputStream class and if so, we need to find new way to recreate stream object here.
@ghost
 
ghost commented on Aug 9, 2013
Just noticed the same behaviour of NSURLRequest/NSMutableURLRequest pointed out by libdx.

It seems that the behaviour of NSMutableURLRequest/NSURLRequest was changed from IOS Version 5.1 to IOS Version 6.1.

With IOS Version 5.1 NSMutableURLRequest/NSURLRequest will return the unaltered instance of the AFMultipartBodyStream.
@cjwirth
 
cjwirth commented on Dec 13, 2013
This bug is fixed in 1.3.2, but unfortunately is re-introduced in 1.3.3 due to issue #1096

  


This bug is fixed in 1.3.2, but unfortunately is re-introduced in 1.3.3 due to issue #1096

posted on 2017-01-12 14:25 不及格的程序员-八神 阅读(...) 评论(...) 编辑 收藏