11# frozen_string_literal: true
22require "http/headers"
3+ require "http/request/body"
34
45module HTTP
56 class Request
@@ -16,16 +17,11 @@ class Writer
1617 # End of a chunked transfer
1718 CHUNKED_END = "#{ ZERO } #{ CRLF } #{ CRLF } " . freeze
1819
19- # Types valid to be used as body source
20- VALID_BODY_TYPES = [ String , NilClass , Enumerable ] . freeze
21-
2220 def initialize ( socket , body , headers , headline )
23- @body = body
21+ @body = Body . new ( body )
2422 @socket = socket
2523 @headers = headers
2624 @request_header = [ headline ]
27-
28- validate_body_type!
2925 end
3026
3127 # Adds headers to the request header from the headers array
@@ -51,13 +47,9 @@ def connect_through_proxy
5147 # Adds the headers to the header array for the given request body we are working
5248 # with
5349 def add_body_type_headers
54- if @body . is_a? ( String ) && !@headers [ Headers ::CONTENT_LENGTH ]
55- @request_header << "#{ Headers ::CONTENT_LENGTH } : #{ @body . bytesize } "
56- elsif @body . nil? && !@headers [ Headers ::CONTENT_LENGTH ]
57- @request_header << "#{ Headers ::CONTENT_LENGTH } : 0"
58- elsif @body . is_a? ( Enumerable ) && CHUNKED != @headers [ Headers ::TRANSFER_ENCODING ]
59- raise ( RequestError , "invalid transfer encoding" )
60- end
50+ return if @headers [ Headers ::CONTENT_LENGTH ] || chunked?
51+
52+ @request_header << "#{ Headers ::CONTENT_LENGTH } : #{ @body . size } "
6153 end
6254
6355 # Joins the headers specified in the request into a correctly formatted
@@ -70,28 +62,42 @@ def join_headers
7062
7163 def send_request
7264 headers = join_headers
65+ chunks = @body . each
7366
7467 # It's important to send the request in a single write call when
7568 # possible in order to play nicely with Nagle's algorithm. Making
7669 # two writes in a row triggers a pathological case where Nagle is
7770 # expecting a third write that never happens.
78- case @body
79- when NilClass
80- write ( headers )
81- when String
82- write ( headers << @body )
83- when Enumerable
71+ begin
72+ first_chunk = encode_chunk ( chunks . next )
73+ write ( headers << first_chunk )
74+ rescue StopIteration
8475 write ( headers )
76+ end
8577
86- @body . each do |chunk |
87- write ( chunk . bytesize . to_s ( 16 ) << CRLF << chunk << CRLF )
88- end
78+ # Kernel#loop automatically rescues StopIteration
79+ loop do
80+ data = encode_chunk ( chunks . next )
81+ write ( data )
82+ end
8983
90- write ( CHUNKED_END )
91- else raise TypeError , "invalid body type: #{ @body . class } "
84+ write ( CHUNKED_END ) if chunked?
85+ end
86+
87+ # Returns the chunk encoded for to the specified "Transfer-Encoding" header.
88+ def encode_chunk ( chunk )
89+ if chunked?
90+ chunk . bytesize . to_s ( 16 ) << CRLF << chunk << CRLF
91+ else
92+ chunk
9293 end
9394 end
9495
96+ # Returns true if the request should be sent in chunked encoding.
97+ def chunked?
98+ @headers [ Headers ::TRANSFER_ENCODING ] == CHUNKED
99+ end
100+
95101 private
96102
97103 def write ( data )
@@ -101,11 +107,6 @@ def write(data)
101107 data = data . byteslice ( length ..-1 )
102108 end
103109 end
104-
105- def validate_body_type!
106- return if VALID_BODY_TYPES . any? { |type | @body . is_a? type }
107- raise RequestError , "body of wrong type: #{ @body . class } "
108- end
109110 end
110111 end
111112end
0 commit comments