Class: BetterCap::Proxy::HTTP::Request

Inherits:
Object
  • Object
show all
Defined in:
lib/bettercap/proxy/http/request.rb

Overview

HTTP request parser.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(default_port = 80) ⇒ Request

Initialize this object setting #port to default_port.



40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/bettercap/proxy/http/request.rb', line 40

def initialize( default_port = 80 )
  @lines          = []
  @method         = nil
  @version        = '1.1'
  @path           = nil
  @host           = nil
  @port           = default_port
  @headers        = {}
  @content_length = 0
  @body           = nil
  @client         = ""
end

Instance Attribute Details

#bodyObject

Request body.



35
36
37
# File 'lib/bettercap/proxy/http/request.rb', line 35

def body
  @body
end

#clientObject

Client address.



37
38
39
# File 'lib/bettercap/proxy/http/request.rb', line 37

def client
  @client
end

#content_lengthObject (readonly)

Content length.



33
34
35
# File 'lib/bettercap/proxy/http/request.rb', line 33

def content_length
  @content_length
end

#headersObject (readonly)

Request headers hash.



31
32
33
# File 'lib/bettercap/proxy/http/request.rb', line 31

def headers
  @headers
end

#hostObject (readonly)

Hostname.



27
28
29
# File 'lib/bettercap/proxy/http/request.rb', line 27

def host
  @host
end

#methodObject (readonly)

HTTP method.



21
22
23
# File 'lib/bettercap/proxy/http/request.rb', line 21

def method
  @method
end

#pathObject (readonly)

Request path + query.



25
26
27
# File 'lib/bettercap/proxy/http/request.rb', line 25

def path
  @path
end

#portObject

Request port.



29
30
31
# File 'lib/bettercap/proxy/http/request.rb', line 29

def port
  @port
end

#versionObject (readonly)

HTTP version.



23
24
25
# File 'lib/bettercap/proxy/http/request.rb', line 23

def version
  @version
end

Class Method Details

.parse(raw) ⇒ Object

Return a Request object from a raw string.



77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/bettercap/proxy/http/request.rb', line 77

def self.parse(raw)
  req = Request.new
  lines = raw.split("\n")
  lines.each_with_index do |line,i|
    req << line
    if line.chomp == ''
      req.body = lines[i + 1..lines.size].join("\n")
      break
    end
  end
  req
end

Instance Method Details

#<<(line) ⇒ Object

Parse a single request line, patch it if needed and append it to #lines.



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/bettercap/proxy/http/request.rb', line 91

def <<(line)
  line = line.chomp

  Logger.debug "  REQUEST LINE: '#{line}'"

  # is this the first line '<VERB> <URI> HTTP/<VERSION>' ?
  if line =~ /^(\w+)\s+(\S+)\s+HTTP\/([\d\.]+)\s*$/
    @method  = $1
    @path    = $2
    @version = $3

    # fix url
    if @path.include? '://'
      uri = URI::parse @path
      @path = "#{uri.path}" + ( uri.query ? "?#{uri.query}" : '' )
    end

  # collect and fix headers
  elsif line =~ /^([^:\s]+)\s*:\s*(.+)$/i
    name = $1
    value = $2

    case name
    when 'Host'
      @host = value
      if @host =~ /([^:]*):([0-9]*)$/
        @host = $1
        @port = $2.to_i
      end
    when 'Content-Length'
      @content_length = value.to_i
    # we don't want to have hundreds of threads running
    when 'Connection'
      value = 'close'
    when 'Proxy-Connection'
      name = 'Connection'
    # disable gzip, chunked, etc encodings
    when 'Accept-Encoding'
      value = 'identity'
    end

    @headers[name] = value
  end
end

#[](name) ⇒ Object

Return the value of header with name or an empty string.



169
170
171
# File 'lib/bettercap/proxy/http/request.rb', line 169

def [](name)
  ( @headers.has_key?(name) ? @headers[name] : "" )
end

#[]=(name, value) ⇒ Object

If the header with name is found, then a value is assigned to it. If value is null and the header is found, it will be removed.



175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/bettercap/proxy/http/request.rb', line 175

def []=(name, value)
  if @headers.has_key?(name)
    if value.nil?
      @headers.delete(name)
    else
      @headers[name] = value
    end
  elsif !value.nil?
    @headers[name] = value
  end

  @host = value if name == 'Host'
end

#base_urlObject

Return SCHEMA://HOST



155
156
157
# File 'lib/bettercap/proxy/http/request.rb', line 155

def base_url
  "#{port == 443 ? 'https' : 'http'}://#{@host}"
end

#post?Boolean

Return true if this is a POST request, otherwise false.

Returns:

  • (Boolean)


137
138
139
# File 'lib/bettercap/proxy/http/request.rb', line 137

def post?
  @method == 'POST'
end

#read(sock) ⇒ Object

Read lines from the sock socket and parse them. Will raise an exception if the #hostname can not be parsed.



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/bettercap/proxy/http/request.rb', line 55

def read(sock)
  # read the first line
  self << sock.readline

  loop do
    line = sock.readline
    self << line

    if line.chomp == ''
      break
    end
  end

  raise "Couldn't extract host from the request." unless @host

  # keep reading the request body if needed
  if @content_length > 0
    @body = sock.read(@content_length)
  end
end

#to_sObject

Return a string representation of the HTTP request.



142
143
144
145
146
147
148
149
150
151
152
# File 'lib/bettercap/proxy/http/request.rb', line 142

def to_s
  raw = "#{@method} #{@path} HTTP/#{@version}\n"

  @headers.each do |name,value|
    raw << "#{name}: #{value}\n"
  end

  raw << "\n"
  raw << ( @body || '' )
  raw
end

#to_url(max_length = 50) ⇒ Object

Return the full request URL trimming it at max_length characters.



160
161
162
163
164
165
166
# File 'lib/bettercap/proxy/http/request.rb', line 160

def to_url(max_length = 50)
  url = "#{base_url}#{@path}"
  unless max_length.nil?
    url = url.slice(0..max_length) + '...' unless url.length <= max_length
  end
  url
end