Class: BetterCap::Proxy::HTTP::Response
- Inherits:
-
Object
- Object
- BetterCap::Proxy::HTTP::Response
- Defined in:
- lib/bettercap/proxy/http/response.rb
Overview
HTTP response parser.
Instance Attribute Summary collapse
-
#body ⇒ Object
Response body.
-
#charset ⇒ Object
readonly
Response charset, default to UTF-8.
-
#chunked ⇒ Object
readonly
True if this is a chunked encoded response, otherwise false.
-
#code ⇒ Object
Response status code.
-
#content_length ⇒ Object
readonly
Response content length.
-
#content_type ⇒ Object
readonly
Response content type.
-
#headers ⇒ Object
A list of response headers.
-
#status ⇒ Object
Response status message.
-
#version ⇒ Object
HTTP protocol version.
Class Method Summary collapse
-
.from_file(filename, content_type) ⇒ Object
Return a 200 response object reading the file
filename
with the specifiedcontent_type
. -
.redirect(url, cookies = []) ⇒ Object
Return a 302 response object redirecting to
url
, setting optionalcookies
.
Instance Method Summary collapse
-
#<<(line) ⇒ Object
Parse a single response
line
. -
#[](name) ⇒ Object
Return the value of header with
name
or an empty string. -
#[]=(name, value) ⇒ Object
If the header with
name
is found, then avalue
is assigned to it, otherwise it’s created. -
#convert_webrick_response!(response) ⇒ Object
Convert a webrick response to this class.
-
#initialize ⇒ Response
constructor
Initialize this response object state.
-
#patch_header!(name, search, replace) ⇒ Object
Search for header
name
and apply a gsub substitution: value.gsub(search
,replace
). -
#textual? ⇒ Boolean
Return true if the response content type is textual, otherwise false.
-
#to_s ⇒ Object
Return a string representation of this response object, patching the Content-Length header if the #body was modified.
Constructor Details
#initialize ⇒ Response
Initialize this response object state.
73 74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/bettercap/proxy/http/response.rb', line 73 def initialize @version = '1.1' @code = 200 @status = 'OK' @content_type = nil @charset = 'UTF-8' @content_length = nil @body = nil @headers = {} @headers_done = false @chunked = false end |
Instance Attribute Details
#body ⇒ Object
Response body.
37 38 39 |
# File 'lib/bettercap/proxy/http/response.rb', line 37 def body @body end |
#charset ⇒ Object (readonly)
Response charset, default to UTF-8.
29 30 31 |
# File 'lib/bettercap/proxy/http/response.rb', line 29 def charset @charset end |
#chunked ⇒ Object (readonly)
True if this is a chunked encoded response, otherwise false.
33 34 35 |
# File 'lib/bettercap/proxy/http/response.rb', line 33 def chunked @chunked end |
#code ⇒ Object
Response status code.
23 24 25 |
# File 'lib/bettercap/proxy/http/response.rb', line 23 def code @code end |
#content_length ⇒ Object (readonly)
Response content length.
31 32 33 |
# File 'lib/bettercap/proxy/http/response.rb', line 31 def content_length @content_length end |
#content_type ⇒ Object (readonly)
Response content type.
27 28 29 |
# File 'lib/bettercap/proxy/http/response.rb', line 27 def content_type @content_type end |
#headers ⇒ Object
A list of response headers.
35 36 37 |
# File 'lib/bettercap/proxy/http/response.rb', line 35 def headers @headers end |
#status ⇒ Object
Response status message
25 26 27 |
# File 'lib/bettercap/proxy/http/response.rb', line 25 def status @status end |
#version ⇒ Object
HTTP protocol version
21 22 23 |
# File 'lib/bettercap/proxy/http/response.rb', line 21 def version @version end |
Class Method Details
.from_file(filename, content_type) ⇒ Object
Return a 200 response object reading the file filename
with the specified content_type
.
41 42 43 44 45 46 47 48 49 50 51 52 53 |
# File 'lib/bettercap/proxy/http/response.rb', line 41 def self.from_file( filename, content_type ) r = Response.new data = File.read(filename) r << "HTTP/1.1 200 OK" r << "Connection: close" r << "Content-Length: #{data.bytesize}" r << "Content-Type: #{content_type}" r << "\n" r << data r end |
.redirect(url, cookies = []) ⇒ Object
Return a 302 response object redirecting to url
, setting optional cookies
.
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/bettercap/proxy/http/response.rb', line 56 def self.redirect( url, = [] ) r = Response.new r << "HTTP/1.1 302 Moved" r << "Location: #{url}" .each do || r << "Set-Cookie: #{}" end r << "Connection: close" r << "\n\n" r end |
Instance Method Details
#<<(line) ⇒ Object
Parse a single response line
.
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 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
# File 'lib/bettercap/proxy/http/response.rb', line 106 def <<(line) # we already parsed the heders, collect response body if @headers_done @body = '' if @body.nil? @body << line.force_encoding( @charset ) else chomped = line.chomp Logger.debug " RESPONSE LINE: '#{chomped}'" # is this the first line 'HTTP/<VERSION> <CODE> <STATUS>' ? if chomped =~ /^HTTP\/([\d\.]+)\s+(\d+)\s+(.+)$/ @version = $1 @code = $2.to_i @status = $3 # collect and fix headers elsif chomped =~ /^([^:\s]+)\s*:\s*(.+)$/i name = $1 value = $2 if name == 'Content-Type' @content_type = value if value =~ /^(.+);\s*charset=(.+)/i @content_type = $1 @charset = $2.chomp end elsif name == 'Content-Length' @content_length = value.to_i # check if we have a chunked encoding elsif name == 'Transfer-Encoding' and value == 'chunked' @chunked = true name = nil value = nil end unless name.nil? or value.nil? if @headers.has_key?(name) if @headers[name].is_a?(Array) @headers[name] << value else @headers[name] = [ @headers[name], value ] end else @headers[name] = value end end # last line, we're done with the headers elsif chomped.empty? @headers_done = true end end end |
#[](name) ⇒ Object
Return the value of header with name
or an empty string.
165 166 167 |
# File 'lib/bettercap/proxy/http/response.rb', line 165 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, otherwise it’s created.
171 172 173 174 175 176 177 178 179 180 181 |
# File 'lib/bettercap/proxy/http/response.rb', line 171 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 end |
#convert_webrick_response!(response) ⇒ Object
Convert a webrick response to this class.
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
# File 'lib/bettercap/proxy/http/response.rb', line 87 def convert_webrick_response!(response) self << "HTTP/#{response.http_version} #{response.code} #{response.msg}" response.each do |key,value| # sometimes webrick joins all 'set-cookie' headers # which might cause issues with HSTS bypass. if key == 'set-cookie' response.get_fields('set-cookie').each do |v| self << "Set-Cookie: #{v}" end else self << "#{key.gsub(/\bwww|^te$|\b\w/){ $&.upcase }}: #{value}" end end self << "\n" @code = response.code @body = response.body || '' end |
#patch_header!(name, search, replace) ⇒ Object
Search for header name
and apply a gsub substitution:
value.gsub( +search+, +replace+ )
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 |
# File 'lib/bettercap/proxy/http/response.rb', line 185 def patch_header!( name, search, replace ) value = self[name] unless value.empty? patched = [] if value.is_a?(Array) value.each do |v| patched << v.gsub( search, replace ) end else patched << value.gsub( search, replace ) end self[name] = patched end end |
#textual? ⇒ Boolean
Return true if the response content type is textual, otherwise false.
160 161 162 |
# File 'lib/bettercap/proxy/http/response.rb', line 160 def textual? @content_type and ( @content_type =~ /^text\/.+/ or @content_type =~ /^application\/.+/ ) end |
#to_s ⇒ Object
Return a string representation of this response object, patching the Content-Length header if the #body was modified.
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 |
# File 'lib/bettercap/proxy/http/response.rb', line 203 def to_s # update content length in case the body was modified. if @headers.has_key?('Content-Length') @headers['Content-Length'] = @body.nil?? 0 : @body.bytesize end s = "HTTP/#{@version} #{@code} #{@status}\n" @headers.each do |name,value| if value.is_a?(Array) value.each do |v| s << "#{name}: #{v}\n" end else s << "#{name}: #{value}\n" end end s << "\n" + ( @body.nil?? "\n" : @body ) s end |