http error: "request body stream exhausted"
'request body stream exhausted' after authentication challenge #661
Assignees
No one assigned
Labels
Projects
None yet
Milestone
No milestone
Notifications
Subscribe
You’re not receiving notifications from this thread.
12 participants
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: 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 |
Closed
Request body stream exhausted - when using multipleFormRequestWithMethod #665
Hi, did you find a way to work around this? |
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 |
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. |
Experiencing the same issue as well at random times. However there is no authentication in the middle. weird issue and tricky to replicate/catch... |
I added a pull request #718 for this issue. |
Fixed by #718. |
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
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. |
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. |
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. |
Facing same issue, |
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? |
+1 |
+1 Also facing this problem. |
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. |
Unless my understanding of the general issue is incorrect, i believe this piece of code achieves what others & myself are attempting todo.
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
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
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]] ;
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
> 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>>
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
> (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>>
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
Reviewers
No reviews
Assignees
No one assigned
Labels
Projects
None yet
Milestone
No milestone
Notifications
Subscribe
You’re not receiving notifications from this thread.
5 participants
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. |
Closed
'request body stream exhausted' after authentication challenge #661
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 |
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 |
Returning |
Closed
Resume upload never resume/restarts #887
Merged
Register error if new bodyStream cannot be created #972
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 { |
I've also noticed one thing, it looks like whenever you set - (NSInputStream *)connection:(NSURLConnection __unused *)connection So If I have understood correctly, |
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. |
This bug is fixed in 1.3.2, but unfortunately is re-introduced in 1.3.3 due to issue #1096 |
Write
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? Thankshttp error: "request body stream exhausted"的更多相关文章
- 上传文件出错:org.apache.commons.fileupload.FileUploadBase$IOFileUploadException: Processing of multipart/form-data request failed. Stream ended unexpectedly
最近做一个web项目中有上传文件的功能,已经写出并在本地和部署到服务器上测试了好几个文件上传都没问题(我用的是tomcat).后来又上传了一个700多K的文件(前边的都是不足600K的,并且这个wor ...
- git clone时出现 error:inflate:data stream error(incorrect data check)
git clone时出现 error:inflate:data stream error(incorrect data check) fatal:serrious inflate inconsiste ...
- axios请求报Uncaught (in promise) Error: Request failed with status code 404
使用axios处理请求时,出现的问题解决 当url是远程接口链接时,会报404的错误: Uncaught (in promise) Error: Request failed with status ...
- EWS code return Error : Request failed. The remote server returned an error: (403) Forbidden OR (401) Unauthorized
Following is my code. ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2007_SP1 ...
- nodejs Error: request entity too large解决方案
错误如图: 解决方案: app.js添加 var bodyParser = require('body-parser'); app.use(bodyParser.json({limit: '50mb' ...
- Uncaught (in promise) Error: Request failed with status code 500解决方案
今天又学到一种修改bug的方法 : let newpwd = crypto.createHash('md5').update(req.body.upwd).digest('hex'); 在点击按钮加 ...
- iOS编程中throttle那些事
不知道大家对throttle这个单词是否看着眼熟,还是说对这个计算机基础概念有很清晰的了解了.今天就来聊聊和throttle相关的一些技术场景. 定义 我经常有一种感觉,对于英语这门语言的语感,会影响 ...
- 获取报告 Stream转string,利用字符串分割转换成DataTable
protected void Button1_Click(object sender, EventArgs e) { MemoryStream stream = new MemoryStream(); ...
- How to fix “HTTP Status Code 505 – HTTP Version Not Supported” error?--转
http://dotnetstock.com/technical/http-status-code-505-http-version-not-supported/ The reason for the ...
随机推荐
- PHP类的反射和依赖注入
/** * Class Point */ class Point { public $x; public $y; /** * Point constructor. * @param int $x ho ...
- C# Winform 自适应
参考:http://yefenme.blog.163.com/blog/static/13069770420132283644288/ 自适应首先考虑的是AutoScaleMode属性设置,其中=DP ...
- ajax 提交数组 泛型集合
ajax 提交数组 泛型集合 发表于2015/12/31 14:26:29 5117人阅读 分类: mvc asp.net webapi ORM 转载:http://blog.csdn.net/li ...
- Fortran程序调试中的“吐核”错误
在CentOS7上安装了PGI编译器,但是调试过程中遇到的“段错误(吐核)”一直让人很头疼. 通常采用在程序中增加屏幕输出代码的方式来追踪和定位出错的变量,比如下面这个样例程序就在第16行和第18行增 ...
- FormData上传文件(input file)
<div> <input type="file" name="FileUpload" id="FileUpload" va ...
- Android 音视频深入 十八 FFmpeg播放视频,有声音(附源码下载)
项目地址https://github.com/979451341/AudioVideoStudyCodeTwo/tree/master/FFmpegv%E6%92%AD%E6%94%BE%E8%A7% ...
- Rhino学习教程——1.2
实战——创建个性化工具栏: 因为我们的制图习惯不同,所以可以吧自己常用的工具放在一起.我上次说的自定义界面就是这个和调整工具栏位置. 1.打开常用>设置 2.选择工具列>编辑>新增工 ...
- python之路-----MySql操作三
mysql 概述 一.主要内容: 视图 create view name (select * from user where id>5); 触发器 函数 存储过程 索引 二.各模块详细说明 1. ...
- ansible 剧本
ansible的管理与剧本 首先我们安装一个ansible. 在7版本,直接用yum安装就可以 yum -y install ansible 然后清空ansible的配置文件,在里面写入自己需要管 ...
- 用with打开文件
rep_word = 'The piece is gone, left the puzzle undone' # \ 换行,跟shell一样 with open('nothing', 'r', enc ...