diff --git a/Contributors.rdoc b/Contributors.rdoc
index 137394f8..820ba8e1 100644
--- a/Contributors.rdoc
+++ b/Contributors.rdoc
@@ -23,3 +23,4 @@ Contributions since:
 * Cody Cutrer (ccutrer)
 * WoodsBagotAndreMarquesLee
 * Rufus Post (mynameisrufus)
+* Akamai Technologies, Inc. (jwedoff)
diff --git a/lib/net/ber/ber_parser.rb b/lib/net/ber/ber_parser.rb
index 39d3737e..aa87b76c 100644
--- a/lib/net/ber/ber_parser.rb
+++ b/lib/net/ber/ber_parser.rb
@@ -25,6 +25,9 @@ module Net::BER::BERParser
   BuiltinSyntax = Net::BER.compile_syntax(:universal => universal,
                                           :context_specific => context)
 
+  # Public: specify the BER socket read timeouts, nil by default (no timeout).
+  attr_accessor :read_ber_timeout
+
   ##
   # This is an extract of our BER object parsing to simplify our
   # understanding of how we parse basic BER object types.
@@ -133,7 +136,7 @@ def parse_ber_object(syntax, id, data)
   # invalid BER length case. Because the "lengthlength" value was not used
   # inside of #read_ber, we no longer return it.
   def read_ber_length
-    n = getbyte
+    n = ber_timeout_getbyte
 
     if n <= 0x7f
       n
@@ -143,10 +146,9 @@ def read_ber_length
       raise Net::BER::BerError, "Invalid BER length 0xFF detected."
     else
       v = 0
-      read(n & 0x7f).each_byte do |b|
+      ber_timeout_read(n & 0x7f).each_byte do |b|
         v = (v << 8) + b
       end
-
       v
     end
   end
@@ -166,7 +168,7 @@ def read_ber(syntax = nil)
     # from streams that don't block when we ask for more data (like
     # StringIOs). At it is, this can throw TypeErrors and other nasties.
 
-    id = getbyte or return nil  # don't trash this value, we'll use it later
+    id = read_ber_id or return nil  # don't trash this value, we'll use it later
     content_length = read_ber_length
 
     yield id, content_length if block_given?
@@ -175,8 +177,126 @@ def read_ber(syntax = nil)
       raise Net::BER::BerError,
             "Indeterminite BER content length not implemented."
     end
-    data = read(content_length)
+    data = ber_timeout_read(content_length)
 
     parse_ber_object(syntax, id, data)
   end
+
+  # Internal: Returns the BER message ID or nil.
+  def read_ber_id
+    ber_timeout_getbyte
+  end
+  private :read_ber_id
+
+  # Internal: specify the BER socket read timeouts, nil by default (no timeout).
+  attr_accessor :ber_io_deadline
+  private :ber_io_deadline
+
+  ##
+  # sets a timeout of timeout seconds for read_ber and ber_timeout_write operations in the provided block the proin the future for if there is not already a earlier deadline set
+  def with_timeout(timeout)
+    timeout = timeout.to_f
+    # don't change deadline if run without timeout
+    return yield if timeout <= 0
+    # clear deadline if it is not in the future
+    self.ber_io_deadline = nil unless ber_io_timeout.to_f > 0
+    new_deadline = Time.now + timeout
+    # don't add deadline if current deadline is shorter
+    return yield if ber_io_deadline && ber_io_deadline < new_deadline
+    old_deadline = ber_io_deadline
+    begin
+      self.ber_io_deadline = new_deadline
+      yield
+    ensure
+      self.ber_io_deadline = old_deadline
+    end
+  end
+
+  # seconds until ber_io_deadline
+  def ber_io_timeout
+    ber_io_deadline ? ber_io_deadline - Time.now : nil
+  end
+  private :ber_io_timeout
+
+  def read_select!
+    return if IO.select([self], nil, nil, ber_io_timeout)
+    raise Net::LDAP::LdapError, "Timed out reading from the socket"
+  end
+  private :read_select!
+
+  def write_select!
+    return if IO.select(nil, [self], nil, ber_io_timeout)
+    raise Net::LDAP::LdapError, "Timed out reading from the socket"
+  end
+  private :write_select!
+
+  # Internal: Replaces `getbyte` with nonblocking implementation.
+  def ber_timeout_getbyte
+    begin
+      read_nonblock(1).ord
+    rescue IO::WaitReadable
+      read_select!
+      retry
+    rescue IO::WaitWritable
+      write_select!
+      retry
+    rescue EOFError
+      # nothing to read on the socket (StringIO)
+      nil
+    end
+  end
+  private :ber_timeout_getbyte
+
+  # Internal: Read `len` bytes, respecting timeout.
+  def ber_timeout_read(len)
+    buffer ||= ''.force_encoding(Encoding::ASCII_8BIT)
+    begin
+      read_nonblock(len, buffer)
+      return buffer if buffer.bytesize >= len
+    rescue IO::WaitReadable, IO::WaitWritable
+      buffer.clear
+    rescue EOFError
+      # nothing to read on the socket (StringIO)
+      nil
+    end
+    block ||= ''.force_encoding(Encoding::ASCII_8BIT)
+    len -= buffer.bytesize
+    loop do
+      begin
+        read_nonblock(len, block)
+      rescue IO::WaitReadable
+        read_select!
+        retry
+      rescue IO::WaitWritable
+        write_select!
+        retry
+      rescue EOFError
+        return buffer.empty? ? nil : buffer
+      end
+      buffer << block
+      len -= block.bytesize
+      return buffer if len <= 0
+    end
+  end
+  private :ber_timeout_read
+
+  ##
+  # Writes val as a plain write would, but respecting the dealine set by with_timeout
+  def ber_timeout_write(val)
+    total_written = 0
+    while 0 < val.bytesize
+      begin
+        written = write_nonblock(val)
+      rescue IO::WaitReadable
+        read_select!
+        retry
+      rescue IO::WaitWritable
+        write_select!
+        retry
+      end
+      total_written += written
+      val = val.byteslice(written..-1)
+    end
+    total_written
+  end
 end
diff --git a/lib/net/ldap.rb b/lib/net/ldap.rb
index f7a98ef5..7b86ffe0 100644
--- a/lib/net/ldap.rb
+++ b/lib/net/ldap.rb
@@ -553,6 +553,7 @@ def initialize(args = {})
     @force_no_page = args[:force_no_page] || DefaultForceNoPage
     @encryption = normalize_encryption(args[:encryption]) # may be nil
     @connect_timeout = args[:connect_timeout]
+    @io_timeout = args[:io_timeout]
 
     if pr = @auth[:password] and pr.respond_to?(:call)
       @auth[:password] = pr.call
@@ -1293,14 +1294,19 @@ def connection=(connection)
   # result from that, and :use_connection: will not yield at all. If not
   # the return value is whatever is returned from the block.
   def use_connection(args)
+    timeout_args = args.has_key?(:io_timeout) ? [args[:io_timeout]] : []
     if @open_connection
-      yield @open_connection
+      @open_connection.with_timeout(*timeout_args) do
+        yield(@open_connection)
+      end
     else
       begin
         conn = new_connection
-        result = conn.bind(args[:auth] || @auth)
-        return result unless result.result_code == Net::LDAP::ResultCodeSuccess
-        yield conn
+        conn.with_timeout(*timeout_args) do
+          result = conn.bind(args[:auth] || @auth)
+          return result unless result.result_code == Net::LDAP::ResultCodeSuccess
+          yield(conn)
+        end
       ensure
         conn.close if conn
       end
@@ -1315,7 +1321,8 @@ def new_connection
       :hosts                   => @hosts,
       :encryption              => @encryption,
       :instrumentation_service => @instrumentation_service,
-      :connect_timeout         => @connect_timeout
+      :connect_timeout         => @connect_timeout,
+      :io_timeout              => @io_timeout
 
     # Force connect to see if there's a connection error
     connection.socket
diff --git a/lib/net/ldap/connection.rb b/lib/net/ldap/connection.rb
index b01984f4..b7810218 100644
--- a/lib/net/ldap/connection.rb
+++ b/lib/net/ldap/connection.rb
@@ -4,7 +4,7 @@ class Net::LDAP::Connection #:nodoc:
   include Net::LDAP::Instrumentation
 
   # Seconds before failing for socket connect timeout
-  DefaultConnectTimeout = 5
+  DefaultTimeout = 5
 
   LdapVersion = 3
 
@@ -42,7 +42,7 @@ def open_connection(server)
     hosts = server[:hosts]
     encryption = server[:encryption]
 
-    timeout = server[:connect_timeout] || DefaultConnectTimeout
+    timeout = server[:connect_timeout] || DefaultTimeout
     socket_opts = {
       connect_timeout: timeout,
     }
@@ -261,7 +261,7 @@ def read(syntax = Net::LDAP::AsnSyntax)
   def write(request, controls = nil, message_id = next_msgid)
     instrument "write.net_ldap_connection" do |payload|
       packet = [message_id.to_ber, request, controls].compact.to_ber_sequence
-      payload[:content_length] = socket.write(packet)
+      payload[:content_length] = socket.ber_timeout_write(packet)
     end
   end
   private :write
@@ -271,11 +271,19 @@ def next_msgid
     @msgid += 1
   end
 
+  ##
+  # calls with_timeout on socket, with timeout defaulting to the :io_timeout parameter
+  def with_timeout(timeout=@server[:io_timeout], &block)
+    socket.with_timeout(timeout, &block)
+  end
+
   def bind(auth)
-    instrument "bind.net_ldap_connection" do |payload|
-      payload[:method] = meth = auth[:method]
-      adapter = Net::LDAP::AuthAdapter[meth]
-      adapter.new(self).bind(auth)
+    with_timeout do
+      instrument "bind.net_ldap_connection" do |payload|
+        payload[:method] = meth = auth[:method]
+        adapter = Net::LDAP::AuthAdapter[meth]
+        adapter.new(self).bind(auth)
+      end
     end
   end
 
@@ -385,6 +393,12 @@ def search(args = nil)
 
     message_id = next_msgid
 
+    timeout = if time > 0
+                time + 0.5 #give remote server half a second to respond before killing connection
+              else
+                @server[:io_timeout]
+              end
+
     instrument "search.net_ldap_connection",
                message_id: message_id,
                filter:     filter,
@@ -396,115 +410,116 @@ def search(args = nil)
                referrals:  refs,
                deref:      deref,
                attributes: attrs do |payload|
-      loop do
-        # should collect this into a private helper to clarify the structure
-        query_limit = 0
-        if size > 0
-          query_limit = if paged
-                          (((size - n_results) < 126) ? (size - n_results) : 0)
-                        else
-                          size
-                        end
-        end
+      with_timeout(timeout) do
+        loop do
+          # should collect this into a private helper to clarify the structure
+          query_limit = 0
+          if size > 0
+            query_limit = if paged
+                            (((size - n_results) < 126) ? (size - n_results) : 0)
+                          else
+                            size
+                          end
+          end
 
-        request = [
-          base.to_ber,
-          scope.to_ber_enumerated,
-          deref.to_ber_enumerated,
-          query_limit.to_ber, # size limit
-          time.to_ber,
-          attrs_only.to_ber,
-          filter.to_ber,
-          ber_attrs.to_ber_sequence,
-        ].to_ber_appsequence(Net::LDAP::PDU::SearchRequest)
-
-        # rfc2696_cookie sometimes contains binary data from Microsoft Active Directory
-        # this breaks when calling to_ber. (Can't force binary data to UTF-8)
-        # we have to disable paging (even though server supports it) to get around this...
-
-        controls = []
-        controls <<
-          [
-            Net::LDAP::LDAPControls::PAGED_RESULTS.to_ber,
-            # Criticality MUST be false to interoperate with normal LDAPs.
-            false.to_ber,
-            rfc2696_cookie.map(&:to_ber).to_ber_sequence.to_s.to_ber,
-          ].to_ber_sequence if paged
-        controls << ber_sort if ber_sort
-        controls = controls.empty? ? nil : controls.to_ber_contextspecific(0)
-
-        write(request, controls, message_id)
-
-        result_pdu = nil
-        controls = []
-
-        while pdu = queued_read(message_id)
-          case pdu.app_tag
-          when Net::LDAP::PDU::SearchReturnedData
-            n_results += 1
-            yield pdu.search_entry if block_given?
-          when Net::LDAP::PDU::SearchResultReferral
-            if refs
-              if block_given?
-                se = Net::LDAP::Entry.new
-                se[:search_referrals] = (pdu.search_referrals || [])
-                yield se
+          request = [
+            base.to_ber,
+            scope.to_ber_enumerated,
+            deref.to_ber_enumerated,
+            query_limit.to_ber, # size limit
+            time.to_ber,
+            attrs_only.to_ber,
+            filter.to_ber,
+            ber_attrs.to_ber_sequence,
+          ].to_ber_appsequence(Net::LDAP::PDU::SearchRequest)
+
+          # rfc2696_cookie sometimes contains binary data from Microsoft Active Directory
+          # this breaks when calling to_ber. (Can't force binary data to UTF-8)
+          # we have to disable paging (even though server supports it) to get around this...
+
+          controls = []
+          controls <<
+            [
+              Net::LDAP::LDAPControls::PAGED_RESULTS.to_ber,
+              # Criticality MUST be false to interoperate with normal LDAPs.
+              false.to_ber,
+              rfc2696_cookie.map(&:to_ber).to_ber_sequence.to_s.to_ber,
+            ].to_ber_sequence if paged
+          controls << ber_sort if ber_sort
+          controls = controls.empty? ? nil : controls.to_ber_contextspecific(0)
+
+          write(request, controls, message_id)
+
+          result_pdu = nil
+          controls = []
+
+          while pdu = queued_read(message_id)
+            case pdu.app_tag
+            when Net::LDAP::PDU::SearchReturnedData
+              n_results += 1
+              yield pdu.search_entry if block_given?
+            when Net::LDAP::PDU::SearchResultReferral
+              if refs
+                if block_given?
+                  se = Net::LDAP::Entry.new
+                  se[:search_referrals] = (pdu.search_referrals || [])
+                  yield se
+                end
               end
-            end
-          when Net::LDAP::PDU::SearchResult
-            result_pdu = pdu
-            controls = pdu.result_controls
-            if refs && pdu.result_code == Net::LDAP::ResultCodeReferral
-              if block_given?
-                se = Net::LDAP::Entry.new
-                se[:search_referrals] = (pdu.search_referrals || [])
-                yield se
+            when Net::LDAP::PDU::SearchResult
+              result_pdu = pdu
+              controls = pdu.result_controls
+              if refs && pdu.result_code == Net::LDAP::ResultCodeReferral
+                if block_given?
+                  se = Net::LDAP::Entry.new
+                  se[:search_referrals] = (pdu.search_referrals || [])
+                  yield se
+                end
               end
+              break
+            else
+              raise Net::LDAP::ResponseTypeInvalidError, "invalid response-type in search: #{pdu.app_tag}"
             end
-            break
-          else
-            raise Net::LDAP::ResponseTypeInvalidError, "invalid response-type in search: #{pdu.app_tag}"
           end
-        end
 
-        if result_pdu.nil?
-          raise Net::LDAP::ResponseMissingOrInvalidError, "response missing"
-        end
+          if result_pdu.nil?
+            raise Net::LDAP::ResponseMissingOrInvalidError, "response missing"
+          end
 
-        # count number of pages of results
-        payload[:page_count] ||= 0
-        payload[:page_count]  += 1
-
-        # When we get here, we have seen a type-5 response. If there is no
-        # error AND there is an RFC-2696 cookie, then query again for the next
-        # page of results. If not, we're done. Don't screw this up or we'll
-        # break every search we do.
-        #
-        # Noticed 02Sep06, look at the read_ber call in this loop, shouldn't
-        # that have a parameter of AsnSyntax? Does this just accidentally
-        # work? According to RFC-2696, the value expected in this position is
-        # of type OCTET STRING, covered in the default syntax supported by
-        # read_ber, so I guess we're ok.
-        more_pages = false
-        if result_pdu.result_code == Net::LDAP::ResultCodeSuccess and controls
-          controls.each do |c|
-            if c.oid == Net::LDAP::LDAPControls::PAGED_RESULTS
-              # just in case some bogus server sends us more than 1 of these.
-              more_pages = false
-              if c.value and c.value.length > 0
-                cookie = c.value.read_ber[1]
-                if cookie and cookie.length > 0
-                  rfc2696_cookie[1] = cookie
-                  more_pages = true
+          # count number of pages of results
+          payload[:page_count] ||= 0
+          payload[:page_count]  += 1
+
+          # When we get here, we have seen a type-5 response. If there is no
+          # error AND there is an RFC-2696 cookie, then query again for the next
+          # page of results. If not, we're done. Don't screw this up or we'll
+          # break every search we do.
+          #
+          # Noticed 02Sep06, look at the read_ber call in this loop, shouldn't
+          # that have a parameter of AsnSyntax? Does this just accidentally
+          # work? According to RFC-2696, the value expected in this position is
+          # of type OCTET STRING, covered in the default syntax supported by
+          # read_ber, so I guess we're ok.
+          more_pages = false
+          if result_pdu.result_code == Net::LDAP::ResultCodeSuccess and controls
+            controls.each do |c|
+              if c.oid == Net::LDAP::LDAPControls::PAGED_RESULTS
+                # just in case some bogus server sends us more than 1 of these.
+                more_pages = false
+                if c.value and c.value.length > 0
+                  cookie = c.value.read_ber[1]
+                  if cookie and cookie.length > 0
+                    rfc2696_cookie[1] = cookie
+                    more_pages = true
+                  end
                 end
               end
             end
           end
-        end
-
-        break unless more_pages
-      end # loop
 
+          break unless more_pages
+        end # loop
+      end # with_timeout
       # track total result count
       payload[:result_count] = n_results
 
diff --git a/test/integration/test_password_modify.rb b/test/integration/test_password_modify.rb
index ed8d4f5b..db1a00a7 100644
--- a/test/integration/test_password_modify.rb
+++ b/test/integration/test_password_modify.rb
@@ -3,7 +3,7 @@
 class TestPasswordModifyIntegration < LDAPIntegrationTestCase
   def setup
     super
-    @admin_account = {dn: 'cn=admin,dc=rubyldap,dc=com', password: 'passworD1', method: :simple}
+    @admin_account = { dn: 'cn=admin,dc=rubyldap,dc=com', password: 'passworD1', method: :simple }
     @ldap.authenticate @admin_account[:dn], @admin_account[:password]
 
     @dn = 'uid=modify-password-user1,ou=People,dc=rubyldap,dc=com'
diff --git a/test/test_auth_adapter.rb b/test/test_auth_adapter.rb
index 9e4c6002..fadf2a76 100644
--- a/test/test_auth_adapter.rb
+++ b/test/test_auth_adapter.rb
@@ -2,6 +2,7 @@
 
 class TestAuthAdapter < Test::Unit::TestCase
   class FakeSocket
+    include Net::BER::BERParser
     def initialize(*args)
     end
   end
diff --git a/test/test_ldap.rb b/test/test_ldap.rb
index 8d6a9a72..9510ee19 100644
--- a/test/test_ldap.rb
+++ b/test/test_ldap.rb
@@ -21,6 +21,10 @@ def search(*args)
       yield @search_success if block_given?
       @search_success
     end
+
+    def with_timeout(timeout = nil, &block)
+      yield
+    end
   end
 
   def setup
diff --git a/test/test_ldap_connection.rb b/test/test_ldap_connection.rb
index 8489c377..5cb63bb3 100644
--- a/test/test_ldap_connection.rb
+++ b/test/test_ldap_connection.rb
@@ -132,22 +132,25 @@ def test_modify_ops_replace
 
   def test_write
     mock = flexmock("socket")
-    mock.should_receive(:write).with([1.to_ber, "request"].to_ber_sequence).and_return(true)
+    mock.extend(Net::BER::BERParser)
+    mock.should_receive(:ber_timeout_write).with([1.to_ber, "request"].to_ber_sequence).and_return(true)
     conn = Net::LDAP::Connection.new(:socket => mock)
     conn.send(:write, "request")
   end
 
   def test_write_with_controls
     mock = flexmock("socket")
-    mock.should_receive(:write).with([1.to_ber, "request", "controls"].to_ber_sequence).and_return(true)
+    mock.extend(Net::BER::BERParser)
+    mock.should_receive(:ber_timeout_write).with([1.to_ber, "request", "controls"].to_ber_sequence).and_return(true)
     conn = Net::LDAP::Connection.new(:socket => mock)
     conn.send(:write, "request", "controls")
   end
 
   def test_write_increments_msgid
     mock = flexmock("socket")
-    mock.should_receive(:write).with([1.to_ber, "request1"].to_ber_sequence).and_return(true)
-    mock.should_receive(:write).with([2.to_ber, "request2"].to_ber_sequence).and_return(true)
+    mock.extend(Net::BER::BERParser)
+    mock.should_receive(:ber_timeout_write).with([1.to_ber, "request1"].to_ber_sequence).and_return(true)
+    mock.should_receive(:ber_timeout_write).with([2.to_ber, "request2"].to_ber_sequence).and_return(true)
     conn = Net::LDAP::Connection.new(:socket => mock)
     conn.send(:write, "request1")
     conn.send(:write, "request2")
@@ -209,7 +212,7 @@ def test_queued_read_modify
     mock.should_receive(:read_ber).
       and_return(result1).
       and_return(result2)
-    mock.should_receive(:write)
+    mock.should_receive(:ber_timeout_write)
     conn = Net::LDAP::Connection.new(:socket => mock)
 
     conn.next_msgid # simulates ongoing query
@@ -230,7 +233,7 @@ def test_queued_read_add
     mock.should_receive(:read_ber).
       and_return(result1).
       and_return(result2)
-    mock.should_receive(:write)
+    mock.should_receive(:ber_timeout_write)
     conn = Net::LDAP::Connection.new(:socket => mock)
 
     conn.next_msgid # simulates ongoing query
@@ -248,7 +251,7 @@ def test_queued_read_rename
     mock.should_receive(:read_ber).
       and_return(result1).
       and_return(result2)
-    mock.should_receive(:write)
+    mock.should_receive(:ber_timeout_write)
     conn = Net::LDAP::Connection.new(:socket => mock)
 
     conn.next_msgid # simulates ongoing query
@@ -269,7 +272,7 @@ def test_queued_read_delete
     mock.should_receive(:read_ber).
       and_return(result1).
       and_return(result2)
-    mock.should_receive(:write)
+    mock.should_receive(:ber_timeout_write)
     conn = Net::LDAP::Connection.new(:socket => mock)
 
     conn.next_msgid # simulates ongoing query
@@ -287,7 +290,7 @@ def test_queued_read_setup_encryption_with_start_tls
     mock.should_receive(:read_ber).
       and_return(result1).
       and_return(result2)
-    mock.should_receive(:write)
+    mock.should_receive(:ber_timeout_write)
     conn = Net::LDAP::Connection.new(:socket => mock)
     flexmock(Net::LDAP::Connection).should_receive(:wrap_with_ssl).with(mock, {}, nil).
       and_return(mock)
@@ -303,10 +306,11 @@ def test_queued_read_bind_simple
     result2 = make_message(2, app_tag: Net::LDAP::PDU::BindResult)
 
     mock = flexmock("socket")
+    mock.extend(Net::BER::BERParser)
     mock.should_receive(:read_ber).
       and_return(result1).
       and_return(result2)
-    mock.should_receive(:write)
+    mock.should_receive(:ber_timeout_write)
     conn = Net::LDAP::Connection.new(:socket => mock)
 
     conn.next_msgid # simulates ongoing query
@@ -324,10 +328,11 @@ def test_queued_read_bind_sasl
     result2 = make_message(2, app_tag: Net::LDAP::PDU::BindResult)
 
     mock = flexmock("socket")
+    mock.extend(Net::BER::BERParser)
     mock.should_receive(:read_ber).
       and_return(result1).
       and_return(result2)
-    mock.should_receive(:write)
+    mock.should_receive(:ber_timeout_write)
     conn = Net::LDAP::Connection.new(:socket => mock)
 
     conn.next_msgid # simulates ongoing query
@@ -345,7 +350,7 @@ def test_queued_read_bind_sasl
 class TestLDAPConnectionErrors < Test::Unit::TestCase
   def setup
     @tcp_socket = flexmock(:connection)
-    @tcp_socket.should_receive(:write)
+    @tcp_socket.should_receive(:ber_timeout_write)
     flexmock(Socket).should_receive(:tcp).and_return(@tcp_socket)
     @connection = Net::LDAP::Connection.new(:host => 'test.mocked.com', :port => 636)
   end
@@ -374,7 +379,8 @@ def test_no_error_on_success
 class TestLDAPConnectionInstrumentation < Test::Unit::TestCase
   def setup
     @tcp_socket = flexmock(:connection)
-    @tcp_socket.should_receive(:write)
+    @tcp_socket.extend(Net::BER::BERParser)
+    @tcp_socket.should_receive(:ber_timeout_write)
     flexmock(Socket).should_receive(:tcp).and_return(@tcp_socket)
 
     @service = MockInstrumentationService.new
diff --git a/test/test_search.rb b/test/test_search.rb
index c577a6a2..ee0fcee7 100644
--- a/test/test_search.rb
+++ b/test/test_search.rb
@@ -6,6 +6,10 @@ class FakeConnection
     def search(args)
       OpenStruct.new(:result_code => Net::LDAP::ResultCodeOperationsError, :message => "error", :success? => false)
     end
+
+    def with_timeout(timeout = nil, &block)
+      yield
+    end
   end
 
   def setup