atom-tools 0.9.0 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README +3 -3
- data/Rakefile +1 -1
- data/bin/atom-client.rb +13 -10
- data/lib/atom/collection.rb +2 -2
- data/lib/atom/element.rb +5 -1
- data/lib/atom/entry.rb +9 -2
- data/lib/atom/feed.rb +11 -6
- data/lib/atom/http.rb +157 -38
- data/lib/atom/service.rb +170 -0
- data/lib/atom/text.rb +15 -2
- data/lib/atom/xml.rb +1 -1
- data/test/conformance/updated.rb +2 -1
- data/test/test_constructs.rb +45 -2
- data/test/test_feed.rb +27 -0
- data/test/test_http.rb +116 -20
- data/test/test_protocol.rb +77 -13
- data/test/test_xml.rb +15 -1
- metadata +3 -38
- data/bin/atom-server.rb~ +0 -71
- data/doc/classes/Atom/App.html +0 -217
- data/doc/classes/Atom/Author.html +0 -130
- data/doc/classes/Atom/Category.html +0 -128
- data/doc/classes/Atom/Collection.html +0 -322
- data/doc/classes/Atom/Content.html +0 -129
- data/doc/classes/Atom/Contributor.html +0 -119
- data/doc/classes/Atom/Element.html +0 -325
- data/doc/classes/Atom/Entry.html +0 -365
- data/doc/classes/Atom/Feed.html +0 -585
- data/doc/classes/Atom/HTTP.html +0 -374
- data/doc/classes/Atom/Link.html +0 -137
- data/doc/classes/Atom/Text.html +0 -229
- data/doc/classes/XHTML.html +0 -118
- data/doc/created.rid +0 -1
- data/doc/files/README.html +0 -213
- data/doc/files/lib/atom/app_rb.html +0 -110
- data/doc/files/lib/atom/collection_rb.html +0 -110
- data/doc/files/lib/atom/element_rb.html +0 -109
- data/doc/files/lib/atom/entry_rb.html +0 -111
- data/doc/files/lib/atom/feed_rb.html +0 -112
- data/doc/files/lib/atom/http_rb.html +0 -109
- data/doc/files/lib/atom/text_rb.html +0 -108
- data/doc/files/lib/atom/xml_rb.html +0 -110
- data/doc/files/lib/atom/yaml_rb.html +0 -109
- data/doc/fr_class_index.html +0 -39
- data/doc/fr_file_index.html +0 -36
- data/doc/fr_method_index.html +0 -62
- data/doc/index.html +0 -24
- data/doc/rdoc-style.css +0 -208
- data/lib/atom/app.rb +0 -87
    
        data/lib/atom/service.rb
    ADDED
    
    | @@ -0,0 +1,170 @@ | |
| 1 | 
            +
            require "uri"
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "atom/http"
         | 
| 4 | 
            +
            require "atom/element"
         | 
| 5 | 
            +
            require "atom/collection"
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            module Atom
         | 
| 8 | 
            +
              PP_NS = "http://purl.org/atom/app#"
         | 
| 9 | 
            +
              
         | 
| 10 | 
            +
              class WrongNamespace < RuntimeError #:nodoc:
         | 
| 11 | 
            +
              end
         | 
| 12 | 
            +
              class WrongMimetype < RuntimeError # :nodoc:
         | 
| 13 | 
            +
              end
         | 
| 14 | 
            +
              class WrongResponse < RuntimeError # :nodoc:
         | 
| 15 | 
            +
              end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
              # an Atom::Workspace has a #title (Atom::Text) and #collections, an Array of Atom::Collection s
         | 
| 18 | 
            +
              class Workspace < Atom::Element
         | 
| 19 | 
            +
                element :collections, Atom::Multiple(Atom::Collection)
         | 
| 20 | 
            +
                element :title, Atom::Text
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                def self.parse(xml, base = "", http = Atom::HTTP.new) # :nodoc:
         | 
| 23 | 
            +
                  ws = Atom::Workspace.new("workspace")
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                  rxml = if xml.is_a? REXML::Document
         | 
| 26 | 
            +
                    xml.root
         | 
| 27 | 
            +
                  elsif xml.is_a? REXML::Element
         | 
| 28 | 
            +
                    xml
         | 
| 29 | 
            +
                  else 
         | 
| 30 | 
            +
                    REXML::Document.new(xml)
         | 
| 31 | 
            +
                  end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                  xml.fill_text_construct(ws, "title")
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                  REXML::XPath.match( rxml, 
         | 
| 36 | 
            +
                                      "./app:collection",
         | 
| 37 | 
            +
                                      {"app" => Atom::PP_NS} ).each do |col_el|
         | 
| 38 | 
            +
                    # absolutize relative URLs
         | 
| 39 | 
            +
                    url = base.to_uri + col_el.attributes["href"].to_uri
         | 
| 40 | 
            +
                   
         | 
| 41 | 
            +
                    coll = Atom::Collection.new(url, http)
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                    # XXX this is a Text Construct, and should be parsed as such
         | 
| 44 | 
            +
                    col_el.fill_text_construct(coll, "title")
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                    accepts = REXML::XPath.first( col_el,
         | 
| 47 | 
            +
                                                  "./app:accept",
         | 
| 48 | 
            +
                                                  {"app" => Atom::PP_NS} )
         | 
| 49 | 
            +
                    coll.accepts = (accepts ? accepts.text : "entry")
         | 
| 50 | 
            +
                    
         | 
| 51 | 
            +
                    ws.collections << coll
         | 
| 52 | 
            +
                  end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                  ws
         | 
| 55 | 
            +
                end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                def to_element # :nodoc:
         | 
| 58 | 
            +
                  root = REXML::Element.new "workspace" 
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                  # damn you, REXML. Damn you and you bizarre handling of namespaces
         | 
| 61 | 
            +
                  title = self.title.to_element
         | 
| 62 | 
            +
                  title.name = "atom:title"
         | 
| 63 | 
            +
                  root << title
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                  self.collections.each do |coll|
         | 
| 66 | 
            +
                    el = REXML::Element.new "collection"
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                    el.attributes["href"] = coll.uri
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                    title = coll.title.to_element
         | 
| 71 | 
            +
                    title.name = "atom:title"
         | 
| 72 | 
            +
                    el << title
         | 
| 73 | 
            +
                   
         | 
| 74 | 
            +
                    unless coll.accepts.nil?
         | 
| 75 | 
            +
                      accepts = REXML::Element.new "accepts"
         | 
| 76 | 
            +
                      accepts.text = coll.accepts
         | 
| 77 | 
            +
                      el << accepts
         | 
| 78 | 
            +
                    end
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                    root << el
         | 
| 81 | 
            +
                  end
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                  root
         | 
| 84 | 
            +
                end
         | 
| 85 | 
            +
              end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
              # Atom::Service represents an Atom Publishing Protocol service
         | 
| 88 | 
            +
              # document. Its only child is #workspaces, which is an Array of 
         | 
| 89 | 
            +
              # Atom::Workspace s
         | 
| 90 | 
            +
              class Service < Atom::Element
         | 
| 91 | 
            +
                element :workspaces, Atom::Multiple(Atom::Workspace)
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                # retrieves and parses an Atom service document.
         | 
| 94 | 
            +
                def initialize(service_url = "", http = Atom::HTTP.new)
         | 
| 95 | 
            +
                  super("service")
         | 
| 96 | 
            +
                  
         | 
| 97 | 
            +
                  @http = http
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                  return if service_url.empty?
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                  base = URI.parse(service_url)
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                  rxml = nil
         | 
| 104 | 
            +
             | 
| 105 | 
            +
                  res = @http.get(base)
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                  unless res.code == "200" # XXX needs to handle redirects, &c.
         | 
| 108 | 
            +
                    raise WrongResponse, "service document URL responded with unexpected code #{res.code}"
         | 
| 109 | 
            +
                  end
         | 
| 110 | 
            +
             | 
| 111 | 
            +
                  unless res.content_type == "application/atomserv+xml"
         | 
| 112 | 
            +
                    raise WrongMimetype, "this isn't an atom service document!"
         | 
| 113 | 
            +
                  end
         | 
| 114 | 
            +
             | 
| 115 | 
            +
                  parse(res.body, base)
         | 
| 116 | 
            +
                end
         | 
| 117 | 
            +
             
         | 
| 118 | 
            +
                # parse a service document, adding its workspaces to this object
         | 
| 119 | 
            +
                def parse xml, base = ""
         | 
| 120 | 
            +
                  rxml = if xml.is_a? REXML::Document
         | 
| 121 | 
            +
                    xml.root
         | 
| 122 | 
            +
                  elsif xml.is_a? REXML::Element
         | 
| 123 | 
            +
                    xml
         | 
| 124 | 
            +
                  else 
         | 
| 125 | 
            +
                    REXML::Document.new(xml)
         | 
| 126 | 
            +
                  end
         | 
| 127 | 
            +
             | 
| 128 | 
            +
                  unless rxml.root.namespace == PP_NS
         | 
| 129 | 
            +
                    raise WrongNamespace, "this isn't an atom service document!"
         | 
| 130 | 
            +
                  end
         | 
| 131 | 
            +
             | 
| 132 | 
            +
                  REXML::XPath.match( rxml, "/app:service/app:workspace", {"app" => Atom::PP_NS} ).each do |ws_el|
         | 
| 133 | 
            +
                    self.workspaces << Atom::Workspace.parse(ws_el, base, @http)
         | 
| 134 | 
            +
                  end
         | 
| 135 | 
            +
             | 
| 136 | 
            +
                  self
         | 
| 137 | 
            +
                end
         | 
| 138 | 
            +
             | 
| 139 | 
            +
                # serialize to a (namespaced) REXML::Document 
         | 
| 140 | 
            +
                def to_xml
         | 
| 141 | 
            +
                  doc = REXML::Document.new
         | 
| 142 | 
            +
                  
         | 
| 143 | 
            +
                  root = REXML::Element.new "service"
         | 
| 144 | 
            +
                  root.add_namespace Atom::PP_NS
         | 
| 145 | 
            +
                  root.add_namespace "atom", Atom::NS
         | 
| 146 | 
            +
             | 
| 147 | 
            +
                  self.workspaces.each do |ws|
         | 
| 148 | 
            +
                    root << ws.to_element
         | 
| 149 | 
            +
                  end
         | 
| 150 | 
            +
             | 
| 151 | 
            +
                  doc << root
         | 
| 152 | 
            +
                  doc
         | 
| 153 | 
            +
                end
         | 
| 154 | 
            +
              end
         | 
| 155 | 
            +
             
         | 
| 156 | 
            +
              class Entry
         | 
| 157 | 
            +
                # the @href of an entry's link[@rel="edit"]
         | 
| 158 | 
            +
                def edit_url
         | 
| 159 | 
            +
                  begin
         | 
| 160 | 
            +
                    edit_link = self.links.find do |link|
         | 
| 161 | 
            +
                      link["rel"] == "edit"
         | 
| 162 | 
            +
                    end
         | 
| 163 | 
            +
             | 
| 164 | 
            +
                    edit_link["href"]
         | 
| 165 | 
            +
                  rescue
         | 
| 166 | 
            +
                    nil
         | 
| 167 | 
            +
                  end
         | 
| 168 | 
            +
                end
         | 
| 169 | 
            +
              end
         | 
| 170 | 
            +
            end
         | 
    
        data/lib/atom/text.rb
    CHANGED
    
    | @@ -37,14 +37,27 @@ module Atom | |
| 37 37 | 
             
                  end
         | 
| 38 38 | 
             
                end
         | 
| 39 39 |  | 
| 40 | 
            -
                #  | 
| 40 | 
            +
                # attempts to parse the content of this element as XML and return it
         | 
| 41 | 
            +
                # as an array of REXML::Elements.
         | 
| 42 | 
            +
                #
         | 
| 43 | 
            +
                # If this self["type"] is "html" and Hpricot is installed, it will
         | 
| 44 | 
            +
                # be converted to XHTML first.
         | 
| 41 45 | 
             
                def xml
         | 
| 42 46 | 
             
                  if self["type"] == "xhtml"
         | 
| 43 47 | 
             
                    @content.children
         | 
| 44 48 | 
             
                  elsif self["type"] == "text"
         | 
| 45 49 | 
             
                    [self.to_s]
         | 
| 50 | 
            +
                  elsif self["type"] == "html"
         | 
| 51 | 
            +
                    begin
         | 
| 52 | 
            +
                      require "hpricot"
         | 
| 53 | 
            +
                    rescue
         | 
| 54 | 
            +
                      raise "Turning HTML content into XML requires Hpricot."
         | 
| 55 | 
            +
                    end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                    fixed = Hpricot(self.to_s, :xhtml_strict => true)
         | 
| 58 | 
            +
                    REXML::Document.new("<div>#{fixed}</div>").root.children
         | 
| 46 59 | 
             
                  else
         | 
| 47 | 
            -
                    # XXX  | 
| 60 | 
            +
                    # XXX check that @type is an XML mimetype and parse it
         | 
| 48 61 | 
             
                    raise "I haven't implemented this yet"
         | 
| 49 62 | 
             
                  end
         | 
| 50 63 | 
             
                end
         | 
    
        data/lib/atom/xml.rb
    CHANGED
    
    | @@ -105,7 +105,7 @@ module REXML # :nodoc: all | |
| 105 105 | 
             
                    thing.class.attrs.each do |name,req|
         | 
| 106 106 | 
             
                      value = elem.ns_attr name.to_s
         | 
| 107 107 | 
             
                      if value and name == :href
         | 
| 108 | 
            -
                        thing[name.to_s] = ( | 
| 108 | 
            +
                        thing[name.to_s] = (top.base.to_uri + value).to_s
         | 
| 109 109 | 
             
                      elsif value
         | 
| 110 110 | 
             
                        thing[name.to_s] = value
         | 
| 111 111 | 
             
                      end
         | 
    
        data/test/conformance/updated.rb
    CHANGED
    
    | @@ -16,7 +16,8 @@ class TestUpdatedConformance < Test::Unit::TestCase | |
| 16 16 |  | 
| 17 17 | 
             
                # this is an insignificant change, 
         | 
| 18 18 | 
             
                # (ie. atom:updated_1 == atom:updated_2),
         | 
| 19 | 
            -
                # | 
| 19 | 
            +
                #
         | 
| 20 | 
            +
                # the update is applied, your application can handle that however it wants.
         | 
| 20 21 | 
             
                feed.update!
         | 
| 21 22 | 
             
                assert_equal "12 of 13 miner<b>s</b> survive mine collapse", feed.entries.first.content.to_s.strip
         | 
| 22 23 |  | 
    
        data/test/test_constructs.rb
    CHANGED
    
    | @@ -2,11 +2,54 @@ require "test/unit" | |
| 2 2 | 
             
            require "atom/entry"
         | 
| 3 3 |  | 
| 4 4 | 
             
            class ConstructTest < Test::Unit::TestCase
         | 
| 5 | 
            +
              def test_text_construct_html_to_xml
         | 
| 6 | 
            +
                begin
         | 
| 7 | 
            +
                  require "hpricot"
         | 
| 8 | 
            +
                rescue
         | 
| 9 | 
            +
                  # hpricot isn't installed, just skip this test
         | 
| 10 | 
            +
                  puts "skipping hpricot tests"
         | 
| 11 | 
            +
                  return
         | 
| 12 | 
            +
                end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                entry = Atom::Entry.new
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            html = <<END
         | 
| 17 | 
            +
            <p>Paragraph 1 contains <a href=http://example.org/>a link
         | 
| 18 | 
            +
            <p>This really is a horrendous mess.
         | 
| 19 | 
            +
            END
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                entry.content = html
         | 
| 22 | 
            +
                entry.content["type"] = "html"
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                xhtml = entry.content.xml
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                # Hpricot is imperfect; for now I'll just test that it's parseable
         | 
| 27 | 
            +
                assert_instance_of Array, xhtml
         | 
| 28 | 
            +
                assert_instance_of REXML::Element, xhtml.first
         | 
| 29 | 
            +
             
         | 
| 30 | 
            +
            =begin
         | 
| 31 | 
            +
                assert_equal 2, xhtml.length
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                first = xhtml.first
         | 
| 34 | 
            +
                assert_equal "p", first.name
         | 
| 35 | 
            +
                assert_equal 2, first.children.length
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                a = first.children.last
         | 
| 38 | 
            +
                assert_equal "a", a.name
         | 
| 39 | 
            +
                assert_equal "http://example.org/", a.attributes["href"]
         | 
| 40 | 
            +
                assert_equal "a link", a.text
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                last = xhtml.last
         | 
| 43 | 
            +
                assert_equal "p", last.name
         | 
| 44 | 
            +
                assert_equal "This really is a horrendous mess.", last.text
         | 
| 45 | 
            +
            =end
         | 
| 46 | 
            +
              end
         | 
| 47 | 
            +
              
         | 
| 5 48 | 
             
              def test_text_construct_text
         | 
| 6 49 | 
             
                entry = Atom::Entry.new
         | 
| 7 50 |  | 
| 8 | 
            -
                assert_nil | 
| 9 | 
            -
                assert_equal | 
| 51 | 
            +
                assert_nil entry.title
         | 
| 52 | 
            +
                assert_equal "", entry.title.to_s 
         | 
| 10 53 |  | 
| 11 54 | 
             
                entry.title = "<3"
         | 
| 12 55 |  | 
    
        data/test/test_feed.rb
    CHANGED
    
    | @@ -81,6 +81,32 @@ END | |
| 81 81 | 
             
                assert_equal 1, feed.entries.length
         | 
| 82 82 | 
             
              end
         | 
| 83 83 |  | 
| 84 | 
            +
              def test_media_types
         | 
| 85 | 
            +
                c = proc do |c_t|
         | 
| 86 | 
            +
                  @s.mount_proc("/") do |req,res|
         | 
| 87 | 
            +
                    res.content_type = c_t
         | 
| 88 | 
            +
                    res.body = @test_feed
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                    @s.stop
         | 
| 91 | 
            +
                  end
         | 
| 92 | 
            +
                  one_shot
         | 
| 93 | 
            +
                end
         | 
| 94 | 
            +
                
         | 
| 95 | 
            +
                feed = Atom::Feed.new "http://localhost:#{@port}/"
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                # even if it looks like a feed, the server's word is law
         | 
| 98 | 
            +
                c.call("text/plain")
         | 
| 99 | 
            +
                assert_raise(Atom::HTTPException) { feed.update! }
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                # text/xml isn't the preferred mimetype, but we'll accept it
         | 
| 102 | 
            +
                c.call("text/xml")
         | 
| 103 | 
            +
                assert_nothing_raised { feed.update! }
         | 
| 104 | 
            +
             | 
| 105 | 
            +
                # same goes for application/xml
         | 
| 106 | 
            +
                # XXX c.call("application/xml")
         | 
| 107 | 
            +
                # assert_nothing_raised { feed.update! }
         | 
| 108 | 
            +
              end
         | 
| 109 | 
            +
             | 
| 84 110 | 
             
              def test_conditional_get
         | 
| 85 111 | 
             
                @s.mount_proc("/") do |req,res|
         | 
| 86 112 | 
             
                  assert_nil req["If-None-Match"]
         | 
| @@ -124,5 +150,6 @@ END | |
| 124 150 | 
             
                assert_equal 1, feed.entries.length
         | 
| 125 151 | 
             
              end
         | 
| 126 152 |  | 
| 153 | 
            +
              # prepares the server for a single request
         | 
| 127 154 | 
             
              def one_shot; Thread.new { @s.start }; end
         | 
| 128 155 | 
             
            end
         | 
    
        data/test/test_http.rb
    CHANGED
    
    | @@ -1,7 +1,14 @@ | |
| 1 | 
            +
            require "test/unit"
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            require "atom/http"
         | 
| 2 4 | 
             
            require "webrick"
         | 
| 3 5 |  | 
| 4 6 | 
             
            class AtomProtocolTest < Test::Unit::TestCase
         | 
| 7 | 
            +
              REALM = "test authentication"
         | 
| 8 | 
            +
              USER = "test_user"
         | 
| 9 | 
            +
              PASS = "aoeuaoeu"
         | 
| 10 | 
            +
              SECRET_DATA = "I kissed a boy once"
         | 
| 11 | 
            +
             | 
| 5 12 | 
             
              def setup
         | 
| 6 13 | 
             
                @http = Atom::HTTP.new
         | 
| 7 14 | 
             
                @port = rand(1024) + 1024
         | 
| @@ -11,13 +18,21 @@ class AtomProtocolTest < Test::Unit::TestCase | |
| 11 18 | 
             
              end
         | 
| 12 19 |  | 
| 13 20 | 
             
              def test_parse_wwwauth
         | 
| 14 | 
            -
                header = ' | 
| 21 | 
            +
                header = 'realm="SokEvo"'
         | 
| 15 22 |  | 
| 16 | 
            -
                 | 
| 17 | 
            -
                 | 
| 23 | 
            +
                params = @http.send :parse_quoted_wwwauth, header
         | 
| 24 | 
            +
                assert_equal "SokEvo", params[:realm]
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                header = 'opaque="07UrfUiCYac5BbWJ", algorithm=MD5-sess, qop="auth", stale=TRUE, nonce="MDAx0Mzk", realm="test authentication"'
         | 
| 18 27 |  | 
| 19 | 
            -
                 | 
| 20 | 
            -
             | 
| 28 | 
            +
                params = @http.send :parse_wwwauth_digest, header
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                assert_equal "test authentication", params[:realm]
         | 
| 31 | 
            +
                assert_equal "MDAx0Mzk", params[:nonce]
         | 
| 32 | 
            +
                assert_equal true, params[:stale]
         | 
| 33 | 
            +
                assert_equal "auth", params[:qop]
         | 
| 34 | 
            +
                assert_equal "MD5-sess", params[:algorithm]
         | 
| 35 | 
            +
                assert_equal "07UrfUiCYac5BbWJ", params[:opaque]
         | 
| 21 36 | 
             
              end
         | 
| 22 37 |  | 
| 23 38 | 
             
              def test_GET
         | 
| @@ -32,11 +47,11 @@ class AtomProtocolTest < Test::Unit::TestCase | |
| 32 47 |  | 
| 33 48 | 
             
                one_shot
         | 
| 34 49 |  | 
| 35 | 
            -
                 | 
| 50 | 
            +
                get_root
         | 
| 36 51 |  | 
| 37 | 
            -
                assert_equal("200", res.code)
         | 
| 38 | 
            -
                assert_equal("text/plain", res.content_type)
         | 
| 39 | 
            -
                assert_equal("just junk", res.body)
         | 
| 52 | 
            +
                assert_equal("200", @res.code)
         | 
| 53 | 
            +
                assert_equal("text/plain", @res.content_type)
         | 
| 54 | 
            +
                assert_equal("just junk", @res.body)
         | 
| 40 55 | 
             
              end
         | 
| 41 56 |  | 
| 42 57 | 
             
              def test_GET_headers
         | 
| @@ -48,39 +63,120 @@ class AtomProtocolTest < Test::Unit::TestCase | |
| 48 63 |  | 
| 49 64 | 
             
                one_shot
         | 
| 50 65 |  | 
| 51 | 
            -
                 | 
| 66 | 
            +
                get_root("User-Agent" => "tester agent")
         | 
| 52 67 |  | 
| 53 | 
            -
                assert_equal("200", res.code)
         | 
| 68 | 
            +
                assert_equal("200", @res.code)
         | 
| 54 69 | 
             
              end
         | 
| 55 70 |  | 
| 56 71 | 
             
              def test_basic_auth
         | 
| 57 72 | 
             
                @s.mount_proc("/") do |req,res|
         | 
| 58 | 
            -
                  WEBrick::HTTPAuth.basic_auth(req, res,  | 
| 59 | 
            -
                    u ==  | 
| 73 | 
            +
                  WEBrick::HTTPAuth.basic_auth(req, res, REALM) do |u,p|
         | 
| 74 | 
            +
                    u == USER and p == PASS
         | 
| 60 75 | 
             
                  end
         | 
| 61 76 |  | 
| 62 | 
            -
                  res.body =  | 
| 77 | 
            +
                  res.body = SECRET_DATA
         | 
| 63 78 | 
             
                  @s.stop
         | 
| 64 79 | 
             
                end
         | 
| 65 80 |  | 
| 66 81 | 
             
                one_shot
         | 
| 67 | 
            -
             | 
| 82 | 
            +
               
         | 
| 83 | 
            +
                # with no credentials
         | 
| 84 | 
            +
                assert_raises(Atom::Unauthorized) { get_root }
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                @http.user = USER
         | 
| 87 | 
            +
                @http.pass = "incorrect_password"
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                # with incorrect credentials
         | 
| 68 90 | 
             
                assert_raises(Atom::Unauthorized) { get_root }
         | 
| 69 91 |  | 
| 70 92 | 
             
                @http.when_auth do |abs_url,realm|
         | 
| 71 93 | 
             
                  assert_equal "http://localhost:#{@port}/", abs_url 
         | 
| 72 | 
            -
                  assert_equal  | 
| 94 | 
            +
                  assert_equal REALM, realm
         | 
| 73 95 |  | 
| 74 | 
            -
                  [ | 
| 96 | 
            +
                  [USER, PASS]
         | 
| 75 97 | 
             
                end
         | 
| 76 98 |  | 
| 77 99 | 
             
                one_shot
         | 
| 78 100 |  | 
| 101 | 
            +
                get_root
         | 
| 102 | 
            +
                assert_equal "200", @res.code 
         | 
| 103 | 
            +
                assert_equal SECRET_DATA, @res.body 
         | 
| 104 | 
            +
              end
         | 
| 105 | 
            +
             | 
| 106 | 
            +
              def test_digest_auth
         | 
| 107 | 
            +
                # a dummy userdb 
         | 
| 108 | 
            +
                userdb = {}
         | 
| 109 | 
            +
                userdb[USER] = PASS
         | 
| 110 | 
            +
             | 
| 111 | 
            +
                def userdb.get_passwd(realm, user, reload)
         | 
| 112 | 
            +
                  Digest::MD5::hexdigest([user, realm, self[user]].join(":"))
         | 
| 113 | 
            +
                end
         | 
| 114 | 
            +
                  
         | 
| 115 | 
            +
                authenticator = WEBrick::HTTPAuth::DigestAuth.new(
         | 
| 116 | 
            +
                  :UserDB => userdb,
         | 
| 117 | 
            +
                  :Realm => REALM,
         | 
| 118 | 
            +
                  :Algorithm => "MD5"
         | 
| 119 | 
            +
                )
         | 
| 120 | 
            +
             | 
| 121 | 
            +
                @s.mount_proc("/") do |req,res|
         | 
| 122 | 
            +
                  authenticator.authenticate(req, res)
         | 
| 123 | 
            +
                  res.body = SECRET_DATA
         | 
| 124 | 
            +
                end
         | 
| 125 | 
            +
               
         | 
| 126 | 
            +
                one_shot
         | 
| 127 | 
            +
             | 
| 128 | 
            +
                # no credentials
         | 
| 129 | 
            +
                assert_raises(Atom::Unauthorized) { get_root }
         | 
| 130 | 
            +
             | 
| 131 | 
            +
                @http.user = USER
         | 
| 132 | 
            +
                @http.pass = PASS
         | 
| 133 | 
            +
             | 
| 134 | 
            +
                # correct credentials
         | 
| 79 135 | 
             
                res = get_root
         | 
| 80 | 
            -
                assert_equal | 
| 81 | 
            -
             | 
| 136 | 
            +
                assert_equal SECRET_DATA, res.body
         | 
| 137 | 
            +
             | 
| 138 | 
            +
                @s.stop
         | 
| 139 | 
            +
              end
         | 
| 140 | 
            +
             | 
| 141 | 
            +
              def test_wsse_auth
         | 
| 142 | 
            +
                @s.mount_proc("/") do |req,res|
         | 
| 143 | 
            +
                  assert_equal 'WSSE profile="UsernameToken"', req["Authorization"]
         | 
| 144 | 
            +
             | 
| 145 | 
            +
                  xwsse = req["X-WSSE"]
         | 
| 146 | 
            +
             | 
| 147 | 
            +
                  p = @http.send :parse_quoted_wwwauth, xwsse
         | 
| 148 | 
            +
             | 
| 149 | 
            +
                  assert_equal USER, p[:Username]
         | 
| 150 | 
            +
                  assert_match /^UsernameToken /, xwsse
         | 
| 151 | 
            +
             | 
| 152 | 
            +
                  # un-base64 in preparation for SHA1-ing
         | 
| 153 | 
            +
                  nonce = p[:Nonce].unpack("m").first
         | 
| 154 | 
            +
             | 
| 155 | 
            +
                  # Base64( SHA1( Nonce + CreationTimestamp + Password ) )
         | 
| 156 | 
            +
                  pd_string = nonce + p[:Created] + PASS
         | 
| 157 | 
            +
                  password_digest = [Digest::SHA1.digest(pd_string)].pack("m").chomp
         | 
| 158 | 
            +
             | 
| 159 | 
            +
                  assert_equal password_digest, p[:PasswordDigest]
         | 
| 160 | 
            +
             | 
| 161 | 
            +
                  res.body = SECRET_DATA
         | 
| 162 | 
            +
                  @s.stop
         | 
| 163 | 
            +
                end
         | 
| 164 | 
            +
             | 
| 165 | 
            +
                one_shot
         | 
| 166 | 
            +
             | 
| 167 | 
            +
                @http.always_auth = :wsse
         | 
| 168 | 
            +
                @http.user = USER
         | 
| 169 | 
            +
                @http.pass = PASS
         | 
| 170 | 
            +
                
         | 
| 171 | 
            +
                get_root
         | 
| 172 | 
            +
             | 
| 173 | 
            +
                assert_equal "200", @res.code 
         | 
| 174 | 
            +
                assert_equal SECRET_DATA, @res.body
         | 
| 175 | 
            +
              end
         | 
| 176 | 
            +
             | 
| 177 | 
            +
              def get_root(*args)
         | 
| 178 | 
            +
                @res = @http.get("http://localhost:#{@port}/", *args)
         | 
| 82 179 | 
             
              end
         | 
| 83 180 |  | 
| 84 | 
            -
              def get_root(*args); @http.get("http://localhost:#{@port}/", *args); end
         | 
| 85 181 | 
             
              def one_shot; Thread.new { @s.start }; end
         | 
| 86 182 | 
             
            end
         |