recovery: configurable congestion control

The purpose of this mega patch is to make congestion control
configurable. Also it's extensive so that we can add more congestion
module later and make it easier to switch between different CC
algorithms using config API.

New Modules
-----------

`cc/` directory is created.

- `cc/mod.rs` contains basic CC constants such as
  INITIAL_WINDOW. Also it defines CongestonControlTrait which has
  method of CC related functions and hooks.

- new_congestion_control(name) is a factory method to return
  a new CC module based on its name.

- `cc/reno.rs` contains `Reno` CC algotirhm, following
  QUIC recovery draft 24 based.

Currently besides of basic method, you need to implement
the following CC-specific hooks called from the stack:

- on_packet_sent_cc()
- on_packet_acked_cc()
- congestion_event()

Note that there is still some CC hooks missing or unimplmented.

Notably,
- `IsAppLimited()``: moved to cc/mod.rs but still dummy
- `InPersistentCongestion()``: still in recovery.rs
- `ProcessECN()``: not implemented
- `OnPacketLost()``: still in recovery.rs. It has no CC specific
  other than calling `CongestionEvent()`` already moved to CC module.

lib.rs
------

- CC is now initialized using `cc::new_congestion_control()`.

recovery.rs
-----------

- CC constants moved to `cc/mod.rs`
- CC fields such as `bytes_in_flight`, `cwnd`, `recovery_start_time`, `ssthresh`
  are moved into cc modules. Also added `cc` field which is CC algorithm object.
  Due to the change above, code accessing fields above now use the following
  methods for reading/writing those values in CC module:
  `self.bytes_in_flight` -> `self.cc.bytes_in_flight()`
  reading `self.cwnd` -> use `self.cc.cwnd()`
  updating `self.cwnd` -> use `self.cc.set_cwnd()`
  reading `self.ssthresh` -> use `self.cc.ssthresh()`
- `OnPacketSentCC`, `OnPacketAckedCC`, `CongestionEvent` moved to
  CC module, so only calling cc methods here.
- `in_recovery()` moved to CC module.
- recovery.cwnd() is renamed to recovery.cwnd_available().

Config API
----------

`Config` struct now has `cc_algorithm`.

There is 2 APIs added for configuration those new parameters:

* config.set_cc_algorithm_name(<name>): set which CC algorithm to use by
  name.
* config.set_cc_algorithm(<enum>): set which CC algorithm to use. values
  are from quiche::CongestionControlAlgorithm.

ffi.rs
------

Corresponing C Config API is also added.

`quiche_config_set_cc_algorithm_name()`
`quiche_config_set_cc_algorithm()`

Those config should be called before new connection is made or accepted.

Examples
--------

Rust example of http3-server., server, http3-client, client has
a new options name `--cc-algorithm` to specify which CC algorithms can be used.

C example of http3-server and server calls quiche_config_set_cc_*() APIs.

Others
------
- Documentation in the source
- Test cases added
diff --git a/examples/client.rs b/examples/client.rs
index 78f49d0..e0da72d 100644
--- a/examples/client.rs
+++ b/examples/client.rs
@@ -47,6 +47,7 @@
   --wire-version VERSION   The version number to send to the server [default: babababa].
   --dump-packets PATH      Dump the incoming packets as files in the given directory.
   --no-verify              Don't verify server's certificate.
+  --cc-algorithm NAME      Set client congestion control algorithm [default: reno].
   -h --help                Show this screen.
 ";
 
@@ -134,6 +135,10 @@
         config.log_keys();
     }
 
+    config
+        .set_cc_algorithm_name(args.get_str("--cc-algorithm"))
+        .unwrap();
+
     // Generate a random source connection ID for the connection.
     let mut scid = [0; quiche::MAX_CONN_ID_LEN];
     SystemRandom::new().fill(&mut scid[..]).unwrap();
diff --git a/examples/http3-client.rs b/examples/http3-client.rs
index 7129f5c..df1f401 100644
--- a/examples/http3-client.rs
+++ b/examples/http3-client.rs
@@ -45,6 +45,7 @@
   --wire-version VERSION   The version number to send to the server [default: babababa].
   --no-verify              Don't verify server's certificate.
   --no-grease              Don't send GREASE.
+  --cc-algorithm NAME      Set client congestion control algorithm [default: reno].
   -H --header HEADER ...   Add a request header.
   -n --requests REQUESTS   Send the given number of identical requests [default: 1].
   -h --help                Show this screen.
@@ -143,6 +144,10 @@
         config.log_keys();
     }
 
+    config
+        .set_cc_algorithm_name(args.get_str("--cc-algorithm"))
+        .unwrap();
+
     // Generate a random source connection ID for the connection.
     let mut scid = [0; quiche::MAX_CONN_ID_LEN];
     SystemRandom::new().fill(&mut scid[..]).unwrap();
diff --git a/examples/http3-server.c b/examples/http3-server.c
index e2b0810..387c0e6 100644
--- a/examples/http3-server.c
+++ b/examples/http3-server.c
@@ -520,6 +520,7 @@
     quiche_config_set_initial_max_streams_bidi(config, 100);
     quiche_config_set_initial_max_streams_uni(config, 100);
     quiche_config_set_disable_active_migration(config, true);
+    quiche_config_set_cc_algorithm(config, QUICHE_CC_RENO);
 
     http3_config = quiche_h3_config_new();
     if (http3_config == NULL) {
diff --git a/examples/http3-server.rs b/examples/http3-server.rs
index f4db650..2f18876 100644
--- a/examples/http3-server.rs
+++ b/examples/http3-server.rs
@@ -52,6 +52,7 @@
   --early-data                Enables receiving early data.
   --no-retry                  Disable stateless retry.
   --no-grease                 Don't send GREASE.
+  --cc-algorithm NAME         Set server congestion control algorithm [default: reno].
   -h --help                   Show this screen.
 ";
 
@@ -147,6 +148,10 @@
         config.log_keys();
     }
 
+    config
+        .set_cc_algorithm_name(args.get_str("--cc-algorithm"))
+        .unwrap();
+
     let h3_config = quiche::h3::Config::new().unwrap();
 
     let mut clients = ClientMap::new();
diff --git a/examples/server.c b/examples/server.c
index 3a49440..121d1b0 100644
--- a/examples/server.c
+++ b/examples/server.c
@@ -451,6 +451,7 @@
     quiche_config_set_initial_max_stream_data_bidi_local(config, 1000000);
     quiche_config_set_initial_max_stream_data_bidi_remote(config, 1000000);
     quiche_config_set_initial_max_streams_bidi(config, 100);
+    quiche_config_set_cc_algorithm(config, QUICHE_CC_RENO);
 
     struct connections c;
     c.sock = sock;
diff --git a/examples/server.rs b/examples/server.rs
index b8f1041..735c618 100644
--- a/examples/server.rs
+++ b/examples/server.rs
@@ -54,6 +54,7 @@
   --dump-packets PATH         Dump the incoming packets as files in the given directory.
   --early-data                Enables receiving early data.
   --no-retry                  Disable stateless retry.
+  --cc-algorithm NAME         Set server congestion control algorithm [default: reno].
   -h --help                   Show this screen.
 ";
 
@@ -149,6 +150,10 @@
         config.log_keys();
     }
 
+    config
+        .set_cc_algorithm_name(args.get_str("--cc-algorithm"))
+        .unwrap();
+
     let mut clients = ClientMap::new();
 
     let mut pkt_count = 0;
diff --git a/include/quiche.h b/include/quiche.h
index 840dbc3..7537e29 100644
--- a/include/quiche.h
+++ b/include/quiche.h
@@ -88,6 +88,9 @@
 
     // The received data exceeds the stream's final size.
     QUICHE_ERR_FINAL_SIZE = -13,
+
+    // Error in congestion control.
+    QUICHE_ERR_CONGESTION_CONTROL = -14,
 };
 
 // Returns a human readable string with the quiche version number.
@@ -161,6 +164,13 @@
 // Sets the `disable_active_migration` transport parameter.
 void quiche_config_set_disable_active_migration(quiche_config *config, bool v);
 
+enum quiche_cc_algorithm {
+    QUICHE_CC_RENO = 0,
+};
+
+// Sets the congestion control algorithm used.
+void quiche_config_set_cc_algorithm(quiche_config *config, enum quiche_cc_algorithm algo);
+
 // Frees the config object.
 void quiche_config_free(quiche_config *config);
 
diff --git a/src/cc/mod.rs b/src/cc/mod.rs
new file mode 100644
index 0000000..fdb0e17
--- /dev/null
+++ b/src/cc/mod.rs
@@ -0,0 +1,154 @@
+// Copyright (C) 2019, Cloudflare, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright notice,
+//       this list of conditions and the following disclaimer.
+//
+//     * Redistributions in binary form must reproduce the above copyright
+//       notice, this list of conditions and the following disclaimer in the
+//       documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+use std::str::FromStr;
+
+use std::time::Duration;
+use std::time::Instant;
+
+use crate::cc;
+use crate::recovery::Sent;
+
+pub const INITIAL_WINDOW_PACKETS: usize = 10;
+
+pub const INITIAL_WINDOW: usize = INITIAL_WINDOW_PACKETS * MAX_DATAGRAM_SIZE;
+
+pub const MINIMUM_WINDOW: usize = 2 * MAX_DATAGRAM_SIZE;
+
+pub const MAX_DATAGRAM_SIZE: usize = 1452;
+
+pub const LOSS_REDUCTION_FACTOR: f64 = 0.5;
+
+/// Available congestion control algorithms.
+///
+/// This enum provides currently available list of congestion control
+/// algorithms.
+#[derive(Debug, Copy, Clone, PartialEq)]
+pub enum Algorithm {
+    /// Reno congestion control algorithm (default). `reno` in a string form.
+    Reno = 0,
+}
+
+impl FromStr for Algorithm {
+    type Err = crate::Error;
+
+    /// Converts a string to `CongestionControlAlgorithm`.
+    ///
+    /// If `name` is not valid, `Error::CongestionControl` is returned.
+    fn from_str(name: &str) -> Result<Self, Self::Err> {
+        match name {
+            "reno" => Ok(Algorithm::Reno),
+            _ => Err(crate::Error::CongestionControl),
+        }
+    }
+}
+
+/// Congestion control algorithm.
+pub trait CongestionControl
+where
+    Self: std::fmt::Debug,
+{
+    fn new() -> Self
+    where
+        Self: Sized;
+
+    fn cwnd(&self) -> usize;
+
+    fn bytes_in_flight(&self) -> usize;
+
+    fn decrease_bytes_in_flight(&mut self, bytes_in_flight: usize);
+
+    fn congestion_recovery_start_time(&self) -> Option<Instant>;
+
+    fn is_app_limited(&self) -> bool {
+        false
+    }
+
+    /// Resets the congestion window to the minimum size.
+    fn collapse_cwnd(&mut self);
+
+    /// OnPacketSentCC(bytes_sent)
+    fn on_packet_sent_cc(&mut self, bytes_sent: usize, trace_id: &str);
+
+    /// InCongestionRecovery(sent_time)
+    fn in_congestion_recovery(&self, sent_time: Instant) -> bool {
+        match self.congestion_recovery_start_time() {
+            Some(congestion_recovery_start_time) =>
+                sent_time <= congestion_recovery_start_time,
+
+            None => false,
+        }
+    }
+
+    /// OnPacketAckedCC(packet)
+    fn on_packet_acked_cc(
+        &mut self, packet: &Sent, srtt: Duration, min_rtt: Duration,
+        trace_id: &str,
+    );
+
+    /// CongestionEvent(time_sent)
+    fn congestion_event(
+        &mut self, time_sent: Instant, now: Instant, trace_id: &str,
+    );
+}
+
+/// Instances a congestion control implementation based on the CC algorithm ID.
+pub fn new_congestion_control(algo: Algorithm) -> Box<dyn CongestionControl> {
+    trace!("Initializing congestion control: {:?}", algo);
+    match algo {
+        Algorithm::Reno => Box::new(cc::reno::Reno::new()),
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn new_cc() {
+        let cc = new_congestion_control(Algorithm::Reno);
+
+        assert!(cc.cwnd() > 0);
+        assert_eq!(cc.bytes_in_flight(), 0);
+    }
+
+    #[test]
+    fn lookup_cc_algo_ok() {
+        let algo = Algorithm::from_str("reno").unwrap();
+
+        assert_eq!(algo, Algorithm::Reno);
+    }
+
+    #[test]
+    fn lookup_cc_algo_bad() {
+        assert_eq!(
+            Algorithm::from_str("???"),
+            Err(crate::Error::CongestionControl)
+        );
+    }
+}
+
+mod reno;
diff --git a/src/cc/reno.rs b/src/cc/reno.rs
new file mode 100644
index 0000000..aa2643d
--- /dev/null
+++ b/src/cc/reno.rs
@@ -0,0 +1,264 @@
+// Copyright (C) 2019, Cloudflare, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright notice,
+//       this list of conditions and the following disclaimer.
+//
+//     * Redistributions in binary form must reproduce the above copyright
+//       notice, this list of conditions and the following disclaimer in the
+//       documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+use std::time::Duration;
+use std::time::Instant;
+
+use crate::cc;
+use crate::recovery::Sent;
+
+/// Reno congestion control implementation.
+pub struct Reno {
+    congestion_window: usize,
+
+    bytes_in_flight: usize,
+
+    congestion_recovery_start_time: Option<Instant>,
+
+    ssthresh: usize,
+    /* TODO: ECN is not implemented.
+     * ecn_ce_counters: [usize; packet::EPOCH_COUNT] */
+}
+
+impl cc::CongestionControl for Reno {
+    fn new() -> Self
+    where
+        Self: Sized,
+    {
+        Reno {
+            congestion_window: cc::INITIAL_WINDOW,
+
+            bytes_in_flight: 0,
+
+            congestion_recovery_start_time: None,
+
+            ssthresh: std::usize::MAX,
+            /* TODO: ECN is not implemented.
+             * ecn_ce_counters: [0; packet::EPOCH_COUNT], */
+        }
+    }
+
+    fn cwnd(&self) -> usize {
+        self.congestion_window
+    }
+
+    fn collapse_cwnd(&mut self) {
+        self.congestion_window = cc::INITIAL_WINDOW;
+    }
+
+    fn bytes_in_flight(&self) -> usize {
+        self.bytes_in_flight
+    }
+
+    fn decrease_bytes_in_flight(&mut self, bytes_in_flight: usize) {
+        self.bytes_in_flight =
+            self.bytes_in_flight.saturating_sub(bytes_in_flight);
+    }
+
+    fn congestion_recovery_start_time(&self) -> Option<Instant> {
+        self.congestion_recovery_start_time
+    }
+
+    fn on_packet_sent_cc(&mut self, bytes_sent: usize, _trace_id: &str) {
+        self.bytes_in_flight += bytes_sent;
+    }
+
+    fn on_packet_acked_cc(
+        &mut self, packet: &Sent, _srtt: Duration, _min_rtt: Duration,
+        _trace_id: &str,
+    ) {
+        self.bytes_in_flight -= packet.size;
+
+        if self.in_congestion_recovery(packet.time) {
+            return;
+        }
+
+        if self.is_app_limited() {
+            return;
+        }
+
+        if self.congestion_window < self.ssthresh {
+            // Slow start.
+            self.congestion_window += packet.size;
+        } else {
+            // Congestion avoidance.
+            self.congestion_window +=
+                (cc::MAX_DATAGRAM_SIZE * packet.size) / self.congestion_window;
+        }
+    }
+
+    fn congestion_event(
+        &mut self, time_sent: Instant, now: Instant, _trace_id: &str,
+    ) {
+        // Start a new congestion event if packet was sent after the
+        // start of the previous congestion recovery period.
+        if !self.in_congestion_recovery(time_sent) {
+            self.congestion_recovery_start_time = Some(now);
+
+            self.congestion_window = (self.congestion_window as f64 *
+                cc::LOSS_REDUCTION_FACTOR)
+                as usize;
+            self.congestion_window =
+                std::cmp::max(self.congestion_window, cc::MINIMUM_WINDOW);
+            self.ssthresh = self.congestion_window;
+        }
+    }
+}
+
+impl std::fmt::Debug for Reno {
+    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+        write!(
+            f,
+            "cwnd={} ssthresh={} bytes_in_flight={}",
+            self.congestion_window, self.ssthresh, self.bytes_in_flight,
+        )
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    const TRACE_ID: &str = "test_id";
+
+    fn init() {
+        let _ = env_logger::builder().is_test(true).try_init();
+    }
+
+    #[test]
+    fn reno_init() {
+        let cc = cc::new_congestion_control(cc::Algorithm::Reno);
+
+        assert!(cc.cwnd() > 0);
+        assert_eq!(cc.bytes_in_flight(), 0);
+    }
+
+    #[test]
+    fn reno_send() {
+        init();
+
+        let mut cc = cc::new_congestion_control(cc::Algorithm::Reno);
+
+        cc.on_packet_sent_cc(1000, TRACE_ID);
+
+        assert_eq!(cc.bytes_in_flight(), 1000);
+    }
+
+    #[test]
+    fn reno_slow_start() {
+        init();
+
+        let mut cc = cc::new_congestion_control(cc::Algorithm::Reno);
+
+        let p = Sent {
+            pkt_num: 0,
+            frames: vec![],
+            time: std::time::Instant::now(),
+            size: 5000,
+            ack_eliciting: true,
+            in_flight: true,
+        };
+
+        // Send 5k x 4 = 20k, higher than default cwnd(~15k)
+        // to become no longer app limited.
+        cc.on_packet_sent_cc(p.size, TRACE_ID);
+        cc.on_packet_sent_cc(p.size, TRACE_ID);
+        cc.on_packet_sent_cc(p.size, TRACE_ID);
+        cc.on_packet_sent_cc(p.size, TRACE_ID);
+
+        let cwnd_prev = cc.cwnd();
+
+        cc.on_packet_acked_cc(
+            &p,
+            Duration::new(0, 1),
+            Duration::new(0, 1),
+            TRACE_ID,
+        );
+
+        // Check if cwnd increased by packet size (slow start).
+        assert_eq!(cc.cwnd(), cwnd_prev + p.size);
+    }
+
+    #[test]
+    fn reno_congestion_event() {
+        init();
+
+        let mut cc = cc::new_congestion_control(cc::Algorithm::Reno);
+        let prev_cwnd = cc.cwnd();
+
+        cc.congestion_event(
+            std::time::Instant::now(),
+            std::time::Instant::now(),
+            TRACE_ID,
+        );
+
+        // In Reno, after congestion event, cwnd will be cut in half.
+        assert_eq!(prev_cwnd / 2, cc.cwnd());
+    }
+
+    #[test]
+    fn reno_congestion_avoidance() {
+        init();
+
+        let mut cc = cc::new_congestion_control(cc::Algorithm::Reno);
+        let prev_cwnd = cc.cwnd();
+
+        // Send 20K bytes.
+        cc.on_packet_sent_cc(20000, TRACE_ID);
+
+        cc.congestion_event(
+            std::time::Instant::now(),
+            std::time::Instant::now(),
+            TRACE_ID,
+        );
+
+        // In Reno, after congestion event, cwnd will be cut in half.
+        assert_eq!(prev_cwnd / 2, cc.cwnd());
+
+        let p = Sent {
+            pkt_num: 0,
+            frames: vec![],
+            time: std::time::Instant::now(),
+            size: 5000,
+            ack_eliciting: true,
+            in_flight: true,
+        };
+
+        let prev_cwnd = cc.cwnd();
+
+        // Ack 5000 bytes.
+        cc.on_packet_acked_cc(
+            &p,
+            Duration::new(0, 1),
+            Duration::new(0, 1),
+            TRACE_ID,
+        );
+
+        // Check if cwnd increase is smaller than a packet size (congestion
+        // avoidance).
+        assert!(cc.cwnd() < prev_cwnd + 1111);
+    }
+}
diff --git a/src/ffi.rs b/src/ffi.rs
index 5fa1eca..8b89a08 100644
--- a/src/ffi.rs
+++ b/src/ffi.rs
@@ -213,6 +213,25 @@
 }
 
 #[no_mangle]
+pub extern fn quiche_config_set_cc_algorithm_name(
+    config: &mut Config, name: *const c_char,
+) -> c_int {
+    let name = unsafe { ffi::CStr::from_ptr(name).to_str().unwrap() };
+    match config.set_cc_algorithm_name(name) {
+        Ok(_) => 0,
+
+        Err(e) => e.to_c() as c_int,
+    }
+}
+
+#[no_mangle]
+pub extern fn quiche_config_set_cc_algorithm(
+    config: &mut Config, algo: cc::Algorithm,
+) {
+    config.set_cc_algorithm(algo);
+}
+
+#[no_mangle]
 pub extern fn quiche_config_free(config: *mut Config) {
     unsafe { Box::from_raw(config) };
 }
diff --git a/src/lib.rs b/src/lib.rs
index 3ffaa42..32606c7 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -229,6 +229,35 @@
 //! [`readable()`]: struct.Connection.html#method.readable
 //! [`stream_recv()`]: struct.Connection.html#method.stream_recv
 //! [HTTP/3 module]: h3/index.html
+//!
+//! ## Congestion Control
+//!
+//! The quiche library provides a high-level API for configuring which
+//! congestion control algorithm to use throughout the QUIC connection.
+//!
+//! When a QUIC connection is created, the application can optionally choose
+//! which CC algorithm to use. See [`CongestionControlAlgorithm`] for currently
+//! available congestion control algorithms.
+//!
+//! For example:
+//!
+//! ```
+//! let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION).unwrap();
+//! config.set_cc_algorithm(quiche::CongestionControlAlgorithm::Reno);
+//! ```
+//!
+//! Alternatively, you can configure the congestion control algorithm to use
+//! by its name.
+//!
+//! ```
+//! let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION).unwrap();
+//! config.set_cc_algorithm_name("reno").unwrap();
+//! ```
+//!
+//! Note that the CC algorithm should be configured before calling [`connect()`]
+//! or [`accept()`]. Otherwise the connection will use a default CC algorithm.
+//!
+//! [`CongestionControlAlgorithm`]: cc/enum.Algorithm.html
 
 #![allow(improper_ctypes)]
 #![warn(missing_docs)]
@@ -240,6 +269,9 @@
 use std::time;
 
 use std::pin::Pin;
+use std::str::FromStr;
+
+pub use crate::cc::Algorithm as CongestionControlAlgorithm;
 
 /// The current QUIC wire version.
 pub const PROTOCOL_VERSION: u32 = 0xff00_0018;
@@ -315,6 +347,9 @@
 
     /// The received data exceeds the stream's final size.
     FinalSize          = -13,
+
+    /// Error in congestion control.
+    CongestionControl  = -14,
 }
 
 impl Error {
@@ -381,6 +416,8 @@
     application_protos: Vec<Vec<u8>>,
 
     grease: bool,
+
+    cc_algorithm: cc::Algorithm,
 }
 
 impl Config {
@@ -401,6 +438,7 @@
             tls_ctx,
             application_protos: Vec::new(),
             grease: true,
+            cc_algorithm: cc::Algorithm::Reno, // default cc algorithm
         })
     }
 
@@ -622,6 +660,31 @@
     pub fn set_disable_active_migration(&mut self, v: bool) {
         self.local_transport_params.disable_active_migration = v;
     }
+
+    /// Sets the congestion control algorithm used by string.
+    ///
+    /// The default value is `reno`. On error `Error::CongestionControl`
+    /// will be returned.
+    ///
+    /// ## Examples:
+    ///
+    /// ```
+    /// # let mut config = quiche::Config::new(0xbabababa)?;
+    /// config.set_cc_algorithm_name("reno");
+    /// # Ok::<(), quiche::Error>(())
+    /// ```
+    pub fn set_cc_algorithm_name(&mut self, name: &str) -> Result<()> {
+        self.cc_algorithm = CongestionControlAlgorithm::from_str(name)?;
+
+        Ok(())
+    }
+
+    /// Sets the congestion control algorithm used.
+    ///
+    /// The default value is `quiche::CongestionControlAlgorithm::Reno`.
+    pub fn set_cc_algorithm(&mut self, algo: CongestionControlAlgorithm) {
+        self.cc_algorithm = algo;
+    }
 }
 
 /// A QUIC connection.
@@ -934,7 +997,7 @@
 
             handshake: tls,
 
-            recovery: recovery::Recovery::default(),
+            recovery: recovery::Recovery::new(&config),
 
             application_protos: config.application_protos.clone(),
 
@@ -1600,7 +1663,7 @@
         }
 
         // Calculate available space in the packet based on congestion window.
-        let mut left = cmp::min(self.recovery.cwnd(), b.cap());
+        let mut left = cmp::min(self.recovery.cwnd_available(), b.cap());
 
         // Limit data sent by the server based on the amount of data received
         // from the client before its address is validated.
@@ -2515,7 +2578,7 @@
             recv: self.recv_count,
             sent: self.sent_count,
             lost: self.recovery.lost_count,
-            cwnd: self.recovery.cwnd(),
+            cwnd: self.recovery.cc.cwnd(),
             rtt: self.recovery.rtt(),
         }
     }
@@ -4872,6 +4935,7 @@
 pub use crate::packet::Type;
 pub use crate::stream::StreamIter;
 
+mod cc;
 mod crypto;
 mod ffi;
 mod frame;
diff --git a/src/recovery.rs b/src/recovery.rs
index 6a494ba..23f5c97 100644
--- a/src/recovery.rs
+++ b/src/recovery.rs
@@ -31,9 +31,11 @@
 
 use std::collections::BTreeMap;
 
+use crate::Config;
 use crate::Error;
 use crate::Result;
 
+use crate::cc;
 use crate::frame;
 use crate::packet;
 use crate::ranges;
@@ -47,14 +49,6 @@
 
 const INITIAL_RTT: Duration = Duration::from_millis(500);
 
-// Congestion Control
-pub const INITIAL_WINDOW_PACKETS: usize = 10;
-
-const MAX_DATAGRAM_SIZE: usize = 1452;
-
-const INITIAL_WINDOW: usize = INITIAL_WINDOW_PACKETS * MAX_DATAGRAM_SIZE;
-const MINIMUM_WINDOW: usize = 2 * MAX_DATAGRAM_SIZE;
-
 const PERSISTENT_CONGESTION_THRESHOLD: u32 = 3;
 
 #[derive(Debug)]
@@ -103,19 +97,13 @@
 
     pub lost_count: usize,
 
-    bytes_in_flight: usize,
-
-    cwnd: usize,
-
-    recovery_start_time: Option<Instant>,
-
-    ssthresh: usize,
-
     pub loss_probes: [usize; packet::EPOCH_COUNT],
+
+    pub cc: Box<dyn cc::CongestionControl>,
 }
 
-impl Default for Recovery {
-    fn default() -> Recovery {
+impl Recovery {
+    pub fn new(config: &Config) -> Self {
         Recovery {
             loss_detection_timer: None,
 
@@ -147,20 +135,12 @@
 
             lost_count: 0,
 
-            bytes_in_flight: 0,
-
-            cwnd: INITIAL_WINDOW,
-
-            recovery_start_time: None,
-
-            ssthresh: std::usize::MAX,
-
             loss_probes: [0; packet::EPOCH_COUNT],
+
+            cc: cc::new_congestion_control(config.cc_algorithm),
         }
     }
-}
 
-impl Recovery {
     pub fn on_packet_sent(
         &mut self, pkt: Sent, epoch: packet::Epoch, handshake_completed: bool,
         now: Instant, trace_id: &str,
@@ -180,7 +160,7 @@
             }
 
             // OnPacketSentCC
-            self.bytes_in_flight += sent_bytes;
+            self.cc.on_packet_sent_cc(sent_bytes, trace_id);
 
             self.set_loss_detection_timer(handshake_completed);
         }
@@ -232,7 +212,7 @@
         // Processing acked packets in reverse order (from largest to smallest)
         // appears to be faster, possibly due to the BTreeMap implementation.
         for pn in ranges.flatten().rev() {
-            let newly_acked = self.on_packet_acked(pn, epoch);
+            let newly_acked = self.on_packet_acked(pn, epoch, trace_id);
             has_newly_acked = cmp::max(has_newly_acked, newly_acked);
 
             if newly_acked {
@@ -292,7 +272,7 @@
             unacked_bytes += p.size;
         }
 
-        self.bytes_in_flight -= unacked_bytes;
+        self.cc.decrease_bytes_in_flight(unacked_bytes);
 
         self.loss_time[epoch] = None;
         self.loss_probes[epoch] = 0;
@@ -307,13 +287,13 @@
         self.loss_detection_timer
     }
 
-    pub fn cwnd(&self) -> usize {
+    pub fn cwnd_available(&self) -> usize {
         // Ignore cwnd when sending probe packets.
         if self.loss_probes.iter().any(|&x| x > 0) {
             return std::usize::MAX;
         }
 
-        self.cwnd.saturating_sub(self.bytes_in_flight)
+        self.cc.cwnd().saturating_sub(self.cc.bytes_in_flight())
     }
 
     pub fn rtt(&self) -> Duration {
@@ -394,7 +374,7 @@
             return;
         }
 
-        if self.bytes_in_flight == 0 {
+        if self.cc.bytes_in_flight() == 0 {
             // TODO: check if peer is awaiting address validation.
             self.loss_detection_timer = None;
             return;
@@ -465,19 +445,13 @@
         }
 
         if !lost_pkt.is_empty() {
-            self.on_packets_lost(lost_pkt, epoch, now);
+            self.on_packets_lost(lost_pkt, epoch, now, trace_id);
         }
     }
 
-    fn in_recovery(&self, sent_time: Instant) -> bool {
-        match self.recovery_start_time {
-            Some(recovery_start_time) => sent_time <= recovery_start_time,
-
-            None => false,
-        }
-    }
-
-    fn on_packet_acked(&mut self, pkt_num: u64, epoch: packet::Epoch) -> bool {
+    fn on_packet_acked(
+        &mut self, pkt_num: u64, epoch: packet::Epoch, trace_id: &str,
+    ) -> bool {
         // If the acked packet number is lower than the lowest unacked packet
         // number it means that the packet is not newly acked, so return early.
         if let Some(lowest) = self.sent[epoch].values().nth(0) {
@@ -491,20 +465,13 @@
             self.acked[epoch].append(&mut p.frames);
 
             if p.in_flight {
-                // OnPacketAckedCC
-                self.bytes_in_flight -= p.size;
-
-                if self.in_recovery(p.time) {
-                    return true;
-                }
-
-                if self.cwnd < self.ssthresh {
-                    // Slow start.
-                    self.cwnd += p.size;
-                } else {
-                    // Congestion avoidance.
-                    self.cwnd += (MAX_DATAGRAM_SIZE * p.size) / self.cwnd;
-                }
+                // OnPacketAckedCC(acked_packet)
+                self.cc.on_packet_acked_cc(
+                    &p,
+                    self.rtt(),
+                    self.min_rtt,
+                    trace_id,
+                );
             }
 
             return true;
@@ -514,6 +481,7 @@
         false
     }
 
+    // TODO: move to Congestion Control and implement draft 24
     fn in_persistent_congestion(&mut self, _largest_lost_pkt: &Sent) -> bool {
         let _congestion_period = self.pto() * PERSISTENT_CONGESTION_THRESHOLD;
 
@@ -521,8 +489,10 @@
         false
     }
 
+    // TODO: move to Congestion Control
     fn on_packets_lost(
         &mut self, lost_pkt: Vec<u64>, epoch: packet::Epoch, now: Instant,
+        trace_id: &str,
     ) {
         // Differently from OnPacketsLost(), we need to handle both
         // in-flight and non-in-flight packets, so need to keep track
@@ -539,7 +509,7 @@
                 continue;
             }
 
-            self.bytes_in_flight -= p.size;
+            self.cc.decrease_bytes_in_flight(p.size);
 
             self.lost[epoch].append(&mut p.frames);
 
@@ -548,16 +518,11 @@
 
         if let Some(largest_lost_pkt) = largest_lost_pkt {
             // CongestionEvent
-            if !self.in_recovery(largest_lost_pkt.time) {
-                self.recovery_start_time = Some(now);
-
-                self.cwnd /= 2;
-                self.cwnd = cmp::max(self.cwnd, MINIMUM_WINDOW);
-                self.ssthresh = self.cwnd;
-            }
+            self.cc
+                .congestion_event(largest_lost_pkt.time, now, trace_id);
 
             if self.in_persistent_congestion(&largest_lost_pkt) {
-                self.cwnd = MINIMUM_WINDOW;
+                self.cc.collapse_cwnd();
             }
         }
     }
@@ -582,14 +547,13 @@
             },
         };
 
-        write!(f, "inflight={} ", self.bytes_in_flight)?;
-        write!(f, "cwnd={} ", self.cwnd)?;
         write!(f, "latest_rtt={:?} ", self.latest_rtt)?;
         write!(f, "srtt={:?} ", self.smoothed_rtt)?;
         write!(f, "min_rtt={:?} ", self.min_rtt)?;
         write!(f, "rttvar={:?} ", self.rttvar)?;
         write!(f, "loss_time={:?} ", self.loss_time)?;
         write!(f, "loss_probes={:?} ", self.loss_probes)?;
+        write!(f, "{:?} ", self.cc)?;
 
         Ok(())
     }