@@ -16,90 +16,32 @@ import NIOCore
1616import NIOHTTP1
1717
1818extension 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 {
4123 try self . validateFieldNames ( )
4224
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-
6225 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
8833 }
8934 }
9035
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)
10137
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+ }
10345 }
10446
10547 private func validateFieldNames( ) throws {
@@ -137,4 +79,34 @@ extension HTTPHeaders {
13779 throw HTTPClientError . invalidHeaderFieldNames ( invalidFieldNames)
13880 }
13981 }
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+ }
140112}
0 commit comments