Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit0ed00b8

Browse files
authored
Always overwriteTransport-Encoding andContent-Length headers (#479)
1 parent8c48625 commit0ed00b8

File tree

8 files changed

+178
-145
lines changed

8 files changed

+178
-145
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the AsyncHTTPClient open source project
4+
//
5+
// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
enumRequestBodyLength:Hashable{
16+
/// size of the request body is not known before starting the request
17+
case dynamic
18+
/// size of the request body is fixed and exactly `length` bytes
19+
case fixed(length:Int)
20+
}

‎Sources/AsyncHTTPClient/HTTPClient.swift‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -883,6 +883,7 @@ public struct HTTPClientError: Error, Equatable, CustomStringConvertible {
883883
case remoteConnectionClosed
884884
case cancelled
885885
case identityCodingIncorrectlyPresent
886+
@available(*, deprecated, message:"AsyncHTTPClient now silently corrects this invalid header.")
886887
case chunkedSpecifiedMultipleTimes
887888
case invalidProxyResponse
888889
case contentLengthMissing
@@ -894,6 +895,7 @@ public struct HTTPClientError: Error, Equatable, CustomStringConvertible {
894895
case invalidHeaderFieldNames([String])
895896
case bodyLengthMismatch
896897
case writeAfterRequestSent
898+
@available(*, deprecated, message:"AsyncHTTPClient now silently corrects invalid headers.")
897899
case incompatibleHeaders
898900
case connectTimeout
899901
case socksHandshakeTimeout
@@ -937,6 +939,7 @@ public struct HTTPClientError: Error, Equatable, CustomStringConvertible {
937939
/// Request contains invalid identity encoding.
938940
publicstaticletidentityCodingIncorrectlyPresent=HTTPClientError(code:.identityCodingIncorrectlyPresent)
939941
/// Request contains multiple chunks definitions.
942+
@available(*, deprecated, message:"AsyncHTTPClient now silently corrects this invalid header.")
940943
publicstaticletchunkedSpecifiedMultipleTimes=HTTPClientError(code:.chunkedSpecifiedMultipleTimes)
941944
/// Proxy response was invalid.
942945
publicstaticletinvalidProxyResponse=HTTPClientError(code:.invalidProxyResponse)
@@ -959,6 +962,7 @@ public struct HTTPClientError: Error, Equatable, CustomStringConvertible {
959962
/// Body part was written after request was fully sent.
960963
publicstaticletwriteAfterRequestSent=HTTPClientError(code:.writeAfterRequestSent)
961964
/// Incompatible headers specified, for example `Transfer-Encoding` and `Content-Length`.
965+
@available(*, deprecated, message:"AsyncHTTPClient now silently corrects invalid headers.")
962966
publicstaticletincompatibleHeaders=HTTPClientError(code:.incompatibleHeaders)
963967
/// Creating a new tcp connection timed out
964968
publicstaticletconnectTimeout=HTTPClientError(code:.connectTimeout)

‎Sources/AsyncHTTPClient/HTTPHandler.swift‎

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ extension HTTPClient {
4343
}
4444
}
4545

46-
/// Body size.Request validation will befailed with `HTTPClientErrors.contentLengthMissing` if nil,
47-
///unless `Trasfer-Encoding: chunked`header is set.
46+
/// Body size.if nil,`Transfer-Encoding` willautomaticallybeset to `chunked`. Otherwise a `Content-Length`
47+
/// header is set with the given `length`.
4848
publicvarlength:Int?
4949
/// Body chunk provider.
5050
publicvarstream:(StreamWriter)->EventLoopFuture<Void>
@@ -62,8 +62,8 @@ extension HTTPClient {
6262
/// Create and stream body using `StreamWriter`.
6363
///
6464
/// - parameters:
65-
/// - length: Body size.Request validationwill befailed with `HTTPClientErrors.contentLengthMissing` if nil,
66-
/// unless `Transfer-Encoding: chunked` header is set.
65+
/// - length: Body size.If nil, `Transfer-Encoding`willautomaticallybeset to `chunked`. Otherwise a `Content-Length`
66+
///header is set with the given `length`.
6767
/// - stream: Body chunk provider.
6868
publicstaticfunc stream(length:Int?=nil, _ stream:@escaping(StreamWriter)->EventLoopFuture<Void>)->Body{
6969
returnBody(length: length, stream: stream)
@@ -309,7 +309,7 @@ extension HTTPClient {
309309
head.headers.add(name:"host", value: host)
310310
}
311311

312-
letmetadata=try head.headers.validate(method:self.method,body:self.body)
312+
letmetadata=try head.headers.validateAndSetTransportFraming(method:self.method,bodyLength:.init(self.body))
313313

314314
return(head, metadata)
315315
}
@@ -820,3 +820,17 @@ internal struct RedirectHandler<ResponseType> {
820820
}
821821
}
822822
}
823+
824+
extensionRequestBodyLength{
825+
init(_ body:HTTPClient.Body?){
826+
guardlet body= bodyelse{
827+
self=.fixed(length:0)
828+
return
829+
}
830+
guardlet length= body.lengthelse{
831+
self=.dynamic
832+
return
833+
}
834+
self=.fixed(length: length)
835+
}
836+
}

‎Sources/AsyncHTTPClient/RequestValidation.swift‎

Lines changed: 49 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -16,90 +16,32 @@ import NIOCore
1616
import NIOHTTP1
1717

1818
extensionHTTPHeaders{
19-
mutatingfunc validate(method:HTTPMethod, body:HTTPClient.Body?)throws->RequestFramingMetadata{
20-
varmetadata=RequestFramingMetadata(connectionClose:false, body:.fixedSize(0))
21-
22-
ifself[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-
ifself.contains(name:"Transfer-Encoding"),self.contains(name:"Content-Length"){
28-
throwHTTPClientError.incompatibleHeaders
29-
}
30-
31-
vartransferEncoding:String?
32-
varcontentLength:Int?
33-
letencodings=self[canonicalForm:"Transfer-Encoding"].map{ $0.lowercased()}
34-
35-
guard !encodings.contains("identity")else{
36-
throwHTTPClientError.identityCodingIncorrectlyPresent
37-
}
38-
39-
self.remove(name:"Transfer-Encoding")
40-
19+
mutatingfunc validateAndSetTransportFraming(
20+
method:HTTPMethod,
21+
bodyLength:RequestBodyLength
22+
)throws->RequestFramingMetadata{
4123
tryself.validateFieldNames()
4224

43-
guardlet body= bodyelse{
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-
throwHTTPClientError.traceRequestWithBody
66-
}
67-
68-
guard(encodings.lazy.filter{ $0=="chunked"}.count<=1)else{
69-
throwHTTPClientError.chunkedSpecifiedMultipleTimes
70-
}
71-
72-
if encodings.isEmpty{
73-
iflet length= body.length{
74-
self.remove(name:"Content-Length")
75-
contentLength= length
76-
}elseif !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-
guardlet length= body.lengthelse{
85-
throwHTTPClientError.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+
throwHTTPClientError.traceRequestWithBody
8833
}
8934
}
9035

91-
// add headers if required
92-
iflet enc= transferEncoding{
93-
self.add(name:"Transfer-Encoding", value: enc)
94-
metadata.body=.stream
95-
}elseiflet 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+
letconnectionClose=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
privatefunc validateFieldNames()throws{
@@ -137,4 +79,34 @@ extension HTTPHeaders {
13779
throwHTTPClientError.invalidHeaderFieldNames(invalidFieldNames)
13880
}
13981
}
82+
83+
privatemutatingfunc 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
}

‎Tests/AsyncHTTPClientTests/HTTPClientTests+XCTest.swift‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ extension HTTPClientTests {
128128
("testErrorAfterCloseWhileBackpressureExerted", testErrorAfterCloseWhileBackpressureExerted),
129129
("testRequestSpecificTLS", testRequestSpecificTLS),
130130
("testConnectionPoolSizeConfigValueIsRespected", testConnectionPoolSizeConfigValueIsRespected),
131-
("testRequestWithHeaderTransferEncodingIdentityFails",testRequestWithHeaderTransferEncodingIdentityFails),
131+
("testRequestWithHeaderTransferEncodingIdentityDoesNotFail",testRequestWithHeaderTransferEncodingIdentityDoesNotFail),
132132
]
133133
}
134134
}

‎Tests/AsyncHTTPClientTests/HTTPClientTests.swift‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2941,22 +2941,23 @@ class HTTPClientTests: XCTestCase {
29412941
XCTAssertEqual(httpBin.createdConnections, poolSize)
29422942
}
29432943

2944-
functestRequestWithHeaderTransferEncodingIdentityFails(){
2944+
functestRequestWithHeaderTransferEncodingIdentityDoesNotFail(){
29452945
letgroup=MultiThreadedEventLoopGroup(numberOfThreads:1)
29462946
defer{XCTAssertNoThrow(try group.syncShutdownGracefully())}
29472947

29482948
letclient=HTTPClient(eventLoopGroupProvider:.shared(group))
29492949
defer{XCTAssertNoThrow(try client.syncShutdown())}
29502950

2951-
guardvar request=try?Request(url:"http://localhost/get")else{
2951+
lethttpBin=HTTPBin()
2952+
defer{XCTAssertNoThrow(try httpBin.shutdown())}
2953+
2954+
guardvar request=try?Request(url:"http://127.0.0.1:\(httpBin.port)/get")else{
29522955
returnXCTFail("Expected to have a request here.")
29532956
}
29542957
request.headers.add(name:"X-Test-Header", value:"X-Test-Value")
29552958
request.headers.add(name:"Transfer-Encoding", value:"identity")
29562959
request.body=.string("1234")
29572960

2958-
XCTAssertThrowsError(try client.execute(request: request).wait()){
2959-
XCTAssertEqual($0as?HTTPClientError,.identityCodingIncorrectlyPresent)
2960-
}
2961+
XCTAssertNoThrow(try client.execute(request: request).wait())
29612962
}
29622963
}

‎Tests/AsyncHTTPClientTests/RequestValidationTests+XCTest.swift‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ extension RequestValidationTests {
4343
("testBothHeadersNoBody", testBothHeadersNoBody),
4444
("testBothHeadersHasBody", testBothHeadersHasBody),
4545
("testHostHeaderIsSetCorrectlyInCreateRequestHead", testHostHeaderIsSetCorrectlyInCreateRequestHead),
46+
("testTraceMethodIsNotAllowedToHaveAFixedLengthBody", testTraceMethodIsNotAllowedToHaveAFixedLengthBody),
47+
("testTraceMethodIsNotAllowedToHaveADynamicLengthBody", testTraceMethodIsNotAllowedToHaveADynamicLengthBody),
48+
("testTransferEncodingsAreOverwrittenIfBodyLengthIsFixed", testTransferEncodingsAreOverwrittenIfBodyLengthIsFixed),
49+
("testTransferEncodingsAreOverwrittenIfBodyLengthIsDynamic", testTransferEncodingsAreOverwrittenIfBodyLengthIsDynamic),
4650
]
4751
}
4852
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp