From fb699e5cb278734a03fe43127798482dec02879f Mon Sep 17 00:00:00 2001
From: Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
Date: Sun, 20 Aug 2017 12:34:02 +0900
Subject: [PATCH] Add STOP_SENDING frame

---
 examples/debug.cc            |  7 ++++++
 lib/includes/ngtcp2/ngtcp2.h |  8 ++++++
 lib/ngtcp2_pkt.c             | 48 ++++++++++++++++++++++++++++++++++++
 lib/ngtcp2_pkt.h             | 28 +++++++++++++++++++++
 tests/main.c                 |  2 ++
 tests/ngtcp2_pkt_test.c      | 22 +++++++++++++++++
 tests/ngtcp2_pkt_test.h      |  1 +
 7 files changed, 116 insertions(+)

diff --git a/examples/debug.cc b/examples/debug.cc
index 57d2605a..0678213b 100644
--- a/examples/debug.cc
+++ b/examples/debug.cc
@@ -137,6 +137,8 @@ std::string strframetype(uint8_t type) {
     return "STREAM_ID_NEEDED";
   case NGTCP2_FRAME_NEW_CONNECTION_ID:
     return "NEW_CONNECTION_ID";
+  case NGTCP2_FRAME_STOP_SENDING:
+    return "STOP_SENDING";
   case NGTCP2_FRAME_ACK:
     return "ACK";
   case NGTCP2_FRAME_STREAM:
@@ -282,6 +284,11 @@ void print_frame(ngtcp2_dir dir, const ngtcp2_frame *fr) {
     fprintf(outfile, "seq=%u conn_id=%016" PRIx64 "\n",
             fr->new_connection_id.seq, fr->new_connection_id.conn_id);
     break;
+  case NGTCP2_FRAME_STOP_SENDING:
+    print_indent();
+    fprintf(outfile, "stream_id=%08x error_code=%08x\n",
+            fr->stop_sending.stream_id, fr->stop_sending.error_code);
+    break;
   }
 }
 } // namespace
diff --git a/lib/includes/ngtcp2/ngtcp2.h b/lib/includes/ngtcp2/ngtcp2.h
index 0b41fef4..c5887bb7 100644
--- a/lib/includes/ngtcp2/ngtcp2.h
+++ b/lib/includes/ngtcp2/ngtcp2.h
@@ -223,6 +223,7 @@ typedef enum {
   NGTCP2_FRAME_STREAM_BLOCKED = 0x09,
   NGTCP2_FRAME_STREAM_ID_NEEDED = 0x0a,
   NGTCP2_FRAME_NEW_CONNECTION_ID = 0x0b,
+  NGTCP2_FRAME_STOP_SENDING = 0x0c,
   NGTCP2_FRAME_ACK = 0xa0,
   NGTCP2_FRAME_STREAM = 0xc0
 } ngtcp2_frame_type;
@@ -347,6 +348,12 @@ typedef struct {
   uint64_t conn_id;
 } ngtcp2_new_connection_id;
 
+typedef struct {
+  uint8_t type;
+  uint32_t stream_id;
+  uint32_t error_code;
+} ngtcp2_stop_sending;
+
 typedef union {
   uint8_t type;
   ngtcp2_stream stream;
@@ -362,6 +369,7 @@ typedef union {
   ngtcp2_stream_blocked stream_blocked;
   ngtcp2_stream_id_needed stream_id_needed;
   ngtcp2_new_connection_id new_connection_id;
+  ngtcp2_stop_sending stop_sending;
 } ngtcp2_frame;
 
 typedef enum {
diff --git a/lib/ngtcp2_pkt.c b/lib/ngtcp2_pkt.c
index 24113515..6e3c8663 100644
--- a/lib/ngtcp2_pkt.c
+++ b/lib/ngtcp2_pkt.c
@@ -291,6 +291,9 @@ ssize_t ngtcp2_pkt_decode_frame(ngtcp2_frame *dest, const uint8_t *payload,
   case NGTCP2_FRAME_NEW_CONNECTION_ID:
     return ngtcp2_pkt_decode_new_connection_id_frame(&dest->new_connection_id,
                                                      payload, payloadlen);
+  case NGTCP2_FRAME_STOP_SENDING:
+    return ngtcp2_pkt_decode_stop_sending_frame(&dest->stop_sending, payload,
+                                                payloadlen);
   default:
     return NGTCP2_ERR_INVALID_ARGUMENT;
   }
@@ -739,6 +742,29 @@ ssize_t ngtcp2_pkt_decode_new_connection_id_frame(
   return (ssize_t)len;
 }
 
+ssize_t ngtcp2_pkt_decode_stop_sending_frame(ngtcp2_stop_sending *dest,
+                                             const uint8_t *payload,
+                                             size_t payloadlen) {
+  size_t len = 1 + 4 + 4;
+  const uint8_t *p;
+
+  if (payloadlen < len) {
+    return NGTCP2_ERR_INVALID_ARGUMENT;
+  }
+
+  p = payload + 1;
+
+  dest->type = NGTCP2_FRAME_STOP_SENDING;
+  dest->stream_id = ngtcp2_get_uint32(p);
+  p += 4;
+  dest->error_code = ngtcp2_get_uint32(p);
+  p += 4;
+
+  assert((size_t)(p - payload) == len);
+
+  return (ssize_t)len;
+}
+
 ssize_t ngtcp2_pkt_encode_frame(uint8_t *out, size_t outlen,
                                 const ngtcp2_frame *fr) {
   switch (fr->type) {
@@ -774,6 +800,8 @@ ssize_t ngtcp2_pkt_encode_frame(uint8_t *out, size_t outlen,
   case NGTCP2_FRAME_NEW_CONNECTION_ID:
     return ngtcp2_pkt_encode_new_connection_id_frame(out, outlen,
                                                      &fr->new_connection_id);
+  case NGTCP2_FRAME_STOP_SENDING:
+    return ngtcp2_pkt_encode_stop_sending_frame(out, outlen, &fr->stop_sending);
   default:
     return NGTCP2_ERR_INVALID_ARGUMENT;
   }
@@ -1139,6 +1167,26 @@ ngtcp2_pkt_encode_new_connection_id_frame(uint8_t *out, size_t outlen,
   return (ssize_t)len;
 }
 
+ssize_t ngtcp2_pkt_encode_stop_sending_frame(uint8_t *out, size_t outlen,
+                                             const ngtcp2_stop_sending *fr) {
+  size_t len = 1 + 4 + 4;
+  uint8_t *p;
+
+  if (outlen < len) {
+    return NGTCP2_ERR_NOBUF;
+  }
+
+  p = out;
+
+  *p++ = NGTCP2_FRAME_STOP_SENDING;
+  p = ngtcp2_put_uint32be(p, fr->stream_id);
+  p = ngtcp2_put_uint32be(p, fr->error_code);
+
+  assert((size_t)(p - out) == len);
+
+  return (ssize_t)len;
+}
+
 int ngtcp2_pkt_verify(const uint8_t *pkt, size_t pktlen) {
   uint64_t a, b;
 
diff --git a/lib/ngtcp2_pkt.h b/lib/ngtcp2_pkt.h
index 847ae0bf..c2258785 100644
--- a/lib/ngtcp2_pkt.h
+++ b/lib/ngtcp2_pkt.h
@@ -348,6 +348,21 @@ ssize_t ngtcp2_pkt_decode_stream_id_needed_frame(ngtcp2_stream_id_needed *dest,
 ssize_t ngtcp2_pkt_decode_new_connection_id_frame(
     ngtcp2_new_connection_id *dest, const uint8_t *payload, size_t payloadlen);
 
+/*
+ * ngtcp2_pkt_decode_stop_sending_frame decodes STOP_SENDING frame
+ * from |payload| of length |payloadlen|.  The result is stored in the
+ * object pointed by |dest|.  STOP_SENDING frame must start at
+ * `payload[0]`.  This function finishes when it decodes one
+ * STOP_SENDING frame, and returns the exact number of bytes read to
+ * decode a frame if it succeeds, or one of the following negative
+ * error codes:
+ *
+ * NGTCP2_ERR_INVALID_ARGUMENT
+ *     Payload is too short to include STOP_SENDING frame.
+ */
+ssize_t ngtcp2_pkt_decode_stop_sending_frame(ngtcp2_stop_sending *dest,
+                                             const uint8_t *payload,
+                                             size_t payloadlen);
 /*
  * ngtcp2_pkt_encode_stream_frame encodes STREAM frame |fr| into the
  * buffer pointed by |out| of length |outlen|.
@@ -526,6 +541,19 @@ ssize_t
 ngtcp2_pkt_encode_new_connection_id_frame(uint8_t *out, size_t outlen,
                                           const ngtcp2_new_connection_id *fr);
 
+/*
+ * ngtcp2_pkt_encode_stop_sending_frame encodes STOP_SENDING frame
+ * |fr| into the buffer pointed by |out| of length |outlen|.
+ *
+ * This function returns the number of bytes written if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ *     Buffer does not have enough capacity to write a frame.
+ */
+ssize_t ngtcp2_pkt_encode_stop_sending_frame(uint8_t *out, size_t outlen,
+                                             const ngtcp2_stop_sending *fr);
+
 /*
  * ngtcp2_pkt_adjust_pkt_num find the full 64 bits packet number for
  * |pkt_num|, which is expected to be least significant |n| bits.  The
diff --git a/tests/main.c b/tests/main.c
index c2550f92..6d218fd7 100644
--- a/tests/main.c
+++ b/tests/main.c
@@ -95,6 +95,8 @@ int main() {
                    test_ngtcp2_pkt_encode_stream_id_needed_frame) ||
       !CU_add_test(pSuite, "pkt_encode_new_connection_id_frame",
                    test_ngtcp2_pkt_encode_new_connection_id_frame) ||
+      !CU_add_test(pSuite, "pkt_encode_stop_sending_frame",
+                   test_ngtcp2_pkt_encode_stop_sending_frame) ||
       !CU_add_test(pSuite, "pkt_adjust_pkt_num",
                    test_ngtcp2_pkt_adjust_pkt_num) ||
       !CU_add_test(pSuite, "pkt_validate_ack", test_ngtcp2_pkt_validate_ack) ||
diff --git a/tests/ngtcp2_pkt_test.c b/tests/ngtcp2_pkt_test.c
index 3ad44790..7cb0271f 100644
--- a/tests/ngtcp2_pkt_test.c
+++ b/tests/ngtcp2_pkt_test.c
@@ -794,6 +794,28 @@ void test_ngtcp2_pkt_encode_new_connection_id_frame(void) {
   CU_ASSERT(fr.conn_id == nfr.conn_id);
 }
 
+void test_ngtcp2_pkt_encode_stop_sending_frame(void) {
+  uint8_t buf[16];
+  ngtcp2_stop_sending fr, nfr;
+  ssize_t rv;
+  size_t framelen = 1 + 4 + 4;
+
+  fr.type = NGTCP2_FRAME_STOP_SENDING;
+  fr.stream_id = 0xf1f2f3f4u;
+  fr.error_code = 0xe1e2e3e4u;
+
+  rv = ngtcp2_pkt_encode_stop_sending_frame(buf, sizeof(buf), &fr);
+
+  CU_ASSERT((ssize_t)framelen == rv);
+
+  rv = ngtcp2_pkt_decode_stop_sending_frame(&nfr, buf, framelen);
+
+  CU_ASSERT((ssize_t)framelen == rv);
+  CU_ASSERT(fr.type == nfr.type);
+  CU_ASSERT(fr.stream_id == nfr.stream_id);
+  CU_ASSERT(fr.error_code == nfr.error_code);
+}
+
 void test_ngtcp2_pkt_adjust_pkt_num(void) {
   CU_ASSERT(0xaa831f94llu ==
             ngtcp2_pkt_adjust_pkt_num(0xaa82f30ellu, 0x1f94, 16));
diff --git a/tests/ngtcp2_pkt_test.h b/tests/ngtcp2_pkt_test.h
index 28318238..4ac80942 100644
--- a/tests/ngtcp2_pkt_test.h
+++ b/tests/ngtcp2_pkt_test.h
@@ -46,6 +46,7 @@ void test_ngtcp2_pkt_encode_blocked_frame(void);
 void test_ngtcp2_pkt_encode_stream_blocked_frame(void);
 void test_ngtcp2_pkt_encode_stream_id_needed_frame(void);
 void test_ngtcp2_pkt_encode_new_connection_id_frame(void);
+void test_ngtcp2_pkt_encode_stop_sending_frame(void);
 void test_ngtcp2_pkt_adjust_pkt_num(void);
 void test_ngtcp2_pkt_validate_ack(void);
 
-- 
GitLab