Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
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
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.UUID;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSocketFactory;
import org.json.JSONException;
Expand Down Expand Up @@ -224,7 +225,16 @@ public void setRequestBody(PluginCall call, JSValue body, String bodyType) throw
this.writeRequestBody(body.toString());
}
} else if (bodyType != null && bodyType.equals("formData")) {
this.writeFormDataRequestBody(contentType, body.toJSArray());
String boundary = extractBoundaryFromContentType(contentType);
if (boundary == null) {
// If no boundary is provided, generate a random one and set the Content-Type header accordingly
// or otherwise servers will not be able to parse the request body. Browsers do this automatically
// but here we need to do this manually in order to comply with browser api behavior.
boundary = UUID.randomUUID().toString();
connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
}

this.writeFormDataRequestBody(boundary, body.toJSArray());
} else {
this.writeRequestBody(body.toString());
}
Expand Down Expand Up @@ -260,9 +270,8 @@ private void writeObjectRequestBody(JSObject object) throws IOException, JSONExc
}
}

private void writeFormDataRequestBody(String contentType, JSArray entries) throws IOException, JSONException {
private void writeFormDataRequestBody(String boundary, JSArray entries) throws IOException, JSONException {
try (DataOutputStream os = new DataOutputStream(connection.getOutputStream())) {
String boundary = contentType.split(";")[1].split("=")[1];
String lineEnd = "\r\n";
String twoHyphens = "--";

Expand Down Expand Up @@ -303,6 +312,39 @@ private void writeFormDataRequestBody(String contentType, JSArray entries) throw
}
}

/**
* Extracts the boundary value from the `Content-Type` header for multipart/form-data requests, if provided.
*
* The boundary value might be surrounded by double quotes (") which will be stripped away.
*
* @param contentType The `Content-Type` header string.
* @return The boundary value if found, otherwise `null`.
*/
public static String extractBoundaryFromContentType(String contentType) {
String boundaryPrefix = "boundary=";
int boundaryIndex = contentType.indexOf(boundaryPrefix);
if (boundaryIndex == -1) {
return null;
}

// Extract the substring starting right after "boundary="
String boundary = contentType.substring(boundaryIndex + boundaryPrefix.length());

// Find the end of the boundary value by looking for the next ";"
int endIndex = boundary.indexOf(";");
if (endIndex != -1) {
boundary = boundary.substring(0, endIndex);
}

// Remove surrounding double quotes if present
boundary = boundary.trim();
if (boundary.startsWith("\"") && boundary.endsWith("\"")) {
boundary = boundary.substring(1, boundary.length() - 1);
}

return boundary;
}

/**
* Opens a communications link to the resource referenced by this
* URL, if such a connection has not already been established.
Expand Down
25 changes: 23 additions & 2 deletions ios/Capacitor/Capacitor/Plugins/CapacitorUrlRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ open class CapacitorUrlRequest: NSObject, URLSessionTaskDelegate {

var data = Data()
var boundary = UUID().uuidString
if contentType.contains("="), let contentBoundary = contentType.components(separatedBy: "=").last {
if contentType.contains("boundary="), let contentBoundary = extractBoundary(from: contentType) {
boundary = contentBoundary
} else {
overrideContentType(boundary)
Expand All @@ -86,6 +86,27 @@ open class CapacitorUrlRequest: NSObject, URLSessionTaskDelegate {
request.setValue(contentType, forHTTPHeaderField: "Content-Type")
headers["Content-Type"] = contentType
}

/**
Extracts the boundary value of the `content-type` header for multiplart/form-data requests, if provided
The boundary value might be surrounded by double quotes (") which will be stripped away.
*/
private func extractBoundary(from contentType: String) -> String? {
if let boundaryRange = contentType.range(of: "boundary=") {
var boundary = contentType[boundaryRange.upperBound...]
if let endRange = boundary.range(of: ";") {
boundary = boundary[..<endRange.lowerBound]
}

if boundary.hasPrefix("\"") && boundary.hasSuffix("\"") {
return String(boundary.dropFirst().dropLast())
} else {
return String(boundary)
}
}

return nil
}

public func getRequestDataAsString(_ data: JSValue) throws -> Data {
guard let stringData = data as? String else {
Expand All @@ -110,7 +131,7 @@ open class CapacitorUrlRequest: NSObject, URLSessionTaskDelegate {
}
var data = Data()
var boundary = UUID().uuidString
if contentType.contains("="), let contentBoundary = contentType.components(separatedBy: "=").last {
if contentType.contains("boundary="), let contentBoundary = extractBoundary(from: contentType) {
boundary = contentBoundary
} else {
overrideContentType(boundary)
Expand Down
Loading