Skip to content

Add 5 missing attributes to NSURLRequest #290

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 126 additions & 50 deletions Foundation/NSURLRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -164,10 +164,6 @@ public enum NSURLRequestNetworkServiceType : UInt {
*/
public class NSURLRequest : NSObject, NSSecureCoding, NSCopying, NSMutableCopying {

private var _URL : NSURL?
private var _mainDocumentURL: NSURL?
private var _httpHeaderFields: [String: String]?

public override func copy() -> AnyObject {
return copyWithZone(nil)
}
Expand Down Expand Up @@ -235,15 +231,38 @@ public class NSURLRequest : NSObject, NSSecureCoding, NSCopying, NSMutableCopyin
*/
public convenience init(URL: NSURL) {
self.init()
_URL = URL
self.URL = URL
}

/*!
@method URL
@abstract Returns the URL of the receiver.
@result The URL of the receiver.
*/
/*@NSCopying */public var URL: NSURL? { return _URL }
/*@NSCopying */public private(set) var URL: NSURL?

/// The cache policy of the receiver.
public private(set) var cachePolicy: NSURLRequestCachePolicy = .UseProtocolCachePolicy

/// The timeout interval.
///
/// The timeout interval specifies the limit on the idle
/// interval allotted to a request in the process of loading. The *idle
/// interval* is defined as the period of time that has passed since the
/// last instance of load activity occurred for a request that is in the
/// process of loading. Hence, when an instance of load activity occurs
/// (e.g. bytes are received from the network for a request), the idle
/// interval for a request is reset to 0. If the idle interval ever
/// becomes greater than or equal to the timeout interval, the request
/// is considered to have timed out. This timeout interval is measured
/// in seconds.
public private(set) var timeoutInterval: NSTimeInterval = 60

/// The `NSURLRequestNetworkServiceType` associated with this request.
///
/// This method is used to provide the network layers with a hint as to the
/// purpose of the request. Most clients should not need to use this method.
public private(set) var networkServiceType: NSURLRequestNetworkServiceType = .NetworkServiceTypeDefault

/*!
@method mainDocumentURL
Expand All @@ -253,14 +272,14 @@ public class NSURLRequest : NSObject, NSSecureCoding, NSCopying, NSMutableCopyin
See setMainDocumentURL:
@result The main document URL.
*/
/*@NSCopying*/ public var mainDocumentURL: NSURL? { return _mainDocumentURL }
/*@NSCopying*/ public private(set) var mainDocumentURL: NSURL?

/*!
@method HTTPMethod
@abstract Returns the HTTP request method of the receiver.
@result the HTTP request method of the receiver.
*/
public var HTTPMethod: String? { return "GET" }
public private(set) var HTTPMethod: String? = "GET"

/*!
@method allHTTPHeaderFields
Expand All @@ -269,7 +288,24 @@ public class NSURLRequest : NSObject, NSSecureCoding, NSCopying, NSMutableCopyin
@result a dictionary containing all the HTTP header fields of the
receiver.
*/
public var allHTTPHeaderFields: [String : String]? { return _httpHeaderFields }
public private(set) var allHTTPHeaderFields: [String: String]?

/// HTTP body data.
///
/// This is sent as the message body, as in an HTTP POST request.
public private(set) var HTTPBody: NSData? {
didSet { precondition(HTTPBody == nil || HTTPBodyStream == nil, "Must not set both HTTPBody and HTTPBodyStream.") }
}

/// HTTP body stream.
///
/// - Returns: `nil` if the body stream has not been set. The returned stream is for examination only -- it is not safe to manipulate the stream in any way.
///
/// - Note: A request can have an HTTP body or an HTTP body stream, only one may be set for a request.
/// - Note: A HTTP body stream is preserved when copying an NSURLRequest object, but is lost when a request is archived using the NSCoding protocol.
public private(set) var HTTPBodyStream: NSInputStream? {
didSet { precondition(HTTPBody == nil || HTTPBodyStream == nil, "Must not set both HTTPBody and HTTPBodyStream.") }
}

/*!
@method valueForHTTPHeaderField:
Expand All @@ -281,8 +317,10 @@ public class NSURLRequest : NSObject, NSSecureCoding, NSCopying, NSMutableCopyin
@result the value associated with the given header field, or nil if
there is no value associated with the given header field.
*/
public func valueForHTTPHeaderField(field: String) -> String? { return _httpHeaderFields?[field.lowercased()] }

public func valueForHTTPHeaderField(field: String) -> String? {
guard let f = allHTTPHeaderFields else { return nil }
return existingHeaderField(field, inHeaderFields: f)?.1
}
}

/*!
Expand Down Expand Up @@ -315,9 +353,6 @@ public class NSURLRequest : NSObject, NSSecureCoding, NSCopying, NSMutableCopyin
</ul>
*/
public class NSMutableURLRequest : NSURLRequest {

private var _HTTPMethod: String? = "GET"

public required init?(coder aDecoder: NSCoder) {
super.init()
}
Expand All @@ -330,14 +365,10 @@ public class NSMutableURLRequest : NSURLRequest {
@param URL The new URL for the receiver.
*/
/*@NSCopying */ public override var URL: NSURL? {
get {
return _URL
}
set(newURL) {
_URL = newURL
}
get { return super.URL }
set { super.URL = newValue }
}

/*!
@method setMainDocumentURL:
@abstract Sets the main document URL
Expand All @@ -350,11 +381,8 @@ public class NSMutableURLRequest : NSURLRequest {
other things in the future.
*/
/*@NSCopying*/ public override var mainDocumentURL: NSURL? {
get {
return _mainDocumentURL
} set(newMainDocumentURL) {
_mainDocumentURL = newMainDocumentURL
}
get { return super.mainDocumentURL }
set { super.mainDocumentURL = newValue }
}


Expand All @@ -364,11 +392,59 @@ public class NSMutableURLRequest : NSURLRequest {
@result the HTTP request method of the receiver.
*/
public override var HTTPMethod: String? {
get {
return _HTTPMethod
} set(newHTTPMethod) {
_HTTPMethod = newHTTPMethod
}
get { return super.HTTPMethod }
set { super.HTTPMethod = newValue }
}

/// The cache policy of the receiver.
public override var cachePolicy: NSURLRequestCachePolicy {
get { return super.cachePolicy }
set { super.cachePolicy = newValue }
}

/// The timeout interval.
///
/// The timeout interval specifies the limit on the idle
/// interval allotted to a request in the process of loading. The *idle
/// interval* is defined as the period of time that has passed since the
/// last instance of load activity occurred for a request that is in the
/// process of loading. Hence, when an instance of load activity occurs
/// (e.g. bytes are received from the network for a request), the idle
/// interval for a request is reset to 0. If the idle interval ever
/// becomes greater than or equal to the timeout interval, the request
/// is considered to have timed out. This timeout interval is measured
/// in seconds.
public override var timeoutInterval: NSTimeInterval {
get { return super.timeoutInterval }
set { super.timeoutInterval = newValue }
}

/// The `NSURLRequestNetworkServiceType` associated with this request.
///
/// This method is used to provide the network layers with a hint as to the
/// purpose of the request. Most clients should not need to use this method.
public override var networkServiceType: NSURLRequestNetworkServiceType {
get { return super.networkServiceType }
set { super.networkServiceType = newValue }
}

/// HTTP body data.
///
/// This is sent as the message body, as in an HTTP POST request.
public override var HTTPBody: NSData? {
get { return super.HTTPBody }
set { super.HTTPBody = newValue.map({ $0.copy() as! NSData }) }
}

/// HTTP body stream.
///
/// - Returns: `nil` if the body stream has not been set. The returned stream is for examination only -- it is not safe to manipulate the stream in any way.
///
/// - Note: A request can have an HTTP body or an HTTP body stream, only one may be set for a request.
/// - Note: A HTTP body stream is preserved when copying an NSURLRequest object, but is lost when a request is archived using the NSCoding protocol.
public override var HTTPBodyStream: NSInputStream? {
get { return super.HTTPBodyStream }
set { super.HTTPBodyStream = newValue }
}

/*!
Expand All @@ -382,16 +458,12 @@ public class NSMutableURLRequest : NSURLRequest {
@param field the header field name (case-insensitive).
*/
public func setValue(value: String?, forHTTPHeaderField field: String) {
if _httpHeaderFields == nil {
_httpHeaderFields = [:]
}
if let existingHeader = _httpHeaderFields?.filter({ (existingField, _) -> Bool in
return existingField.lowercased() == field.lowercased()
}).first {
let (existingField, _) = existingHeader
_httpHeaderFields?.removeValue(forKey: existingField)
var f: [String: String] = allHTTPHeaderFields ?? [:]
if let old = existingHeaderField(field, inHeaderFields: f) {
f.removeValue(forKey: old.0)
}
_httpHeaderFields?[field] = value
f[field] = value
allHTTPHeaderFields = f
}

/*!
Expand All @@ -409,18 +481,22 @@ public class NSMutableURLRequest : NSURLRequest {
@param field the header field name (case-insensitive).
*/
public func addValue(value: String, forHTTPHeaderField field: String) {
if _httpHeaderFields == nil {
_httpHeaderFields = [:]
}
if let existingHeader = _httpHeaderFields?.filter({ (existingField, _) -> Bool in
return existingField.lowercased() == field.lowercased()
}).first {
let (existingField, existingValue) = existingHeader
_httpHeaderFields?[existingField] = "\(existingValue),\(value)"
var f: [String: String] = allHTTPHeaderFields ?? [:]
if let old = existingHeaderField(field, inHeaderFields: f) {
f[old.0] = old.1 + "," + value
} else {
_httpHeaderFields?[field] = value
f[field] = value
}
allHTTPHeaderFields = f
}
}


/// Returns an existing key-value pair inside the header fields if it exists.
private func existingHeaderField(key: String, inHeaderFields fields: [String: String]) -> (String, String)? {
for (k, v) in fields {
if k.lowercased() == key.lowercased() {
return (k, v)
}
}
return nil
}
34 changes: 34 additions & 0 deletions TestFoundation/TestNSURLRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,13 @@ class TestNSURLRequest : XCTestCase {
XCTAssertNotNil(request)
XCTAssertEqual(request.URL, URL)
XCTAssertEqual(request.HTTPMethod, "GET")
XCTAssertEqual(request.cachePolicy, NSURLRequestCachePolicy.UseProtocolCachePolicy)
XCTAssertEqual(request.timeoutInterval, 60)
XCTAssertEqual(request.networkServiceType, NSURLRequestNetworkServiceType.NetworkServiceTypeDefault)
XCTAssertNil(request.allHTTPHeaderFields)
XCTAssertNil(request.mainDocumentURL)
XCTAssertNil(request.HTTPBody)
XCTAssertNil(request.HTTPBodyStream)
}

func test_mutableConstruction() {
Expand All @@ -46,20 +51,49 @@ class TestNSURLRequest : XCTestCase {
XCTAssertNotNil(request)
XCTAssertEqual(request.URL, URL)
XCTAssertEqual(request.HTTPMethod, "GET")
XCTAssertEqual(request.cachePolicy, NSURLRequestCachePolicy.UseProtocolCachePolicy)
XCTAssertEqual(request.timeoutInterval, 60)
XCTAssertEqual(request.networkServiceType, NSURLRequestNetworkServiceType.NetworkServiceTypeDefault)
XCTAssertNil(request.allHTTPHeaderFields)
XCTAssertNil(request.mainDocumentURL)
XCTAssertNil(request.HTTPBody)
XCTAssertNil(request.HTTPBodyStream)

request.mainDocumentURL = URL
XCTAssertEqual(request.mainDocumentURL, URL)

request.HTTPMethod = "POST"
XCTAssertEqual(request.HTTPMethod, "POST")

let newPolicy = NSURLRequestCachePolicy.ReloadIgnoringCacheData
request.cachePolicy = newPolicy
XCTAssertEqual(request.cachePolicy, newPolicy)

let newTimeout: NSTimeInterval = 42
request.timeoutInterval = newTimeout
XCTAssertEqual(request.timeoutInterval, newTimeout)

let newServiceType = NSURLRequestNetworkServiceType.NetworkServiceTypeVoice
request.networkServiceType = newServiceType
XCTAssertEqual(request.networkServiceType, newServiceType)

let newData = NSMutableData()
newData.appendData("A".dataUsingEncoding(NSUTF8StringEncoding)!)
request.HTTPBody = newData
newData.appendData("B".dataUsingEncoding(NSUTF8StringEncoding)!)
if let d = request.HTTPBody {
XCTAssertEqual(String(data: d, encoding: NSUTF8StringEncoding), "A")
} else {
XCTFail()
}

let newURL = NSURL(string: "http://github.com")!
request.URL = newURL
XCTAssertEqual(request.URL, newURL)
}

//TODO: Test HTTPBodyStream when NSInputStream is implemented.

func test_headerFields() {
let request = NSMutableURLRequest(URL: URL)

Expand Down