Class: ICAPrb::Server::Response

Inherits:
Object
  • Object
show all
Defined in:
lib/icaprb/server/response.rb

Overview

The Response class creates a valid ICAP response to send over the socket.

Constant Summary collapse

ERROR_TEMPLATE =

basic template for a HTML error page

ERB.new('<html><head><meta charset="utf-8" /><title>ICAP.rb<% unless params["title"].nil? %> ::'+
' <%= params["title"] %><% end %></title></head>'+
'<body><h1><%= params["title"] || "Error" %></h1><div>'+
'<%= params["content"] || "Content missing" %></div></body></html>')

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeResponse

creates a new instance of ICAPrb::Server::Response and initialises some headers



18
19
20
21
22
23
24
25
# File 'lib/icaprb/server/response.rb', line 18

def initialize
  # the ISTag is used to let the proxy know that the old response (probably cached)
  # is invalid if this value changes
  @icap_header = {'Date' => Time.now.gmtime, 'Server' => 'ICAP.rb', 'Connection' => 'Close', 'ISTag' => '"replace"'}
  @icap_version = '1.0'
  @icap_status_code = 200
  @components = []
end

Instance Attribute Details

#componentsObject

the parts of the ICAP response



16
17
18
# File 'lib/icaprb/server/response.rb', line 16

def components
  @components
end

#icap_headerObject

A Hash containing the header of the ICAP response



10
11
12
# File 'lib/icaprb/server/response.rb', line 10

def icap_header
  @icap_header
end

#icap_status_codeObject

The ICAP status code like 200 for OK



14
15
16
# File 'lib/icaprb/server/response.rb', line 14

def icap_status_code
  @icap_status_code
end

#icap_versionObject

The ICAP Version - usually 1.0



12
13
14
# File 'lib/icaprb/server/response.rb', line 12

def icap_version
  @icap_version
end

Class Method Details

.continue(io, icap_version = '1.0') ⇒ Object

sends the information to the client, that it should send the rest of the file. This method does not keep track of your connection and if you call it twice, your client may have trouble with your response. Use it only once and only in preview mode.



150
151
152
# File 'lib/icaprb/server/response.rb', line 150

def self.continue(io,icap_version = '1.0')
  io.write "ICAP/#{icap_version} 100 Continue\r\n\r\n"
end

.display_error_page(io, status, params) ⇒ Object

display an error page when something is not ok this is a server function because it is also required for errors which cannot be caught by a service

Params

io

The object, where the response should be written to

status

ICAP status code to send

params

parameters for the template and the response as well



123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/icaprb/server/response.rb', line 123

def self.display_error_page(io,status, params)
  response = Response.new
  response.icap_status_code = status
  http_resp_header = ResponseHeader.new(params[:http_version],params[:http_status])
  http_resp_header['Content-Type'] = 'text/html; charset=utf-8'
  http_resp_body = ResponseBody.new(ERROR_TEMPLATE.result(binding), false)
  http_resp_header['Content-Length'] = http_resp_body.length
  response.components << http_resp_header
  response.components << http_resp_body
  response.write_headers_to_socket io
  io.write(http_resp_body.to_chunk)
  send_last_chunk(io,false)
  io.close
end

.error_response(io) ⇒ Object

this method is an alternative to display_error_page. It does not send any http information to the client. instead it will send an ICAP header which will indicate an error.



140
141
142
143
144
145
# File 'lib/icaprb/server/response.rb', line 140

def self.error_response(io)
  response = Response.new
  response.icap_status_code = 500
  response.components << NullBody.new
  response.write_headers_to_socket io
end

.send_last_chunk(io, in_preview = false) ⇒ Object

this method sends the last (empty) chunk and it will add the ieof marker if it is requested. this empty chunk is used to indicate the end of the body encoded in chunked encondig.



156
157
158
159
160
161
162
163
# File 'lib/icaprb/server/response.rb', line 156

def self.send_last_chunk(io,in_preview = false)
  data = '0'
  if in_preview
    data += '; ieof'
  end
  data += "\r\n\r\n"
  io.write(data)
end

Instance Method Details

#encapsulated_headerObject

creates the encapsulated header from an array of components which it is for Params:

components

an array of the components of the ICAP response The components can be an instance of RequestHeader, RequestBody, ResponseHeader or ResponseBody

Returns

A Hash containing only one entry with the key ‘Encapsulated’ which holds the offsets of the components



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/icaprb/server/response.rb', line 58

def encapsulated_header
  encapsulated_hdr = 'Encapsulated'
  encapsulated_hdr_list = []
  offset = 0
  @components.sort.each do |component|
    case component
      when RequestHeader
        encapsulated_hdr_list << "req-hdr=#{offset}"
      when ResponseHeader
        encapsulated_hdr_list << "res-hdr=#{offset}"
      when RequestBody
        encapsulated_hdr_list << "req-body=#{offset}"
      when ResponseBody
        encapsulated_hdr_list << "res-body=#{offset}"
      when NullBody
        encapsulated_hdr_list << "null-body=#{offset}"
    end
    offset += component.to_s.length
  end
  {encapsulated_hdr => encapsulated_hdr_list.join(', ')}
end

#hash_to_headerObject

convert an hash of keys (header names) and values to a String

Params:

value

the hash to convert

Returns

the ICAP headers as String



39
40
41
42
43
44
45
46
47
48
49
# File 'lib/icaprb/server/response.rb', line 39

def hash_to_header
  headers = []
  # add Encapsulated Header if we have a body
  encapsulated = encapsulated_header
  value = @icap_header
  value = @icap_header.merge(encapsulated) if encapsulated['Encapsulated'].length > 0
  value.each do |key, value|
    headers << "#{key}: #{value}"
  end
  headers.join("\r\n") + "\r\n\r\n"
end

#response_lineObject

creates the status line for the ICAP protocol Returns the status line as a String



29
30
31
# File 'lib/icaprb/server/response.rb', line 29

def response_line
  "ICAP/#{@icap_version} #{@icap_status_code} #{ICAP_STATUS_CODES[@icap_status_code]}\r\n"
end

#write_headersObject

writes the headers into a string and returns them it raises an exception if the response would be incorrectly created (for example multiple headers) it will create the full ICAP + HTTP header (if available)



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/icaprb/server/response.rb', line 82

def write_headers
  output  = response_line
  output += hash_to_header
  s_comp = @components.sort

  # add request header if it exists
  request_header = s_comp.select {|component| component.class == RequestHeader}
  raise 'The request header can be included only once' if request_header.count > 1
  request_header.each do |rh|
    output += rh.to_s
  end

  # add response header to the response if it exists
  response_header = s_comp.select {|component| component.class == ResponseHeader}
  raise 'The request header can be included only once' if response_header.count > 1
  response_header.each do |rh|
    output += rh.to_s
  end
  # return the output
  output
end

#write_headers_to_socket(io) ⇒ Object

send headers to the client.

Params:

io

Socket where the headers should be sent to



108
109
110
# File 'lib/icaprb/server/response.rb', line 108

def write_headers_to_socket(io)
  io.write write_headers
end