 	/// - Parameter headers: Dictionary of HTTP header values
 	/// - Returns: Promise of Data
 	open func custom(method: HTTPMethod = .get, baseURL: URL? = nil, path: String? = nil, parameters: [String: Any]? = nil, headers: [String: String]? = nil) -> Promise<Data> {
-		return request(.custom(method: method, baseURL: baseURL, path: path, parameters: parameters, headers: headers)) {
-			Alamofire.request($0).validateSalesforceResponse().responseData()
-		}
-	}
-	
-	private func request<T>(_ resource: Resource, promiser: @escaping (URLRequest) -> Promise<T>) -> Promise<T> {
-		let requestor = {
-			(authData: OAuth2Result) throws -> Promise<T> in
-			let req = try resource.asURLRequest(authData: authData)
-			return promiser(req)
-		}
-		return request(requestor: requestor)
-	}
-	
-	private func request<T>(requestor: @escaping (OAuth2Result) throws -> Promise<T>) -> Promise<T> {
-		return Promise<OAuth2Result> {
-			// Get credentials
-			(fulfill, reject) -> () in
-			if let authData = connectedApp.authData {
-				// Use credentials we already have
-				fulfill(authData)
-			}
-			else {
-				reject(SalesforceError.userAuthenticationRequired)
-			}
-		}.then {
-			// Send request
-			return try requestor($0)
-		}.recover {
-			// Recover from expired session token error - fail on other errors
-			(error: Error) -> Promise<T> in
-			if case SalesforceError.userAuthenticationRequired = error {
-				return self.connectedApp.authorize().then {
-					return try requestor($0)
-				}
-			}
-			else {
-				throw error
-			}
-		}
+		return requestor.request(resource: .custom(method: method, baseURL: baseURL, path: path, parameters: parameters, headers: headers))
 	}
 }

	/// - Parameter fields: Optional array of field names to retrieve. If nil, all fields will be retrieved
	/// - Returns: Promise of a dictionary keyed by field names (aka "Record")
	open func retrieve(type: String, id: String, fields: [String]? = nil) -> Promise<Record> {
		return request(.retrieve(type: type, id: id, fields: fields, version: version)) {
			Alamofire.request($0).validateSalesforceResponse().responseJSONDictionary()
		}
	}
	
	/// Asynchronously retrieves multiple records of the same type, by ID

	/// - Parameter path: the 'nextRecordsPath' property of a previously-obtained QueryResult.
	/// - Returns: Promise of a QueryResult
	open func queryNext(path: String) -> Promise<QueryResult> {
		return requestor.request(resource: .queryNext(path: path)).asJSON().then(on: q) {
			(json: [String: Any]) -> QueryResult in
			return try QueryResult(json: json)
		}
