Class: PDTP::Client::HttpClient

Inherits:
EventMachine::Protocols::HttpClient
  • Object
show all
Defined in:
lib/pdtp/client/http_client.rb

Instance Method Summary collapse

Instance Method Details

#send_request(args) ⇒ Object

Override send_request to support the additional features we need



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/pdtp/client/http_client.rb', line 36

def send_request args
  args[:verb] ||= args[:method] # Support :method as an alternative to :verb.
  args[:verb] ||= :get # IS THIS A GOOD IDEA, to default to GET if nothing was specified?

  verb = args[:verb].to_s.upcase
  unless ["GET", "POST", "PUT", "DELETE", "HEAD"].include?(verb)
    set_deferred_status :failed, {:status => 0} # TODO, not signalling the error type
    return # NOTE THE EARLY RETURN, we're not sending any data.
  end

  request = args[:request] || "/"
  unless request[0,1] == "/"
    request = "/" + request
  end

  qs = args[:query_string] || ""
  if qs.length > 0 and qs[0,1] != '?'
    qs = "?" + qs
  end

  # Allow an override for the host header if it's not the connect-string.
  port = args[:port]
  host = args[:host_header] || "#{args[:host]}#{port}" || "_"
  
  # POST items.
  postcontenttype = args[:contenttype] || "application/octet-stream"
  postcontent = args[:content] || ""
  raise "oversized content in HTTP POST" if postcontent.length > MaxPostContentLength

  # ESSENTIAL for the request's line-endings to be CRLF, not LF. Some servers misbehave otherwise.
  # TODO: We ASSUME the caller wants to send a 1.1 request. May not be a good assumption.
  req = [
    "#{verb} #{request}#{qs} HTTP/1.1",
    "Host: #{host}",
    "User-Agent: #{args[:agent] || 'Ruby EventMachine'}"
  ]

  if verb == "POST" || verb == "PUT"
    req << "Content-Type: #{postcontenttype}"
    req << "Content-Length: #{postcontent.length}"
  end
  
  if args[:range]
    # Convert the range to an array if it isn't one already
    args[:range] = [args[:range]] unless args[:range].is_a?(Array)

    # Transform it to a text string
    range = args[:range].map { |v| v.is_a?(Range) ? "#{v.min}-#{v.max}" : (v >= 0 ? "#{v}-" : v.to_s) }.join(',')
    req << "Range: bytes=#{range}"
  end
  
  if args[:headers]
    headers = case args[:headers]
    when Array then args[:headers]
    when String then [args[:headers]]
    else raise ArgumentError, "headers must be an array or string"
    end
    
    req += headers
  end

  # TODO, this cookie handler assumes it's getting a single, semicolon-delimited string.
  # Eventually we will want to deal intelligently with arrays and hashes.
  if args[:cookie]
    req << "Cookie: #{args[:cookie]}"
  end

  req << ""
  reqstring = req.map {|l| "#{l}\r\n"}.join
  send_data reqstring

  if verb == "POST" || verb == "PUT"
    send_data postcontent
  end
end