From 6a5116197a687798f1dc9b533796a8319cb82c2e Mon Sep 17 00:00:00 2001 From: zedshaw Date: Sat, 28 Jan 2006 19:34:12 +0000 Subject: [PATCH] First work at making a functional response class. git-svn-id: svn+ssh://rubyforge.org/var/svn/mongrel/trunk@9 19e92222-5c0b-0410-8929-a290d50e31e9 --- examples/simpletest.rb | 7 ++-- lib/mongrel.rb | 93 ++++++++++++++++++++++++++++++++++++++---- test/test_response.rb | 48 ++++++++++++++++++++++ 3 files changed, 137 insertions(+), 11 deletions(-) create mode 100644 test/test_response.rb diff --git a/examples/simpletest.rb b/examples/simpletest.rb index c05a7456..864c2269 100644 --- a/examples/simpletest.rb +++ b/examples/simpletest.rb @@ -2,11 +2,12 @@ require 'mongrel' require 'yaml' class SimpleHandler < Mongrel::HttpHandler - def process(request, response) - response.socket.write("HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nhello!\n") + response.start do |head,out| + head["Content-Type"] = "text/plain" + out.write("hello!\n") + end end - end h = Mongrel::HttpServer.new("0.0.0.0", "3000") diff --git a/lib/mongrel.rb b/lib/mongrel.rb index c46ccab3..c24cc672 100644 --- a/lib/mongrel.rb +++ b/lib/mongrel.rb @@ -1,13 +1,53 @@ require 'socket' require 'http11' require 'thread' - +require 'stringio' # Mongrel module containing all of the classes (include C extensions) for running # a Mongrel web server. It contains a minimalist HTTP server with just enough # functionality to service web application requests fast as possible. module Mongrel + HTTP_STATUS_CODES = { + 100 => 'Continue', + 101 => 'Switching Protocols', + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authoritative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Moved Temporarily', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Time-out', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Large', + 415 => 'Unsupported Media Type', + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Time-out', + 505 => 'HTTP Version not supported' + } + # When a handler is found for a registered URI then this class is constructed # and passed to your HttpHandler::process method. You should assume that # *one* handler processes all requests. Included in the HttpReqeust is a @@ -41,19 +81,55 @@ module Mongrel end end - # Very very simple response object. You basically write your stuff raw - # to the HttpResponse.socket variable. This will be made *much* easier - # in future releases allowing you to set status and request headers prior - # to sending the response. + + class HeaderOut + attr_reader :out + + def initialize(out) + @out = out + end + + def[]=(key,value) + @out.write(key) + @out.write(": ") + @out.write(value) + @out.write("\r\n") + end + end + + class HttpResponse attr_reader :socket - + attr_reader :out + attr_reader :header + attr_reader :status + attr_writer :status + def initialize(socket) @socket = socket + @out = StringIO.new + @status = 404 + @header = HeaderOut.new(StringIO.new) + end + + def start(status=200) + @status = status + yield @header, @out + finished + end + + def finished + @header.out.rewind + @out.rewind + + @socket.write("HTTP/1.1 #{@status} #{HTTP_STATUS_CODES[@status]}\r\nContent-Length: #{@out.length}\r\n") + @socket.write(@header.out.read) + @socket.write("\r\n") + @socket.write(@out.read) end - end + # You implement your application handler with this. It's very light giving # just the minimum necessary for you to handle a request and shoot back # a response. Look at the HttpRequest and HttpResponse objects for how @@ -65,6 +141,7 @@ module Mongrel end end + # The server normally returns a 404 response if a URI is requested, but it # also returns a lame empty message. This lets you do a 404 response # with a custom message for special URIs. @@ -107,7 +184,7 @@ module Mongrel ERROR_404_RESPONSE="HTTP/1.1 404 Not Found\r\nConnection: close\r\nContent-Type: text/plain\r\nServer: Mongrel/0.1\r\n\r\n" # For now we just read 2k chunks. Not optimal at all. - CHUNK_SIZE=2048 + CHUNK_SIZE=2048 # Creates a working server on host:port (strange things happen if port isn't a Number). # Use HttpServer::run to start the server. diff --git a/test/test_response.rb b/test/test_response.rb new file mode 100644 index 00000000..f033ba6a --- /dev/null +++ b/test/test_response.rb @@ -0,0 +1,48 @@ +require 'test/unit' +require 'mongrel' + +include Mongrel + +class ResponseTest < Test::Unit::TestCase + + def test_response_headers + out = StringIO.new + resp = HttpResponse.new(out) + resp.status = 200 + resp.header["Accept"] = "text/plain" + resp.header["X-Whatever"] = "stuff" + resp.out.write("test") + resp.finished + + out.rewind + puts out.read + end + + def test_response_200 + io = StringIO.new + resp = HttpResponse.new(io) + resp.start do |head,out| + head["Accept"] = "text/plain" + out.write("tested") + out.write("hello!") + end + + io.rewind + puts io.read + end + + def test_response_404 + io = StringIO.new + + resp = HttpResponse.new(io) + resp.start(404) do |head,out| + head['Accept'] = "text/plain" + out.write("NOT FOUND") + end + + io.rewind + puts io.read + end + +end +