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 |
|
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. |
|
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 |
|
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 ...
随机推荐
- 通信技术以及5G和AI保障电网安全与网络安全
摘 要:电网安全是电力的基础,随着智能电网的快速发展,越来越多的ICT信息通信技术被应用到电力网络.本文分析了历史上一些重大电网安全与网络安全事故,介绍了电网安全与网络安全.通信技术与电网安全的关系以 ...
- 了解计算机与操作系统发展阶段以及android操作系统的发展史
计算机与操作系统发展阶段 计算机的发展: 第一代(1946-1957年),电子管计算机 第二代(1958-1964年),晶体管计算机 第三代 (1964—1970年),集成电路数字机 第四代 (19 ...
- import com.sun.org.apache.xerces.internal.impl.dv.util.Base64报错
该类不属于JDK标准库范畴,但在JDK中包含了该类,可以直接使用.但是在eclipse中直接使用却找不到该类. 以下是解决方法步骤: Properties-->JavaBulid Path ...
- HIbernate常见异常(转载)
SSH阶段常见的异常系列之一hibernate(15条) 异常一 异常一 异常描述: Sax解析异常:cvc-复杂的类型,发现了以元素maping开头的无效内容,应该是以 ‘{“http://www. ...
- Python-接口自动化(四)
python基础知识(四) (四)处理文件 a.文件的格式主要有txt.html.xml,接下来主要讲的是txt格式的文件处理 对文件进行读写等操作会用到的函数是open(),第一个参数file是指传 ...
- presto 函数中使用子查询
我们已知 在sql中子查询可以配合 in 或者 exists 来使用,但是如何把子查询的结果传给函数呢? 场景: 我们有一个 省份表 数据如下: id province 1 广东 2 ...
- 按住CTRL多选,按住shift连选的实现
<tr class="address" v-for="(counts, index) in counts" :key="index" ...
- mongodb数据迁移到hbase
mongodb数据迁移到hbase 导入包 # encoding: utf-8 ''' @author: zcc @license: (C) Copyright 2013-2017, Node Sup ...
- redis应用--HyperLogLog
如果你负责开发维护一个大型的网站,有一天老板找产品经理要网站每个网页每天的 UV 数据,然后让你来开发这个统计模块,你会如何实现? 如果统计 PV 那非常好办,给每个网页一个独立的 Redis 计数器 ...
- 命令提示符出现-bash-4.1$如何解决
故障原因:用户模式/etc/skel内容丢失 解决方法 复制etc/skel里内容到当前用户 [root@web01 ~]# su - test -bash-4.1$ cp /etc/skel/.ba ...