Skip to content

Commit

Permalink
support directive parameter quoting (#2441)
Browse files Browse the repository at this point in the history
  • Loading branch information
tanner0101 committed Jul 15, 2020
1 parent 662e355 commit 75c7236
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 3 deletions.
22 changes: 20 additions & 2 deletions Sources/Vapor/HTTP/Headers/HTTPHeaders+Directive.swift
Expand Up @@ -177,7 +177,11 @@ extension HTTPHeaders {
for directive in directives {
let string: String
if let parameter = directive.parameter {
string = "\(directive.value)=\(parameter)"
if self.shouldQuote(parameter) {
string = "\(directive.value)=\"\(parameter.escapingDoubleQuotes())\""
} else {
string = "\(directive.value)=\(parameter)"
}
} else {
string = .init(directive.value)
}
Expand All @@ -188,20 +192,31 @@ extension HTTPHeaders {

return main.joined(separator: ", ")
}

private func shouldQuote(_ parameter: Substring) -> Bool {
parameter.contains(where: {
$0 == .space || $0 == .doubleQuote
})
}
}
}

private extension Substring {
/// Converts all `\"` to `"`.
func unescapingDoubleQuotes() -> Substring {
return self.lazy.split(separator: "\\").reduce(into: "") { (result, part) in
self.lazy.split(separator: "\\").reduce(into: "") { (result, part) in
if result.isEmpty || part.first == "\"" {
result += part
} else {
result += "\\" + part
}
}
}

/// Converts all `"` to `\"`.
func escapingDoubleQuotes() -> String {
self.split(separator: "\"").joined(separator: "\\\"")
}
}


Expand All @@ -227,6 +242,9 @@ private extension Character {
static var period: Self {
.init(".")
}
static var space: Self {
.init(" ")
}

var isDirectiveKey: Bool {
self.isLetter || self.isNumber || self == .dash || self == .underscore || self == .period
Expand Down
13 changes: 12 additions & 1 deletion Tests/VaporTests/HTTPHeaderTests.swift
@@ -1,7 +1,7 @@
@testable import Vapor
import XCTest

final class HTTPHeaderValueTests: XCTestCase {
final class HTTPHeaderTests: XCTestCase {
func testValue() throws {
var parser = HTTPHeaders.DirectiveParser(string: "foobar")
XCTAssertEqual(parser.nextDirectives(), [.init(value: "foobar")])
Expand Down Expand Up @@ -196,4 +196,15 @@ final class HTTPHeaderValueTests: XCTestCase {
let upper = HTTPMediaType(type: "foo", subType: "BAR")
XCTAssertEqual(lower, upper)
}

// https://github.com/vapor/vapor/issues/2439
func testContentDispositionQuotedFilename() throws {
var headers = HTTPHeaders()
headers.contentDisposition = .init(.formData, filename: "foo")
XCTAssertEqual(headers.first(name: .contentDisposition), "form-data; filename=foo")
headers.contentDisposition = .init(.formData, filename: "foo bar")
XCTAssertEqual(headers.first(name: .contentDisposition), #"form-data; filename="foo bar""#)
headers.contentDisposition = .init(.formData, filename: "foo\"bar")
XCTAssertEqual(headers.first(name: .contentDisposition), #"form-data; filename="foo\"bar""#)
}
}

0 comments on commit 75c7236

Please sign in to comment.