attempt to parse invalid protos as strings (#8)

diff --git a/lib/src/driver/driver_connection.dart b/lib/src/driver/driver_connection.dart
index 787fcfd..b170586 100644
--- a/lib/src/driver/driver_connection.dart
+++ b/lib/src/driver/driver_connection.dart
@@ -3,10 +3,12 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'dart:async';
+import 'dart:convert';
 import 'dart:io';
 
 import '../async_message_grouper.dart';
 import '../worker_protocol.pb.dart';
+import '../constants.dart';
 import '../utils.dart';
 
 /// A connection from a `BazelWorkerDriver` to a worker.
@@ -35,12 +37,35 @@
       new StdDriverConnection(
           inputStream: worker.stdout, outputStream: worker.stdin);
 
+  /// Note: This will attempts to recover from invalid proto messages by parsing
+  /// them as strings. This is a common error case for workers (they print a
+  /// message to stdout on accident). This isn't perfect however as it only
+  /// happens if the parsing throws, you can still hang indefinitely if the
+  /// [MessageGrouper] doesn't find what it thinks is the end of a proto
+  /// message.
   @override
   Future<WorkResponse> readResponse() async {
     var buffer = await _messageGrouper.next;
     if (buffer == null) return null;
 
-    return new WorkResponse.fromBuffer(buffer);
+    WorkResponse response;
+    try {
+      response = new WorkResponse.fromBuffer(buffer);
+    } catch (_) {
+      try {
+        // Try parsing the message as a string and set that as the output.
+        var output = UTF8.decode(buffer);
+        var response = new WorkResponse()
+          ..exitCode = EXIT_CODE_ERROR
+          ..output = 'Worker sent an invalid response:\n$output';
+        return response;
+      } catch (_) {
+        // Fall back to original exception and rethrow if we fail to parse as
+        // a string.
+      }
+      rethrow;
+    }
+    return response;
   }
 
   @override