Class: Thin::FunEmbed

Inherits:
EM::Connection
  • Object
show all
Includes:
Constants
Defined in:
lib/thin/fun_embed.rb,
lib/thin/fun_embed/version.rb,
lib/thin/fun_embed/constants.rb

Overview

minimalistic HTTP server for embedding into EventMachine’d applications

Method #handle_http_request accept fully formed Rack environment and should call send http status+headers+body using one of #send_200_ok, #send_status_body, #send_raw_string and #send_rack_response methods or manually. If you send response manually, then you should call #consider_keep_alive method with your wish: whether you want to try keep-alive connection or not.

require 'thin/fun_embed.rb'

class Simple200ok < Thin::FunEmbed
  def handle_http_request(env)
    if rand(2) == 1
      send_200_ok('{"hello":"world"}', 'application/javascript')
    else
      send_200_ok('hello world')
    end
  end
end

EM.run do
  EM.start_server '0.0.0.0', 8080, Simple200ok
end

Direct Known Subclasses

FunRackLike

Defined Under Namespace

Modules: Constants

Constant Summary collapse

FULL_KEEP_ALIVE =
"Connection: keep-alive\r\n".freeze
RN =
"\r\n".freeze
RNRN =
"\r\n\r\n".freeze
NL =
"\n".freeze
CONTENT_LENGTH =
"Content-Length".freeze
CONTENT_LENGTH_DT =
"\r\nContent-Length: ".freeze
CONNECTION =
'Connection'.freeze
KEEP_ALIVE =
'keep-alive'.freeze
CLOSE =
'close'.freeze
TEXT_PLAIN =
'text/plain'.freeze
HTTP_STATUS_CODES =
Thin::HTTP_STATUS_CODES
VERSION =
"0.0.7"

Constants included from Constants

Constants::DELETE, Constants::GET, Constants::PATH_INFO, Constants::POST, Constants::PUT, Constants::QUERY_STRING, Constants::REQUEST_METHOD, Constants::SCRIPT_NAME, Constants::SERVER_NAME, Constants::SERVER_PORT

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#keep_aliveObject (readonly)

signals if client may accept keep-alive connection



59
60
61
# File 'lib/thin/fun_embed.rb', line 59

def keep_alive
  @keep_alive
end

#unbind_callbackObject

callback, which called from #unbind. Could be used for monitoring connections.



56
57
58
# File 'lib/thin/fun_embed.rb', line 56

def unbind_callback
  @unbind_callback
end

Instance Method Details

#consider_keep_alive(try_keep_alive = true) ⇒ Object

call this when you fully send response



180
181
182
183
184
185
186
187
# File 'lib/thin/fun_embed.rb', line 180

def consider_keep_alive(try_keep_alive = true)
  @parser.close  rescue nil
  if @keep_alive && try_keep_alive && !@force_close
    post_init
  else
    close_connection_after_writing
  end
end

#force_close!Object



193
194
195
# File 'lib/thin/fun_embed.rb', line 193

def force_close!
  @force_close = true
end

#handle_http_request(env) ⇒ Object

main method for override you ought to put your application logic here



76
77
78
# File 'lib/thin/fun_embed.rb', line 76

def handle_http_request(env)
  send_200_ok "you should override #handle_http_request"
end

#post_initObject



61
62
63
64
65
# File 'lib/thin/fun_embed.rb', line 61

def post_init
  @keep_alive = nil
  @force_close = false
  @parser = Thin::Request.new
end

#receive_data(data) ⇒ Object



67
68
69
70
71
72
# File 'lib/thin/fun_embed.rb', line 67

def receive_data(data)
  if @parser.parse(data)
    @keep_alive = @parser.persistent?
    handle_http_request(@parser.env)
  end
end

#send_200_ok(body = '', type = TEXT_PLAIN, try_keep_alive = true) ⇒ Object

send simple ‘200 OK’ response with a body



81
82
83
84
85
86
87
# File 'lib/thin/fun_embed.rb', line 81

def send_200_ok(body = '', type = TEXT_PLAIN, try_keep_alive = true)
  send_data("HTTP/1.1 200 OK\r\nContent-Type: #{type}\r\n"\
            "Content-Length: #{body.bytesize}\r\n"\
            "#{keep_alive ? FULL_KEEP_ALIVE : nil}\r\n")
  send_data(body)
  consider_keep_alive(try_keep_alive)
end

#send_rack_response(status, headers, body, try_keep_alive = true) ⇒ Object

send Rack like response (status, headers, body)

This method tries to implement as much of Rack as it progmatically needed, but not more. Test it before use



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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/thin/fun_embed.rb', line 120

def send_rack_response(status, headers, body, try_keep_alive = true)
  status = status.to_i
  out = "HTTP/1.1 #{status} #{HTTP_STATUS_CODES[status]}\r\n"

  if String === headers
    try_keep_alive &&= headers.index(CONTENT_LENGTH_DT) && headers.index(FULL_KEEP_ALIVE)
    out << headers
  else
    content_length = connection = nil
    headers.each do |k, v|
      if k == CONTENT_LENGTH
        content_length = v
      elsif k == CONNECTION
        connection = v
      end
      if String === v
        unless v.index(NL)
          out << "#{k}: #{v}\r\n"
        else
          v.each_line{|l| out << "#{k}: #{l}\r\n"}
        end
      elsif v.respond_to?(:each)
        v.each{|l| out << "#{k}: #{l}\r\n"}
      else
        unless (v=v.to_s).index(NL)
          out << "#{k}: #{v}\r\n"
        else
          v.each_line{|l| out << "#{k}: #{l}\r\n"}
        end
      end
    end

    if content_length.nil?
      if String === body
        content_length = body.bytesize
      elsif Array === body && body.all?{|b| String === body}
        content_length = 0
        body.each{|s| content_length += s.bytesize}
      end
      unless content_length.nil?
        out << "Content-Length: #{content_length}\r\n"
      end
    end

    try_keep_alive &&= @keep_alive && content_length && (!connection || connection == KEEP_ALIVE)
    out << FULL_KEEP_ALIVE  if try_keep_alive && !connection
  end
  out << RN
  send_data out

  if String === body
    send_data(body)
  else
    body.each{|s| send_data(s)}
  end

  consider_keep_alive(try_keep_alive)
end

#send_raw_string(string, try_keep_alive = true) ⇒ Object

send fully formatted HTTP response



106
107
108
109
110
111
112
113
114
# File 'lib/thin/fun_embed.rb', line 106

def send_raw_string(string, try_keep_alive = true)
  send_data(string)
  try_keep_alive &&= @keep_alive &&
    (cl = string.index(CONTENT_LENGTH_DT)) &&
    (kai = string.index(FULL_KEEP_ALIVE)) && 
    kai < (rn = string.index(RNRN)) &&
    cl < rn
  consider_keep_alive(try_keep_alive)
end

#send_status_body(status, body = '', type = TEXT_PLAIN, try_keep_alive = true) ⇒ Object

send simple response with status, body and type



90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/thin/fun_embed.rb', line 90

def send_status_body(status, body = '', type = TEXT_PLAIN, try_keep_alive = true)
  status = status.to_i
  if status < 200 || status == 204 || status == 304
    send_data("HTTP/1.1 #{status} #{HTTP_STATUS_CODES[status]}\r\n"\
            "#{keep_alive ? FULL_KEEP_ALIVE : nil}\r\n")
  else
    send_data("HTTP/1.1 #{status} #{HTTP_STATUS_CODES[status]}\r\n"\
            "Content-Type: #{type}\r\n"\
            "Content-Length: #{body.bytesize}\r\n"\
            "#{keep_alive ? FULL_KEEP_ALIVE : nil}\r\n")
    send_data(body)
  end
  consider_keep_alive(try_keep_alive)
end

#unbindObject

:nodoc:



189
190
191
# File 'lib/thin/fun_embed.rb', line 189

def unbind # :nodoc:
  @unbind_callback && @unbind_callback.call(self)
end