Skip to content

Commit

Permalink
write initial spec
Browse files Browse the repository at this point in the history
  • Loading branch information
jmettraux committed Jul 20, 2013
1 parent 658ba50 commit 947d488
Show file tree
Hide file tree
Showing 6 changed files with 384 additions and 44 deletions.
1 change: 1 addition & 0 deletions .rspec
@@ -0,0 +1 @@
--colour --format documentation
4 changes: 3 additions & 1 deletion TODO.txt
@@ -1,7 +1,9 @@

[ ] Travis CI
[ ] stop using response class re-opening to add methods (cache, etc)
[ ] get rid of doc/*.css

[ ] enhance nonce generation: http://www.intertwingly.net/blog/1585.html

[ ] stop using response class re-opening to add methods (cache, etc)
re-open Net::HTTPOK and co instead

1 change: 1 addition & 0 deletions spec/support/test.htdigest
@@ -0,0 +1 @@
test:test_realm:fbab5e064c6fa79c414735bcdcf2ce4a
349 changes: 349 additions & 0 deletions spec/support/test_server_helper.rb
@@ -0,0 +1,349 @@

#
# an in-memory resource set for testing rufus-verbs
#
# jmettraux@gmail.com
#
# Fri Jan 11 12:36:45 JST 2008
#

require 'date'
require 'webrick'


$dcount = 0 # tracking the number of hits when doing digest auth


#
# the hash for the /items resource (collection)
#
class LastModifiedHash

def initialize

@hash = {}
touch
end

def touch(key=nil)

now = Time.now.httpdate
@hash[key] = now if key
@hash['__self'] = now
end

def delete(key)

@hash.delete key
@hash['__self'] = Time.now.httpdate
end

def last_modified (key)

key = key || '__self'
@hash[key]
end

def clear

@hash.clear
@hash['__self'] = Time.now.httpdate
end
end


#
# This servlet provides a RESTful in-memory resource "/items".
#
class ItemServlet < WEBrick::HTTPServlet::AbstractServlet

@@items = {}
@@last_item_id = -1

@@lastmod = LastModifiedHash.new

@@authenticator = WEBrick::HTTPAuth::DigestAuth.new(
:UserDB => WEBrick::HTTPAuth::Htdigest.new('spec/support/test.htdigest'),
:Realm => 'test_realm')

def initialize(server, *options)

super
@auth = server.auth
end

#
# Overriding the service() method to perform a potential auth check
#
def service(req, res)

if @auth == :basic

WEBrick::HTTPAuth.basic_auth(req, res, 'items') do |u, p|
(u != nil and u == p)
end

elsif @auth == :digest

$dcount += 1
@@authenticator.authenticate(req, res)
end

super
end

def do_GET (req, res)

i = item_id req

return reply(res, 404, "no item '#{i}'") \
if i and not items[i]

representation, et, lm = fetch_representation i

since = req['If-Modified-Since']
since = DateTime.parse(since) if since
match = req['If-None-Match']

if ((not since and not match) or
(since and (since > DateTime.parse(lm))) or
(match and (match != et)))

res['Etag'] = et
res['Last-Modified'] = lm
res.body = representation.inspect + "\n"

else

reply(res, 304, "Not Modified")
end
end

def do_POST (req, res)

query = WEBrick::HTTPUtils::parse_query(req.query_string)
m = query['_method']
m = m.downcase if m
return do_PUT(req, res) if m == 'put'
return do_DELETE(req, res) if m == 'delete'

i = item_id req

i = (@@last_item_id += 1) unless i

items[i] = req.body
lastmod.touch i

res['Location'] = "#{@host}/items/#{i}"
reply res, 201, "item created"
end

def do_PUT (req, res)

i = item_id req

return reply(res, 404, "no item '#{i}'") unless items[i]

items[i] = req.body
lastmod.touch i

reply res, 200, "item updated"
end

def do_DELETE (req, res)

i = item_id req

return reply(res, 404, "no item '#{i}'") unless items[i]

items.delete i
lastmod.delete i

reply res, 200, "item deleted"
end

#
# clears the items
#
def self.flush

@@items.clear
@@lastmod.clear
end

protected

def items
@@items
end

def lastmod
@@lastmod
end

def is_modified (req, key)

since = req['If-Modified-Since']
match = req['If-None-Match']

return true unless since or match

#puts
#p [ since, match ]
#puts

(since or match)
end

#
# Returns representation, etag, last_modified
#
def fetch_representation (key=nil)

representation = if key
items[key]
else
items
end

[ representation,
representation.inspect.hash.to_s,
lastmod.last_modified(key) ]
end

def reply(res, code, message)

res.status = code
res.body = message + "\n"
res['Content-type'] = "text/plain"
end

def item_id (req)

p = req.path_info[1..-1]
return nil if not p or p == ''
p.to_i
end
end

#
# just redirecting to the ItemServlet...
#
class ThingServlet < WEBrick::HTTPServlet::AbstractServlet

def do_GET (req, res)

res.set_redirect(
WEBrick::HTTPStatus[303],
"http://localhost:7777/items")
end
end

#
# testing Rufus::Verbs cookies...
#
class CookieServlet < WEBrick::HTTPServlet::AbstractServlet

@@sessions = {}

def do_GET (req, res)

res.body = get_session(req, res).inspect
end

def do_POST (req, res)

get_session(req, res) << req.body.strip
res.body = "ok."
end

protected

def get_session (req, res)

c = req.cookies.find { |c| c.name == 'tcookie' }

if c
@@sessions[c.value]
else
s = []
key = (Time.now.to_f * 100000).to_i.to_s
@@sessions[key] = s
res.cookies << WEBrick::Cookie.new('tcookie', key)
s
end
end
end

#
# a servlet that doesn't reply (for timeout testing)
#
class LostServlet < WEBrick::HTTPServlet::AbstractServlet

def do_GET (req, res)

sleep 200
end
end

#
# Serving items, a dummy resource...
# Also serving things, which just redirect to items...
#
class ItemServer

def initialize(args={})

#al = [
# [ "", WEBrick::AccessLog::COMMON_LOG_FORMAT ],
# [ "", WEBrick::AccessLog::REFERER_LOG_FORMAT ]]

@server = WEBrick::HTTPServer.new(
:Port => args[:port] || 7777,
:AccessLog => nil)

class << @server
attr_accessor :auth
end

@server.auth = args[:auth]

@server.mount('/items', ItemServlet)
@server.mount('/things', ThingServlet)
@server.mount('/cookie', CookieServlet)
@server.mount('/lost', LostServlet)

%w[ INT TERM ].each do |signal|
trap(signal) { shutdown }
end
end

def start

Thread.new { @server.start }
end

def shutdown

ItemServlet.flush
@server.shutdown
end
end


module TestServerHelper

def start_test_server

@server = ItemServer.new
@server.start
end

def stop_test_server

@server.shutdown
end
end

RSpec.configure { |c| c.include(TestServerHelper) }

0 comments on commit 947d488

Please sign in to comment.