11package com .mindee .parsing ;
22
33import java .io .BufferedReader ;
4+ import java .io .ByteArrayInputStream ;
45import java .io .File ;
56import java .io .IOException ;
67import java .io .InputStream ;
78import java .io .InputStreamReader ;
9+ import java .io .Reader ;
10+ import java .io .StringReader ;
11+ import java .io .UncheckedIOException ;
812import java .nio .charset .StandardCharsets ;
913import java .nio .file .Files ;
1014import java .nio .file .Path ;
1115import java .security .InvalidKeyException ;
16+ import java .security .MessageDigest ;
1217import java .security .NoSuchAlgorithmException ;
13- import java .util .stream .Collectors ;
14- import java .util .stream .Stream ;
1518import javax .crypto .Mac ;
1619import javax .crypto .spec .SecretKeySpec ;
1720import lombok .Getter ;
@@ -25,25 +28,41 @@ public abstract class BaseLocalResponse {
2528 protected final byte [] file ;
2629
2730 /**
28- * Load from an {@link InputStream} .
31+ * Load from a string .
2932 *
3033 * @param input will be decoded as UTF-8.
3134 */
32- public BaseLocalResponse (InputStream input ) {
33- this .file = this
34- .getBytes (new BufferedReader (new InputStreamReader (input , StandardCharsets .UTF_8 )).lines ());
35+ public BaseLocalResponse (String input ) {
36+ if (input == null ) {
37+ throw new IllegalArgumentException ("Input string cannot be null." );
38+ }
39+ this .file = readToCleanUtf8Bytes (new StringReader (input ));
40+ }
41+
42+ /**
43+ * Load from a byte array.
44+ *
45+ * @param input assumes UTF-8 encoding.
46+ */
47+ public BaseLocalResponse (byte [] input ) {
48+ if (input == null ) {
49+ throw new IllegalArgumentException ("Input byte array cannot be null." );
50+ }
51+ this .file = readToCleanUtf8Bytes (
52+ new InputStreamReader (new ByteArrayInputStream (input ), StandardCharsets .UTF_8 )
53+ );
3554 }
3655
3756 /**
38- * Load from a {@link String }.
57+ * Load from an {@link InputStream }.
3958 *
4059 * @param input will be decoded as UTF-8.
4160 */
42- public BaseLocalResponse (String input ) {
43- if (input == null || input . isEmpty () ) {
44- throw new IllegalArgumentException ("Input string cannot be empty or null." );
61+ public BaseLocalResponse (InputStream input ) {
62+ if (input == null ) {
63+ throw new IllegalArgumentException ("Input stream cannot be null." );
4564 }
46- this .file = input . getBytes ( StandardCharsets .UTF_8 );
65+ this .file = readToCleanUtf8Bytes ( new InputStreamReader ( input , StandardCharsets .UTF_8 ) );
4766 }
4867
4968 /**
@@ -52,7 +71,12 @@ public BaseLocalResponse(String input) {
5271 * @param input will be decoded as UTF-8.
5372 */
5473 public BaseLocalResponse (File input ) throws IOException {
55- this .file = this .getBytes (Files .lines (input .toPath (), StandardCharsets .UTF_8 ));
74+ if (input == null ) {
75+ throw new IllegalArgumentException ("Input file cannot be null." );
76+ }
77+ this .file = readToCleanUtf8Bytes (
78+ Files .newBufferedReader (input .toPath (), StandardCharsets .UTF_8 )
79+ );
5680 }
5781
5882 /**
@@ -61,11 +85,39 @@ public BaseLocalResponse(File input) throws IOException {
6185 * @param input will be decoded as UTF-8.
6286 */
6387 public BaseLocalResponse (Path input ) throws IOException {
64- this .file = this .getBytes (Files .lines (input , StandardCharsets .UTF_8 ));
88+ if (input == null ) {
89+ throw new IllegalArgumentException ("Input path cannot be null." );
90+ }
91+ this .file = readToCleanUtf8Bytes (Files .newBufferedReader (input , StandardCharsets .UTF_8 ));
6592 }
6693
67- private byte [] getBytes (Stream <String > stream ) {
68- return stream .collect (Collectors .joining ("" )).getBytes ();
94+ /**
95+ * Read, remove line endings, transform to UTF-8 bytes.
96+ */
97+ private static byte [] readToCleanUtf8Bytes (Reader reader ) {
98+ try (
99+ BufferedReader bufferedReader = (reader instanceof BufferedReader )
100+ ? (BufferedReader ) reader
101+ : new BufferedReader (reader )
102+ ) {
103+
104+ StringBuilder stringBuilder = new StringBuilder ();
105+ String line ;
106+
107+ while ((line = bufferedReader .readLine ()) != null ) {
108+ stringBuilder .append (line );
109+ }
110+
111+ String cleanJson = stringBuilder .toString ();
112+
113+ if (cleanJson .trim ().isEmpty ()) {
114+ throw new IllegalArgumentException ("Input cannot be empty or contain only whitespace." );
115+ }
116+
117+ return cleanJson .getBytes (StandardCharsets .UTF_8 );
118+ } catch (IOException e ) {
119+ throw new UncheckedIOException ("Failed to read input response payload." , e );
120+ }
69121 }
70122
71123 /**
@@ -84,7 +136,6 @@ public String getHmacSignature(String secretKey) {
84136 try {
85137 mac = Mac .getInstance (algorithm );
86138 } catch (NoSuchAlgorithmException err ) {
87- // this should never happen as the algorithm is hard-coded.
88139 return "" ;
89140 }
90141 try {
@@ -103,6 +154,22 @@ public String getHmacSignature(String secretKey) {
103154 * @return true if the signatures match.
104155 */
105156 public boolean isValidHmacSignature (String secretKey , String signature ) {
106- return signature .equals (getHmacSignature (secretKey ));
157+ if (signature == null || secretKey == null ) {
158+ return false ;
159+ }
160+ byte [] expectedBytes = getHmacSignature (secretKey ).getBytes (StandardCharsets .UTF_8 );
161+ byte [] actualBytes = signature
162+ .toLowerCase (java .util .Locale .ROOT )
163+ .getBytes (StandardCharsets .UTF_8 );
164+
165+ return MessageDigest .isEqual (expectedBytes , actualBytes );
166+ }
167+
168+ /**
169+ * Print the file as a UTF-8 string.
170+ */
171+ @ Override
172+ public String toString () {
173+ return new String (this .file , StandardCharsets .UTF_8 );
107174 }
108175}
0 commit comments