Feature #6435 ยป net.http.breakup.patch
| lib/net/http/backward.rb (revision 0) | ||
|---|---|---|
| 
     # for backward compatibility 
   | 
||
| 
     # :enddoc: 
   | 
||
| 
     class Net::HTTP 
   | 
||
| 
       ProxyMod = ProxyDelta 
   | 
||
| 
       HTTPSession = self 
   | 
||
| 
     end 
   | 
||
| 
     module Net::NetPrivate 
   | 
||
| 
       HTTPRequest = ::Net::HTTPRequest 
   | 
||
| 
     end 
   | 
||
| 
     Net::HTTPInformationCode  = Net::HTTPInformation 
   | 
||
| 
     Net::HTTPSuccessCode      = Net::HTTPSuccess 
   | 
||
| 
     Net::HTTPRedirectionCode  = Net::HTTPRedirection 
   | 
||
| 
     Net::HTTPRetriableCode    = Net::HTTPRedirection 
   | 
||
| 
     Net::HTTPClientErrorCode  = Net::HTTPClientError 
   | 
||
| 
     Net::HTTPFatalErrorCode   = Net::HTTPClientError 
   | 
||
| 
     Net::HTTPServerErrorCode  = Net::HTTPServerError 
   | 
||
| 
     Net::HTTPResponceReceiver = Net::HTTPResponse 
   | 
||
| lib/net/http/response.rb (revision 0) | ||
|---|---|---|
| 
     # HTTP response class. 
   | 
||
| 
     # 
   | 
||
| 
     # This class wraps together the response header and the response body (the 
   | 
||
| 
     # entity requested). 
   | 
||
| 
     # 
   | 
||
| 
     # It mixes in the HTTPHeader module, which provides access to response 
   | 
||
| 
     # header values both via hash-like methods and via individual readers. 
   | 
||
| 
     # 
   | 
||
| 
     # Note that each possible HTTP response code defines its own 
   | 
||
| 
     # HTTPResponse subclass.  These are listed below. 
   | 
||
| 
     # 
   | 
||
| 
     # All classes are 
   | 
||
| 
     # defined under the Net module. Indentation indicates inheritance. 
   | 
||
| 
     # 
   | 
||
| 
     #   xxx        HTTPResponse 
   | 
||
| 
     # 
   | 
||
| 
     #     1xx        HTTPInformation 
   | 
||
| 
     #       100        HTTPContinue 
   | 
||
| 
     #       101        HTTPSwitchProtocol 
   | 
||
| 
     # 
   | 
||
| 
     #     2xx        HTTPSuccess 
   | 
||
| 
     #       200        HTTPOK 
   | 
||
| 
     #       201        HTTPCreated 
   | 
||
| 
     #       202        HTTPAccepted 
   | 
||
| 
     #       203        HTTPNonAuthoritativeInformation 
   | 
||
| 
     #       204        HTTPNoContent 
   | 
||
| 
     #       205        HTTPResetContent 
   | 
||
| 
     #       206        HTTPPartialContent 
   | 
||
| 
     # 
   | 
||
| 
     #     3xx        HTTPRedirection 
   | 
||
| 
     #       300        HTTPMultipleChoice 
   | 
||
| 
     #       301        HTTPMovedPermanently 
   | 
||
| 
     #       302        HTTPFound 
   | 
||
| 
     #       303        HTTPSeeOther 
   | 
||
| 
     #       304        HTTPNotModified 
   | 
||
| 
     #       305        HTTPUseProxy 
   | 
||
| 
     #       307        HTTPTemporaryRedirect 
   | 
||
| 
     # 
   | 
||
| 
     #     4xx        HTTPClientError 
   | 
||
| 
     #       400        HTTPBadRequest 
   | 
||
| 
     #       401        HTTPUnauthorized 
   | 
||
| 
     #       402        HTTPPaymentRequired 
   | 
||
| 
     #       403        HTTPForbidden 
   | 
||
| 
     #       404        HTTPNotFound 
   | 
||
| 
     #       405        HTTPMethodNotAllowed 
   | 
||
| 
     #       406        HTTPNotAcceptable 
   | 
||
| 
     #       407        HTTPProxyAuthenticationRequired 
   | 
||
| 
     #       408        HTTPRequestTimeOut 
   | 
||
| 
     #       409        HTTPConflict 
   | 
||
| 
     #       410        HTTPGone 
   | 
||
| 
     #       411        HTTPLengthRequired 
   | 
||
| 
     #       412        HTTPPreconditionFailed 
   | 
||
| 
     #       413        HTTPRequestEntityTooLarge 
   | 
||
| 
     #       414        HTTPRequestURITooLong 
   | 
||
| 
     #       415        HTTPUnsupportedMediaType 
   | 
||
| 
     #       416        HTTPRequestedRangeNotSatisfiable 
   | 
||
| 
     #       417        HTTPExpectationFailed 
   | 
||
| 
     # 
   | 
||
| 
     #     5xx        HTTPServerError 
   | 
||
| 
     #       500        HTTPInternalServerError 
   | 
||
| 
     #       501        HTTPNotImplemented 
   | 
||
| 
     #       502        HTTPBadGateway 
   | 
||
| 
     #       503        HTTPServiceUnavailable 
   | 
||
| 
     #       504        HTTPGatewayTimeOut 
   | 
||
| 
     #       505        HTTPVersionNotSupported 
   | 
||
| 
     # 
   | 
||
| 
     #     xxx        HTTPUnknownResponse 
   | 
||
| 
     # 
   | 
||
| 
     class Net::HTTPResponse 
   | 
||
| 
       class << self 
   | 
||
| 
         # true if the response has a body. 
   | 
||
| 
         def body_permitted? 
   | 
||
| 
           self::HAS_BODY 
   | 
||
| 
         end 
   | 
||
| 
         def exception_type   # :nodoc: internal use only 
   | 
||
| 
           self::EXCEPTION_TYPE 
   | 
||
| 
         end 
   | 
||
| 
         def read_new(sock)   #:nodoc: internal use only 
   | 
||
| 
           httpv, code, msg = read_status_line(sock) 
   | 
||
| 
           res = response_class(code).new(httpv, code, msg) 
   | 
||
| 
           each_response_header(sock) do |k,v| 
   | 
||
| 
             res.add_field k, v 
   | 
||
| 
           end 
   | 
||
| 
           res 
   | 
||
| 
         end 
   | 
||
| 
         private 
   | 
||
| 
         def read_status_line(sock) 
   | 
||
| 
           str = sock.readline 
   | 
||
| 
           m = /\AHTTP(?:\/(\d+\.\d+))?\s+(\d\d\d)\s*(.*)\z/in.match(str) or 
   | 
||
| 
             raise Net::HTTPBadResponse, "wrong status line: #{str.dump}" 
   | 
||
| 
           m.captures 
   | 
||
| 
         end 
   | 
||
| 
         def response_class(code) 
   | 
||
| 
           CODE_TO_OBJ[code] or 
   | 
||
| 
           CODE_CLASS_TO_OBJ[code[0,1]] or 
   | 
||
| 
           Net::HTTPUnknownResponse 
   | 
||
| 
         end 
   | 
||
| 
         def each_response_header(sock) 
   | 
||
| 
           key = value = nil 
   | 
||
| 
           while true 
   | 
||
| 
             line = sock.readuntil("\n", true).sub(/\s+\z/, '') 
   | 
||
| 
             break if line.empty? 
   | 
||
| 
             if line[0] == ?\s or line[0] == ?\t and value 
   | 
||
| 
               value << ' ' unless value.empty? 
   | 
||
| 
               value << line.strip 
   | 
||
| 
             else 
   | 
||
| 
               yield key, value if key 
   | 
||
| 
               key, value = line.strip.split(/\s*:\s*/, 2) 
   | 
||
| 
               raise Net::HTTPBadResponse, 'wrong header line format' if value.nil? 
   | 
||
| 
             end 
   | 
||
| 
           end 
   | 
||
| 
           yield key, value if key 
   | 
||
| 
         end 
   | 
||
| 
       end 
   | 
||
| 
       # next is to fix bug in RDoc, where the private inside class << self 
   | 
||
| 
       # spills out. 
   | 
||
| 
       public 
   | 
||
| 
       include Net::HTTPHeader 
   | 
||
| 
       def initialize(httpv, code, msg)   #:nodoc: internal use only 
   | 
||
| 
         @http_version = httpv 
   | 
||
| 
         @code         = code 
   | 
||
| 
         @message      = msg 
   | 
||
| 
         initialize_http_header nil 
   | 
||
| 
         @body = nil 
   | 
||
| 
         @read = false 
   | 
||
| 
       end 
   | 
||
| 
       # The HTTP version supported by the server. 
   | 
||
| 
       attr_reader :http_version 
   | 
||
| 
       # The HTTP result code string. For example, '302'.  You can also 
   | 
||
| 
       # determine the response type by examining which response subclass 
   | 
||
| 
       # the response object is an instance of. 
   | 
||
| 
       attr_reader :code 
   | 
||
| 
       # The HTTP result message sent by the server. For example, 'Not Found'. 
   | 
||
| 
       attr_reader :message 
   | 
||
| 
       alias msg message   # :nodoc: obsolete 
   | 
||
| 
       def inspect 
   | 
||
| 
         "#<#{self.class} #{@code} #{@message} readbody=#{@read}>" 
   | 
||
| 
       end 
   | 
||
| 
       # 
   | 
||
| 
       # response <-> exception relationship 
   | 
||
| 
       # 
   | 
||
| 
       def code_type   #:nodoc: 
   | 
||
| 
         self.class 
   | 
||
| 
       end 
   | 
||
| 
       def error!   #:nodoc: 
   | 
||
| 
         raise error_type().new(@code + ' ' + @message.dump, self) 
   | 
||
| 
       end 
   | 
||
| 
       def error_type   #:nodoc: 
   | 
||
| 
         self.class::EXCEPTION_TYPE 
   | 
||
| 
       end 
   | 
||
| 
       # Raises an HTTP error if the response is not 2xx (success). 
   | 
||
| 
       def value 
   | 
||
| 
         error! unless self.kind_of?(Net::HTTPSuccess) 
   | 
||
| 
       end 
   | 
||
| 
       # 
   | 
||
| 
       # header (for backward compatibility only; DO NOT USE) 
   | 
||
| 
       # 
   | 
||
| 
       def response   #:nodoc: 
   | 
||
| 
         warn "#{caller(1)[0]}: warning: Net::HTTPResponse#response is obsolete" if $VERBOSE 
   | 
||
| 
         self 
   | 
||
| 
       end 
   | 
||
| 
       def header   #:nodoc: 
   | 
||
| 
         warn "#{caller(1)[0]}: warning: Net::HTTPResponse#header is obsolete" if $VERBOSE 
   | 
||
| 
         self 
   | 
||
| 
       end 
   | 
||
| 
       def read_header   #:nodoc: 
   | 
||
| 
         warn "#{caller(1)[0]}: warning: Net::HTTPResponse#read_header is obsolete" if $VERBOSE 
   | 
||
| 
         self 
   | 
||
| 
       end 
   | 
||
| 
       # 
   | 
||
| 
       # body 
   | 
||
| 
       # 
   | 
||
| 
       def reading_body(sock, reqmethodallowbody)  #:nodoc: internal use only 
   | 
||
| 
         @socket = sock 
   | 
||
| 
         @body_exist = reqmethodallowbody && self.class.body_permitted? 
   | 
||
| 
         begin 
   | 
||
| 
           yield 
   | 
||
| 
           self.body   # ensure to read body 
   | 
||
| 
         ensure 
   | 
||
| 
           @socket = nil 
   | 
||
| 
         end 
   | 
||
| 
       end 
   | 
||
| 
       # Gets the entity body returned by the remote HTTP server. 
   | 
||
| 
       # 
   | 
||
| 
       # If a block is given, the body is passed to the block, and 
   | 
||
| 
       # the body is provided in fragments, as it is read in from the socket. 
   | 
||
| 
       # 
   | 
||
| 
       # Calling this method a second or subsequent time for the same 
   | 
||
| 
       # HTTPResponse object will return the value already read. 
   | 
||
| 
       # 
   | 
||
| 
       #   http.request_get('/index.html') {|res| 
   | 
||
| 
       #     puts res.read_body 
   | 
||
| 
       #   } 
   | 
||
| 
       # 
   | 
||
| 
       #   http.request_get('/index.html') {|res| 
   | 
||
| 
       #     p res.read_body.object_id   # 538149362 
   | 
||
| 
       #     p res.read_body.object_id   # 538149362 
   | 
||
| 
       #   } 
   | 
||
| 
       # 
   | 
||
| 
       #   # using iterator 
   | 
||
| 
       #   http.request_get('/index.html') {|res| 
   | 
||
| 
       #     res.read_body do |segment| 
   | 
||
| 
       #       print segment 
   | 
||
| 
       #     end 
   | 
||
| 
       #   } 
   | 
||
| 
       # 
   | 
||
| 
       def read_body(dest = nil, &block) 
   | 
||
| 
         if @read 
   | 
||
| 
           raise IOError, "#{self.class}\#read_body called twice" if dest or block 
   | 
||
| 
           return @body 
   | 
||
| 
         end 
   | 
||
| 
         to = procdest(dest, block) 
   | 
||
| 
         stream_check 
   | 
||
| 
         if @body_exist 
   | 
||
| 
           read_body_0 to 
   | 
||
| 
           @body = to 
   | 
||
| 
         else 
   | 
||
| 
           @body = nil 
   | 
||
| 
         end 
   | 
||
| 
         @read = true 
   | 
||
| 
         @body 
   | 
||
| 
       end 
   | 
||
| 
       # Returns the full entity body. 
   | 
||
| 
       # 
   | 
||
| 
       # Calling this method a second or subsequent time will return the 
   | 
||
| 
       # string already read. 
   | 
||
| 
       # 
   | 
||
| 
       #   http.request_get('/index.html') {|res| 
   | 
||
| 
       #     puts res.body 
   | 
||
| 
       #   } 
   | 
||
| 
       # 
   | 
||
| 
       #   http.request_get('/index.html') {|res| 
   | 
||
| 
       #     p res.body.object_id   # 538149362 
   | 
||
| 
       #     p res.body.object_id   # 538149362 
   | 
||
| 
       #   } 
   | 
||
| 
       # 
   | 
||
| 
       def body 
   | 
||
| 
         read_body() 
   | 
||
| 
       end 
   | 
||
| 
       # Because it may be necessary to modify the body, Eg, decompression 
   | 
||
| 
       # this method facilitates that. 
   | 
||
| 
       def body=(value) 
   | 
||
| 
         @body = value 
   | 
||
| 
       end 
   | 
||
| 
       alias entity body   #:nodoc: obsolete 
   | 
||
| 
       private 
   | 
||
| 
       def read_body_0(dest) 
   | 
||
| 
         if chunked? 
   | 
||
| 
           read_chunked dest 
   | 
||
| 
           return 
   | 
||
| 
         end 
   | 
||
| 
         clen = content_length() 
   | 
||
| 
         if clen 
   | 
||
| 
           @socket.read clen, dest, true   # ignore EOF 
   | 
||
| 
           return 
   | 
||
| 
         end 
   | 
||
| 
         clen = range_length() 
   | 
||
| 
         if clen 
   | 
||
| 
           @socket.read clen, dest 
   | 
||
| 
           return 
   | 
||
| 
         end 
   | 
||
| 
         @socket.read_all dest 
   | 
||
| 
       end 
   | 
||
| 
       def read_chunked(dest) 
   | 
||
| 
         len = nil 
   | 
||
| 
         total = 0 
   | 
||
| 
         while true 
   | 
||
| 
           line = @socket.readline 
   | 
||
| 
           hexlen = line.slice(/[0-9a-fA-F]+/) or 
   | 
||
| 
               raise Net::HTTPBadResponse, "wrong chunk size line: #{line}" 
   | 
||
| 
           len = hexlen.hex 
   | 
||
| 
           break if len == 0 
   | 
||
| 
           begin 
   | 
||
| 
             @socket.read len, dest 
   | 
||
| 
           ensure 
   | 
||
| 
             total += len 
   | 
||
| 
             @socket.read 2   # \r\n 
   | 
||
| 
           end 
   | 
||
| 
         end 
   | 
||
| 
         until @socket.readline.empty? 
   | 
||
| 
           # none 
   | 
||
| 
         end 
   | 
||
| 
       end 
   | 
||
| 
       def stream_check 
   | 
||
| 
         raise IOError, 'attempt to read body out of block' if @socket.closed? 
   | 
||
| 
       end 
   | 
||
| 
       def procdest(dest, block) 
   | 
||
| 
         raise ArgumentError, 'both arg and block given for HTTP method' \ 
   | 
||
| 
             if dest and block 
   | 
||
| 
         if block 
   | 
||
| 
           Net::ReadAdapter.new(block) 
   | 
||
| 
         else 
   | 
||
| 
           dest || '' 
   | 
||
| 
         end 
   | 
||
| 
       end 
   | 
||
| 
     end 
   | 
||
| lib/net/http/exceptions.rb (revision 0) | ||
|---|---|---|
| 
     # Net::HTTP exception class. 
   | 
||
| 
     # You cannot use Net::HTTPExceptions directly; instead, you must use 
   | 
||
| 
     # its subclasses. 
   | 
||
| 
     module Net::HTTPExceptions 
   | 
||
| 
       def initialize(msg, res)   #:nodoc: 
   | 
||
| 
         super msg 
   | 
||
| 
         @response = res 
   | 
||
| 
       end 
   | 
||
| 
       attr_reader :response 
   | 
||
| 
       alias data response    #:nodoc: obsolete 
   | 
||
| 
     end 
   | 
||
| 
     class Net::HTTPError < Net::ProtocolError 
   | 
||
| 
       include Net::HTTPExceptions 
   | 
||
| 
     end 
   | 
||
| 
     class Net::HTTPRetriableError < Net::ProtoRetriableError 
   | 
||
| 
       include Net::HTTPExceptions 
   | 
||
| 
     end 
   | 
||
| 
     class Net::HTTPServerException < Net::ProtoServerError 
   | 
||
| 
       # We cannot use the name "HTTPServerError", it is the name of the response. 
   | 
||
| 
       include Net::HTTPExceptions 
   | 
||
| 
     end 
   | 
||
| 
     class Net::HTTPFatalError < Net::ProtoFatalError 
   | 
||
| 
       include Net::HTTPExceptions 
   | 
||
| 
     end 
   | 
||
| lib/net/http/responses.rb (revision 0) | ||
|---|---|---|
| 
     # :stopdoc: 
   | 
||
| 
     class Net::HTTPUnknownResponse < Net::HTTPResponse 
   | 
||
| 
       HAS_BODY = true 
   | 
||
| 
       EXCEPTION_TYPE = Net::HTTPError 
   | 
||
| 
     end 
   | 
||
| 
     class Net::HTTPInformation < Net::HTTPResponse           # 1xx 
   | 
||
| 
       HAS_BODY = false 
   | 
||
| 
       EXCEPTION_TYPE = Net::HTTPError 
   | 
||
| 
     end 
   | 
||
| 
     class Net::HTTPSuccess < Net::HTTPResponse               # 2xx 
   | 
||
| 
       HAS_BODY = true 
   | 
||
| 
       EXCEPTION_TYPE = Net::HTTPError 
   | 
||
| 
     end 
   | 
||
| 
     class Net::HTTPRedirection < Net::HTTPResponse           # 3xx 
   | 
||
| 
       HAS_BODY = true 
   | 
||
| 
       EXCEPTION_TYPE = Net::HTTPRetriableError 
   | 
||
| 
     end 
   | 
||
| 
     class Net::HTTPClientError < Net::HTTPResponse           # 4xx 
   | 
||
| 
       HAS_BODY = true 
   | 
||
| 
       EXCEPTION_TYPE = Net::HTTPServerException   # for backward compatibility 
   | 
||
| 
     end 
   | 
||
| 
     class Net::HTTPServerError < Net::HTTPResponse           # 5xx 
   | 
||
| 
       HAS_BODY = true 
   | 
||
| 
       EXCEPTION_TYPE = Net::HTTPFatalError    # for backward compatibility 
   | 
||
| 
     end 
   | 
||
| 
     class Net::HTTPContinue < Net::HTTPInformation           # 100 
   | 
||
| 
       HAS_BODY = false 
   | 
||
| 
     end 
   | 
||
| 
     class Net::HTTPSwitchProtocol < Net::HTTPInformation     # 101 
   | 
||
| 
       HAS_BODY = false 
   | 
||
| 
     end 
   | 
||
| 
     class Net::HTTPOK < Net::HTTPSuccess                            # 200 
   | 
||
| 
       HAS_BODY = true 
   | 
||
| 
     end 
   | 
||
| 
     class Net::HTTPCreated < Net::HTTPSuccess                       # 201 
   | 
||
| 
       HAS_BODY = true 
   | 
||
| 
     end 
   | 
||
| 
     class Net::HTTPAccepted < Net::HTTPSuccess                      # 202 
   | 
||
| 
       HAS_BODY = true 
   | 
||
| 
     end 
   | 
||
| 
     class Net::HTTPNonAuthoritativeInformation < Net::HTTPSuccess   # 203 
   | 
||
| 
       HAS_BODY = true 
   | 
||
| 
     end 
   | 
||
| 
     class Net::HTTPNoContent < Net::HTTPSuccess                     # 204 
   | 
||
| 
       HAS_BODY = false 
   | 
||
| 
     end 
   | 
||
| 
     class Net::HTTPResetContent < Net::HTTPSuccess                  # 205 
   | 
||
| 
       HAS_BODY = false 
   | 
||
| 
     end 
   | 
||
| 
     class Net::HTTPPartialContent < Net::HTTPSuccess                # 206 
   | 
||
| 
       HAS_BODY = true 
   | 
||
| 
     end 
   | 
||
| 
     class Net::HTTPMultipleChoice < Net::HTTPRedirection     # 300 
   | 
||
| 
       HAS_BODY = true 
   | 
||
| 
     end 
   | 
||
| 
     class Net::HTTPMovedPermanently < Net::HTTPRedirection   # 301 
   | 
||
| 
       HAS_BODY = true 
   | 
||
| 
     end 
   | 
||
| 
     class Net::HTTPFound < Net::HTTPRedirection              # 302 
   | 
||
| 
       HAS_BODY = true 
   | 
||
| 
     end 
   | 
||
| 
     Net::HTTPMovedTemporarily = Net::HTTPFound 
   | 
||
| 
     class Net::HTTPSeeOther < Net::HTTPRedirection           # 303 
   | 
||
| 
       HAS_BODY = true 
   | 
||
| 
     end 
   | 
||
| 
     class Net::HTTPNotModified < Net::HTTPRedirection        # 304 
   | 
||
| 
       HAS_BODY = false 
   | 
||
| 
     end 
   | 
||
| 
     class Net::HTTPUseProxy < Net::HTTPRedirection           # 305 
   | 
||
| 
       HAS_BODY = false 
   | 
||
| 
     end 
   | 
||
| 
     # 306 unused 
   | 
||
| 
     class Net::HTTPTemporaryRedirect < Net::HTTPRedirection  # 307 
   | 
||
| 
       HAS_BODY = true 
   | 
||
| 
     end 
   | 
||
| 
     class Net::HTTPBadRequest < Net::HTTPClientError                    # 400 
   | 
||
| 
       HAS_BODY = true 
   | 
||
| 
     end 
   | 
||
| 
     class Net::HTTPUnauthorized < Net::HTTPClientError                  # 401 
   | 
||
| 
       HAS_BODY = true 
   | 
||
| 
     end 
   | 
||
| 
     class Net::HTTPPaymentRequired < Net::HTTPClientError               # 402 
   | 
||
| 
       HAS_BODY = true 
   | 
||
| 
     end 
   | 
||
| 
     class Net::HTTPForbidden < Net::HTTPClientError                     # 403 
   | 
||
| 
       HAS_BODY = true 
   | 
||
| 
     end 
   | 
||
| 
     class Net::HTTPNotFound < Net::HTTPClientError                      # 404 
   | 
||
| 
       HAS_BODY = true 
   | 
||
| 
     end 
   | 
||
| 
     class Net::HTTPMethodNotAllowed < Net::HTTPClientError              # 405 
   | 
||
| 
       HAS_BODY = true 
   | 
||
| 
     end 
   | 
||
| 
     class Net::HTTPNotAcceptable < Net::HTTPClientError                 # 406 
   | 
||
| 
       HAS_BODY = true 
   | 
||
| 
     end 
   | 
||
| 
     class Net::HTTPProxyAuthenticationRequired < Net::HTTPClientError   # 407 
   | 
||
| 
       HAS_BODY = true 
   | 
||
| 
     end 
   | 
||
| 
     class Net::HTTPRequestTimeOut < Net::HTTPClientError                # 408 
   | 
||
| 
       HAS_BODY = true 
   | 
||
| 
     end 
   | 
||
| 
     class Net::HTTPConflict < Net::HTTPClientError                      # 409 
   | 
||
| 
       HAS_BODY = true 
   | 
||
| 
     end 
   | 
||
| 
     class Net::HTTPGone < Net::HTTPClientError                          # 410 
   | 
||
| 
       HAS_BODY = true 
   | 
||
| 
     end 
   | 
||
| 
     class Net::HTTPLengthRequired < Net::HTTPClientError                # 411 
   | 
||
| 
       HAS_BODY = true 
   | 
||
| 
     end 
   | 
||
| 
     class Net::HTTPPreconditionFailed < Net::HTTPClientError            # 412 
   | 
||
| 
       HAS_BODY = true 
   | 
||
| 
     end 
   | 
||
| 
     class Net::HTTPRequestEntityTooLarge < Net::HTTPClientError         # 413 
   | 
||
| 
       HAS_BODY = true 
   | 
||
| 
     end 
   | 
||
| 
     class Net::HTTPRequestURITooLong < Net::HTTPClientError             # 414 
   | 
||
| 
       HAS_BODY = true 
   | 
||
| 
     end 
   | 
||
| 
     Net::HTTPRequestURITooLarge = Net::HTTPRequestURITooLong 
   | 
||
| 
     class Net::HTTPUnsupportedMediaType < Net::HTTPClientError          # 415 
   | 
||
| 
       HAS_BODY = true 
   | 
||
| 
     end 
   | 
||
| 
     class Net::HTTPRequestedRangeNotSatisfiable < Net::HTTPClientError  # 416 
   | 
||
| 
       HAS_BODY = true 
   | 
||
| 
     end 
   | 
||
| 
     class Net::HTTPExpectationFailed < Net::HTTPClientError             # 417 
   | 
||
| 
       HAS_BODY = true 
   | 
||
| 
     end 
   | 
||
| 
     class Net::HTTPInternalServerError < Net::HTTPServerError   # 500 
   | 
||
| 
       HAS_BODY = true 
   | 
||
| 
     end 
   | 
||
| 
     class Net::HTTPNotImplemented < Net::HTTPServerError        # 501 
   | 
||
| 
       HAS_BODY = true 
   | 
||
| 
     end 
   | 
||
| 
     class Net::HTTPBadGateway < Net::HTTPServerError            # 502 
   | 
||
| 
       HAS_BODY = true 
   | 
||
| 
     end 
   | 
||
| 
     class Net::HTTPServiceUnavailable < Net::HTTPServerError    # 503 
   | 
||
| 
       HAS_BODY = true 
   | 
||
| 
     end 
   | 
||
| 
     class Net::HTTPGatewayTimeOut < Net::HTTPServerError        # 504 
   | 
||
| 
       HAS_BODY = true 
   | 
||
| 
     end 
   | 
||
| 
     class Net::HTTPVersionNotSupported < Net::HTTPServerError   # 505 
   | 
||
| 
       HAS_BODY = true 
   | 
||
| 
     end 
   | 
||
| 
     class Net::HTTPResponse 
   | 
||
| 
       CODE_CLASS_TO_OBJ = { 
   | 
||
| 
         '1' => Net::HTTPInformation, 
   | 
||
| 
         '2' => Net::HTTPSuccess, 
   | 
||
| 
         '3' => Net::HTTPRedirection, 
   | 
||
| 
         '4' => Net::HTTPClientError, 
   | 
||
| 
         '5' => Net::HTTPServerError 
   | 
||
| 
       } 
   | 
||
| 
       CODE_TO_OBJ = { 
   | 
||
| 
         '100' => Net::HTTPContinue, 
   | 
||
| 
         '101' => Net::HTTPSwitchProtocol, 
   | 
||
| 
         '200' => Net::HTTPOK, 
   | 
||
| 
         '201' => Net::HTTPCreated, 
   | 
||
| 
         '202' => Net::HTTPAccepted, 
   | 
||
| 
         '203' => Net::HTTPNonAuthoritativeInformation, 
   | 
||
| 
         '204' => Net::HTTPNoContent, 
   | 
||
| 
         '205' => Net::HTTPResetContent, 
   | 
||
| 
         '206' => Net::HTTPPartialContent, 
   | 
||
| 
         '300' => Net::HTTPMultipleChoice, 
   | 
||
| 
         '301' => Net::HTTPMovedPermanently, 
   | 
||
| 
         '302' => Net::HTTPFound, 
   | 
||
| 
         '303' => Net::HTTPSeeOther, 
   | 
||
| 
         '304' => Net::HTTPNotModified, 
   | 
||
| 
         '305' => Net::HTTPUseProxy, 
   | 
||
| 
         '307' => Net::HTTPTemporaryRedirect, 
   | 
||
| 
         '400' => Net::HTTPBadRequest, 
   | 
||
| 
         '401' => Net::HTTPUnauthorized, 
   | 
||
| 
         '402' => Net::HTTPPaymentRequired, 
   | 
||
| 
         '403' => Net::HTTPForbidden, 
   | 
||
| 
         '404' => Net::HTTPNotFound, 
   | 
||
| 
         '405' => Net::HTTPMethodNotAllowed, 
   | 
||
| 
         '406' => Net::HTTPNotAcceptable, 
   | 
||
| 
         '407' => Net::HTTPProxyAuthenticationRequired, 
   | 
||
| 
         '408' => Net::HTTPRequestTimeOut, 
   | 
||
| 
         '409' => Net::HTTPConflict, 
   | 
||
| 
         '410' => Net::HTTPGone, 
   | 
||
| 
         '411' => Net::HTTPLengthRequired, 
   | 
||
| 
         '412' => Net::HTTPPreconditionFailed, 
   | 
||
| 
         '413' => Net::HTTPRequestEntityTooLarge, 
   | 
||
| 
         '414' => Net::HTTPRequestURITooLong, 
   | 
||
| 
         '415' => Net::HTTPUnsupportedMediaType, 
   | 
||
| 
         '416' => Net::HTTPRequestedRangeNotSatisfiable, 
   | 
||
| 
         '417' => Net::HTTPExpectationFailed, 
   | 
||
| 
         '500' => Net::HTTPInternalServerError, 
   | 
||
| 
         '501' => Net::HTTPNotImplemented, 
   | 
||
| 
         '502' => Net::HTTPBadGateway, 
   | 
||
| 
         '503' => Net::HTTPServiceUnavailable, 
   | 
||
| 
         '504' => Net::HTTPGatewayTimeOut, 
   | 
||
| 
         '505' => Net::HTTPVersionNotSupported 
   | 
||
| 
       } 
   | 
||
| 
     end 
   | 
||
| 
     # :startdoc: 
   | 
||
| lib/net/http/generic_request.rb (revision 0) | ||
|---|---|---|
| 
     # HTTPGenericRequest is the parent of the HTTPRequest class. 
   | 
||
| 
     # Do not use this directly; use a subclass of HTTPRequest. 
   | 
||
| 
     # 
   | 
||
| 
     # Mixes in the HTTPHeader module to provide easier access to HTTP headers. 
   | 
||
| 
     # 
   | 
||
| 
     class Net::HTTPGenericRequest 
   | 
||
| 
       include Net::HTTPHeader 
   | 
||
| 
       def initialize(m, reqbody, resbody, path, initheader = nil) 
   | 
||
| 
         @method = m 
   | 
||
| 
         @request_has_body = reqbody 
   | 
||
| 
         @response_has_body = resbody 
   | 
||
| 
         raise ArgumentError, "no HTTP request path given" unless path 
   | 
||
| 
         raise ArgumentError, "HTTP request path is empty" if path.empty? 
   | 
||
| 
         @path = path 
   | 
||
| 
         initialize_http_header initheader 
   | 
||
| 
         self['Accept'] ||= '*/*' 
   | 
||
| 
         self['User-Agent'] ||= 'Ruby' 
   | 
||
| 
         @body = nil 
   | 
||
| 
         @body_stream = nil 
   | 
||
| 
         @body_data = nil 
   | 
||
| 
       end 
   | 
||
| 
       attr_reader :method 
   | 
||
| 
       attr_reader :path 
   | 
||
| 
       def inspect 
   | 
||
| 
         "\#<#{self.class} #{@method}>" 
   | 
||
| 
       end 
   | 
||
| 
       def request_body_permitted? 
   | 
||
| 
         @request_has_body 
   | 
||
| 
       end 
   | 
||
| 
       def response_body_permitted? 
   | 
||
| 
         @response_has_body 
   | 
||
| 
       end 
   | 
||
| 
       def body_exist? 
   | 
||
| 
         warn "Net::HTTPRequest#body_exist? is obsolete; use response_body_permitted?" if $VERBOSE 
   | 
||
| 
         response_body_permitted? 
   | 
||
| 
       end 
   | 
||
| 
       attr_reader :body 
   | 
||
| 
       def body=(str) 
   | 
||
| 
         @body = str 
   | 
||
| 
         @body_stream = nil 
   | 
||
| 
         @body_data = nil 
   | 
||
| 
         str 
   | 
||
| 
       end 
   | 
||
| 
       attr_reader :body_stream 
   | 
||
| 
       def body_stream=(input) 
   | 
||
| 
         @body = nil 
   | 
||
| 
         @body_stream = input 
   | 
||
| 
         @body_data = nil 
   | 
||
| 
         input 
   | 
||
| 
       end 
   | 
||
| 
       def set_body_internal(str)   #:nodoc: internal use only 
   | 
||
| 
         raise ArgumentError, "both of body argument and HTTPRequest#body set" if str and (@body or @body_stream) 
   | 
||
| 
         self.body = str if str 
   | 
||
| 
       end 
   | 
||
| 
       # 
   | 
||
| 
       # write 
   | 
||
| 
       # 
   | 
||
| 
       def exec(sock, ver, path)   #:nodoc: internal use only 
   | 
||
| 
         if @body 
   | 
||
| 
           send_request_with_body sock, ver, path, @body 
   | 
||
| 
         elsif @body_stream 
   | 
||
| 
           send_request_with_body_stream sock, ver, path, @body_stream 
   | 
||
| 
         elsif @body_data 
   | 
||
| 
           send_request_with_body_data sock, ver, path, @body_data 
   | 
||
| 
         else 
   | 
||
| 
           write_header sock, ver, path 
   | 
||
| 
         end 
   | 
||
| 
       end 
   | 
||
| 
       private 
   | 
||
| 
       class Chunker #:nodoc: 
   | 
||
| 
         def initialize(sock) 
   | 
||
| 
           @sock = sock 
   | 
||
| 
           @prev = nil 
   | 
||
| 
         end 
   | 
||
| 
         def write(buf) 
   | 
||
| 
           # avoid memcpy() of buf, buf can huge and eat memory bandwidth 
   | 
||
| 
           @sock.write("#{buf.bytesize.to_s(16)}\r\n") 
   | 
||
| 
           rv = @sock.write(buf) 
   | 
||
| 
           @sock.write("\r\n") 
   | 
||
| 
           rv 
   | 
||
| 
         end 
   | 
||
| 
         def finish 
   | 
||
| 
           @sock.write("0\r\n\r\n") 
   | 
||
| 
         end 
   | 
||
| 
       end 
   | 
||
| 
       def send_request_with_body(sock, ver, path, body) 
   | 
||
| 
         self.content_length = body.bytesize 
   | 
||
| 
         delete 'Transfer-Encoding' 
   | 
||
| 
         supply_default_content_type 
   | 
||
| 
         write_header sock, ver, path 
   | 
||
| 
         wait_for_continue sock, ver if sock.continue_timeout 
   | 
||
| 
         sock.write body 
   | 
||
| 
       end 
   | 
||
| 
       def send_request_with_body_stream(sock, ver, path, f) 
   | 
||
| 
         unless content_length() or chunked? 
   | 
||
| 
           raise ArgumentError, 
   | 
||
| 
               "Content-Length not given and Transfer-Encoding is not `chunked'" 
   | 
||
| 
         end 
   | 
||
| 
         supply_default_content_type 
   | 
||
| 
         write_header sock, ver, path 
   | 
||
| 
         wait_for_continue sock, ver if sock.continue_timeout 
   | 
||
| 
         if chunked? 
   | 
||
| 
           chunker = Chunker.new(sock) 
   | 
||
| 
           IO.copy_stream(f, chunker) 
   | 
||
| 
           chunker.finish 
   | 
||
| 
         else 
   | 
||
| 
           # copy_stream can sendfile() to sock.io unless we use SSL. 
   | 
||
| 
           # If sock.io is an SSLSocket, copy_stream will hit SSL_write() 
   | 
||
| 
           IO.copy_stream(f, sock.io) 
   | 
||
| 
         end 
   | 
||
| 
       end 
   | 
||
| 
       def send_request_with_body_data(sock, ver, path, params) 
   | 
||
| 
         if /\Amultipart\/form-data\z/i !~ self.content_type 
   | 
||
| 
           self.content_type = 'application/x-www-form-urlencoded' 
   | 
||
| 
           return send_request_with_body(sock, ver, path, URI.encode_www_form(params)) 
   | 
||
| 
         end 
   | 
||
| 
         opt = @form_option.dup 
   | 
||
| 
         require 'securerandom' unless defined?(SecureRandom) 
   | 
||
| 
         opt[:boundary] ||= SecureRandom.urlsafe_base64(40) 
   | 
||
| 
         self.set_content_type(self.content_type, boundary: opt[:boundary]) 
   | 
||
| 
         if chunked? 
   | 
||
| 
           write_header sock, ver, path 
   | 
||
| 
           encode_multipart_form_data(sock, params, opt) 
   | 
||
| 
         else 
   | 
||
| 
           require 'tempfile' 
   | 
||
| 
           file = Tempfile.new('multipart') 
   | 
||
| 
           file.binmode 
   | 
||
| 
           encode_multipart_form_data(file, params, opt) 
   | 
||
| 
           file.rewind 
   | 
||
| 
           self.content_length = file.size 
   | 
||
| 
           write_header sock, ver, path 
   | 
||
| 
           IO.copy_stream(file, sock) 
   | 
||
| 
         end 
   | 
||
| 
       end 
   | 
||
| 
       def encode_multipart_form_data(out, params, opt) 
   | 
||
| 
         charset = opt[:charset] 
   | 
||
| 
         boundary = opt[:boundary] 
   | 
||
| 
         require 'securerandom' unless defined?(SecureRandom) 
   | 
||
| 
         boundary ||= SecureRandom.urlsafe_base64(40) 
   | 
||
| 
         chunked_p = chunked? 
   | 
||
| 
         buf = '' 
   | 
||
| 
         params.each do |key, value, h={}| 
   | 
||
| 
           key = quote_string(key, charset) 
   | 
||
| 
           filename = 
   | 
||
| 
             h.key?(:filename) ? h[:filename] : 
   | 
||
| 
             value.respond_to?(:to_path) ? File.basename(value.to_path) : 
   | 
||
| 
             nil 
   | 
||
| 
           buf << "--#{boundary}\r\n" 
   | 
||
| 
           if filename 
   | 
||
| 
             filename = quote_string(filename, charset) 
   | 
||
| 
             type = h[:content_type] || 'application/octet-stream' 
   | 
||
| 
             buf << "Content-Disposition: form-data; " \ 
   | 
||
| 
               "name=\"#{key}\"; filename=\"#{filename}\"\r\n" \ 
   | 
||
| 
               "Content-Type: #{type}\r\n\r\n" 
   | 
||
| 
             if !out.respond_to?(:write) || !value.respond_to?(:read) 
   | 
||
| 
               # if +out+ is not an IO or +value+ is not an IO 
   | 
||
| 
               buf << (value.respond_to?(:read) ? value.read : value) 
   | 
||
| 
             elsif value.respond_to?(:size) && chunked_p 
   | 
||
| 
               # if +out+ is an IO and +value+ is a File, use IO.copy_stream 
   | 
||
| 
               flush_buffer(out, buf, chunked_p) 
   | 
||
| 
               out << "%x\r\n" % value.size if chunked_p 
   | 
||
| 
               IO.copy_stream(value, out) 
   | 
||
| 
               out << "\r\n" if chunked_p 
   | 
||
| 
             else 
   | 
||
| 
               # +out+ is an IO, and +value+ is not a File but an IO 
   | 
||
| 
               flush_buffer(out, buf, chunked_p) 
   | 
||
| 
               1 while flush_buffer(out, value.read(4096), chunked_p) 
   | 
||
| 
             end 
   | 
||
| 
           else 
   | 
||
| 
             # non-file field: 
   | 
||
| 
             #   HTML5 says, "The parts of the generated multipart/form-data 
   | 
||
| 
             #   resource that correspond to non-file fields must not have a 
   | 
||
| 
             #   Content-Type header specified." 
   | 
||
| 
             buf << "Content-Disposition: form-data; name=\"#{key}\"\r\n\r\n" 
   | 
||
| 
             buf << (value.respond_to?(:read) ? value.read : value) 
   | 
||
| 
           end 
   | 
||
| 
           buf << "\r\n" 
   | 
||
| 
         end 
   | 
||
| 
         buf << "--#{boundary}--\r\n" 
   | 
||
| 
         flush_buffer(out, buf, chunked_p) 
   | 
||
| 
         out << "0\r\n\r\n" if chunked_p 
   | 
||
| 
       end 
   | 
||
| 
       def quote_string(str, charset) 
   | 
||
| 
         str = str.encode(charset, fallback:->(c){'&#%d;'%c.encode("UTF-8").ord}) if charset 
   | 
||
| 
         str = str.gsub(/[\\"]/, '\\\\\&') 
   | 
||
| 
       end 
   | 
||
| 
       def flush_buffer(out, buf, chunked_p) 
   | 
||
| 
         return unless buf 
   | 
||
| 
         out << "%x\r\n"%buf.bytesize if chunked_p 
   | 
||
| 
         out << buf 
   | 
||
| 
         out << "\r\n" if chunked_p 
   | 
||
| 
         buf.clear 
   | 
||
| 
       end 
   | 
||
| 
       def supply_default_content_type 
   | 
||
| 
         return if content_type() 
   | 
||
| 
         warn 'net/http: warning: Content-Type did not set; using application/x-www-form-urlencoded' if $VERBOSE 
   | 
||
| 
         set_content_type 'application/x-www-form-urlencoded' 
   | 
||
| 
       end 
   | 
||
| 
       ## 
   | 
||
| 
       # Waits up to the continue timeout for a response from the server provided 
   | 
||
| 
       # we're speaking HTTP 1.1 and are expecting a 100-continue response. 
   | 
||
| 
       def wait_for_continue(sock, ver) 
   | 
||
| 
         if ver >= '1.1' and @header['expect'] and 
   | 
||
| 
             @header['expect'].include?('100-continue') 
   | 
||
| 
           if IO.select([sock.io], nil, nil, sock.continue_timeout) 
   | 
||
| 
             res = Net::HTTPResponse.read_new(sock) 
   | 
||
| 
             unless res.kind_of?(Net::HTTPContinue) 
   | 
||
| 
               throw :response, res 
   | 
||
| 
             end 
   | 
||
| 
           end 
   | 
||
| 
         end 
   | 
||
| 
       end 
   | 
||
| 
       def write_header(sock, ver, path) 
   | 
||
| 
         buf = "#{@method} #{path} HTTP/#{ver}\r\n" 
   | 
||
| 
         each_capitalized do |k,v| 
   | 
||
| 
           buf << "#{k}: #{v}\r\n" 
   | 
||
| 
         end 
   | 
||
| 
         buf << "\r\n" 
   | 
||
| 
         sock.write buf 
   | 
||
| 
       end 
   | 
||
| 
     end 
   | 
||
| lib/net/http/header.rb (revision 0) | ||
|---|---|---|
| 
     # The HTTPHeader module defines methods for reading and writing 
   | 
||
| 
     # HTTP headers. 
   | 
||
| 
     # 
   | 
||
| 
     # It is used as a mixin by other classes, to provide hash-like 
   | 
||
| 
     # access to HTTP header values. Unlike raw hash access, HTTPHeader 
   | 
||
| 
     # provides access via case-insensitive keys. It also provides 
   | 
||
| 
     # methods for accessing commonly-used HTTP header values in more 
   | 
||
| 
     # convenient formats. 
   | 
||
| 
     # 
   | 
||
| 
     module Net::HTTPHeader 
   | 
||
| 
       def initialize_http_header(initheader) 
   | 
||
| 
         @header = {} 
   | 
||
| 
         return unless initheader 
   | 
||
| 
         initheader.each do |key, value| 
   | 
||
| 
           warn "net/http: warning: duplicated HTTP header: #{key}" if key?(key) and $VERBOSE 
   | 
||
| 
           @header[key.downcase] = [value.strip] 
   | 
||
| 
         end 
   | 
||
| 
       end 
   | 
||
| 
       def size   #:nodoc: obsolete 
   | 
||
| 
         @header.size 
   | 
||
| 
       end 
   | 
||
| 
       alias length size   #:nodoc: obsolete 
   | 
||
| 
       # Returns the header field corresponding to the case-insensitive key. 
   | 
||
| 
       # For example, a key of "Content-Type" might return "text/html" 
   | 
||
| 
       def [](key) 
   | 
||
| 
         a = @header[key.downcase] or return nil 
   | 
||
| 
         a.join(', ') 
   | 
||
| 
       end 
   | 
||
| 
       # Sets the header field corresponding to the case-insensitive key. 
   | 
||
| 
       def []=(key, val) 
   | 
||
| 
         unless val 
   | 
||
| 
           @header.delete key.downcase 
   | 
||
| 
           return val 
   | 
||
| 
         end 
   | 
||
| 
         @header[key.downcase] = [val] 
   | 
||
| 
       end 
   | 
||
| 
       # [Ruby 1.8.3] 
   | 
||
| 
       # Adds a value to a named header field, instead of replacing its value. 
   | 
||
| 
       # Second argument +val+ must be a String. 
   | 
||
| 
       # See also #[]=, #[] and #get_fields. 
   | 
||
| 
       # 
   | 
||
| 
       #   request.add_field 'X-My-Header', 'a' 
   | 
||
| 
       #   p request['X-My-Header']              #=> "a" 
   | 
||
| 
       #   p request.get_fields('X-My-Header')   #=> ["a"] 
   | 
||
| 
       #   request.add_field 'X-My-Header', 'b' 
   | 
||
| 
       #   p request['X-My-Header']              #=> "a, b" 
   | 
||
| 
       #   p request.get_fields('X-My-Header')   #=> ["a", "b"] 
   | 
||
| 
       #   request.add_field 'X-My-Header', 'c' 
   | 
||
| 
       #   p request['X-My-Header']              #=> "a, b, c" 
   | 
||
| 
       #   p request.get_fields('X-My-Header')   #=> ["a", "b", "c"] 
   | 
||
| 
       # 
   | 
||
| 
       def add_field(key, val) 
   | 
||
| 
         if @header.key?(key.downcase) 
   | 
||
| 
           @header[key.downcase].push val 
   | 
||
| 
         else 
   | 
||
| 
           @header[key.downcase] = [val] 
   | 
||
| 
         end 
   | 
||
| 
       end 
   | 
||
| 
       # [Ruby 1.8.3] 
   | 
||
| 
       # Returns an array of header field strings corresponding to the 
   | 
||
| 
       # case-insensitive +key+.  This method allows you to get duplicated 
   | 
||
| 
       # header fields without any processing.  See also #[]. 
   | 
||
| 
       # 
   | 
||
| 
       #   p response.get_fields('Set-Cookie') 
   | 
||
| 
       #     #=> ["session=al98axx; expires=Fri, 31-Dec-1999 23:58:23", 
   | 
||
| 
       #          "query=rubyscript; expires=Fri, 31-Dec-1999 23:58:23"] 
   | 
||
| 
       #   p response['Set-Cookie'] 
   | 
||
| 
       #     #=> "session=al98axx; expires=Fri, 31-Dec-1999 23:58:23, query=rubyscript; expires=Fri, 31-Dec-1999 23:58:23" 
   | 
||
| 
       # 
   | 
||
| 
       def get_fields(key) 
   | 
||
| 
         return nil unless @header[key.downcase] 
   | 
||
| 
         @header[key.downcase].dup 
   | 
||
| 
       end 
   | 
||
| 
       # Returns the header field corresponding to the case-insensitive key. 
   | 
||
| 
       # Returns the default value +args+, or the result of the block, or 
   | 
||
| 
       # raises an IndexError if there's no header field named +key+ 
   | 
||
| 
       # See Hash#fetch 
   | 
||
| 
       def fetch(key, *args, &block)   #:yield: +key+ 
   | 
||
| 
         a = @header.fetch(key.downcase, *args, &block) 
   | 
||
| 
         a.kind_of?(Array) ? a.join(', ') : a 
   | 
||
| 
       end 
   | 
||
| 
       # Iterates through the header names and values, passing in the name 
   | 
||
| 
       # and value to the code block supplied. 
   | 
||
| 
       # 
   | 
||
| 
       # Example: 
   | 
||
| 
       # 
   | 
||
| 
       #     response.header.each_header {|key,value| puts "#{key} = #{value}" } 
   | 
||
| 
       # 
   | 
||
| 
       def each_header   #:yield: +key+, +value+ 
   | 
||
| 
         block_given? or return enum_for(__method__) 
   | 
||
| 
         @header.each do |k,va| 
   | 
||
| 
           yield k, va.join(', ') 
   | 
||
| 
         end 
   | 
||
| 
       end 
   | 
||
| 
       alias each each_header 
   | 
||
| 
       # Iterates through the header names in the header, passing 
   | 
||
| 
       # each header name to the code block. 
   | 
||
| 
       def each_name(&block)   #:yield: +key+ 
   | 
||
| 
         block_given? or return enum_for(__method__) 
   | 
||
| 
         @header.each_key(&block) 
   | 
||
| 
       end 
   | 
||
| 
       alias each_key each_name 
   | 
||
| 
       # Iterates through the header names in the header, passing 
   | 
||
| 
       # capitalized header names to the code block. 
   | 
||
| 
       # 
   | 
||
| 
       # Note that header names are capitalized systematically; 
   | 
||
| 
       # capitalization may not match that used by the remote HTTP 
   | 
||
| 
       # server in its response. 
   | 
||
| 
       def each_capitalized_name  #:yield: +key+ 
   | 
||
| 
         block_given? or return enum_for(__method__) 
   | 
||
| 
         @header.each_key do |k| 
   | 
||
| 
           yield capitalize(k) 
   | 
||
| 
         end 
   | 
||
| 
       end 
   | 
||
| 
       # Iterates through header values, passing each value to the 
   | 
||
| 
       # code block. 
   | 
||
| 
       def each_value   #:yield: +value+ 
   | 
||
| 
         block_given? or return enum_for(__method__) 
   | 
||
| 
         @header.each_value do |va| 
   | 
||
| 
           yield va.join(', ') 
   | 
||
| 
         end 
   | 
||
| 
       end 
   | 
||
| 
       # Removes a header field, specified by case-insensitive key. 
   | 
||
| 
       def delete(key) 
   | 
||
| 
         @header.delete(key.downcase) 
   | 
||
| 
       end 
   | 
||
| 
       # true if +key+ header exists. 
   | 
||
| 
       def key?(key) 
   | 
||
| 
         @header.key?(key.downcase) 
   | 
||
| 
       end 
   | 
||
| 
       # Returns a Hash consisting of header names and values. 
   | 
||
| 
       # e.g. 
   | 
||
| 
       # {"cache-control" => "private", 
   | 
||
| 
       #  "content-type" => "text/html", 
   | 
||
| 
       #  "date" => "Wed, 22 Jun 2005 22:11:50 GMT"} 
   | 
||
| 
       def to_hash 
   | 
||
| 
         @header.dup 
   | 
||
| 
       end 
   | 
||
| 
       # As for #each_header, except the keys are provided in capitalized form. 
   | 
||
| 
       # 
   | 
||
| 
       # Note that header names are capitalized systematically; 
   | 
||
| 
       # capitalization may not match that used by the remote HTTP 
   | 
||
| 
       # server in its response. 
   | 
||
| 
       def each_capitalized 
   | 
||
| 
         block_given? or return enum_for(__method__) 
   | 
||
| 
         @header.each do |k,v| 
   | 
||
| 
           yield capitalize(k), v.join(', ') 
   | 
||
| 
         end 
   | 
||
| 
       end 
   | 
||
| 
       alias canonical_each each_capitalized 
   | 
||
| 
       def capitalize(name) 
   | 
||
| 
         name.split(/-/).map {|s| s.capitalize }.join('-') 
   | 
||
| 
       end 
   | 
||
| 
       private :capitalize 
   | 
||
| 
       # Returns an Array of Range objects which represent the Range: 
   | 
||
| 
       # HTTP header field, or +nil+ if there is no such header. 
   | 
||
| 
       def range 
   | 
||
| 
         return nil unless @header['range'] 
   | 
||
| 
         self['Range'].split(/,/).map {|spec| 
   | 
||
| 
           m = /bytes\s*=\s*(\d+)?\s*-\s*(\d+)?/i.match(spec) or 
   | 
||
| 
                   raise HTTPHeaderSyntaxError, "wrong Range: #{spec}" 
   | 
||
| 
           d1 = m[1].to_i 
   | 
||
| 
           d2 = m[2].to_i 
   | 
||
| 
           if    m[1] and m[2] then  d1..d2 
   | 
||
| 
           elsif m[1]          then  d1..-1 
   | 
||
| 
           elsif          m[2] then -d2..-1 
   | 
||
| 
           else 
   | 
||
| 
             raise HTTPHeaderSyntaxError, 'range is not specified' 
   | 
||
| 
           end 
   | 
||
| 
         } 
   | 
||
| 
       end 
   | 
||
| 
       # Sets the HTTP Range: header. 
   | 
||
| 
       # Accepts either a Range object as a single argument, 
   | 
||
| 
       # or a beginning index and a length from that index. 
   | 
||
| 
       # Example: 
   | 
||
| 
       # 
   | 
||
| 
       #   req.range = (0..1023) 
   | 
||
| 
       #   req.set_range 0, 1023 
   | 
||
| 
       # 
   | 
||
| 
       def set_range(r, e = nil) 
   | 
||
| 
         unless r 
   | 
||
| 
           @header.delete 'range' 
   | 
||
| 
           return r 
   | 
||
| 
         end 
   | 
||
| 
         r = (r...r+e) if e 
   | 
||
| 
         case r 
   | 
||
| 
         when Numeric 
   | 
||
| 
           n = r.to_i 
   | 
||
| 
           rangestr = (n > 0 ? "0-#{n-1}" : "-#{-n}") 
   | 
||
| 
         when Range 
   | 
||
| 
           first = r.first 
   | 
||
| 
           last = r.last 
   | 
||
| 
           last -= 1 if r.exclude_end? 
   | 
||
| 
           if last == -1 
   | 
||
| 
             rangestr = (first > 0 ? "#{first}-" : "-#{-first}") 
   | 
||
| 
           else 
   | 
||
| 
             raise Net::HTTPHeaderSyntaxError, 'range.first is negative' if first < 0 
   | 
||
| 
             raise Net::HTTPHeaderSyntaxError, 'range.last is negative' if last < 0 
   | 
||
| 
             raise Net::HTTPHeaderSyntaxError, 'must be .first < .last' if first > last 
   | 
||
| 
             rangestr = "#{first}-#{last}" 
   | 
||
| 
           end 
   | 
||
| 
         else 
   | 
||
| 
           raise TypeError, 'Range/Integer is required' 
   | 
||
| 
         end 
   | 
||
| 
         @header['range'] = ["bytes=#{rangestr}"] 
   | 
||
| 
         r 
   | 
||
| 
       end 
   | 
||
| 
       alias range= set_range 
   | 
||
| 
       # Returns an Integer object which represents the HTTP Content-Length: 
   | 
||
| 
       # header field, or +nil+ if that field was not provided. 
   | 
||
| 
       def content_length 
   | 
||
| 
         return nil unless key?('Content-Length') 
   | 
||
| 
         len = self['Content-Length'].slice(/\d+/) or 
   | 
||
| 
             raise Net::HTTPHeaderSyntaxError, 'wrong Content-Length format' 
   | 
||
| 
         len.to_i 
   | 
||
| 
       end 
   | 
||
| 
       def content_length=(len) 
   | 
||
| 
         unless len 
   | 
||
| 
           @header.delete 'content-length' 
   | 
||
| 
           return nil 
   | 
||
| 
         end 
   | 
||
| 
         @header['content-length'] = [len.to_i.to_s] 
   | 
||
| 
       end 
   | 
||
| 
       # Returns "true" if the "transfer-encoding" header is present and 
   | 
||
| 
       # set to "chunked".  This is an HTTP/1.1 feature, allowing the 
   | 
||
| 
       # the content to be sent in "chunks" without at the outset 
   | 
||
| 
       # stating the entire content length. 
   | 
||
| 
       def chunked? 
   | 
||
| 
         return false unless @header['transfer-encoding'] 
   | 
||
| 
         field = self['Transfer-Encoding'] 
   | 
||
| 
         (/(?:\A|[^\-\w])chunked(?![\-\w])/i =~ field) ? true : false 
   | 
||
| 
       end 
   | 
||
| 
       # Returns a Range object which represents the value of the Content-Range: 
   | 
||
| 
       # header field. 
   | 
||
| 
       # For a partial entity body, this indicates where this fragment 
   | 
||
| 
       # fits inside the full entity body, as range of byte offsets. 
   | 
||
| 
       def content_range 
   | 
||
| 
         return nil unless @header['content-range'] 
   | 
||
| 
         m = %r<bytes\s+(\d+)-(\d+)/(\d+|\*)>i.match(self['Content-Range']) or 
   | 
||
| 
             raise Net::HTTPHeaderSyntaxError, 'wrong Content-Range format' 
   | 
||
| 
         m[1].to_i .. m[2].to_i 
   | 
||
| 
       end 
   | 
||
| 
       # The length of the range represented in Content-Range: header. 
   | 
||
| 
       def range_length 
   | 
||
| 
         r = content_range() or return nil 
   | 
||
| 
         r.end - r.begin + 1 
   | 
||
| 
       end 
   | 
||
| 
       # Returns a content type string such as "text/html". 
   | 
||
| 
       # This method returns nil if Content-Type: header field does not exist. 
   | 
||
| 
       def content_type 
   | 
||
| 
         return nil unless main_type() 
   | 
||
| 
         if sub_type() 
   | 
||
| 
         then "#{main_type()}/#{sub_type()}" 
   | 
||
| 
         else main_type() 
   | 
||
| 
         end 
   | 
||
| 
       end 
   | 
||
| 
       # Returns a content type string such as "text". 
   | 
||
| 
       # This method returns nil if Content-Type: header field does not exist. 
   | 
||
| 
       def main_type 
   | 
||
| 
         return nil unless @header['content-type'] 
   | 
||
| 
         self['Content-Type'].split(';').first.to_s.split('/')[0].to_s.strip 
   | 
||
| 
       end 
   | 
||
| 
       # Returns a content type string such as "html". 
   | 
||
| 
       # This method returns nil if Content-Type: header field does not exist 
   | 
||
| 
       # or sub-type is not given (e.g. "Content-Type: text"). 
   | 
||
| 
       def sub_type 
   | 
||
| 
         return nil unless @header['content-type'] 
   | 
||
| 
         _, sub = *self['Content-Type'].split(';').first.to_s.split('/') 
   | 
||
| 
         return nil unless sub 
   | 
||
| 
         sub.strip 
   | 
||
| 
       end 
   | 
||
| 
       # Any parameters specified for the content type, returned as a Hash. 
   | 
||
| 
       # For example, a header of Content-Type: text/html; charset=EUC-JP 
   | 
||
| 
       # would result in type_params returning {'charset' => 'EUC-JP'} 
   | 
||
| 
       def type_params 
   | 
||
| 
         result = {} 
   | 
||
| 
         list = self['Content-Type'].to_s.split(';') 
   | 
||
| 
         list.shift 
   | 
||
| 
         list.each do |param| 
   | 
||
| 
           k, v = *param.split('=', 2) 
   | 
||
| 
           result[k.strip] = v.strip 
   | 
||
| 
         end 
   | 
||
| 
         result 
   | 
||
| 
       end 
   | 
||
| 
       # Sets the content type in an HTTP header. 
   | 
||
| 
       # The +type+ should be a full HTTP content type, e.g. "text/html". 
   | 
||
| 
       # The +params+ are an optional Hash of parameters to add after the 
   | 
||
| 
       # content type, e.g. {'charset' => 'iso-8859-1'} 
   | 
||
| 
       def set_content_type(type, params = {}) 
   | 
||
| 
         @header['content-type'] = [type + params.map{|k,v|"; #{k}=#{v}"}.join('')] 
   | 
||
| 
       end 
   | 
||
| 
       alias content_type= set_content_type 
   | 
||
| 
       # Set header fields and a body from HTML form data. 
   | 
||
| 
       # +params+ should be an Array of Arrays or 
   | 
||
| 
       # a Hash containing HTML form data. 
   | 
||
| 
       # Optional argument +sep+ means data record separator. 
   | 
||
| 
       # 
   | 
||
| 
       # Values are URL encoded as necessary and the content-type is set to 
   | 
||
| 
       # application/x-www-form-urlencoded 
   | 
||
| 
       # 
   | 
||
| 
       # Example: 
   | 
||
| 
       #    http.form_data = {"q" => "ruby", "lang" => "en"} 
   | 
||
| 
       #    http.form_data = {"q" => ["ruby", "perl"], "lang" => "en"} 
   | 
||
| 
       #    http.set_form_data({"q" => "ruby", "lang" => "en"}, ';') 
   | 
||
| 
       # 
   | 
||
| 
       def set_form_data(params, sep = '&') 
   | 
||
| 
         query = URI.encode_www_form(params) 
   | 
||
| 
         query.gsub!(/&/, sep) if sep != '&' 
   | 
||
| 
         self.body = query 
   | 
||
| 
         self.content_type = 'application/x-www-form-urlencoded' 
   | 
||
| 
       end 
   | 
||
| 
       alias form_data= set_form_data 
   | 
||
| 
       # Set a HTML form data set. 
   | 
||
| 
       # +params+ is the form data set; it is an Array of Arrays or a Hash 
   | 
||
| 
       # +enctype is the type to encode the form data set. 
   | 
||
| 
       # It is application/x-www-form-urlencoded or multipart/form-data. 
   | 
||
| 
       # +formpot+ is an optional hash to specify the detail. 
   | 
||
| 
       # 
   | 
||
| 
       # boundary:: the boundary of the multipart message 
   | 
||
| 
       # charset::  the charset of the message. All names and the values of 
   | 
||
| 
       #            non-file fields are encoded as the charset. 
   | 
||
| 
       # 
   | 
||
| 
       # Each item of params is an array and contains following items: 
   | 
||
| 
       # +name+::  the name of the field 
   | 
||
| 
       # +value+:: the value of the field, it should be a String or a File 
   | 
||
| 
       # +opt+::   an optional hash to specify additional information 
   | 
||
| 
       # 
   | 
||
| 
       # Each item is a file field or a normal field. 
   | 
||
| 
       # If +value+ is a File object or the +opt+ have a filename key, 
   | 
||
| 
       # the item is treated as a file field. 
   | 
||
| 
       # 
   | 
||
| 
       # If Transfer-Encoding is set as chunked, this send the request in 
   | 
||
| 
       # chunked encoding. Because chunked encoding is HTTP/1.1 feature, 
   | 
||
| 
       # you must confirm the server to support HTTP/1.1 before sending it. 
   | 
||
| 
       # 
   | 
||
| 
       # Example: 
   | 
||
| 
       #    http.set_form([["q", "ruby"], ["lang", "en"]]) 
   | 
||
| 
       # 
   | 
||
| 
       # See also RFC 2388, RFC 2616, HTML 4.01, and HTML5 
   | 
||
| 
       # 
   | 
||
| 
       def set_form(params, enctype='application/x-www-form-urlencoded', formopt={}) 
   | 
||
| 
         @body_data = params 
   | 
||
| 
         @body = nil 
   | 
||
| 
         @body_stream = nil 
   | 
||
| 
         @form_option = formopt 
   | 
||
| 
         case enctype 
   | 
||
| 
         when /\Aapplication\/x-www-form-urlencoded\z/i, 
   | 
||
| 
           /\Amultipart\/form-data\z/i 
   | 
||
| 
           self.content_type = enctype 
   | 
||
| 
         else 
   | 
||
| 
           raise ArgumentError, "invalid enctype: #{enctype}" 
   | 
||
| 
         end 
   | 
||
| 
       end 
   | 
||
| 
       # Set the Authorization: header for "Basic" authorization. 
   | 
||
| 
       def basic_auth(account, password) 
   | 
||
| 
         @header['authorization'] = [basic_encode(account, password)] 
   | 
||
| 
       end 
   | 
||
| 
       # Set Proxy-Authorization: header for "Basic" authorization. 
   | 
||
| 
       def proxy_basic_auth(account, password) 
   | 
||
| 
         @header['proxy-authorization'] = [basic_encode(account, password)] 
   | 
||
| 
       end 
   | 
||
| 
       def basic_encode(account, password) 
   | 
||
| 
         'Basic ' + ["#{account}:#{password}"].pack('m').delete("\r\n") 
   | 
||
| 
       end 
   | 
||
| 
       private :basic_encode 
   | 
||
| 
       def connection_close? 
   | 
||
| 
         tokens(@header['connection']).include?('close') or 
   | 
||
| 
         tokens(@header['proxy-connection']).include?('close') 
   | 
||
| 
       end 
   | 
||
| 
       def connection_keep_alive? 
   | 
||
| 
         tokens(@header['connection']).include?('keep-alive') or 
   | 
||
| 
         tokens(@header['proxy-connection']).include?('keep-alive') 
   | 
||
| 
       end 
   | 
||
| 
       def tokens(vals) 
   | 
||
| 
         return [] unless vals 
   | 
||
| 
         vals.map {|v| v.split(',') }.flatten\ 
   | 
||
| 
             .reject {|str| str.strip.empty? }\ 
   | 
||
| 
             .map {|tok| tok.strip.downcase } 
   | 
||
| 
       end 
   | 
||
| 
       private :tokens 
   | 
||
| 
     end 
   | 
||
| lib/net/http/request.rb (revision 0) | ||
|---|---|---|
| 
     # HTTP request class. 
   | 
||
| 
     # This class wraps together the request header and the request path. 
   | 
||
| 
     # You cannot use this class directly. Instead, you should use one of its 
   | 
||
| 
     # subclasses: Net::HTTP::Get, Net::HTTP::Post, Net::HTTP::Head. 
   | 
||
| 
     # 
   | 
||
| 
     class Net::HTTPRequest < Net::HTTPGenericRequest 
   | 
||
| 
       # Creates HTTP request object. 
   | 
||
| 
       def initialize(path, initheader = nil) 
   | 
||
| 
         super self.class::METHOD, 
   | 
||
| 
               self.class::REQUEST_HAS_BODY, 
   | 
||
| 
               self.class::RESPONSE_HAS_BODY, 
   | 
||
| 
               path, initheader 
   | 
||
| 
       end 
   | 
||
| 
     end 
   | 
||
| lib/net/http/proxy_delta.rb (revision 0) | ||
|---|---|---|
| 
     module Net::HTTP::ProxyDelta   #:nodoc: internal use only 
   | 
||
| 
       private 
   | 
||
| 
       def conn_address 
   | 
||
| 
         proxy_address() 
   | 
||
| 
       end 
   | 
||
| 
       def conn_port 
   | 
||
| 
         proxy_port() 
   | 
||
| 
       end 
   | 
||
| 
       def edit_path(path) 
   | 
||
| 
         use_ssl? ? path : "http://#{addr_port()}#{path}" 
   | 
||
| 
       end 
   | 
||
| 
     end 
   | 
||
| lib/net/http/requests.rb (revision 0) | ||
|---|---|---|
| 
     # 
   | 
||
| 
     # HTTP/1.1 methods --- RFC2616 
   | 
||
| 
     # 
   | 
||
| 
     # See Net::HTTPGenericRequest for attributes and methods. 
   | 
||
| 
     # See Net::HTTP for usage examples. 
   | 
||
| 
     class Net::HTTP::Get < Net::HTTPRequest 
   | 
||
| 
       METHOD = 'GET' 
   | 
||
| 
       REQUEST_HAS_BODY  = false 
   | 
||
| 
       RESPONSE_HAS_BODY = true 
   | 
||
| 
     end 
   | 
||
| 
     # See Net::HTTPGenericRequest for attributes and methods. 
   | 
||
| 
     # See Net::HTTP for usage examples. 
   | 
||
| 
     class Net::HTTP::Head < Net::HTTPRequest 
   | 
||
| 
       METHOD = 'HEAD' 
   | 
||
| 
       REQUEST_HAS_BODY = false 
   | 
||
| 
       RESPONSE_HAS_BODY = false 
   | 
||
| 
     end 
   | 
||
| 
     # See Net::HTTPGenericRequest for attributes and methods. 
   | 
||
| 
     # See Net::HTTP for usage examples. 
   | 
||
| 
     class Net::HTTP::Post < Net::HTTPRequest 
   | 
||
| 
       METHOD = 'POST' 
   | 
||
| 
       REQUEST_HAS_BODY = true 
   | 
||
| 
       RESPONSE_HAS_BODY = true 
   | 
||
| 
     end 
   | 
||
| 
     # See Net::HTTPGenericRequest for attributes and methods. 
   | 
||
| 
     # See Net::HTTP for usage examples. 
   | 
||
| 
     class Net::HTTP::Put < Net::HTTPRequest 
   | 
||
| 
       METHOD = 'PUT' 
   | 
||
| 
       REQUEST_HAS_BODY = true 
   | 
||
| 
       RESPONSE_HAS_BODY = true 
   | 
||
| 
     end 
   | 
||
| 
     # See Net::HTTPGenericRequest for attributes and methods. 
   | 
||
| 
     # See Net::HTTP for usage examples. 
   | 
||
| 
     class Net::HTTP::Delete < Net::HTTPRequest 
   | 
||
| 
       METHOD = 'DELETE' 
   | 
||
| 
       REQUEST_HAS_BODY = false 
   | 
||
| 
       RESPONSE_HAS_BODY = true 
   | 
||
| 
     end 
   | 
||
| 
     # See Net::HTTPGenericRequest for attributes and methods. 
   | 
||
| 
     class Net::HTTP::Options < Net::HTTPRequest 
   | 
||
| 
       METHOD = 'OPTIONS' 
   | 
||
| 
       REQUEST_HAS_BODY = false 
   | 
||
| 
       RESPONSE_HAS_BODY = false 
   | 
||
| 
     end 
   | 
||
| 
     # See Net::HTTPGenericRequest for attributes and methods. 
   | 
||
| 
     class Net::HTTP::Trace < Net::HTTPRequest 
   | 
||
| 
       METHOD = 'TRACE' 
   | 
||
| 
       REQUEST_HAS_BODY = false 
   | 
||
| 
       RESPONSE_HAS_BODY = true 
   | 
||
| 
     end 
   | 
||
| 
     # 
   | 
||
| 
     # PATCH method --- RFC5789 
   | 
||
| 
     # 
   | 
||
| 
     # See Net::HTTPGenericRequest for attributes and methods. 
   | 
||
| 
     class Net::HTTP::Patch < Net::HTTPRequest 
   | 
||
| 
       METHOD = 'PATCH' 
   | 
||
| 
       REQUEST_HAS_BODY = true 
   | 
||
| 
       RESPONSE_HAS_BODY = true 
   | 
||
| 
     end 
   | 
||
| 
     # 
   | 
||
| 
     # WebDAV methods --- RFC2518 
   | 
||
| 
     # 
   | 
||
| 
     # See Net::HTTPGenericRequest for attributes and methods. 
   | 
||
| 
     class Net::HTTP::Propfind < Net::HTTPRequest 
   | 
||
| 
       METHOD = 'PROPFIND' 
   | 
||
| 
       REQUEST_HAS_BODY = true 
   | 
||
| 
       RESPONSE_HAS_BODY = true 
   | 
||
| 
     end 
   | 
||
| 
     # See Net::HTTPGenericRequest for attributes and methods. 
   | 
||
| 
     class Net::HTTP::Proppatch < Net::HTTPRequest 
   | 
||
| 
       METHOD = 'PROPPATCH' 
   | 
||
| 
       REQUEST_HAS_BODY = true 
   | 
||
| 
       RESPONSE_HAS_BODY = true 
   | 
||
| 
     end 
   | 
||
| 
     # See Net::HTTPGenericRequest for attributes and methods. 
   | 
||
| 
     class Net::HTTP::Mkcol < Net::HTTPRequest 
   | 
||
| 
       METHOD = 'MKCOL' 
   | 
||
| 
       REQUEST_HAS_BODY = true 
   | 
||
| 
       RESPONSE_HAS_BODY = true 
   | 
||
| 
     end 
   | 
||
| 
     # See Net::HTTPGenericRequest for attributes and methods. 
   | 
||
| 
     class Net::HTTP::Copy < Net::HTTPRequest 
   | 
||
| 
       METHOD = 'COPY' 
   | 
||
| 
       REQUEST_HAS_BODY = false 
   | 
||
| 
       RESPONSE_HAS_BODY = true 
   | 
||
| 
     end 
   | 
||
| 
     # See Net::HTTPGenericRequest for attributes and methods. 
   | 
||
| 
     class Net::HTTP::Move < Net::HTTPRequest 
   | 
||
| 
       METHOD = 'MOVE' 
   | 
||
| 
       REQUEST_HAS_BODY = false 
   | 
||
| 
       RESPONSE_HAS_BODY = true 
   | 
||