Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

fixed bug that form data request can't add custom Content-Type to header #356

Open
wants to merge 4 commits into from

5 participants

@OpenFibers

hi pokeb:
Custom Content-Type was overwriten in buildURLEncodedPostBody or buildMultipartFormDataPostBody. This PR fixed this issue.

if it is a feature, ignore this PR :-D

@zhangdan

good!

In several broken network, network operation, the interface will be stuck, caused by abnormal maybe methods, [ASIHTTPRequest findProxyCredentials], [ASIHTTPRequest markAsFinished]. For me it is important to do this can be resolved as soon as possible...

Hello @lixiasandy , does the original repo of ASIHTTPRequest works for this situation?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
View
4 Classes/ASIDataCompressor.m
@@ -161,7 +161,7 @@ + (BOOL)compressDataFromFile:(NSString *)sourcePath toFile:(NSString *)destinati
readLength = [inputStream read:inputData maxLength:DATA_CHUNK_SIZE];
// Make sure nothing went wrong
- if ([inputStream streamStatus] == NSStreamEventErrorOccurred) {
+ if ([inputStream streamStatus] == NSStreamStatusError) {
if (err) {
*err = [NSError errorWithDomain:NetworkRequestErrorDomain code:ASICompressionError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Compression of %@ failed because we were unable to read from the source data file",sourcePath],NSLocalizedDescriptionKey,[inputStream streamError],NSUnderlyingErrorKey,nil]];
}
@@ -187,7 +187,7 @@ + (BOOL)compressDataFromFile:(NSString *)sourcePath toFile:(NSString *)destinati
[outputStream write:(const uint8_t *)[outputData bytes] maxLength:[outputData length]];
// Make sure nothing went wrong
- if ([inputStream streamStatus] == NSStreamEventErrorOccurred) {
+ if ([inputStream streamStatus] == NSStreamStatusError) {
if (err) {
*err = [NSError errorWithDomain:NetworkRequestErrorDomain code:ASICompressionError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Compression of %@ failed because we were unable to write to the destination data file at %@",sourcePath,destinationPath],NSLocalizedDescriptionKey,[outputStream streamError],NSUnderlyingErrorKey,nil]];
}
View
4 Classes/ASIDataDecompressor.m
@@ -158,7 +158,7 @@ + (BOOL)uncompressDataFromFile:(NSString *)sourcePath toFile:(NSString *)destina
readLength = [inputStream read:inputData maxLength:DATA_CHUNK_SIZE];
// Make sure nothing went wrong
- if ([inputStream streamStatus] == NSStreamEventErrorOccurred) {
+ if ([inputStream streamStatus] == NSStreamStatusError) {
if (err) {
*err = [NSError errorWithDomain:NetworkRequestErrorDomain code:ASICompressionError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Decompression of %@ failed because we were unable to read from the source data file",sourcePath],NSLocalizedDescriptionKey,[inputStream streamError],NSUnderlyingErrorKey,nil]];
}
@@ -184,7 +184,7 @@ + (BOOL)uncompressDataFromFile:(NSString *)sourcePath toFile:(NSString *)destina
[outputStream write:(Bytef*)[outputData bytes] maxLength:[outputData length]];
// Make sure nothing went wrong
- if ([inputStream streamStatus] == NSStreamEventErrorOccurred) {
+ if ([inputStream streamStatus] == NSStreamStatusError) {
if (err) {
*err = [NSError errorWithDomain:NetworkRequestErrorDomain code:ASICompressionError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Decompression of %@ failed because we were unable to write to the destination data file at %@",sourcePath,destinationPath],NSLocalizedDescriptionKey,[outputStream streamError],NSUnderlyingErrorKey,nil]];
}
View
25 Classes/ASIFormDataRequest.m
@@ -213,6 +213,19 @@ - (void)buildPostBody
#endif
}
+- (BOOL)isHeaderContainsContentType
+{
+ BOOL headerContainsContentType = NO;
+ for (id key in self.requestHeaders.allKeys)
+ {
+ if ([key isKindOfClass:[NSString class]] && [key isEqualToString:@"Content-Type"])
+ {
+ headerContainsContentType = YES;
+ break;
+ }
+ }
+ return headerContainsContentType;
+}
- (void)buildMultipartFormDataPostBody
{
@@ -228,8 +241,11 @@ - (void)buildMultipartFormDataPostBody
CFRelease(uuid);
NSString *stringBoundary = [NSString stringWithFormat:@"0xKhTmLbOuNdArY-%@",uuidString];
- [self addRequestHeader:@"Content-Type" value:[NSString stringWithFormat:@"multipart/form-data; charset=%@; boundary=%@", charset, stringBoundary]];
-
+ if (![self isHeaderContainsContentType])
+ {
+ [self addRequestHeader:@"Content-Type" value:[NSString stringWithFormat:@"multipart/form-data; charset=%@; boundary=%@", charset, stringBoundary]];
+ }
+
[self appendPostString:[NSString stringWithFormat:@"--%@\r\n",stringBoundary]];
// Adds post data
@@ -288,7 +304,10 @@ - (void)buildURLEncodedPostBody
NSString *charset = (NSString *)CFStringConvertEncodingToIANACharSetName(CFStringConvertNSStringEncodingToEncoding([self stringEncoding]));
- [self addRequestHeader:@"Content-Type" value:[NSString stringWithFormat:@"application/x-www-form-urlencoded; charset=%@",charset]];
+ if (![self isHeaderContainsContentType])
+ {
+ [self addRequestHeader:@"Content-Type" value:[NSString stringWithFormat:@"application/x-www-form-urlencoded; charset=%@",charset]];
+ }
NSUInteger i=0;
View
35 Classes/ASIHTTPRequest.m
@@ -1173,8 +1173,10 @@ - (void)startRequest
// Are we gzipping the request body?
if ([self compressedPostBodyFilePath] && [fileManager fileExistsAtPath:[self compressedPostBodyFilePath]]) {
[self setPostBodyReadStream:[ASIInputStream inputStreamWithFileAtPath:[self compressedPostBodyFilePath] request:self]];
+// [self setPostBodyReadStream:[NSInputStream inputStreamWithFileAtPath:[self compressedPostBodyFilePath]]];
} else {
[self setPostBodyReadStream:[ASIInputStream inputStreamWithFileAtPath:[self postBodyFilePath] request:self]];
+// [self setPostBodyReadStream:[NSInputStream inputStreamWithFileAtPath:[self postBodyFilePath]]];
}
[self setReadStream:[NSMakeCollectable(CFReadStreamCreateForStreamedHTTPRequest(kCFAllocatorDefault, request,(CFReadStreamRef)[self postBodyReadStream])) autorelease]];
} else {
@@ -1183,8 +1185,10 @@ - (void)startRequest
if ([self postBody] && [[self postBody] length] > 0) {
if ([self shouldCompressRequestBody] && [self compressedPostBody]) {
[self setPostBodyReadStream:[ASIInputStream inputStreamWithData:[self compressedPostBody] request:self]];
+// [self setPostBodyReadStream:[NSInputStream inputStreamWithData:[self compressedPostBody]]];
} else if ([self postBody]) {
[self setPostBodyReadStream:[ASIInputStream inputStreamWithData:[self postBody] request:self]];
+// [self setPostBodyReadStream:[NSInputStream inputStreamWithData:[self postBody]]];
}
[self setReadStream:[NSMakeCollectable(CFReadStreamCreateForStreamedHTTPRequest(kCFAllocatorDefault, request,(CFReadStreamRef)[self postBodyReadStream])) autorelease]];
@@ -1212,17 +1216,30 @@ - (void)startRequest
// see: http://iphonedevelopment.blogspot.com/2010/05/nsstream-tcp-and-ssl.html
NSDictionary *sslProperties = [[NSDictionary alloc] initWithObjectsAndKeys:
- [NSNumber numberWithBool:YES], kCFStreamSSLAllowsExpiredCertificates,
- [NSNumber numberWithBool:YES], kCFStreamSSLAllowsAnyRoot,
- [NSNumber numberWithBool:NO], kCFStreamSSLValidatesCertificateChain,
- kCFNull,kCFStreamSSLPeerName,
- nil];
+ [NSNumber numberWithBool:YES], kCFStreamSSLAllowsExpiredCertificates,
+ [NSNumber numberWithBool:YES], kCFStreamSSLAllowsAnyRoot,
+ [NSNumber numberWithBool:NO], kCFStreamSSLValidatesCertificateChain,
+ kCFNull,kCFStreamSSLPeerName,
+ @"kCFStreamSocketSecurityLevelTLSv1_0SSLv3", kCFStreamSSLLevel,
+ nil];
- CFReadStreamSetProperty((CFReadStreamRef)[self readStream],
- kCFStreamPropertySSLSettings,
+ CFReadStreamSetProperty((CFReadStreamRef)[self readStream],
+ kCFStreamPropertySSLSettings,
(CFTypeRef)sslProperties);
[sslProperties release];
- }
+ } else {
+ NSDictionary *sslProperties = [[NSDictionary alloc] initWithObjectsAndKeys:
+ [NSNumber numberWithBool:NO], kCFStreamSSLAllowsExpiredCertificates,
+ [NSNumber numberWithBool:NO], kCFStreamSSLAllowsAnyRoot,
+ [NSNumber numberWithBool:YES], kCFStreamSSLValidatesCertificateChain,
+ @"kCFStreamSocketSecurityLevelTLSv1_0SSLv3", kCFStreamSSLLevel,
+ nil];
+
+ CFReadStreamSetProperty((CFReadStreamRef)[self readStream],
+ kCFStreamPropertySSLSettings,
+ (CFTypeRef)sslProperties);
+ [sslProperties release];
+ }
// Tell CFNetwork to use a client certificate
if (clientCertificateIdentity) {
@@ -4866,7 +4883,7 @@ + (NSDate *)expiryDateForRequest:(ASIHTTPRequest *)request maxAge:(NSTimeInterva
// RFC 2612 says max-age must override any Expires header
if (maxAge) {
- return [[NSDate date] addTimeInterval:maxAge];
+ return [[NSDate date] dateByAddingTimeInterval:maxAge];
} else {
NSString *expires = [responseHeaders objectForKey:@"Expires"];
if (expires) {
View
8 Classes/ASIInputStream.h
@@ -14,13 +14,11 @@
// Subclassing NSInputStream seems to be tricky, and may involve overriding undocumented methods, so we'll cheat instead.
// It is used by ASIHTTPRequest whenever we have a request body, and handles measuring and throttling the bandwidth used for uploading
-@interface ASIInputStream : NSObject {
- NSInputStream *stream;
- ASIHTTPRequest *request;
-}
+@interface ASIInputStream : NSObject<NSStreamDelegate>
+
+ (id)inputStreamWithFileAtPath:(NSString *)path request:(ASIHTTPRequest *)request;
+ (id)inputStreamWithData:(NSData *)data request:(ASIHTTPRequest *)request;
+- (id)initWithInputStream:(NSInputStream *)stream;
-@property (retain, nonatomic) NSInputStream *stream;
@property (assign, nonatomic) ASIHTTPRequest *request;
@end
View
218 Classes/ASIInputStream.m
@@ -8,11 +8,21 @@
#import "ASIInputStream.h"
#import "ASIHTTPRequest.h"
+#import <objc/runtime.h>
// Used to ensure only one request can read data at once
static NSLock *readLock = nil;
@implementation ASIInputStream
+{
+ NSInputStream *stream;
+ id<NSStreamDelegate> delegate;
+
+ CFReadStreamClientCallBack copiedCallback;
+ CFStreamClientContext copiedContext;
+ CFOptionFlags requestedEvents;
+ ASIHTTPRequest *request;
+}
+ (void)initialize
{
@@ -23,53 +33,42 @@ + (void)initialize
+ (id)inputStreamWithFileAtPath:(NSString *)path request:(ASIHTTPRequest *)theRequest
{
- ASIInputStream *theStream = [[[self alloc] init] autorelease];
+ ASIInputStream *theStream = [[[ASIInputStream alloc] initWithInputStream:[NSInputStream inputStreamWithFileAtPath:path]] autorelease];
[theStream setRequest:theRequest];
- [theStream setStream:[NSInputStream inputStreamWithFileAtPath:path]];
return theStream;
}
+ (id)inputStreamWithData:(NSData *)data request:(ASIHTTPRequest *)theRequest
{
- ASIInputStream *theStream = [[[self alloc] init] autorelease];
+ ASIInputStream *theStream = [[[ASIInputStream alloc] initWithInputStream:[NSInputStream inputStreamWithData:data]] autorelease];
[theStream setRequest:theRequest];
- [theStream setStream:[NSInputStream inputStreamWithData:data]];
return theStream;
}
-- (void)dealloc
+#pragma mark - Object lifecycle
+
+- (id)initWithInputStream:(NSInputStream *)aStream
{
- [stream release];
- [super dealloc];
+ self = [super init];
+ if (self) {
+ // Initialization code here.
+ stream = [aStream retain];
+ [stream setDelegate:self];
+
+ [self setDelegate:self];
+ }
+
+ return self;
}
-// Called when CFNetwork wants to read more of our request body
-// When throttling is on, we ask ASIHTTPRequest for the maximum amount of data we can read
-- (NSInteger)read:(uint8_t *)buffer maxLength:(NSUInteger)len
+- (void)dealloc
{
- [readLock lock];
- unsigned long toRead = len;
- if ([ASIHTTPRequest isBandwidthThrottled]) {
- toRead = [ASIHTTPRequest maxUploadReadLength];
- if (toRead > len) {
- toRead = len;
- } else if (toRead == 0) {
- toRead = 1;
- }
- [request performThrottling];
- }
- [readLock unlock];
- NSInteger rv = [stream read:buffer maxLength:toRead];
- if (rv > 0)
- [ASIHTTPRequest incrementBandwidthUsedInLastSecond:rv];
- return rv;
+ [stream release];
+ [super dealloc];
}
-/*
- * Implement NSInputStream mandatory methods to make sure they are implemented
- * (necessary for MacRuby for example) and avoid the overhead of method
- * forwarding for these common methods.
- */
+#pragma mark - NSStream subclass methods
+
- (void)open
{
[stream open];
@@ -80,14 +79,19 @@ - (void)close
[stream close];
}
-- (id)delegate
+- (id <NSStreamDelegate> )delegate
{
- return [stream delegate];
+ return delegate;
}
-- (void)setDelegate:(id)delegate
+- (void)setDelegate:(id<NSStreamDelegate>)aDelegate
{
- [stream setDelegate:delegate];
+ if (aDelegate == nil) {
+ delegate = self;
+ }
+ else {
+ delegate = aDelegate;
+ }
}
- (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode
@@ -120,6 +124,149 @@ - (NSError *)streamError
return [stream streamError];
}
+#pragma mark - NSInputStream subclass methods
+
+// Called when CFNetwork wants to read more of our request body
+// When throttling is on, we ask ASIHTTPRequest for the maximum amount of data we can read
+- (NSInteger)read:(uint8_t *)buffer maxLength:(NSUInteger)len
+{
+ [readLock lock];
+ unsigned long toRead = len;
+ if ([ASIHTTPRequest isBandwidthThrottled]) {
+ toRead = [ASIHTTPRequest maxUploadReadLength];
+ if (toRead > len) {
+ toRead = len;
+ } else if (toRead == 0) {
+ toRead = 1;
+ }
+ [request performThrottling];
+ }
+ [readLock unlock];
+ NSInteger rv = [stream read:buffer maxLength:toRead];
+ if (rv > 0)
+ [ASIHTTPRequest incrementBandwidthUsedInLastSecond:rv];
+ return rv;
+}
+
+
+- (BOOL)getBuffer:(uint8_t **)buffer length:(NSUInteger *)len
+{
+ // We cannot implement our character-counting in O(1) time,
+ // so we return NO as indicated in the NSInputStream
+ // documentation.
+ return NO;
+}
+
+- (BOOL)hasBytesAvailable
+{
+ return [stream hasBytesAvailable];
+}
+
+#pragma mark - Undocumented CFReadStream bridged methods
+
++ (BOOL)resolveInstanceMethod:(SEL) selector
+{
+ NSString *name = NSStringFromSelector(selector);
+
+ if ([name hasPrefix:@"_"]){
+ name = [name substringFromIndex:1];
+ SEL aSelector = NSSelectorFromString(name);
+ Method method = class_getInstanceMethod(self, aSelector);
+
+ if (method)
+ {
+ class_addMethod(self,
+ selector,
+ method_getImplementation(method),
+ method_getTypeEncoding(method));
+ return YES;
+ }
+ }
+ return [super resolveInstanceMethod:selector];
+}
+
+- (void)scheduleInCFRunLoop:(CFRunLoopRef)aRunLoop forMode:(CFStringRef)aMode
+{
+ CFReadStreamScheduleWithRunLoop((CFReadStreamRef)stream, aRunLoop, aMode);
+}
+
+- (BOOL)setCFClientFlags:(CFOptionFlags)inFlags callback:(CFReadStreamClientCallBack)inCallback context:(CFStreamClientContext *)inContext
+{
+ if (inCallback != NULL) {
+ requestedEvents = inFlags;
+ copiedCallback = inCallback;
+ memcpy(&copiedContext, inContext, sizeof(CFStreamClientContext));
+
+ if (copiedContext.info && copiedContext.retain) {
+ copiedContext.retain(copiedContext.info);
+ }
+ }
+ else {
+ requestedEvents = kCFStreamEventNone;
+ copiedCallback = NULL;
+ if (copiedContext.info && copiedContext.release) {
+ copiedContext.release(copiedContext.info);
+ }
+
+ memset(&copiedContext, 0, sizeof(CFStreamClientContext));
+ }
+
+ return YES;
+}
+
+- (void)unscheduleFromCFRunLoop:(CFRunLoopRef)aRunLoop forMode:(CFStringRef)aMode
+{
+ CFReadStreamUnscheduleFromRunLoop((CFReadStreamRef)stream, aRunLoop, aMode);
+}
+
+#pragma mark - NSStreamDelegate methods
+
+- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
+{
+ assert(aStream == stream);
+
+ switch (eventCode) {
+ case NSStreamEventOpenCompleted:
+ if (requestedEvents & kCFStreamEventOpenCompleted) {
+ copiedCallback((CFReadStreamRef)self,
+ kCFStreamEventOpenCompleted,
+ copiedContext.info);
+ }
+ break;
+
+ case NSStreamEventHasBytesAvailable:
+ if (requestedEvents & kCFStreamEventHasBytesAvailable) {
+ copiedCallback((CFReadStreamRef)self,
+ kCFStreamEventHasBytesAvailable,
+ copiedContext.info);
+ }
+ break;
+
+ case NSStreamEventErrorOccurred:
+ if (requestedEvents & kCFStreamEventErrorOccurred) {
+ copiedCallback((CFReadStreamRef)self,
+ kCFStreamEventErrorOccurred,
+ copiedContext.info);
+ }
+ break;
+
+ case NSStreamEventEndEncountered:
+ if (requestedEvents & kCFStreamEventEndEncountered) {
+ copiedCallback((CFReadStreamRef)self,
+ kCFStreamEventEndEncountered,
+ copiedContext.info);
+ }
+ break;
+
+ case NSStreamEventHasSpaceAvailable:
+ // This doesn't make sense for a read stream
+ break;
+
+ default:
+ break;
+ }
+}
+
// If we get asked to perform a method we don't have (probably internal ones),
// we'll just forward the message to our stream
@@ -133,6 +280,5 @@ - (void)forwardInvocation:(NSInvocation *)anInvocation
[anInvocation invokeWithTarget:stream];
}
-@synthesize stream;
@synthesize request;
@end
Something went wrong with that request. Please try again.