如何在HTTP请求之后使用“Expect:100-continue”标题流式传输数据正文?

时间:2017-07-12 12:52:22

标签: ios http stream nsurlsession

我的应用程序通过HTTP传输数据。 有效,我这样做:

- 初始化并开始连接:

NSURLSessionConfiguration* defaultConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession* session = [NSURLSession sessionWithConfiguration:defaultConfiguration delegate:self delegateQueue:nil];

NSInputStream* inputStream = [NSInputStream inputStreamWithData:/*data*/];

NSURL* url = /*init from string*/
NSMutableRequest* request : [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST";
request.HTTPBodyStream = inputStream;
[request addValue:@"no-cache" forHTTPHeaderField:@"Cache-Control"];
[request addValue:@"9999999" forHTTPHeaderField:@"Content-Length"];  // Weird isn't it? But this is in server's specs for live streaming.

NSURLSessionUploadTask* uploadTask = [session uploadTaskWithStreamedRequest:request];
[uploadTask resume];

- NSURLSessionTaskDelegate实施的一部分:

-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
    needNewBodyStream:(void (^)(NSInputStream * bodyStream))completionHandler
{
    NSInputStream* inputStream = nil;
    NSOutputStream* outputStream = nil;

    // Bind inputStream and outputStream

    self.inputStream = inputStream;
    self.outputStream = outputStream;
    [self.outputStream setDelegate:/*NSStreamDelegate implementation*/];
    [self.outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [self.outputStream open];

    completionHandler(self.inputStream);
}

- NSStreamDelegate实施的一部分:

- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
{
    switch (eventCode) {
        case NSStreamEventOpenCompleted:
            // to handle...
            break;
        case NSStreamEventHasSpaceAvailable:
            // Producer/Consumer's consumer here, and when data is available:
            [self.outputStream write:/*data*/ maxLength:/*data length*/];                
            break;
        case NSStreamEventErrorOccurred:
            // to handle...
            break;
        case NSStreamEventEndEncountered:
            // to handle...
            break;
        default:
            break;
    }
}

准确地说,我的代码中没有显示的内容:我的连接需要摘要式身份验证。我用来构建请求的URL既不包含登录信息也不包含密码信息。在我发送第一个数据包之后,服务器发送给我401,因此我的应用程序以didReceiveChallenge:方法执行身份验证并使用auth发送请求(此处为Digest)并且数据正在连续发送。一切都像魅力!

现在,我的问题。较新的服务器版本仅在整个请求结束时响应。所以当我发送我的请求和我的数据体(连续发送)服务器忽略数据时,最后给我发送一个401:我无法执行Digest身份验证。我的解决方案是添加标题“Expect:100-continue”......它的工作原理(部分地,继续阅读;))服务器响应我:D ...但是没有数据体传输并且连接已关闭juste在我的应用程序使用Digest auth信息发回请求之后。试想一下,我在示例代码中添加了[request addValue:@"100-continue" forHTTPHeaderField:@"Expect"];needNewBodyStream:根本没有电话。

摘要(Wireshark跟踪):

使用我的应用:

  • 发送没有身份验证的TCP请求标头
  • 接收HTTP 401
  • 使用Digest auth
  • 发送TCP请求标头
  • 关闭连接。

使用curl和相同的标题:

  • 发送没有身份验证的TCP请求标头
  • 接收HTTP 401
  • 使用Digest auth
  • 发送TCP请求标头
  • 连续发送数据......直到结束......
  • 关闭连接。

修改

我的应用程序使用Digest auth发送请求后,服务器没有响应。我认为这是问题,这就是我的连接关闭的原因。但是curl无论如何都要发送数据,我也想做同样的事情。有什么想法吗?

EDIT2:

我应该实现自己的NSURLProtocol并在身份验证后删除Expect: 100-continue标头,以使我的应用能够发送数据正文吗?更好:有没有办法覆盖默认的NSURLProtocol HTTP规范,而不是从头开始重写?

替代方法(重要):

我让我的应用程序像黑客一样工作。首先,我发送一个HTTP(POST)请求,既没有数据也没有凭据来强制didReceiveChallenge:被调用。在此回调中,我使用NSURLCredentialPersistenceForSession参数计算身份验证,以便在会话的所有生命周期中将此信息与会话绑定。然后我取消这个请求并创建一个新的负责流数据(就像我的示例代码一样,在这篇帖子的顶部):Digest auth自动包含在发送的第一个数据包中。

这是一个解决方案,使其在这种情况下起作用并帮助人们。但这不是我想要的解决方案,所以我让这个问题得不到解决,并希望找到一个使用Expect: 100-continue和单个请求的解决方案。谢谢!

0 个答案:

没有答案