Add support for fuzzing cppdap

Add build rules, scripts, basic corpus, and dictionary.
Currently requires recent clang toolchain.
diff --git a/.gitignore b/.gitignore
index 7590faf..af8d40a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,6 @@
 build/
+fuzz/corpus
+fuzz/logs
 .vs/
 .vscode/settings.json
 CMakeSettings.json
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 9a85195..29baf54 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -18,6 +18,14 @@
             "args": []
         },
         {
+            "type": "cppdbg",
+            "request": "launch",
+            "name": "fuzzer (lldb)",
+            "program": "${workspaceFolder}/fuzz/build/cppdap-fuzzer",
+            "cwd": "${workspaceRoot}",
+            "args": []
+        },
+        {
             "name": "unittests (gdb)",
             "type": "cppdbg",
             "request": "launch",
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3231185..2e2f28f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -31,6 +31,7 @@
 option_if_not_defined(CPPDAP_WARNINGS_AS_ERRORS "Treat warnings as errors" OFF)
 option_if_not_defined(CPPDAP_BUILD_EXAMPLES "Build example applications" OFF)
 option_if_not_defined(CPPDAP_BUILD_TESTS "Build tests" OFF)
+option_if_not_defined(CPPDAP_BUILD_FUZZER "Build fuzzer" OFF)
 option_if_not_defined(CPPDAP_ASAN "Build dap with address sanitizer" OFF)
 option_if_not_defined(CPPDAP_MSAN "Build dap with memory sanitizer" OFF)
 option_if_not_defined(CPPDAP_TSAN "Build dap with thread sanitizer" OFF)
@@ -223,6 +224,26 @@
     target_link_libraries(cppdap-unittests cppdap "${CPPDAP_OS_LIBS}")
 endif(CPPDAP_BUILD_TESTS)
 
+# fuzzer
+if(CPPDAP_BUILD_FUZZER)
+    if (NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
+        message(FATAL_ERROR "CPPDAP_BUILD_FUZZER can currently only be used with the clang toolchain")
+    endif()
+    set(DAP_FUZZER_LIST
+        ${CPPDAP_LIST}
+        ${CMAKE_CURRENT_SOURCE_DIR}/fuzz/fuzz.cpp
+    )
+    add_executable(cppdap-fuzzer ${DAP_FUZZER_LIST})
+    target_compile_options(cppdap-fuzzer PUBLIC "-fsanitize=fuzzer,address")
+    target_link_libraries(cppdap-fuzzer "-fsanitize=fuzzer,address")
+    target_include_directories(cppdap-fuzzer PUBLIC
+        ${CPPDAP_INCLUDE_DIR}
+        ${CPPDAP_SRC_DIR}
+        ${CPPDAP_JSON_DIR}/include/
+    )
+    target_link_libraries(cppdap-fuzzer cppdap "${CPPDAP_OS_LIBS}")
+endif(CPPDAP_BUILD_FUZZER)
+
 # examples
 if(CPPDAP_BUILD_EXAMPLES)
     function(build_example target)
diff --git a/fuzz/dictionary.txt b/fuzz/dictionary.txt
new file mode 100644
index 0000000..bb30ef9
--- /dev/null
+++ b/fuzz/dictionary.txt
@@ -0,0 +1,225 @@
+"accessType"
+"adapterData"
+"adapterID"
+"additionalModuleColumns"
+"address"
+"addressRange"
+"algorithm"
+"allThreadsContinued"
+"allThreadsStopped"
+"args"
+"arguments"
+"attach"
+"attributeName"
+"attributes"
+"breakMode"
+"breakpoint"
+"breakpointLocations"
+"breakpoints"
+"cancel"
+"cancellable"
+"capabilities"
+"category"
+"checksum"
+"checksums"
+"clientID"
+"clientName"
+"column"
+"columnsStartAt1"
+"command"
+"completions"
+"completionTriggerCharacters"
+"condition"
+"configurationDone"
+"context"
+"continue"
+"continued"
+"count"
+"cwd"
+"data"
+"dataBreakpointInfo"
+"dataId"
+"dateTimeStamp"
+"default"
+"description"
+"disassemble"
+"disconnect"
+"endColumn"
+"endLine"
+"env"
+"evaluate"
+"evaluateName"
+"exceptionBreakpointFilters"
+"exceptionInfo"
+"exceptionOptions"
+"exitCode"
+"exited"
+"expensive"
+"expression"
+"expression"
+"filter"
+"filter"
+"filters"
+"format"
+"frameId"
+"fullTypeName"
+"goto"
+"gotoTargets"
+"group"
+"hex"
+"hitCondition"
+"id"
+"includeAll"
+"indexedVariables"
+"indexedVariables"
+"initialized"
+"innerException"
+"instruction"
+"instructionBytes"
+"instructionCount"
+"instructionOffset"
+"instructionPointerReference"
+"isLocalProcess"
+"isOptimized"
+"isUserCode"
+"kind"
+"label"
+"launch"
+"length"
+"levels"
+"line"
+"lines"
+"linesStartAt1"
+"loadedSource"
+"loadedSources"
+"locale"
+"location"
+"logMessage"
+"memoryReference"
+"message"
+"module"
+"moduleCount"
+"moduleId"
+"modules"
+"name"
+"namedVariables"
+"names"
+"negate"
+"next"
+"noDebug"
+"offset"
+"origin"
+"output"
+"parameterNames"
+"parameters"
+"parameterTypes"
+"parameterValues"
+"path"
+"pathFormat"
+"pause"
+"percentage"
+"pointerSize"
+"presentationHint"
+"preserveFocusHint"
+"process"
+"progressEnd"
+"progressId"
+"progressStart"
+"progressUpdate"
+"readMemory"
+"reason"
+"requestId"
+"resolveSymbols"
+"restart"
+"restartFrame"
+"reverseContinue"
+"runInTerminal"
+"scopes"
+"selectionLength"
+"selectionStart"
+"sendTelemetry"
+"seq"
+"setBreakpoints"
+"setDataBreakpoints"
+"setExceptionBreakpoints"
+"setExpression"
+"setFunctionBreakpoints"
+"setVariable"
+"showUser"
+"sortText"
+"source"
+"sourceModified"
+"sourceReference"
+"sources"
+"stackTrace"
+"start"
+"startFrame"
+"startMethod"
+"startModule"
+"stepBack"
+"stepIn"
+"stepInTargets"
+"stepOut"
+"stopped"
+"supportedChecksumAlgorithms"
+"supportsBreakpointLocationsRequest"
+"supportsCancelRequest"
+"supportsClipboardContext"
+"supportsCompletionsRequest"
+"supportsConditionalBreakpoints"
+"supportsConfigurationDoneRequest"
+"supportsDataBreakpoints"
+"supportsDelayedStackTraceLoading"
+"supportsDisassembleRequest"
+"supportsEvaluateForHovers"
+"supportsExceptionInfoRequest"
+"supportsExceptionOptions"
+"supportsFunctionBreakpoints"
+"supportsGotoTargetsRequest"
+"supportsHitConditionalBreakpoints"
+"supportsLoadedSourcesRequest"
+"supportsLogPoints"
+"supportsMemoryReferences"
+"supportsModulesRequest"
+"supportsProgressReporting"
+"supportsReadMemoryRequest"
+"supportsRestartFrame"
+"supportsRestartRequest"
+"supportsRunInTerminalRequest"
+"supportsSetExpression"
+"supportsSetVariable"
+"supportsStepBack"
+"supportsStepInTargetsRequest"
+"supportsTerminateRequest"
+"supportsTerminateThreadsRequest"
+"supportsValueFormattingOptions"
+"supportsVariablePaging"
+"supportsVariableType"
+"supportTerminateDebuggee"
+"symbol"
+"symbolFilePath"
+"symbolStatus"
+"systemProcessId"
+"targetId"
+"terminate"
+"terminated"
+"terminateDebuggee"
+"terminateThreads"
+"text"
+"thread"
+"threadId"
+"threadIds"
+"threads"
+"title"
+"title"
+"type"
+"typeName"
+"url"
+"urlLabel"
+"value"
+"variables"
+"variablesReference"
+"verified"
+"version"
+"visibility"
+"width"
\ No newline at end of file
diff --git a/fuzz/fuzz.cpp b/fuzz/fuzz.cpp
new file mode 100644
index 0000000..6f459be
--- /dev/null
+++ b/fuzz/fuzz.cpp
@@ -0,0 +1,173 @@
+// Copyright 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// cppdap fuzzer program.
+// Run with: ${CPPDAP_PATH}/fuzz/run.sh
+// Requires modern clang toolchain.
+
+#include "content_stream.h"
+#include "string_buffer.h"
+
+#include "dap/protocol.h"
+#include "dap/session.h"
+
+#include <condition_variable>
+#include <mutex>
+
+namespace {
+
+// Event provides a basic wait and signal synchronization primitive.
+class Event {
+ public:
+  // wait() blocks until the event is fired or the given timeout is reached.
+  template <typename DURATION>
+  inline void wait(const DURATION& duration) {
+    std::unique_lock<std::mutex> lock(mutex);
+    cv.wait_for(lock, duration, [&] { return fired; });
+  }
+
+  // fire() sets signals the event, and unblocks any calls to wait().
+  inline void fire() {
+    std::unique_lock<std::mutex> lock(mutex);
+    fired = true;
+    cv.notify_all();
+  }
+
+ private:
+  std::mutex mutex;
+  std::condition_variable cv;
+  bool fired = false;
+};
+
+}  // namespace
+
+// List of requests that we handle for fuzzing.
+#define DAP_REQUEST_LIST()                                                     \
+  DAP_REQUEST(dap::AttachRequest, dap::AttachResponse)                         \
+  DAP_REQUEST(dap::BreakpointLocationsRequest,                                 \
+              dap::BreakpointLocationsResponse)                                \
+  DAP_REQUEST(dap::CancelRequest, dap::CancelResponse)                         \
+  DAP_REQUEST(dap::CompletionsRequest, dap::CompletionsResponse)               \
+  DAP_REQUEST(dap::ConfigurationDoneRequest, dap::ConfigurationDoneResponse)   \
+  DAP_REQUEST(dap::ContinueRequest, dap::ContinueResponse)                     \
+  DAP_REQUEST(dap::DataBreakpointInfoRequest, dap::DataBreakpointInfoResponse) \
+  DAP_REQUEST(dap::DisassembleRequest, dap::DisassembleResponse)               \
+  DAP_REQUEST(dap::DisconnectRequest, dap::DisconnectResponse)                 \
+  DAP_REQUEST(dap::EvaluateRequest, dap::EvaluateResponse)                     \
+  DAP_REQUEST(dap::ExceptionInfoRequest, dap::ExceptionInfoResponse)           \
+  DAP_REQUEST(dap::GotoRequest, dap::GotoResponse)                             \
+  DAP_REQUEST(dap::GotoTargetsRequest, dap::GotoTargetsResponse)               \
+  DAP_REQUEST(dap::InitializeRequest, dap::InitializeResponse)                 \
+  DAP_REQUEST(dap::LaunchRequest, dap::LaunchResponse)                         \
+  DAP_REQUEST(dap::LoadedSourcesRequest, dap::LoadedSourcesResponse)           \
+  DAP_REQUEST(dap::ModulesRequest, dap::ModulesResponse)                       \
+  DAP_REQUEST(dap::NextRequest, dap::NextResponse)                             \
+  DAP_REQUEST(dap::PauseRequest, dap::PauseResponse)                           \
+  DAP_REQUEST(dap::ReadMemoryRequest, dap::ReadMemoryResponse)                 \
+  DAP_REQUEST(dap::RestartFrameRequest, dap::RestartFrameResponse)             \
+  DAP_REQUEST(dap::RestartRequest, dap::RestartResponse)                       \
+  DAP_REQUEST(dap::ReverseContinueRequest, dap::ReverseContinueResponse)       \
+  DAP_REQUEST(dap::RunInTerminalRequest, dap::RunInTerminalResponse)           \
+  DAP_REQUEST(dap::ScopesRequest, dap::ScopesResponse)                         \
+  DAP_REQUEST(dap::SetBreakpointsRequest, dap::SetBreakpointsResponse)         \
+  DAP_REQUEST(dap::SetDataBreakpointsRequest, dap::SetDataBreakpointsResponse) \
+  DAP_REQUEST(dap::SetExceptionBreakpointsRequest,                             \
+              dap::SetExceptionBreakpointsResponse)                            \
+  DAP_REQUEST(dap::SetExpressionRequest, dap::SetExpressionResponse)           \
+  DAP_REQUEST(dap::SetFunctionBreakpointsRequest,                              \
+              dap::SetFunctionBreakpointsResponse)                             \
+  DAP_REQUEST(dap::SetVariableRequest, dap::SetVariableResponse)               \
+  DAP_REQUEST(dap::SourceRequest, dap::SourceResponse)                         \
+  DAP_REQUEST(dap::StackTraceRequest, dap::StackTraceResponse)                 \
+  DAP_REQUEST(dap::StepBackRequest, dap::StepBackResponse)                     \
+  DAP_REQUEST(dap::StepInRequest, dap::StepInResponse)                         \
+  DAP_REQUEST(dap::StepInTargetsRequest, dap::StepInTargetsResponse)           \
+  DAP_REQUEST(dap::StepOutRequest, dap::StepOutResponse)                       \
+  DAP_REQUEST(dap::TerminateRequest, dap::TerminateResponse)                   \
+  DAP_REQUEST(dap::TerminateThreadsRequest, dap::TerminateThreadsResponse)     \
+  DAP_REQUEST(dap::ThreadsRequest, dap::ThreadsResponse)                       \
+  DAP_REQUEST(dap::VariablesRequest, dap::VariablesResponse)
+
+// Fuzzing main function.
+// See http://llvm.org/docs/LibFuzzer.html for details.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  // The first byte can optionally control fuzzing mode.
+  enum class ControlMode {
+    // Don't wrap the input data with a stream writer. Allows testing for stream
+    // writing.
+    TestStreamWriter,
+
+    // Don't append a 'done' request. This may cause the test to take longer to
+    // complete (it may have to block on a timeout), but exercises the
+    // unrecognised-message cases.
+    DontAppendDoneRequest,
+
+    // Number of control modes in this enum.
+    Count,
+  };
+
+  // Scan first byte for control mode.
+  bool useContentStreamWriter = true;
+  bool appendDoneRequest = true;
+  if (size > 0 && data[0] < static_cast<uint8_t>(ControlMode::Count)) {
+    useContentStreamWriter =
+        data[0] != static_cast<uint8_t>(ControlMode::TestStreamWriter);
+    appendDoneRequest =
+        data[0] != static_cast<uint8_t>(ControlMode::DontAppendDoneRequest);
+    data++;
+    size--;
+  }
+
+  // in contains the input data
+  auto in = std::make_shared<dap::StringBuffer>();
+
+  dap::ContentWriter writer(in);
+  if (useContentStreamWriter) {
+    writer.write(std::string(reinterpret_cast<const char*>(data), size));
+  } else {
+    in->write(data, size);
+  }
+
+  if (appendDoneRequest) {
+    writer.write(R"(
+  {
+      "seq": 10,
+      "type": "request",
+      "command": "done",
+  }
+    )");
+  }
+
+  // Each test is done if we receive a request, or report an error.
+  Event requestOrError;
+
+#define DAP_REQUEST(REQUEST, RESPONSE)           \
+  session->registerHandler([&](const REQUEST&) { \
+    requestOrError.fire();                       \
+    return RESPONSE{};                           \
+  });
+
+  auto session = dap::Session::create();
+  DAP_REQUEST_LIST();
+
+  session->onError([&](const char*) { requestOrError.fire(); });
+
+  auto out = std::make_shared<dap::StringBuffer>();
+  session->bind(dap::ReaderWriter::create(in, out));
+
+  // Give up after a second if we don't get a request or error reported.
+  requestOrError.wait(std::chrono::seconds(1));
+
+  return 0;
+}
\ No newline at end of file
diff --git a/fuzz/run.sh b/fuzz/run.sh
new file mode 100755
index 0000000..2d54039
--- /dev/null
+++ b/fuzz/run.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+set -e # Fail on any error.
+
+FUZZ_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd )"
+cd ${FUZZ_DIR}
+
+# Ensure we're testing with latest build
+[ ! -d "build" ] && mkdir "build"
+cd "build"
+cmake ../.. -GNinja -DCPPDAP_BUILD_FUZZER=1 -DCMAKE_BUILD_TYPE=RelWithDebInfo
+ninja
+
+cd ${FUZZ_DIR}
+[ ! -d "corpus" ] && mkdir "corpus"
+[ ! -d "logs" ] && mkdir "logs"
+cd "logs"
+rm crash-* fuzz-* || true
+${FUZZ_DIR}/build/cppdap-fuzzer ${FUZZ_DIR}/corpus ${FUZZ_DIR}/seed -dict=${FUZZ_DIR}/dictionary.txt -jobs=128
diff --git a/fuzz/seed/empty_json b/fuzz/seed/empty_json
new file mode 100644
index 0000000..9e26dfe
--- /dev/null
+++ b/fuzz/seed/empty_json
@@ -0,0 +1 @@
+{}
\ No newline at end of file
diff --git a/fuzz/seed/request b/fuzz/seed/request
new file mode 100644
index 0000000..7a183ad
--- /dev/null
+++ b/fuzz/seed/request
@@ -0,0 +1,8 @@
+{
+    "seq": 153,
+    "type": "request",
+    "command": "next",
+    "arguments": {
+        "threadId": 3
+    }
+}
\ No newline at end of file
diff --git a/include/dap/io.h b/include/dap/io.h
index e1f378d..22a638f 100644
--- a/include/dap/io.h
+++ b/include/dap/io.h
@@ -52,6 +52,7 @@
 
 // ReaderWriter is an interface that combines the Reader and Writer interfaces.
 class ReaderWriter : public Reader, public Writer {
+ public:
   // create() returns a ReaderWriter that delegates the interface methods on to
   // the provided Reader and Writer.
   // isOpen() returns true if the Reader and Writer both return true for