Browse Source

[2429] introduce a no-throw factor of RRTTL and use it in the loader.

JINMEI Tatuya 12 years ago
parent
commit
33d80f8689
4 changed files with 119 additions and 18 deletions
  1. 10 5
      src/lib/dns/master_loader.cc
  2. 56 12
      src/lib/dns/rrttl.cc
  3. 32 1
      src/lib/dns/rrttl.h
  4. 21 0
      src/lib/dns/tests/rrttl_unittest.cc

+ 10 - 5
src/lib/dns/master_loader.cc

@@ -187,18 +187,23 @@ private:
         }
     }
 
-    // Try to set/reset the current TTL from a candidate TTL.  It's possible
+    // Try to set/reset the current TTL from candidate TTL text.  It's possible
     // it does not actually represent a TTL (which is not immediately
     // considered an error).  Return true iff it's recognized as a valid TTL
     // (and only in which case the current TTL is set).
     bool setCurrentTTL(const string& ttl_txt) {
-        try {
-            setCurrentTTL(RRTTL(ttl_txt));
+        // We use the factory version instead of RRTTL constructor as we
+        // need to expect cases where ttl_txt does not actually represent a TTL
+        // but an RR class or type.
+        RRTTL* ttl = RRTTL::createFromText(ttl_txt, current_ttl_.get());
+        if (ttl != NULL) {
+            if (!current_ttl_) {
+                current_ttl_.reset(ttl);
+            }
             limitTTL(*current_ttl_, false);
             return (true);
-        } catch (const InvalidRRTTL&) {
-            return (false);
         }
+        return (false);
     }
 
     // Determine the TTL of the current RR based on the given parsing context.

+ 56 - 12
src/lib/dns/rrttl.cc

@@ -57,9 +57,14 @@ Unit units[] = {
 namespace isc {
 namespace dns {
 
-RRTTL::RRTTL(const std::string& ttlstr) {
+namespace {
+bool
+parseTTLStr(const string& ttlstr, uint32_t& ttlval, string* error_txt) {
     if (ttlstr.empty()) {
-        isc_throw(InvalidRRTTL, "Empty TTL string");
+        if (error_txt != NULL) {
+            *error_txt = "Empty TTL string";
+        }
+        return (false);
     }
     // We use a larger data type during the computation. This is because
     // some compilers don't fail when out of range, so we check the range
@@ -80,8 +85,10 @@ RRTTL::RRTTL(const std::string& ttlstr) {
             if (unit == end) {
                 if (units_mode) {
                     // We had some units before. The last one is missing unit.
-                    isc_throw(InvalidRRTTL, "Missing the last unit: " <<
-                              ttlstr);
+                    if (error_txt != NULL) {
+                        *error_txt = "Missing the last unit: " + ttlstr;
+                    }
+                    return (false);
                 } else {
                     // Case without any units at all. Just convert and store
                     // it.
@@ -102,12 +109,18 @@ RRTTL::RRTTL(const std::string& ttlstr) {
                 }
             }
             if (!found) {
-                isc_throw(InvalidRRTTL, "Unknown unit used: " << *unit <<
-                          " in: " << ttlstr);
+                if (error_txt != NULL) {
+                    *error_txt = "Unknown unit used: " +
+                        boost::lexical_cast<string>(*unit) + " in: " + ttlstr;
+                }
+                return (false);
             }
             // Now extract the number.
             if (unit == pos) {
-                isc_throw(InvalidRRTTL, "Missing number in TTL: " << ttlstr);
+                if (error_txt != NULL) {
+                    *error_txt = "Missing number in TTL: " + ttlstr;
+                }
+                return (false);
             }
             const int64_t value = boost::lexical_cast<int64_t>(string(pos,
                                                                       unit));
@@ -118,21 +131,52 @@ RRTTL::RRTTL(const std::string& ttlstr) {
             // there's no need to continue).
             if (value < 0 || value > 0xffffffff || val < 0 ||
                 val > 0xffffffff) {
-                isc_throw(InvalidRRTTL, "Part of TTL out of range: " <<
-                          ttlstr);
+                if (error_txt != NULL) {
+                    *error_txt = "Part of TTL out of range: "  + ttlstr;
+                }
+                return (false);
             }
             // Move to after the unit.
             pos = unit + 1;
         }
     } catch (const boost::bad_lexical_cast&) {
-        isc_throw(InvalidRRTTL, "invalid TTL: " << ttlstr);
+        if (error_txt != NULL) {
+            *error_txt = "invalid TTL: " + ttlstr;
+        }
+        return (false);
     }
 
     if (val >= 0 && val <= 0xffffffff) {
-        ttlval_ = val;
+        ttlval = val;
     } else {
-        isc_throw(InvalidRRTTL, "TTL out of range: " << ttlstr);
+        if (error_txt != NULL) {
+            *error_txt = "TTL out of range: " + ttlstr;
+        }
+        return (false);
+    }
+
+    return (true);
+}
+}
+
+RRTTL::RRTTL(const std::string& ttlstr) {
+    string error_txt;
+    if (!parseTTLStr(ttlstr, ttlval_, &error_txt)) {
+        isc_throw(InvalidRRTTL, error_txt);
+    }
+}
+
+RRTTL*
+RRTTL::createFromText(const string& ttlstr, RRTTL* placeholder) {
+    uint32_t ttlval;
+    if (parseTTLStr(ttlstr, ttlval, NULL)) {
+        if (placeholder != NULL) {
+            *placeholder = RRTTL(ttlval);
+            return (placeholder);
+        }
+        return (new RRTTL(ttlval));
     }
+    return (NULL);
 }
 
 RRTTL::RRTTL(InputBuffer& buffer) {

+ 32 - 1
src/lib/dns/rrttl.h

@@ -61,7 +61,7 @@ public:
 class RRTTL {
 public:
     ///
-    /// \name Constructors and Destructor
+    /// \name Constructors, Factory and Destructor
     ///
     /// Note: We use the default copy constructor and the default copy
     /// assignment operator intentionally.
@@ -72,6 +72,7 @@ public:
     ///
     /// \param ttlval An 32-bit integer of the RRTTL.
     explicit RRTTL(uint32_t ttlval) : ttlval_(ttlval) {}
+
     /// Constructor from a string.
     ///
     /// It accepts either a decimal number, specifying number of seconds. Or,
@@ -87,6 +88,7 @@ public:
     /// \throw InvalidRRTTL in case the string is not recognized as valid
     ///     TTL representation.
     explicit RRTTL(const std::string& ttlstr);
+
     /// Constructor from wire-format data.
     ///
     /// The \c buffer parameter normally stores a complete DNS message
@@ -98,6 +100,35 @@ public:
     ///
     /// \param buffer A buffer storing the wire format data.
     explicit RRTTL(isc::util::InputBuffer& buffer);
+
+    /// A separate factory of RRTTL from text.
+    ///
+    /// This static method is similar to the constructor that takes a string
+    /// object, but works as a factory and reports parsing failure in return
+    /// value.  Normally the constructor version should suffice, but in some
+    /// cases the caller may have to expect mixture of valid and invalid input,
+    /// and may want to minimize the overhead of possible exception handling.
+    /// This version is provided for such purpose.
+    ///
+    /// When the \c placeholder parameter is NULL, it creates a new RRTTL
+    /// object, allocating memory for it; the caller is responsible for
+    /// releasing the memory using the \c delete operator.  If \c placeholder
+    /// is non NULL, it will override the placeholder object with an RRTTL
+    /// corresponding to the given text and return a pointer to the placeholder
+    /// object.  This way, the caller can also minimize the overhead of memory
+    /// allocation if it needs to call this method many times.
+    ///
+    /// If the given text does not represent a valid RRTTL, it returns NULL;
+    /// if \c placeholder is non NULL, it will be intact.
+    ///
+    /// This function never throws the \c InvalidRRTTL exception.
+    ///
+    /// \param ttlstr A string representation of the \c RRTTL.
+    /// \param placeholder If non NULL, an RRTTL object to be overridden
+    /// with an RRTTL for \c ttlstr.
+    /// \return A pointer to the created or overridden RRTTL object.
+    static RRTTL* createFromText(const std::string& ttlstr,
+                                 RRTTL* placeholder);
     ///
     //@}
 

+ 21 - 0
src/lib/dns/tests/rrttl_unittest.cc

@@ -20,6 +20,8 @@
 
 #include <dns/tests/unittest_util.h>
 
+#include <boost/scoped_ptr.hpp>
+
 using namespace std;
 using namespace isc;
 using namespace isc::dns;
@@ -85,6 +87,25 @@ TEST_F(RRTTLTest, fromText) {
     EXPECT_THROW(RRTTL("4294967296"), InvalidRRTTL); // must be 32-bit
 }
 
+TEST_F(RRTTLTest, createFromText) {
+    // If placeholder is NULL, a new RRTTL object is allocated
+    boost::scoped_ptr<RRTTL> ttl_ptr;
+    ttl_ptr.reset(RRTTL::createFromText("3600", NULL));
+    ASSERT_TRUE(ttl_ptr);
+    EXPECT_EQ(RRTTL(3600), *ttl_ptr);
+
+    // If placeholder is non NULL, it will be overwritten
+    RRTTL ttl(3600);
+    EXPECT_NE(static_cast<RRTTL*>(NULL), RRTTL::createFromText("1800", &ttl));
+    EXPECT_EQ(RRTTL(1800), ttl);
+
+    // If text parsing fails, NULL is returned; if placeholder is given,
+    // it will be intact.
+    EXPECT_EQ(static_cast<RRTTL*>(NULL), RRTTL::createFromText("bad", NULL));
+    EXPECT_EQ(static_cast<RRTTL*>(NULL), RRTTL::createFromText("bad", &ttl));
+    EXPECT_EQ(RRTTL(1800), ttl);
+}
+
 void
 checkUnit(unsigned multiply, char suffix) {
     SCOPED_TRACE(string("Unit check with suffix ") + suffix);