Class: Segregate

Inherits:
Object
  • Object
show all
Defined in:
lib/segregate.rb,
lib/segregate/version.rb,
lib/segregate/http_headers.rb,
lib/segregate/http_methods.rb,
lib/segregate/http_regular_expressions.rb

Constant Summary collapse

VERSION =
"0.6.0".freeze
DATE =
"2016-12-13".freeze
GENERAL_HEADERS =
[
	"cache-control",
	"connection",
	"date",
	"pragma",
	"trailer",
	"transfer-encoding",
	"upgrade",
	"via",
	"warning"
]
REQUEST_HEADERS =
[
	"accept",
	"accept-charset",
	"accept-encoding",
	"accept-language",
	"authorization",
	"expect",
	"from",
	"host",
	"if-match",
	"if-modified-Since",
	"if-none-Match",
	"if-range",
	"if-unmodified-Since",
	"max-forwards",
	"proxy-authorization",
	"range",
	"referer",
	"te",
	"user-agent"
]
RESPONSE_HEADERS =
[
	"accept-ranges",
	"age",
	"etag",
	"location",
	"proxy-authenticate",
	"retry-after",
	"server",
	"vary",
	"www-authenticate"
]
ENTITY_HEADERS =
[
	"allow",
	"content-encoding",
	"content-language",
	"content-length",
	"content-location",
	"content-md5",
	"content-range",
	"content-type",
	"expires",
	"last-modified"
]
ALL_HEADERS =
GENERAL_HEADERS + REQUEST_HEADERS + RESPONSE_HEADERS + ENTITY_HEADERS
HTTP_METHODS =
[
	"OPTIONS",
	"GET",
	"HEAD",
	"POST",
	"PUT",
	"DELETE",
	"TRACE",
	"CONNECT",
	"PATCH"
]
REQUEST_LINE =
/^(#{HTTP_METHODS.join("|")})\s(\*|\S+)\sHTTP\/(\d).(\d)$/
STATUS_LINE =
/^HTTP\/(\d).(\d)\s(\d{3})\s(.+)$/
UNKNOWN_REQUEST_LINE =
/^(\w+)\s(\*|\S+)\sHTTP\/(\d).(\d)$/

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(callback = nil, *args, **kwargs) ⇒ Segregate

Returns a new instance of Segregate.



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/segregate.rb', line 31

def initialize callback = nil, *args, **kwargs
  @debug = kwargs[:debug] ? true : false
  @callback = callback
  @http_version = [nil, nil]

  # :request, :response
  @type = Juncture.new :request, :response
  @state = Juncture.new :waiting, :headers, :body, :done, default: :waiting

  @headers = Hashie::Mash.new
  @body = ""

  @stashed_data = ""
  @stashed_body = ""

  @header_order = []
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(meth, *args, &block) ⇒ Object



13
14
15
16
17
18
19
# File 'lib/segregate.rb', line 13

def method_missing meth, *args, &block
  if @uri.respond_to? meth
    @uri.send meth, *args, &block
  else
    super
  end
end

Instance Attribute Details

#bodyObject

Returns the value of attribute body.



11
12
13
# File 'lib/segregate.rb', line 11

def body
  @body
end

#headersObject (readonly)

Returns the value of attribute headers.



10
11
12
# File 'lib/segregate.rb', line 10

def headers
  @headers
end

#http_versionObject (readonly)

Returns the value of attribute http_version.



10
11
12
# File 'lib/segregate.rb', line 10

def http_version
  @http_version
end

#request_methodObject

Returns the value of attribute request_method.



11
12
13
# File 'lib/segregate.rb', line 11

def request_method
  @request_method
end

#stateObject (readonly)

Returns the value of attribute state.



10
11
12
# File 'lib/segregate.rb', line 10

def state
  @state
end

#status_codeObject

Returns the value of attribute status_code.



11
12
13
# File 'lib/segregate.rb', line 11

def status_code
  @status_code
end

#status_phraseObject

Returns the value of attribute status_phrase.



11
12
13
# File 'lib/segregate.rb', line 11

def status_phrase
  @status_phrase
end

#typeObject (readonly)

Returns the value of attribute type.



10
11
12
# File 'lib/segregate.rb', line 10

def type
  @type
end

#uriObject (readonly)

Returns the value of attribute uri.



10
11
12
# File 'lib/segregate.rb', line 10

def uri
  @uri
end

Instance Method Details

#build_body(raw_message) ⇒ Object



126
127
128
129
130
131
132
133
134
135
136
# File 'lib/segregate.rb', line 126

def build_body raw_message
  new_body, size = deflate_if_needed(@body)
  if @headers['content-length']
    raw_message << new_body
    raw_message << "\r\n\r\n"
  elsif @headers['transfer-encoding'] == 'chunked'
    raw_message << "%s\r\n" % (size.to_s(16))
    raw_message << new_body + "\r\n"
    raw_message << "0\r\n\r\n"
  end
end

#build_headers(raw_message) ⇒ Object



111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/segregate.rb', line 111

def build_headers raw_message
  request? ? raw_message << request_line + "\r\n" : raw_message << status_line + "\r\n"
  @header_order.each do |header|
    values = @headers[header.downcase]
    if values.kind_of?(Array)
      values.each do |value|
        raw_message << "%s: %s\r\n" % [header, value]
      end
    else
      raw_message << "%s: %s\r\n" % [header, values]
    end
  end
  raw_message << "\r\n"
end

#debug(message) ⇒ Object



25
26
27
28
29
# File 'lib/segregate.rb', line 25

def debug message
  if @debug
    puts "DEBUG: " + message.to_s
  end
end

#deflate_if_needed(body) ⇒ Object



138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/segregate.rb', line 138

def deflate_if_needed(body)
  return [body, body.size] unless gzip_encoded_body?

  str_io = StringIO.new('w')
  w_gz = Zlib::GzipWriter.new(str_io)
  w_gz.write(body)
  w_gz.close

  # To specify the number of bytes for a gzip string
  # we should check for the buffer size instead of the string size
  [str_io.string, str_io.size]
end

#done?Boolean

Returns:

  • (Boolean)


61
62
63
# File 'lib/segregate.rb', line 61

def done?
  @state >= :done
end

#headers_complete?Boolean

Returns:

  • (Boolean)


57
58
59
# File 'lib/segregate.rb', line 57

def headers_complete?
  @state > :headers
end

#major_http_versionObject



77
78
79
# File 'lib/segregate.rb', line 77

def major_http_version
  http_version[0]
end

#major_http_version=(value) ⇒ Object



81
82
83
# File 'lib/segregate.rb', line 81

def major_http_version= value
  http_version[0] = value
end

#minor_http_versionObject



85
86
87
# File 'lib/segregate.rb', line 85

def minor_http_version
  http_version[1]
end

#minor_http_version=(value) ⇒ Object



89
90
91
# File 'lib/segregate.rb', line 89

def minor_http_version= value
  http_version[1] = value
end

#parse_data(data) ⇒ Object



151
152
153
154
155
156
157
158
159
160
161
# File 'lib/segregate.rb', line 151

def parse_data data
  data = StringIO.new(@stashed_data + data)
  @stashed_data = ""

  while !data.eof? && @state < :done
    line, complete_line = get_next_line data
    complete_line ? parse_line(line) : @stashed_data = line
  end

  data.close
end

#parse_line(line) ⇒ Object



163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/segregate.rb', line 163

def parse_line line
  case @state.state
  when :waiting
    read_in_first_line line
  when :headers
    read_in_headers line
  when :body
    read_in_body line
  end

  @callback.on_message_complete self if @callback.respond_to?(:on_message_complete) && done?
end

#raw_dataObject



101
102
103
104
105
106
107
108
109
# File 'lib/segregate.rb', line 101

def raw_data
  raw_message = ""
  update_content_length

  build_headers raw_message
  build_body raw_message

  return raw_message
end

#request?Boolean

Returns:

  • (Boolean)


49
50
51
# File 'lib/segregate.rb', line 49

def request?
  @type == :request
end

#request_lineObject



65
66
67
# File 'lib/segregate.rb', line 65

def request_line
  request? ? "%s %s HTTP/%d.%d" % [request_method, request_url.to_s, *http_version] : nil
end

#request_urlObject



73
74
75
# File 'lib/segregate.rb', line 73

def request_url
  uri ? uri.to_s : nil
end

#respond_to?(meth, include_private = false) ⇒ Boolean

Returns:

  • (Boolean)


21
22
23
# File 'lib/segregate.rb', line 21

def respond_to?(meth, include_private = false)
  @uri.respond_to?(meth, include_private) || super
end

#response?Boolean

Returns:

  • (Boolean)


53
54
55
# File 'lib/segregate.rb', line 53

def response?
  @type == :response
end

#status_lineObject



69
70
71
# File 'lib/segregate.rb', line 69

def status_line
  response? ? "HTTP/%d.%d %d %s" % [*http_version, status_code, status_phrase] : nil
end

#update_content_lengthObject



93
94
95
96
97
98
99
# File 'lib/segregate.rb', line 93

def update_content_length
  unless @body.empty?
    if @headers['content-length']
      @headers['content-length'] = @body.length.to_s
    end
  end
end