@@ -16,79 +16,35 @@ import NIOCore
16
16
import NIOHTTP1
17
17
18
18
extension HTTPHeaders {
19
- mutating func validateAndFixTransportFraming (
19
+ mutating func validateAndSetTransportFraming (
20
20
method: HTTPMethod ,
21
21
bodyLength: RequestBodyLength
22
22
) throws -> RequestFramingMetadata {
23
- let contentLength = self . first ( name: " Content-Length " )
24
- let encodings = self [ canonicalForm: " Transfer-Encoding " ]
25
-
26
- // "Transfer-Encoding" and "Content-Length" are not allowed to present at the same time (https://tools.ietf.org/html/rfc7230#section-3.3.1)
27
- guard encodings. isEmpty || contentLength == nil else {
28
- throw HTTPClientError . incompatibleHeaders
29
- }
30
-
31
23
try self . validateFieldNames ( )
32
- try Self . validateTransferEncoding ( encodings)
33
-
34
- if contentLength != nil {
35
- self . remove ( name: " Content-Length " )
36
- }
37
- if !encodings. isEmpty {
38
- self . remove ( name: " Transfer-Encoding " )
39
- }
40
-
41
- let connectionClose = self [ canonicalForm: " connection " ] . lazy. map { $0. lowercased ( ) } . contains ( " close " )
42
-
43
- switch bodyLength {
44
- case . fixed( 0 ) :
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.
24
+
25
+ if case . TRACE = method {
26
+ switch bodyLength {
27
+ case . fixed( length: 0 ) :
52
28
break
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
- }
59
- return . init( connectionClose: connectionClose, body: . fixedSize( 0 ) )
60
- case . fixed( let length) :
61
- if case . TRACE = method {
29
+ case . dynamic, . fixed( _) :
62
30
// A client MUST NOT send a message body in a TRACE request.
63
31
// https://tools.ietf.org/html/rfc7230#section-4.3.8
64
32
throw HTTPClientError . traceRequestWithBody
65
33
}
66
- if encodings. isEmpty {
67
- self . add ( name: " Content-Length " , value: String ( length) )
68
- return . init( connectionClose: connectionClose, body: . fixedSize( length) )
69
- } else {
70
- self . add ( name: " Transfer-Encoding " , value: encodings. joined ( separator: " , " ) )
71
- return . init( connectionClose: connectionClose, body: . stream)
72
- }
34
+ }
35
+
36
+ self . setTransportFraming ( method: method, bodyLength: bodyLength)
37
+
38
+ let connectionClose = self [ canonicalForm: " connection " ] . lazy. map { $0. lowercased ( ) } . contains ( " close " )
39
+ switch bodyLength {
73
40
case . dynamic:
74
- if case . TRACE = method {
75
- // A client MUST NOT send a message body in a TRACE request.
76
- // https://tools.ietf.org/html/rfc7230#section-4.3.8
77
- throw HTTPClientError . traceRequestWithBody
78
- }
79
-
80
- if encodings. isEmpty && contentLength == nil {
81
- // if a user forgot to specify a Content-Length and Transfer-Encoding, we will set it for them
82
- self . add ( name: " Transfer-Encoding " , value: " chunked " )
83
- } else {
84
- self . add ( name: " Transfer-Encoding " , value: encodings. joined ( separator: " , " ) )
85
- }
86
-
87
41
return . init( connectionClose: connectionClose, body: . stream)
42
+ case . fixed( let length) :
43
+ return . init( connectionClose: connectionClose, body: . fixedSize( length) )
88
44
}
89
45
}
90
46
91
- func validateFieldNames( ) throws {
47
+ private func validateFieldNames( ) throws {
92
48
let invalidFieldNames = self . compactMap { ( name, _) -> String ? in
93
49
let satisfy = name. utf8. allSatisfy { ( char) -> Bool in
94
50
switch char {
@@ -123,33 +79,33 @@ extension HTTPHeaders {
123
79
throw HTTPClientError . invalidHeaderFieldNames ( invalidFieldNames)
124
80
}
125
81
}
126
-
127
- static func validateTransferEncoding< Encodings> (
128
- _ encodings: Encodings
129
- ) throws where Encodings: Sequence , Encodings. Element: StringProtocol {
130
- let encodings = encodings. map { $0. lowercased ( ) }
131
-
132
- guard !encodings. contains ( " identity " ) else {
133
- throw HTTPClientError . identityCodingIncorrectlyPresent
134
- }
135
-
136
- // If `Transfer-Encoding` is specified, `chunked` needs to be the last encoding and should not be specified multiple times
137
- // https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.1
138
- let chunkedEncodingCount = encodings. lazy. filter { $0 == " chunked " } . count
139
- switch chunkedEncodingCount {
140
- case 0 :
141
- if !encodings. isEmpty {
142
- throw HTTPClientError . transferEncodingSpecifiedButChunkedIsNotTheFinalEncoding
143
- }
144
- case 1 :
145
- guard encodings. last == " chunked " else {
146
- throw HTTPClientError . transferEncodingSpecifiedButChunkedIsNotTheFinalEncoding
82
+ private mutating func setTransportFraming(
83
+ method: HTTPMethod ,
84
+ bodyLength: RequestBodyLength
85
+ ) {
86
+ self . remove ( name: " Content-Length " )
87
+ self . remove ( name: " Transfer-Encoding " )
88
+
89
+ switch bodyLength {
90
+ case . fixed( 0 ) :
91
+ // if we don't have a body we might not need to send the Content-Length field
92
+ // https://tools.ietf.org/html/rfc7230#section-3.3.2
93
+ switch method {
94
+ case . GET, . HEAD, . DELETE, . CONNECT, . TRACE:
95
+ // A user agent SHOULD NOT send a Content-Length header field when the request
96
+ // message does not contain a payload body and the method semantics do not
97
+ // anticipate such a body.
98
+ break
99
+ default :
100
+ // A user agent SHOULD send a Content-Length in a request message when
101
+ // no Transfer-Encoding is sent and the request method defines a meaning
102
+ // for an enclosed payload body.
103
+ self . add ( name: " Content-Length " , value: " 0 " )
147
104
}
148
- case 2 ... :
149
- throw HTTPClientError . chunkedSpecifiedMultipleTimes
150
- default :
151
- // unreachable because `chunkedEncodingCount` is guaranteed to be positive
152
- preconditionFailure ( )
105
+ case . fixed( let length) :
106
+ self . add ( name: " Content-Length " , value: String ( length) )
107
+ case . dynamic:
108
+ self . add ( name: " Transfer-Encoding " , value: " chunked " )
153
109
}
154
110
}
155
111
}
0 commit comments