Skip to content

Commit

Permalink
Merge pull request OAuthSwift#287 from ktakayama/fix-multipart-with-p…
Browse files Browse the repository at this point in the history
…arameter

Fix multipart request with some parameters (master)
  • Loading branch information
phimage authored Oct 7, 2016
2 parents 1681b70 + e16d080 commit 3683ed5
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 39 deletions.
35 changes: 35 additions & 0 deletions OAuthSwiftTests/OAuthSwiftClientTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,41 @@ class OAuthSwiftClientTests: XCTestCase {
XCTFail("\(e)")
}
}

func testMultiPartBodyFromParams() {
let binary = "binary".data(using: OAuthSwiftDataEncoding)!
let parameters: OAuthSwift.Parameters = [ "media": binary, "a": "b" ]
let data = client.multiPartBody(from: parameters, boundary: "boundary")
let result = String(data: data, encoding: OAuthSwiftDataEncoding)!

let expectedString = "--boundary\r\nContent-Disposition: form-data; name=\"a\";\r\n\r\nb\r\n--boundary\r\nContent-Disposition: form-data; name=\"media\"; filename=\"file\"\r\nContent-Type: image/jpeg\r\n\r\nbinary\r\n--boundary--\r\n"
XCTAssertEqual(result, expectedString)
}

func testMakeMultipartRequest() {
let binary = "binary".data(using: OAuthSwiftDataEncoding)!
let multiparts = [ OAuthSwiftMultipartData(name: "media", data: binary, fileName: "file", mimeType: "image/jpeg") ]
let request = client.makeMultiPartRequest(url, method: .POST, multiparts: multiparts)!

XCTAssertEqualURL(request.config.url!, URL(string: url)!)

let bodyString = String(data: request.config.urlRequest.httpBody!, encoding: OAuthSwiftDataEncoding)
XCTAssertTrue(bodyString?.contains("image/jpeg\r\n\r\nbinary\r\n") == true)
}

func testMakeMultipartRequestWithParameter() {
let binary = "binary".data(using: OAuthSwiftDataEncoding)!
let multiparts = [ OAuthSwiftMultipartData(name: "media", data: binary, fileName: "file", mimeType: "image/jpeg") ]
let parameters: OAuthSwift.Parameters = [ "a": "b" ]
let request = client.makeMultiPartRequest(url, method: .POST, parameters: parameters, multiparts: multiparts)!

XCTAssertEqualURL(request.config.url!, URL(string: url)!)

let bodyString = String(data: request.config.urlRequest.httpBody!, encoding: OAuthSwiftDataEncoding)
XCTAssertTrue(bodyString?.contains("image/jpeg\r\n\r\nbinary\r\n") == true)
XCTAssertTrue(bodyString?.contains("form-data; name=\"a\";\r\n\r\nb") == true)
}

}

extension XCTestCase {
Expand Down
70 changes: 31 additions & 39 deletions Sources/OAuthSwiftClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -111,64 +111,56 @@ open class OAuthSwiftClient: NSObject {
return self.multiPartRequest(url: urlString, method: .POST, parameters: parameters, image: image, success: success, failure: failure)
}

func multiPartRequest(url: String, method: OAuthSwiftHTTPRequest.Method, parameters: OAuthSwift.Parameters, image: Data, success: OAuthSwiftHTTPRequest.SuccessHandler?, failure: OAuthSwiftHTTPRequest.FailureHandler?) -> OAuthSwiftRequestHandle? {

let paramImage: OAuthSwift.Parameters = ["media": image]
open func makeMultiPartRequest(_ urlString: String, method: OAuthSwiftHTTPRequest.Method, parameters: OAuthSwift.Parameters = [:], multiparts: Array<OAuthSwiftMultipartData> = [], headers: OAuthSwift.Headers? = nil) -> OAuthSwiftHTTPRequest? {
let boundary = "AS-boundary-\(arc4random())-\(arc4random())"
let type = "multipart/form-data; boundary=\(boundary)"
let body = self.multiPartBody(from: paramImage, boundary: boundary)
let headers = [kHTTPHeaderContentType: type]
let body = self.multiDataFromObject(parameters, multiparts: multiparts, boundary: boundary)

var finalHeaders = [kHTTPHeaderContentType: type]
finalHeaders += headers ?? [:]

return makeRequest(urlString, method: method, parameters: parameters, headers: finalHeaders, body: body)
}

func multiPartRequest(url: String, method: OAuthSwiftHTTPRequest.Method, parameters: OAuthSwift.Parameters, image: Data, success: OAuthSwiftHTTPRequest.SuccessHandler?, failure: OAuthSwiftHTTPRequest.FailureHandler?) -> OAuthSwiftRequestHandle? {
let multiparts = [ OAuthSwiftMultipartData(name: "media", data: image, fileName: "file", mimeType: "image/jpeg") ]

if let request = makeRequest(url, method: method, parameters: parameters, headers: headers, body: body) { // TODO check if headers do not override others...
if let request = makeMultiPartRequest(url, method: method, parameters: parameters, multiparts: multiparts) {
request.successHandler = success
request.failureHandler = failure
request.start()
return request
}

return nil
}

open func multiPartBody(from parameters: OAuthSwift.Parameters, boundary: String) -> Data {
var data = Data()

let prefixString = "--\(boundary)\r\n"
let prefixData = prefixString.data(using: OAuthSwiftDataEncoding)!

open func multiPartBody(from inputParameters: OAuthSwift.Parameters, boundary: String) -> Data {
var parameters = OAuthSwift.Parameters()
var multiparts = Array<OAuthSwiftMultipartData>()

for (key, value) in parameters {
var sectionData: Data
var sectionType: String?
var sectionFilename: String?
if let multiData = value as? Data , key == "media" {
sectionData = multiData
sectionType = "image/jpeg"
sectionFilename = "file"
for (key, value) in inputParameters {
if let data = value as? Data , key == "media" {
let sectionType = "image/jpeg"
let sectionFilename = "file"
multiparts.append(OAuthSwiftMultipartData(name: key, data: data, fileName: sectionFilename, mimeType: sectionType))
} else {
sectionData = "\(value)".data(using: OAuthSwiftDataEncoding)!
parameters[key] = value
}

data.append(prefixData)
let multipartData = OAuthSwiftMultipartData(name: key, data: sectionData, fileName: sectionFilename, mimeType: sectionType)
data.append(multipartData, encoding: OAuthSwiftDataEncoding, separatorData: OAuthSwiftClient.separatorData)
}

let endingString = "--\(boundary)--\r\n"
let endingData = endingString.data(using: OAuthSwiftDataEncoding)!
data.append(endingData)
return data

return multiDataFromObject(parameters, multiparts: multiparts, boundary: boundary)
}

@discardableResult
open func postMultiPartRequest(_ url: String, method: OAuthSwiftHTTPRequest.Method, parameters: OAuthSwift.Parameters, headers: Dictionary<String, String>? = nil, multiparts: Array<OAuthSwiftMultipartData> = [], checkTokenExpiration: Bool = true, success: OAuthSwiftHTTPRequest.SuccessHandler?, failure: OAuthSwiftHTTPRequest.FailureHandler?) -> OAuthSwiftRequestHandle? {

let boundary = "POST-boundary-\(arc4random())-\(arc4random())"
let type = "multipart/form-data; boundary=\(boundary)"
let body = self.multiDataFromObject(parameters, multiparts: multiparts, boundary: boundary)

var finalHeaders = [kHTTPHeaderContentType: type]
finalHeaders += headers ?? [:]

if let request = makeRequest(url, method: method, parameters: parameters, headers: finalHeaders, body: body) { // TODO check if headers do not override
if checkTokenExpiration && self.credential.isTokenExpired() {
failure?(OAuthSwiftError.tokenExpired(error: nil))
return nil
}

if let request = makeMultiPartRequest(url, method: method, parameters: parameters, multiparts: multiparts, headers: headers) {
request.successHandler = success
request.failureHandler = failure
request.start()
Expand All @@ -177,7 +169,7 @@ open class OAuthSwiftClient: NSObject {
return nil
}

func multiDataFromObject(_ object: OAuthSwift.Parameters, multiparts: Array<OAuthSwiftMultipartData>, boundary: String) -> Data? {
func multiDataFromObject(_ object: OAuthSwift.Parameters, multiparts: Array<OAuthSwiftMultipartData>, boundary: String) -> Data {
var data = Data()

let prefixString = "--\(boundary)\r\n"
Expand Down
3 changes: 3 additions & 0 deletions Sources/OAuthSwiftHTTPRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,9 @@ open class OAuthSwiftHTTPRequest: NSObject, URLSessionDelegate, OAuthSwiftReques
request.setValue("application/json; charset=\(charset)", forHTTPHeaderField: kHTTPHeaderContentType)
request.httpBody = jsonData
}
else if let contentType = headers[kHTTPHeaderContentType], contentType.contains("multipart/form-data") {
// snip
}
else {
request.setValue("application/x-www-form-urlencoded; charset=\(charset)", forHTTPHeaderField: kHTTPHeaderContentType)
let queryString = finalParameters.urlEncodedQuery
Expand Down

0 comments on commit 3683ed5

Please sign in to comment.