ferencd@0: require 'net/protocol' ferencd@0: ferencd@0: module Net ferencd@0: class HTTP < Protocol ferencd@0: module Server ferencd@0: module Requests ferencd@0: # Default ports for common URI schemes ferencd@0: DEFAULT_PORTS = { ferencd@0: 'https' => 443, ferencd@0: 'http' => 80 ferencd@0: } ferencd@0: ferencd@0: protected ferencd@0: ferencd@0: # ferencd@0: # Reads a HTTP Request from the stream. ferencd@0: # ferencd@0: # @param [IO] stream ferencd@0: # The stream to read from. ferencd@0: # ferencd@0: # @return [String, nil] ferencd@0: # The raw HTTP Request or `nil` if the Request was malformed. ferencd@0: # ferencd@0: def read_request(stream) ferencd@0: buffer = '' ferencd@0: ferencd@0: begin ferencd@0: request_line = stream.readline("\r\n") ferencd@0: post_req = false ferencd@0: if request_line.start_with? 'POST' ferencd@0: post_req = true ferencd@0: end ferencd@0: # the request line must contain 'HTTP/' ferencd@0: return unless request_line.include?('HTTP/') ferencd@0: ferencd@0: buffer << request_line ferencd@0: ferencd@0: stream.each_line("\r\n") do |header| ferencd@0: buffer << header ferencd@0: # a header line must contain a ':' character followed by ferencd@0: # linear-white-space (either ' ' or "\t"). ferencd@0: unless (header.include?(': ') || header.include?(":\t")) ferencd@0: # if this is not a header line, check if it is the end ferencd@0: # of the request ferencd@0: if header == "\r\n" ferencd@0: if post_req ferencd@0: # end of the request, what comes here is the possible POST data ferencd@0: extra = '' ferencd@0: begin ferencd@0: extra = stream.read_nonblock(256) ferencd@0: rescue ferencd@0: extra = '' ferencd@0: end ferencd@0: buffer = buffer.strip ferencd@0: buffer << "\r\nX-Extra: #{extra}" ferencd@0: buffer += "\r\n\r\n" ferencd@0: end ferencd@0: break ferencd@0: else ferencd@0: # invalid header line ferencd@0: return ferencd@0: end ferencd@0: end ferencd@0: end ferencd@0: ferencd@0: rescue IOError, SystemCallError ferencd@0: return ferencd@0: end ferencd@0: ferencd@0: return buffer ferencd@0: ferencd@0: end ferencd@0: ferencd@0: # ferencd@0: # Normalizes the `:uri` part of the request. ferencd@0: # ferencd@0: # @param [Hash] request ferencd@0: # The unnormalized HTTP request. ferencd@0: # ferencd@0: def normalize_uri(request) ferencd@0: uri = request[:uri] ferencd@0: ferencd@0: case uri ferencd@0: when Hash ferencd@0: if uri[:scheme] ferencd@0: uri[:port] = unless uri[:port] ferencd@0: DEFAULT_PORTS[uri[:scheme]] ferencd@0: else ferencd@0: uri[:port].to_i ferencd@0: end ferencd@0: end ferencd@0: when '*' ferencd@0: request[:uri] = {} ferencd@0: end ferencd@0: end ferencd@0: ferencd@0: # ferencd@0: # Normalizes the `:headers` part of the request. ferencd@0: # ferencd@0: # @param [Hash] request ferencd@0: # The unnormalized HTTP request. ferencd@0: # ferencd@0: def normalize_headers(request) ferencd@0: headers = request[:headers] ferencd@0: normalized_headers = {} ferencd@0: ferencd@0: unless headers.empty? ferencd@0: headers.each do |header| ferencd@0: name = header[:name].to_s ferencd@0: value = header[:value].to_s ferencd@0: ferencd@0: if normalized_headers.has_key?(name) ferencd@0: previous_value = normalized_headers[name] ferencd@0: ferencd@0: if previous_value.kind_of?(Array) ferencd@0: previous_value << value ferencd@0: else ferencd@0: normalized_headers[name] = [previous_value, value] ferencd@0: end ferencd@0: else ferencd@0: normalized_headers[name] = value ferencd@0: end ferencd@0: end ferencd@0: end ferencd@0: ferencd@0: request[:headers] = normalized_headers ferencd@0: end ferencd@0: ferencd@0: # ferencd@0: # Normalizes a HTTP request. ferencd@0: # ferencd@0: # @param [Hash] request ferencd@0: # The unnormalized HTTP request. ferencd@0: # ferencd@0: def normalize_request(request) ferencd@0: normalize_uri(request) ferencd@0: normalize_headers(request) ferencd@0: end ferencd@0: ferencd@0: end ferencd@0: end ferencd@0: end ferencd@0: end