Project

General

Profile

Feature #6435 ยป net.http.breakup.patch

drbrain (Eric Hodel), 05/16/2012 05:01 AM

View differences:

lib/net/http/backward.rb (revision 0)
1
# for backward compatibility
2

  
3
# :enddoc:
4

  
5
class Net::HTTP
6
  ProxyMod = ProxyDelta
7
  HTTPSession = self
8
end
9

  
10
module Net::NetPrivate
11
  HTTPRequest = ::Net::HTTPRequest
12
end
13

  
14
Net::HTTPInformationCode  = Net::HTTPInformation
15
Net::HTTPSuccessCode      = Net::HTTPSuccess
16
Net::HTTPRedirectionCode  = Net::HTTPRedirection
17
Net::HTTPRetriableCode    = Net::HTTPRedirection
18
Net::HTTPClientErrorCode  = Net::HTTPClientError
19
Net::HTTPFatalErrorCode   = Net::HTTPClientError
20
Net::HTTPServerErrorCode  = Net::HTTPServerError
21
Net::HTTPResponceReceiver = Net::HTTPResponse
22

  
lib/net/http/response.rb (revision 0)
1
# HTTP response class.
2
#
3
# This class wraps together the response header and the response body (the
4
# entity requested).
5
#
6
# It mixes in the HTTPHeader module, which provides access to response
7
# header values both via hash-like methods and via individual readers.
8
#
9
# Note that each possible HTTP response code defines its own
10
# HTTPResponse subclass.  These are listed below.
11
#
12
# All classes are
13
# defined under the Net module. Indentation indicates inheritance.
14
#
15
#   xxx        HTTPResponse
16
#
17
#     1xx        HTTPInformation
18
#       100        HTTPContinue
19
#       101        HTTPSwitchProtocol
20
#
21
#     2xx        HTTPSuccess
22
#       200        HTTPOK
23
#       201        HTTPCreated
24
#       202        HTTPAccepted
25
#       203        HTTPNonAuthoritativeInformation
26
#       204        HTTPNoContent
27
#       205        HTTPResetContent
28
#       206        HTTPPartialContent
29
#
30
#     3xx        HTTPRedirection
31
#       300        HTTPMultipleChoice
32
#       301        HTTPMovedPermanently
33
#       302        HTTPFound
34
#       303        HTTPSeeOther
35
#       304        HTTPNotModified
36
#       305        HTTPUseProxy
37
#       307        HTTPTemporaryRedirect
38
#
39
#     4xx        HTTPClientError
40
#       400        HTTPBadRequest
41
#       401        HTTPUnauthorized
42
#       402        HTTPPaymentRequired
43
#       403        HTTPForbidden
44
#       404        HTTPNotFound
45
#       405        HTTPMethodNotAllowed
46
#       406        HTTPNotAcceptable
47
#       407        HTTPProxyAuthenticationRequired
48
#       408        HTTPRequestTimeOut
49
#       409        HTTPConflict
50
#       410        HTTPGone
51
#       411        HTTPLengthRequired
52
#       412        HTTPPreconditionFailed
53
#       413        HTTPRequestEntityTooLarge
54
#       414        HTTPRequestURITooLong
55
#       415        HTTPUnsupportedMediaType
56
#       416        HTTPRequestedRangeNotSatisfiable
57
#       417        HTTPExpectationFailed
58
#
59
#     5xx        HTTPServerError
60
#       500        HTTPInternalServerError
61
#       501        HTTPNotImplemented
62
#       502        HTTPBadGateway
63
#       503        HTTPServiceUnavailable
64
#       504        HTTPGatewayTimeOut
65
#       505        HTTPVersionNotSupported
66
#
67
#     xxx        HTTPUnknownResponse
68
#
69
class Net::HTTPResponse
70
  class << self
71
    # true if the response has a body.
72
    def body_permitted?
73
      self::HAS_BODY
74
    end
75

  
76
    def exception_type   # :nodoc: internal use only
77
      self::EXCEPTION_TYPE
78
    end
79

  
80
    def read_new(sock)   #:nodoc: internal use only
81
      httpv, code, msg = read_status_line(sock)
82
      res = response_class(code).new(httpv, code, msg)
83
      each_response_header(sock) do |k,v|
84
        res.add_field k, v
85
      end
86
      res
87
    end
88

  
89
    private
90

  
91
    def read_status_line(sock)
92
      str = sock.readline
93
      m = /\AHTTP(?:\/(\d+\.\d+))?\s+(\d\d\d)\s*(.*)\z/in.match(str) or
94
        raise Net::HTTPBadResponse, "wrong status line: #{str.dump}"
95
      m.captures
96
    end
97

  
98
    def response_class(code)
99
      CODE_TO_OBJ[code] or
100
      CODE_CLASS_TO_OBJ[code[0,1]] or
101
      Net::HTTPUnknownResponse
102
    end
103

  
104
    def each_response_header(sock)
105
      key = value = nil
106
      while true
107
        line = sock.readuntil("\n", true).sub(/\s+\z/, '')
108
        break if line.empty?
109
        if line[0] == ?\s or line[0] == ?\t and value
110
          value << ' ' unless value.empty?
111
          value << line.strip
112
        else
113
          yield key, value if key
114
          key, value = line.strip.split(/\s*:\s*/, 2)
115
          raise Net::HTTPBadResponse, 'wrong header line format' if value.nil?
116
        end
117
      end
118
      yield key, value if key
119
    end
120
  end
121

  
122
  # next is to fix bug in RDoc, where the private inside class << self
123
  # spills out.
124
  public
125

  
126
  include Net::HTTPHeader
127

  
128
  def initialize(httpv, code, msg)   #:nodoc: internal use only
129
    @http_version = httpv
130
    @code         = code
131
    @message      = msg
132
    initialize_http_header nil
133
    @body = nil
134
    @read = false
135
  end
136

  
137
  # The HTTP version supported by the server.
138
  attr_reader :http_version
139

  
140
  # The HTTP result code string. For example, '302'.  You can also
141
  # determine the response type by examining which response subclass
142
  # the response object is an instance of.
143
  attr_reader :code
144

  
145
  # The HTTP result message sent by the server. For example, 'Not Found'.
146
  attr_reader :message
147
  alias msg message   # :nodoc: obsolete
148

  
149
  def inspect
150
    "#<#{self.class} #{@code} #{@message} readbody=#{@read}>"
151
  end
152

  
153
  #
154
  # response <-> exception relationship
155
  #
156

  
157
  def code_type   #:nodoc:
158
    self.class
159
  end
160

  
161
  def error!   #:nodoc:
162
    raise error_type().new(@code + ' ' + @message.dump, self)
163
  end
164

  
165
  def error_type   #:nodoc:
166
    self.class::EXCEPTION_TYPE
167
  end
168

  
169
  # Raises an HTTP error if the response is not 2xx (success).
170
  def value
171
    error! unless self.kind_of?(Net::HTTPSuccess)
172
  end
173

  
174
  #
175
  # header (for backward compatibility only; DO NOT USE)
176
  #
177

  
178
  def response   #:nodoc:
179
    warn "#{caller(1)[0]}: warning: Net::HTTPResponse#response is obsolete" if $VERBOSE
180
    self
181
  end
182

  
183
  def header   #:nodoc:
184
    warn "#{caller(1)[0]}: warning: Net::HTTPResponse#header is obsolete" if $VERBOSE
185
    self
186
  end
187

  
188
  def read_header   #:nodoc:
189
    warn "#{caller(1)[0]}: warning: Net::HTTPResponse#read_header is obsolete" if $VERBOSE
190
    self
191
  end
192

  
193
  #
194
  # body
195
  #
196

  
197
  def reading_body(sock, reqmethodallowbody)  #:nodoc: internal use only
198
    @socket = sock
199
    @body_exist = reqmethodallowbody && self.class.body_permitted?
200
    begin
201
      yield
202
      self.body   # ensure to read body
203
    ensure
204
      @socket = nil
205
    end
206
  end
207

  
208
  # Gets the entity body returned by the remote HTTP server.
209
  #
210
  # If a block is given, the body is passed to the block, and
211
  # the body is provided in fragments, as it is read in from the socket.
212
  #
213
  # Calling this method a second or subsequent time for the same
214
  # HTTPResponse object will return the value already read.
215
  #
216
  #   http.request_get('/index.html') {|res|
217
  #     puts res.read_body
218
  #   }
219
  #
220
  #   http.request_get('/index.html') {|res|
221
  #     p res.read_body.object_id   # 538149362
222
  #     p res.read_body.object_id   # 538149362
223
  #   }
224
  #
225
  #   # using iterator
226
  #   http.request_get('/index.html') {|res|
227
  #     res.read_body do |segment|
228
  #       print segment
229
  #     end
230
  #   }
231
  #
232
  def read_body(dest = nil, &block)
233
    if @read
234
      raise IOError, "#{self.class}\#read_body called twice" if dest or block
235
      return @body
236
    end
237
    to = procdest(dest, block)
238
    stream_check
239
    if @body_exist
240
      read_body_0 to
241
      @body = to
242
    else
243
      @body = nil
244
    end
245
    @read = true
246

  
247
    @body
248
  end
249

  
250
  # Returns the full entity body.
251
  #
252
  # Calling this method a second or subsequent time will return the
253
  # string already read.
254
  #
255
  #   http.request_get('/index.html') {|res|
256
  #     puts res.body
257
  #   }
258
  #
259
  #   http.request_get('/index.html') {|res|
260
  #     p res.body.object_id   # 538149362
261
  #     p res.body.object_id   # 538149362
262
  #   }
263
  #
264
  def body
265
    read_body()
266
  end
267

  
268
  # Because it may be necessary to modify the body, Eg, decompression
269
  # this method facilitates that.
270
  def body=(value)
271
    @body = value
272
  end
273

  
274
  alias entity body   #:nodoc: obsolete
275

  
276
  private
277

  
278
  def read_body_0(dest)
279
    if chunked?
280
      read_chunked dest
281
      return
282
    end
283
    clen = content_length()
284
    if clen
285
      @socket.read clen, dest, true   # ignore EOF
286
      return
287
    end
288
    clen = range_length()
289
    if clen
290
      @socket.read clen, dest
291
      return
292
    end
293
    @socket.read_all dest
294
  end
295

  
296
  def read_chunked(dest)
297
    len = nil
298
    total = 0
299
    while true
300
      line = @socket.readline
301
      hexlen = line.slice(/[0-9a-fA-F]+/) or
302
          raise Net::HTTPBadResponse, "wrong chunk size line: #{line}"
303
      len = hexlen.hex
304
      break if len == 0
305
      begin
306
        @socket.read len, dest
307
      ensure
308
        total += len
309
        @socket.read 2   # \r\n
310
      end
311
    end
312
    until @socket.readline.empty?
313
      # none
314
    end
315
  end
316

  
317
  def stream_check
318
    raise IOError, 'attempt to read body out of block' if @socket.closed?
319
  end
320

  
321
  def procdest(dest, block)
322
    raise ArgumentError, 'both arg and block given for HTTP method' \
323
        if dest and block
324
    if block
325
      Net::ReadAdapter.new(block)
326
    else
327
      dest || ''
328
    end
329
  end
330

  
331
end
332

  
lib/net/http/exceptions.rb (revision 0)
1
# Net::HTTP exception class.
2
# You cannot use Net::HTTPExceptions directly; instead, you must use
3
# its subclasses.
4
module Net::HTTPExceptions
5
  def initialize(msg, res)   #:nodoc:
6
    super msg
7
    @response = res
8
  end
9
  attr_reader :response
10
  alias data response    #:nodoc: obsolete
11
end
12
class Net::HTTPError < Net::ProtocolError
13
  include Net::HTTPExceptions
14
end
15
class Net::HTTPRetriableError < Net::ProtoRetriableError
16
  include Net::HTTPExceptions
17
end
18
class Net::HTTPServerException < Net::ProtoServerError
19
  # We cannot use the name "HTTPServerError", it is the name of the response.
20
  include Net::HTTPExceptions
21
end
22
class Net::HTTPFatalError < Net::ProtoFatalError
23
  include Net::HTTPExceptions
24
end
25

  
lib/net/http/responses.rb (revision 0)
1
# :stopdoc:
2
class Net::HTTPUnknownResponse < Net::HTTPResponse
3
  HAS_BODY = true
4
  EXCEPTION_TYPE = Net::HTTPError
5
end
6
class Net::HTTPInformation < Net::HTTPResponse           # 1xx
7
  HAS_BODY = false
8
  EXCEPTION_TYPE = Net::HTTPError
9
end
10
class Net::HTTPSuccess < Net::HTTPResponse               # 2xx
11
  HAS_BODY = true
12
  EXCEPTION_TYPE = Net::HTTPError
13
end
14
class Net::HTTPRedirection < Net::HTTPResponse           # 3xx
15
  HAS_BODY = true
16
  EXCEPTION_TYPE = Net::HTTPRetriableError
17
end
18
class Net::HTTPClientError < Net::HTTPResponse           # 4xx
19
  HAS_BODY = true
20
  EXCEPTION_TYPE = Net::HTTPServerException   # for backward compatibility
21
end
22
class Net::HTTPServerError < Net::HTTPResponse           # 5xx
23
  HAS_BODY = true
24
  EXCEPTION_TYPE = Net::HTTPFatalError    # for backward compatibility
25
end
26

  
27
class Net::HTTPContinue < Net::HTTPInformation           # 100
28
  HAS_BODY = false
29
end
30
class Net::HTTPSwitchProtocol < Net::HTTPInformation     # 101
31
  HAS_BODY = false
32
end
33

  
34
class Net::HTTPOK < Net::HTTPSuccess                            # 200
35
  HAS_BODY = true
36
end
37
class Net::HTTPCreated < Net::HTTPSuccess                       # 201
38
  HAS_BODY = true
39
end
40
class Net::HTTPAccepted < Net::HTTPSuccess                      # 202
41
  HAS_BODY = true
42
end
43
class Net::HTTPNonAuthoritativeInformation < Net::HTTPSuccess   # 203
44
  HAS_BODY = true
45
end
46
class Net::HTTPNoContent < Net::HTTPSuccess                     # 204
47
  HAS_BODY = false
48
end
49
class Net::HTTPResetContent < Net::HTTPSuccess                  # 205
50
  HAS_BODY = false
51
end
52
class Net::HTTPPartialContent < Net::HTTPSuccess                # 206
53
  HAS_BODY = true
54
end
55

  
56
class Net::HTTPMultipleChoice < Net::HTTPRedirection     # 300
57
  HAS_BODY = true
58
end
59
class Net::HTTPMovedPermanently < Net::HTTPRedirection   # 301
60
  HAS_BODY = true
61
end
62
class Net::HTTPFound < Net::HTTPRedirection              # 302
63
  HAS_BODY = true
64
end
65
Net::HTTPMovedTemporarily = Net::HTTPFound
66
class Net::HTTPSeeOther < Net::HTTPRedirection           # 303
67
  HAS_BODY = true
68
end
69
class Net::HTTPNotModified < Net::HTTPRedirection        # 304
70
  HAS_BODY = false
71
end
72
class Net::HTTPUseProxy < Net::HTTPRedirection           # 305
73
  HAS_BODY = false
74
end
75
# 306 unused
76
class Net::HTTPTemporaryRedirect < Net::HTTPRedirection  # 307
77
  HAS_BODY = true
78
end
79

  
80
class Net::HTTPBadRequest < Net::HTTPClientError                    # 400
81
  HAS_BODY = true
82
end
83
class Net::HTTPUnauthorized < Net::HTTPClientError                  # 401
84
  HAS_BODY = true
85
end
86
class Net::HTTPPaymentRequired < Net::HTTPClientError               # 402
87
  HAS_BODY = true
88
end
89
class Net::HTTPForbidden < Net::HTTPClientError                     # 403
90
  HAS_BODY = true
91
end
92
class Net::HTTPNotFound < Net::HTTPClientError                      # 404
93
  HAS_BODY = true
94
end
95
class Net::HTTPMethodNotAllowed < Net::HTTPClientError              # 405
96
  HAS_BODY = true
97
end
98
class Net::HTTPNotAcceptable < Net::HTTPClientError                 # 406
99
  HAS_BODY = true
100
end
101
class Net::HTTPProxyAuthenticationRequired < Net::HTTPClientError   # 407
102
  HAS_BODY = true
103
end
104
class Net::HTTPRequestTimeOut < Net::HTTPClientError                # 408
105
  HAS_BODY = true
106
end
107
class Net::HTTPConflict < Net::HTTPClientError                      # 409
108
  HAS_BODY = true
109
end
110
class Net::HTTPGone < Net::HTTPClientError                          # 410
111
  HAS_BODY = true
112
end
113
class Net::HTTPLengthRequired < Net::HTTPClientError                # 411
114
  HAS_BODY = true
115
end
116
class Net::HTTPPreconditionFailed < Net::HTTPClientError            # 412
117
  HAS_BODY = true
118
end
119
class Net::HTTPRequestEntityTooLarge < Net::HTTPClientError         # 413
120
  HAS_BODY = true
121
end
122
class Net::HTTPRequestURITooLong < Net::HTTPClientError             # 414
123
  HAS_BODY = true
124
end
125
Net::HTTPRequestURITooLarge = Net::HTTPRequestURITooLong
126
class Net::HTTPUnsupportedMediaType < Net::HTTPClientError          # 415
127
  HAS_BODY = true
128
end
129
class Net::HTTPRequestedRangeNotSatisfiable < Net::HTTPClientError  # 416
130
  HAS_BODY = true
131
end
132
class Net::HTTPExpectationFailed < Net::HTTPClientError             # 417
133
  HAS_BODY = true
134
end
135

  
136
class Net::HTTPInternalServerError < Net::HTTPServerError   # 500
137
  HAS_BODY = true
138
end
139
class Net::HTTPNotImplemented < Net::HTTPServerError        # 501
140
  HAS_BODY = true
141
end
142
class Net::HTTPBadGateway < Net::HTTPServerError            # 502
143
  HAS_BODY = true
144
end
145
class Net::HTTPServiceUnavailable < Net::HTTPServerError    # 503
146
  HAS_BODY = true
147
end
148
class Net::HTTPGatewayTimeOut < Net::HTTPServerError        # 504
149
  HAS_BODY = true
150
end
151
class Net::HTTPVersionNotSupported < Net::HTTPServerError   # 505
152
  HAS_BODY = true
153
end
154

  
155
class Net::HTTPResponse
156
  CODE_CLASS_TO_OBJ = {
157
    '1' => Net::HTTPInformation,
158
    '2' => Net::HTTPSuccess,
159
    '3' => Net::HTTPRedirection,
160
    '4' => Net::HTTPClientError,
161
    '5' => Net::HTTPServerError
162
  }
163
  CODE_TO_OBJ = {
164
    '100' => Net::HTTPContinue,
165
    '101' => Net::HTTPSwitchProtocol,
166

  
167
    '200' => Net::HTTPOK,
168
    '201' => Net::HTTPCreated,
169
    '202' => Net::HTTPAccepted,
170
    '203' => Net::HTTPNonAuthoritativeInformation,
171
    '204' => Net::HTTPNoContent,
172
    '205' => Net::HTTPResetContent,
173
    '206' => Net::HTTPPartialContent,
174

  
175
    '300' => Net::HTTPMultipleChoice,
176
    '301' => Net::HTTPMovedPermanently,
177
    '302' => Net::HTTPFound,
178
    '303' => Net::HTTPSeeOther,
179
    '304' => Net::HTTPNotModified,
180
    '305' => Net::HTTPUseProxy,
181
    '307' => Net::HTTPTemporaryRedirect,
182

  
183
    '400' => Net::HTTPBadRequest,
184
    '401' => Net::HTTPUnauthorized,
185
    '402' => Net::HTTPPaymentRequired,
186
    '403' => Net::HTTPForbidden,
187
    '404' => Net::HTTPNotFound,
188
    '405' => Net::HTTPMethodNotAllowed,
189
    '406' => Net::HTTPNotAcceptable,
190
    '407' => Net::HTTPProxyAuthenticationRequired,
191
    '408' => Net::HTTPRequestTimeOut,
192
    '409' => Net::HTTPConflict,
193
    '410' => Net::HTTPGone,
194
    '411' => Net::HTTPLengthRequired,
195
    '412' => Net::HTTPPreconditionFailed,
196
    '413' => Net::HTTPRequestEntityTooLarge,
197
    '414' => Net::HTTPRequestURITooLong,
198
    '415' => Net::HTTPUnsupportedMediaType,
199
    '416' => Net::HTTPRequestedRangeNotSatisfiable,
200
    '417' => Net::HTTPExpectationFailed,
201

  
202
    '500' => Net::HTTPInternalServerError,
203
    '501' => Net::HTTPNotImplemented,
204
    '502' => Net::HTTPBadGateway,
205
    '503' => Net::HTTPServiceUnavailable,
206
    '504' => Net::HTTPGatewayTimeOut,
207
    '505' => Net::HTTPVersionNotSupported
208
  }
209
end
210

  
211
# :startdoc:
212

  
lib/net/http/generic_request.rb (revision 0)
1
# HTTPGenericRequest is the parent of the HTTPRequest class.
2
# Do not use this directly; use a subclass of HTTPRequest.
3
#
4
# Mixes in the HTTPHeader module to provide easier access to HTTP headers.
5
#
6
class Net::HTTPGenericRequest
7

  
8
  include Net::HTTPHeader
9

  
10
  def initialize(m, reqbody, resbody, path, initheader = nil)
11
    @method = m
12
    @request_has_body = reqbody
13
    @response_has_body = resbody
14
    raise ArgumentError, "no HTTP request path given" unless path
15
    raise ArgumentError, "HTTP request path is empty" if path.empty?
16
    @path = path
17
    initialize_http_header initheader
18
    self['Accept'] ||= '*/*'
19
    self['User-Agent'] ||= 'Ruby'
20
    @body = nil
21
    @body_stream = nil
22
    @body_data = nil
23
  end
24

  
25
  attr_reader :method
26
  attr_reader :path
27

  
28
  def inspect
29
    "\#<#{self.class} #{@method}>"
30
  end
31

  
32
  def request_body_permitted?
33
    @request_has_body
34
  end
35

  
36
  def response_body_permitted?
37
    @response_has_body
38
  end
39

  
40
  def body_exist?
41
    warn "Net::HTTPRequest#body_exist? is obsolete; use response_body_permitted?" if $VERBOSE
42
    response_body_permitted?
43
  end
44

  
45
  attr_reader :body
46

  
47
  def body=(str)
48
    @body = str
49
    @body_stream = nil
50
    @body_data = nil
51
    str
52
  end
53

  
54
  attr_reader :body_stream
55

  
56
  def body_stream=(input)
57
    @body = nil
58
    @body_stream = input
59
    @body_data = nil
60
    input
61
  end
62

  
63
  def set_body_internal(str)   #:nodoc: internal use only
64
    raise ArgumentError, "both of body argument and HTTPRequest#body set" if str and (@body or @body_stream)
65
    self.body = str if str
66
  end
67

  
68
  #
69
  # write
70
  #
71

  
72
  def exec(sock, ver, path)   #:nodoc: internal use only
73
    if @body
74
      send_request_with_body sock, ver, path, @body
75
    elsif @body_stream
76
      send_request_with_body_stream sock, ver, path, @body_stream
77
    elsif @body_data
78
      send_request_with_body_data sock, ver, path, @body_data
79
    else
80
      write_header sock, ver, path
81
    end
82
  end
83

  
84
  private
85

  
86
  class Chunker #:nodoc:
87
    def initialize(sock)
88
      @sock = sock
89
      @prev = nil
90
    end
91

  
92
    def write(buf)
93
      # avoid memcpy() of buf, buf can huge and eat memory bandwidth
94
      @sock.write("#{buf.bytesize.to_s(16)}\r\n")
95
      rv = @sock.write(buf)
96
      @sock.write("\r\n")
97
      rv
98
    end
99

  
100
    def finish
101
      @sock.write("0\r\n\r\n")
102
    end
103
  end
104

  
105
  def send_request_with_body(sock, ver, path, body)
106
    self.content_length = body.bytesize
107
    delete 'Transfer-Encoding'
108
    supply_default_content_type
109
    write_header sock, ver, path
110
    wait_for_continue sock, ver if sock.continue_timeout
111
    sock.write body
112
  end
113

  
114
  def send_request_with_body_stream(sock, ver, path, f)
115
    unless content_length() or chunked?
116
      raise ArgumentError,
117
          "Content-Length not given and Transfer-Encoding is not `chunked'"
118
    end
119
    supply_default_content_type
120
    write_header sock, ver, path
121
    wait_for_continue sock, ver if sock.continue_timeout
122
    if chunked?
123
      chunker = Chunker.new(sock)
124
      IO.copy_stream(f, chunker)
125
      chunker.finish
126
    else
127
      # copy_stream can sendfile() to sock.io unless we use SSL.
128
      # If sock.io is an SSLSocket, copy_stream will hit SSL_write()
129
      IO.copy_stream(f, sock.io)
130
    end
131
  end
132

  
133
  def send_request_with_body_data(sock, ver, path, params)
134
    if /\Amultipart\/form-data\z/i !~ self.content_type
135
      self.content_type = 'application/x-www-form-urlencoded'
136
      return send_request_with_body(sock, ver, path, URI.encode_www_form(params))
137
    end
138

  
139
    opt = @form_option.dup
140
    require 'securerandom' unless defined?(SecureRandom)
141
    opt[:boundary] ||= SecureRandom.urlsafe_base64(40)
142
    self.set_content_type(self.content_type, boundary: opt[:boundary])
143
    if chunked?
144
      write_header sock, ver, path
145
      encode_multipart_form_data(sock, params, opt)
146
    else
147
      require 'tempfile'
148
      file = Tempfile.new('multipart')
149
      file.binmode
150
      encode_multipart_form_data(file, params, opt)
151
      file.rewind
152
      self.content_length = file.size
153
      write_header sock, ver, path
154
      IO.copy_stream(file, sock)
155
    end
156
  end
157

  
158
  def encode_multipart_form_data(out, params, opt)
159
    charset = opt[:charset]
160
    boundary = opt[:boundary]
161
    require 'securerandom' unless defined?(SecureRandom)
162
    boundary ||= SecureRandom.urlsafe_base64(40)
163
    chunked_p = chunked?
164

  
165
    buf = ''
166
    params.each do |key, value, h={}|
167
      key = quote_string(key, charset)
168
      filename =
169
        h.key?(:filename) ? h[:filename] :
170
        value.respond_to?(:to_path) ? File.basename(value.to_path) :
171
        nil
172

  
173
      buf << "--#{boundary}\r\n"
174
      if filename
175
        filename = quote_string(filename, charset)
176
        type = h[:content_type] || 'application/octet-stream'
177
        buf << "Content-Disposition: form-data; " \
178
          "name=\"#{key}\"; filename=\"#{filename}\"\r\n" \
179
          "Content-Type: #{type}\r\n\r\n"
180
        if !out.respond_to?(:write) || !value.respond_to?(:read)
181
          # if +out+ is not an IO or +value+ is not an IO
182
          buf << (value.respond_to?(:read) ? value.read : value)
183
        elsif value.respond_to?(:size) && chunked_p
184
          # if +out+ is an IO and +value+ is a File, use IO.copy_stream
185
          flush_buffer(out, buf, chunked_p)
186
          out << "%x\r\n" % value.size if chunked_p
187
          IO.copy_stream(value, out)
188
          out << "\r\n" if chunked_p
189
        else
190
          # +out+ is an IO, and +value+ is not a File but an IO
191
          flush_buffer(out, buf, chunked_p)
192
          1 while flush_buffer(out, value.read(4096), chunked_p)
193
        end
194
      else
195
        # non-file field:
196
        #   HTML5 says, "The parts of the generated multipart/form-data
197
        #   resource that correspond to non-file fields must not have a
198
        #   Content-Type header specified."
199
        buf << "Content-Disposition: form-data; name=\"#{key}\"\r\n\r\n"
200
        buf << (value.respond_to?(:read) ? value.read : value)
201
      end
202
      buf << "\r\n"
203
    end
204
    buf << "--#{boundary}--\r\n"
205
    flush_buffer(out, buf, chunked_p)
206
    out << "0\r\n\r\n" if chunked_p
207
  end
208

  
209
  def quote_string(str, charset)
210
    str = str.encode(charset, fallback:->(c){'&#%d;'%c.encode("UTF-8").ord}) if charset
211
    str = str.gsub(/[\\"]/, '\\\\\&')
212
  end
213

  
214
  def flush_buffer(out, buf, chunked_p)
215
    return unless buf
216
    out << "%x\r\n"%buf.bytesize if chunked_p
217
    out << buf
218
    out << "\r\n" if chunked_p
219
    buf.clear
220
  end
221

  
222
  def supply_default_content_type
223
    return if content_type()
224
    warn 'net/http: warning: Content-Type did not set; using application/x-www-form-urlencoded' if $VERBOSE
225
    set_content_type 'application/x-www-form-urlencoded'
226
  end
227

  
228
  ##
229
  # Waits up to the continue timeout for a response from the server provided
230
  # we're speaking HTTP 1.1 and are expecting a 100-continue response.
231

  
232
  def wait_for_continue(sock, ver)
233
    if ver >= '1.1' and @header['expect'] and
234
        @header['expect'].include?('100-continue')
235
      if IO.select([sock.io], nil, nil, sock.continue_timeout)
236
        res = Net::HTTPResponse.read_new(sock)
237
        unless res.kind_of?(Net::HTTPContinue)
238
          throw :response, res
239
        end
240
      end
241
    end
242
  end
243

  
244
  def write_header(sock, ver, path)
245
    buf = "#{@method} #{path} HTTP/#{ver}\r\n"
246
    each_capitalized do |k,v|
247
      buf << "#{k}: #{v}\r\n"
248
    end
249
    buf << "\r\n"
250
    sock.write buf
251
  end
252

  
253
end
254

  
lib/net/http/header.rb (revision 0)
1
# The HTTPHeader module defines methods for reading and writing
2
# HTTP headers.
3
#
4
# It is used as a mixin by other classes, to provide hash-like
5
# access to HTTP header values. Unlike raw hash access, HTTPHeader
6
# provides access via case-insensitive keys. It also provides
7
# methods for accessing commonly-used HTTP header values in more
8
# convenient formats.
9
#
10
module Net::HTTPHeader
11

  
12
  def initialize_http_header(initheader)
13
    @header = {}
14
    return unless initheader
15
    initheader.each do |key, value|
16
      warn "net/http: warning: duplicated HTTP header: #{key}" if key?(key) and $VERBOSE
17
      @header[key.downcase] = [value.strip]
18
    end
19
  end
20

  
21
  def size   #:nodoc: obsolete
22
    @header.size
23
  end
24

  
25
  alias length size   #:nodoc: obsolete
26

  
27
  # Returns the header field corresponding to the case-insensitive key.
28
  # For example, a key of "Content-Type" might return "text/html"
29
  def [](key)
30
    a = @header[key.downcase] or return nil
31
    a.join(', ')
32
  end
33

  
34
  # Sets the header field corresponding to the case-insensitive key.
35
  def []=(key, val)
36
    unless val
37
      @header.delete key.downcase
38
      return val
39
    end
40
    @header[key.downcase] = [val]
41
  end
42

  
43
  # [Ruby 1.8.3]
44
  # Adds a value to a named header field, instead of replacing its value.
45
  # Second argument +val+ must be a String.
46
  # See also #[]=, #[] and #get_fields.
47
  #
48
  #   request.add_field 'X-My-Header', 'a'
49
  #   p request['X-My-Header']              #=> "a"
50
  #   p request.get_fields('X-My-Header')   #=> ["a"]
51
  #   request.add_field 'X-My-Header', 'b'
52
  #   p request['X-My-Header']              #=> "a, b"
53
  #   p request.get_fields('X-My-Header')   #=> ["a", "b"]
54
  #   request.add_field 'X-My-Header', 'c'
55
  #   p request['X-My-Header']              #=> "a, b, c"
56
  #   p request.get_fields('X-My-Header')   #=> ["a", "b", "c"]
57
  #
58
  def add_field(key, val)
59
    if @header.key?(key.downcase)
60
      @header[key.downcase].push val
61
    else
62
      @header[key.downcase] = [val]
63
    end
64
  end
65

  
66
  # [Ruby 1.8.3]
67
  # Returns an array of header field strings corresponding to the
68
  # case-insensitive +key+.  This method allows you to get duplicated
69
  # header fields without any processing.  See also #[].
70
  #
71
  #   p response.get_fields('Set-Cookie')
72
  #     #=> ["session=al98axx; expires=Fri, 31-Dec-1999 23:58:23",
73
  #          "query=rubyscript; expires=Fri, 31-Dec-1999 23:58:23"]
74
  #   p response['Set-Cookie']
75
  #     #=> "session=al98axx; expires=Fri, 31-Dec-1999 23:58:23, query=rubyscript; expires=Fri, 31-Dec-1999 23:58:23"
76
  #
77
  def get_fields(key)
78
    return nil unless @header[key.downcase]
79
    @header[key.downcase].dup
80
  end
81

  
82
  # Returns the header field corresponding to the case-insensitive key.
83
  # Returns the default value +args+, or the result of the block, or
84
  # raises an IndexError if there's no header field named +key+
85
  # See Hash#fetch
86
  def fetch(key, *args, &block)   #:yield: +key+
87
    a = @header.fetch(key.downcase, *args, &block)
88
    a.kind_of?(Array) ? a.join(', ') : a
89
  end
90

  
91
  # Iterates through the header names and values, passing in the name
92
  # and value to the code block supplied.
93
  #
94
  # Example:
95
  #
96
  #     response.header.each_header {|key,value| puts "#{key} = #{value}" }
97
  #
98
  def each_header   #:yield: +key+, +value+
99
    block_given? or return enum_for(__method__)
100
    @header.each do |k,va|
101
      yield k, va.join(', ')
102
    end
103
  end
104

  
105
  alias each each_header
106

  
107
  # Iterates through the header names in the header, passing
108
  # each header name to the code block.
109
  def each_name(&block)   #:yield: +key+
110
    block_given? or return enum_for(__method__)
111
    @header.each_key(&block)
112
  end
113

  
114
  alias each_key each_name
115

  
116
  # Iterates through the header names in the header, passing
117
  # capitalized header names to the code block.
118
  #
119
  # Note that header names are capitalized systematically;
120
  # capitalization may not match that used by the remote HTTP
121
  # server in its response.
122
  def each_capitalized_name  #:yield: +key+
123
    block_given? or return enum_for(__method__)
124
    @header.each_key do |k|
125
      yield capitalize(k)
126
    end
127
  end
128

  
129
  # Iterates through header values, passing each value to the
130
  # code block.
131
  def each_value   #:yield: +value+
132
    block_given? or return enum_for(__method__)
133
    @header.each_value do |va|
134
      yield va.join(', ')
135
    end
136
  end
137

  
138
  # Removes a header field, specified by case-insensitive key.
139
  def delete(key)
140
    @header.delete(key.downcase)
141
  end
142

  
143
  # true if +key+ header exists.
144
  def key?(key)
145
    @header.key?(key.downcase)
146
  end
147

  
148
  # Returns a Hash consisting of header names and values.
149
  # e.g.
150
  # {"cache-control" => "private",
151
  #  "content-type" => "text/html",
152
  #  "date" => "Wed, 22 Jun 2005 22:11:50 GMT"}
153
  def to_hash
154
    @header.dup
155
  end
156

  
157
  # As for #each_header, except the keys are provided in capitalized form.
158
  #
159
  # Note that header names are capitalized systematically;
160
  # capitalization may not match that used by the remote HTTP
161
  # server in its response.
162
  def each_capitalized
163
    block_given? or return enum_for(__method__)
164
    @header.each do |k,v|
165
      yield capitalize(k), v.join(', ')
166
    end
167
  end
168

  
169
  alias canonical_each each_capitalized
170

  
171
  def capitalize(name)
172
    name.split(/-/).map {|s| s.capitalize }.join('-')
173
  end
174
  private :capitalize
175

  
176
  # Returns an Array of Range objects which represent the Range:
177
  # HTTP header field, or +nil+ if there is no such header.
178
  def range
179
    return nil unless @header['range']
180
    self['Range'].split(/,/).map {|spec|
181
      m = /bytes\s*=\s*(\d+)?\s*-\s*(\d+)?/i.match(spec) or
182
              raise HTTPHeaderSyntaxError, "wrong Range: #{spec}"
183
      d1 = m[1].to_i
184
      d2 = m[2].to_i
185
      if    m[1] and m[2] then  d1..d2
186
      elsif m[1]          then  d1..-1
187
      elsif          m[2] then -d2..-1
188
      else
189
        raise HTTPHeaderSyntaxError, 'range is not specified'
190
      end
191
    }
192
  end
193

  
194
  # Sets the HTTP Range: header.
195
  # Accepts either a Range object as a single argument,
196
  # or a beginning index and a length from that index.
197
  # Example:
198
  #
199
  #   req.range = (0..1023)
200
  #   req.set_range 0, 1023
201
  #
202
  def set_range(r, e = nil)
203
    unless r
204
      @header.delete 'range'
205
      return r
206
    end
207
    r = (r...r+e) if e
208
    case r
209
    when Numeric
210
      n = r.to_i
211
      rangestr = (n > 0 ? "0-#{n-1}" : "-#{-n}")
212
    when Range
213
      first = r.first
214
      last = r.last
215
      last -= 1 if r.exclude_end?
216
      if last == -1
217
        rangestr = (first > 0 ? "#{first}-" : "-#{-first}")
218
      else
219
        raise Net::HTTPHeaderSyntaxError, 'range.first is negative' if first < 0
220
        raise Net::HTTPHeaderSyntaxError, 'range.last is negative' if last < 0
221
        raise Net::HTTPHeaderSyntaxError, 'must be .first < .last' if first > last
222
        rangestr = "#{first}-#{last}"
223
      end
224
    else
225
      raise TypeError, 'Range/Integer is required'
226
    end
227
    @header['range'] = ["bytes=#{rangestr}"]
228
    r
229
  end
230

  
231
  alias range= set_range
232

  
233
  # Returns an Integer object which represents the HTTP Content-Length:
234
  # header field, or +nil+ if that field was not provided.
235
  def content_length
236
    return nil unless key?('Content-Length')
237
    len = self['Content-Length'].slice(/\d+/) or
238
        raise Net::HTTPHeaderSyntaxError, 'wrong Content-Length format'
239
    len.to_i
240
  end
241

  
242
  def content_length=(len)
243
    unless len
244
      @header.delete 'content-length'
245
      return nil
246
    end
247
    @header['content-length'] = [len.to_i.to_s]
248
  end
249

  
250
  # Returns "true" if the "transfer-encoding" header is present and
251
  # set to "chunked".  This is an HTTP/1.1 feature, allowing the
252
  # the content to be sent in "chunks" without at the outset
253
  # stating the entire content length.
254
  def chunked?
255
    return false unless @header['transfer-encoding']
256
    field = self['Transfer-Encoding']
257
    (/(?:\A|[^\-\w])chunked(?![\-\w])/i =~ field) ? true : false
258
  end
259

  
260
  # Returns a Range object which represents the value of the Content-Range:
261
  # header field.
262
  # For a partial entity body, this indicates where this fragment
263
  # fits inside the full entity body, as range of byte offsets.
264
  def content_range
265
    return nil unless @header['content-range']
266
    m = %r<bytes\s+(\d+)-(\d+)/(\d+|\*)>i.match(self['Content-Range']) or
267
        raise Net::HTTPHeaderSyntaxError, 'wrong Content-Range format'
268
    m[1].to_i .. m[2].to_i
269
  end
270

  
271
  # The length of the range represented in Content-Range: header.
272
  def range_length
273
    r = content_range() or return nil
274
    r.end - r.begin + 1
275
  end
276

  
277
  # Returns a content type string such as "text/html".
278
  # This method returns nil if Content-Type: header field does not exist.
279
  def content_type
280
    return nil unless main_type()
281
    if sub_type()
282
    then "#{main_type()}/#{sub_type()}"
283
    else main_type()
284
    end
285
  end
286

  
287
  # Returns a content type string such as "text".
288
  # This method returns nil if Content-Type: header field does not exist.
289
  def main_type
290
    return nil unless @header['content-type']
291
    self['Content-Type'].split(';').first.to_s.split('/')[0].to_s.strip
292
  end
293

  
294
  # Returns a content type string such as "html".
295
  # This method returns nil if Content-Type: header field does not exist
296
  # or sub-type is not given (e.g. "Content-Type: text").
297
  def sub_type
298
    return nil unless @header['content-type']
299
    _, sub = *self['Content-Type'].split(';').first.to_s.split('/')
300
    return nil unless sub
301
    sub.strip
302
  end
303

  
304
  # Any parameters specified for the content type, returned as a Hash.
305
  # For example, a header of Content-Type: text/html; charset=EUC-JP
306
  # would result in type_params returning {'charset' => 'EUC-JP'}
307
  def type_params
308
    result = {}
309
    list = self['Content-Type'].to_s.split(';')
310
    list.shift
311
    list.each do |param|
312
      k, v = *param.split('=', 2)
313
      result[k.strip] = v.strip
314
    end
315
    result
316
  end
317

  
318
  # Sets the content type in an HTTP header.
319
  # The +type+ should be a full HTTP content type, e.g. "text/html".
320
  # The +params+ are an optional Hash of parameters to add after the
321
  # content type, e.g. {'charset' => 'iso-8859-1'}
322
  def set_content_type(type, params = {})
323
    @header['content-type'] = [type + params.map{|k,v|"; #{k}=#{v}"}.join('')]
324
  end
325

  
326
  alias content_type= set_content_type
327

  
328
  # Set header fields and a body from HTML form data.
329
  # +params+ should be an Array of Arrays or
330
  # a Hash containing HTML form data.
331
  # Optional argument +sep+ means data record separator.
332
  #
333
  # Values are URL encoded as necessary and the content-type is set to
334
  # application/x-www-form-urlencoded
335
  #
336
  # Example:
337
  #    http.form_data = {"q" => "ruby", "lang" => "en"}
338
  #    http.form_data = {"q" => ["ruby", "perl"], "lang" => "en"}
339
  #    http.set_form_data({"q" => "ruby", "lang" => "en"}, ';')
340
  #
341
  def set_form_data(params, sep = '&')
342
    query = URI.encode_www_form(params)
343
    query.gsub!(/&/, sep) if sep != '&'
344
    self.body = query
345
    self.content_type = 'application/x-www-form-urlencoded'
346
  end
347

  
348
  alias form_data= set_form_data
349

  
350
  # Set a HTML form data set.
351
  # +params+ is the form data set; it is an Array of Arrays or a Hash
352
  # +enctype is the type to encode the form data set.
353
  # It is application/x-www-form-urlencoded or multipart/form-data.
354
  # +formpot+ is an optional hash to specify the detail.
355
  #
356
  # boundary:: the boundary of the multipart message
357
  # charset::  the charset of the message. All names and the values of
358
  #            non-file fields are encoded as the charset.
359
  #
360
  # Each item of params is an array and contains following items:
361
  # +name+::  the name of the field
362
  # +value+:: the value of the field, it should be a String or a File
363
  # +opt+::   an optional hash to specify additional information
364
  #
365
  # Each item is a file field or a normal field.
366
  # If +value+ is a File object or the +opt+ have a filename key,
367
  # the item is treated as a file field.
368
  #
369
  # If Transfer-Encoding is set as chunked, this send the request in
370
  # chunked encoding. Because chunked encoding is HTTP/1.1 feature,
371
  # you must confirm the server to support HTTP/1.1 before sending it.
372
  #
373
  # Example:
374
  #    http.set_form([["q", "ruby"], ["lang", "en"]])
375
  #
376
  # See also RFC 2388, RFC 2616, HTML 4.01, and HTML5
377
  #
378
  def set_form(params, enctype='application/x-www-form-urlencoded', formopt={})
379
    @body_data = params
380
    @body = nil
381
    @body_stream = nil
382
    @form_option = formopt
383
    case enctype
384
    when /\Aapplication\/x-www-form-urlencoded\z/i,
385
      /\Amultipart\/form-data\z/i
386
      self.content_type = enctype
387
    else
388
      raise ArgumentError, "invalid enctype: #{enctype}"
389
    end
390
  end
391

  
392
  # Set the Authorization: header for "Basic" authorization.
393
  def basic_auth(account, password)
394
    @header['authorization'] = [basic_encode(account, password)]
395
  end
396

  
397
  # Set Proxy-Authorization: header for "Basic" authorization.
398
  def proxy_basic_auth(account, password)
399
    @header['proxy-authorization'] = [basic_encode(account, password)]
400
  end
401

  
402
  def basic_encode(account, password)
403
    'Basic ' + ["#{account}:#{password}"].pack('m').delete("\r\n")
404
  end
405
  private :basic_encode
406

  
407
  def connection_close?
408
    tokens(@header['connection']).include?('close') or
409
    tokens(@header['proxy-connection']).include?('close')
410
  end
411

  
412
  def connection_keep_alive?
413
    tokens(@header['connection']).include?('keep-alive') or
414
    tokens(@header['proxy-connection']).include?('keep-alive')
415
  end
416

  
417
  def tokens(vals)
418
    return [] unless vals
419
    vals.map {|v| v.split(',') }.flatten\
420
        .reject {|str| str.strip.empty? }\
421
        .map {|tok| tok.strip.downcase }
422
  end
423
  private :tokens
424

  
425
end
426

  
lib/net/http/request.rb (revision 0)
1
# HTTP request class.
2
# This class wraps together the request header and the request path.
3
# You cannot use this class directly. Instead, you should use one of its
4
# subclasses: Net::HTTP::Get, Net::HTTP::Post, Net::HTTP::Head.
5
#
6
class Net::HTTPRequest < Net::HTTPGenericRequest
7
  # Creates HTTP request object.
8
  def initialize(path, initheader = nil)
9
    super self.class::METHOD,
10
          self.class::REQUEST_HAS_BODY,
11
          self.class::RESPONSE_HAS_BODY,
12
          path, initheader
13
  end
14
end
15

  
lib/net/http/proxy_delta.rb (revision 0)
1
module Net::HTTP::ProxyDelta   #:nodoc: internal use only
2
  private
3

  
4
  def conn_address
5
    proxy_address()
6
  end
7

  
8
  def conn_port
9
    proxy_port()
10
  end
11

  
12
  def edit_path(path)
13
    use_ssl? ? path : "http://#{addr_port()}#{path}"
14
  end
15
end
16

  
lib/net/http/requests.rb (revision 0)
1
#
2
# HTTP/1.1 methods --- RFC2616
3
#
4

  
5
# See Net::HTTPGenericRequest for attributes and methods.
6
# See Net::HTTP for usage examples.
7
class Net::HTTP::Get < Net::HTTPRequest
8
  METHOD = 'GET'
9
  REQUEST_HAS_BODY  = false
10
  RESPONSE_HAS_BODY = true
11
end
12

  
13
# See Net::HTTPGenericRequest for attributes and methods.
14
# See Net::HTTP for usage examples.
15
class Net::HTTP::Head < Net::HTTPRequest
16
  METHOD = 'HEAD'
17
  REQUEST_HAS_BODY = false
18
  RESPONSE_HAS_BODY = false
19
end
20

  
21
# See Net::HTTPGenericRequest for attributes and methods.
22
# See Net::HTTP for usage examples.
23
class Net::HTTP::Post < Net::HTTPRequest
24
  METHOD = 'POST'
25
  REQUEST_HAS_BODY = true
26
  RESPONSE_HAS_BODY = true
27
end
28

  
29
# See Net::HTTPGenericRequest for attributes and methods.
30
# See Net::HTTP for usage examples.
31
class Net::HTTP::Put < Net::HTTPRequest
32
  METHOD = 'PUT'
33
  REQUEST_HAS_BODY = true
34
  RESPONSE_HAS_BODY = true
35
end
36

  
37
# See Net::HTTPGenericRequest for attributes and methods.
38
# See Net::HTTP for usage examples.
39
class Net::HTTP::Delete < Net::HTTPRequest
40
  METHOD = 'DELETE'
41
  REQUEST_HAS_BODY = false
42
  RESPONSE_HAS_BODY = true
43
end
44

  
45
# See Net::HTTPGenericRequest for attributes and methods.
46
class Net::HTTP::Options < Net::HTTPRequest
47
  METHOD = 'OPTIONS'
48
  REQUEST_HAS_BODY = false
49
  RESPONSE_HAS_BODY = false
50
end
51

  
52
# See Net::HTTPGenericRequest for attributes and methods.
53
class Net::HTTP::Trace < Net::HTTPRequest
54
  METHOD = 'TRACE'
55
  REQUEST_HAS_BODY = false
56
  RESPONSE_HAS_BODY = true
57
end
58

  
59
#
60
# PATCH method --- RFC5789
61
#
62

  
63
# See Net::HTTPGenericRequest for attributes and methods.
64
class Net::HTTP::Patch < Net::HTTPRequest
65
  METHOD = 'PATCH'
66
  REQUEST_HAS_BODY = true
67
  RESPONSE_HAS_BODY = true
68
end
69

  
70
#
71
# WebDAV methods --- RFC2518
72
#
73

  
74
# See Net::HTTPGenericRequest for attributes and methods.
75
class Net::HTTP::Propfind < Net::HTTPRequest
76
  METHOD = 'PROPFIND'
77
  REQUEST_HAS_BODY = true
78
  RESPONSE_HAS_BODY = true
79
end
80

  
81
# See Net::HTTPGenericRequest for attributes and methods.
82
class Net::HTTP::Proppatch < Net::HTTPRequest
83
  METHOD = 'PROPPATCH'
84
  REQUEST_HAS_BODY = true
85
  RESPONSE_HAS_BODY = true
86
end
87

  
88
# See Net::HTTPGenericRequest for attributes and methods.
89
class Net::HTTP::Mkcol < Net::HTTPRequest
90
  METHOD = 'MKCOL'
91
  REQUEST_HAS_BODY = true
92
  RESPONSE_HAS_BODY = true
93
end
94

  
95
# See Net::HTTPGenericRequest for attributes and methods.
96
class Net::HTTP::Copy < Net::HTTPRequest
97
  METHOD = 'COPY'
98
  REQUEST_HAS_BODY = false
99
  RESPONSE_HAS_BODY = true
100
end
101

  
102
# See Net::HTTPGenericRequest for attributes and methods.
103
class Net::HTTP::Move < Net::HTTPRequest
104
  METHOD = 'MOVE'
105
  REQUEST_HAS_BODY = false
106
  RESPONSE_HAS_BODY = true
... This diff was truncated because it exceeds the maximum size that can be displayed.