@@ -16,79 +16,35 @@ import NIOCore
1616import NIOHTTP1
1717
1818extension HTTPHeaders {
19- mutating func validateAndFixTransportFraming (
19+ mutating func validateAndSetTransportFraming (
2020 method: HTTPMethod ,
2121 bodyLength: RequestBodyLength
2222 ) 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-
3123 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 ) :
5228 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( _) :
6230 // A client MUST NOT send a message body in a TRACE request.
6331 // https://tools.ietf.org/html/rfc7230#section-4.3.8
6432 throw HTTPClientError . traceRequestWithBody
6533 }
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 {
7340 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-
8741 return . init( connectionClose: connectionClose, body: . stream)
42+ case . fixed( let length) :
43+ return . init( connectionClose: connectionClose, body: . fixedSize( length) )
8844 }
8945 }
9046
91- func validateFieldNames( ) throws {
47+ private func validateFieldNames( ) throws {
9248 let invalidFieldNames = self . compactMap { ( name, _) -> String ? in
9349 let satisfy = name. utf8. allSatisfy { ( char) -> Bool in
9450 switch char {
@@ -123,33 +79,33 @@ extension HTTPHeaders {
12379 throw HTTPClientError . invalidHeaderFieldNames ( invalidFieldNames)
12480 }
12581 }
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 " )
147104 }
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 " )
153109 }
154110 }
155111}
0 commit comments