@@ -16,90 +16,32 @@ import NIOCore
16
16
import NIOHTTP1
17
17
18
18
extension HTTPHeaders {
19
- mutating func validate( method: HTTPMethod , body: HTTPClient . Body ? ) throws -> RequestFramingMetadata {
20
- var metadata = RequestFramingMetadata ( connectionClose: false , body: . fixedSize( 0 ) )
21
-
22
- if self [ canonicalForm: " connection " ] . lazy. map ( { $0. lowercased ( ) } ) . contains ( " close " ) {
23
- metadata. connectionClose = true
24
- }
25
-
26
- // validate transfer encoding and content length (https://tools.ietf.org/html/rfc7230#section-3.3.1)
27
- if self . contains ( name: " Transfer-Encoding " ) , self . contains ( name: " Content-Length " ) {
28
- throw HTTPClientError . incompatibleHeaders
29
- }
30
-
31
- var transferEncoding : String ?
32
- var contentLength : Int ?
33
- let encodings = self [ canonicalForm: " Transfer-Encoding " ] . map { $0. lowercased ( ) }
34
-
35
- guard !encodings. contains ( " identity " ) else {
36
- throw HTTPClientError . identityCodingIncorrectlyPresent
37
- }
38
-
39
- self . remove ( name: " Transfer-Encoding " )
40
-
19
+ mutating func validateAndSetTransportFraming(
20
+ method: HTTPMethod ,
21
+ bodyLength: RequestBodyLength
22
+ ) throws -> RequestFramingMetadata {
41
23
try self . validateFieldNames ( )
42
24
43
- guard let body = body else {
44
- self . remove ( name: " Content-Length " )
45
- // if we don't have a body we might not need to send the Content-Length field
46
- // https://tools.ietf.org/html/rfc7230#section-3.3.2
47
- switch method {
48
- case . GET, . HEAD, . DELETE, . CONNECT, . TRACE:
49
- // A user agent SHOULD NOT send a Content-Length header field when the request
50
- // message does not contain a payload body and the method semantics do not
51
- // anticipate such a body.
52
- return metadata
53
- default :
54
- // A user agent SHOULD send a Content-Length in a request message when
55
- // no Transfer-Encoding is sent and the request method defines a meaning
56
- // for an enclosed payload body.
57
- self . add ( name: " Content-Length " , value: " 0 " )
58
- return metadata
59
- }
60
- }
61
-
62
25
if case . TRACE = method {
63
- // A client MUST NOT send a message body in a TRACE request.
64
- // https://tools.ietf.org/html/rfc7230#section-4.3.8
65
- throw HTTPClientError . traceRequestWithBody
66
- }
67
-
68
- guard ( encodings. lazy. filter { $0 == " chunked " } . count <= 1 ) else {
69
- throw HTTPClientError . chunkedSpecifiedMultipleTimes
70
- }
71
-
72
- if encodings. isEmpty {
73
- if let length = body. length {
74
- self . remove ( name: " Content-Length " )
75
- contentLength = length
76
- } else if !self . contains ( name: " Content-Length " ) {
77
- transferEncoding = " chunked "
78
- }
79
- } else {
80
- self . remove ( name: " Content-Length " )
81
-
82
- transferEncoding = encodings. joined ( separator: " , " )
83
- if !encodings. contains ( " chunked " ) {
84
- guard let length = body. length else {
85
- throw HTTPClientError . contentLengthMissing
86
- }
87
- contentLength = length
26
+ switch bodyLength {
27
+ case . fixed( length: 0 ) :
28
+ break
29
+ case . dynamic, . fixed:
30
+ // A client MUST NOT send a message body in a TRACE request.
31
+ // https://tools.ietf.org/html/rfc7230#section-4.3.8
32
+ throw HTTPClientError . traceRequestWithBody
88
33
}
89
34
}
90
35
91
- // add headers if required
92
- if let enc = transferEncoding {
93
- self . add ( name: " Transfer-Encoding " , value: enc)
94
- metadata. body = . stream
95
- } else if let length = contentLength {
96
- // A sender MUST NOT send a Content-Length header field in any message
97
- // that contains a Transfer-Encoding header field.
98
- self . add ( name: " Content-Length " , value: String ( length) )
99
- metadata. body = . fixedSize( length)
100
- }
36
+ self . setTransportFraming ( method: method, bodyLength: bodyLength)
101
37
102
- return metadata
38
+ let connectionClose = self [ canonicalForm: " connection " ] . lazy. map { $0. lowercased ( ) } . contains ( " close " )
39
+ switch bodyLength {
40
+ case . dynamic:
41
+ return . init( connectionClose: connectionClose, body: . stream)
42
+ case . fixed( let length) :
43
+ return . init( connectionClose: connectionClose, body: . fixedSize( length) )
44
+ }
103
45
}
104
46
105
47
private func validateFieldNames( ) throws {
@@ -137,4 +79,34 @@ extension HTTPHeaders {
137
79
throw HTTPClientError . invalidHeaderFieldNames ( invalidFieldNames)
138
80
}
139
81
}
82
+
83
+ private mutating func setTransportFraming(
84
+ method: HTTPMethod ,
85
+ bodyLength: RequestBodyLength
86
+ ) {
87
+ self . remove ( name: " Content-Length " )
88
+ self . remove ( name: " Transfer-Encoding " )
89
+
90
+ switch bodyLength {
91
+ case . fixed( 0 ) :
92
+ // if we don't have a body we might not need to send the Content-Length field
93
+ // https://tools.ietf.org/html/rfc7230#section-3.3.2
94
+ switch method {
95
+ case . GET, . HEAD, . DELETE, . CONNECT, . TRACE:
96
+ // A user agent SHOULD NOT send a Content-Length header field when the request
97
+ // message does not contain a payload body and the method semantics do not
98
+ // anticipate such a body.
99
+ break
100
+ default :
101
+ // A user agent SHOULD send a Content-Length in a request message when
102
+ // no Transfer-Encoding is sent and the request method defines a meaning
103
+ // for an enclosed payload body.
104
+ self . add ( name: " Content-Length " , value: " 0 " )
105
+ }
106
+ case . fixed( let length) :
107
+ self . add ( name: " Content-Length " , value: String ( length) )
108
+ case . dynamic:
109
+ self . add ( name: " Transfer-Encoding " , value: " chunked " )
110
+ }
111
+ }
140
112
}
0 commit comments