33import androidx .annotation .NonNull ;
44import androidx .annotation .Nullable ;
55
6- import java .io .BufferedReader ;
76import java .io .ByteArrayOutputStream ;
87import java .io .IOException ;
98import java .io .InputStream ;
10- import java .io .InputStreamReader ;
119import java .nio .charset .Charset ;
1210import java .nio .charset .StandardCharsets ;
1311import java .security .cert .Certificate ;
@@ -33,44 +31,41 @@ class RawHttpResponseParser {
3331 */
3432 @ NonNull
3533 public HttpResponse parseHttpResponse (@ NonNull InputStream inputStream , Certificate [] serverCertificates ) throws IOException {
36- try (BufferedReader reader = new BufferedReader (new InputStreamReader (inputStream , StandardCharsets .UTF_8 ))) {
37-
38- // 1. Read and parse status line
39- String statusLine = reader .readLine ();
40- if (statusLine == null ) {
41- throw new IOException ("No HTTP response received from server" );
42- }
43-
44- Logger .v ("Parsing HTTP status line: " + statusLine );
45- int statusCode = parseStatusCode (statusLine );
46-
47- // 2. Read and parse response headers
48- ParsedResponseHeaders responseHeaders = getParsedResponseHeaders (reader );
49-
50- // 3. Determine charset from Content-Type header
51- Charset bodyCharset = extractCharsetFromContentType (responseHeaders .mContentType );
52-
53- // 4. Read response body based on encoding type
54- String responseBody = readResponseBody (inputStream , responseHeaders .mIsChunked , bodyCharset , responseHeaders .mContentLength , responseHeaders .mConnectionClose );
55-
56- // 5. Create and return HttpResponse
57- if (responseBody != null && !responseBody .trim ().isEmpty ()) {
58- return new HttpResponseImpl (statusCode , responseBody , serverCertificates );
59- } else {
60- return new HttpResponseImpl (statusCode , serverCertificates );
61- }
34+ // 1. Read and parse status line
35+ String statusLine = readLineFromStream (inputStream );
36+ if (statusLine == null ) {
37+ throw new IOException ("No HTTP response received from server" );
38+ }
39+
40+ Logger .v ("Parsing HTTP status line: " + statusLine );
41+ int statusCode = parseStatusCode (statusLine );
42+
43+ // 2. Read and parse response headers directly (single-pass)
44+ ParsedResponseHeaders responseHeaders = parseHeadersDirectly (inputStream );
45+
46+ // 3. Determine charset from Content-Type header
47+ Charset bodyCharset = extractCharsetFromContentType (responseHeaders .mContentType );
48+
49+ // 4. Read response body using the same InputStream (correctly positioned after headers)
50+ String responseBody = readResponseBody (inputStream , responseHeaders .mIsChunked , bodyCharset , responseHeaders .mContentLength , responseHeaders .mConnectionClose );
51+
52+ // 5. Create and return HttpResponse
53+ if (responseBody != null && !responseBody .trim ().isEmpty ()) {
54+ return new HttpResponseImpl (statusCode , responseBody , serverCertificates );
55+ } else {
56+ return new HttpResponseImpl (statusCode , serverCertificates );
6257 }
6358 }
6459
6560 @ NonNull
66- private static ParsedResponseHeaders getParsedResponseHeaders ( BufferedReader reader ) throws IOException {
67- int contentLength = 0 ;
61+ private ParsedResponseHeaders parseHeadersDirectly ( @ NonNull InputStream inputStream ) throws IOException {
62+ int contentLength = - 1 ;
6863 boolean isChunked = false ;
6964 boolean connectionClose = false ;
7065 String contentType = null ;
7166 String headerLine ;
72-
73- while ((headerLine = reader . readLine ( )) != null && !headerLine .trim ().isEmpty ()) {
67+
68+ while ((headerLine = readLineFromStream ( inputStream )) != null && !headerLine .trim ().isEmpty ()) {
7469 Logger .v ("Parsing HTTP header: " + headerLine );
7570 int colonIndex = headerLine .indexOf (':' );
7671 if (colonIndex > 0 ) {
@@ -96,6 +91,8 @@ private static ParsedResponseHeaders getParsedResponseHeaders(BufferedReader rea
9691 return new ParsedResponseHeaders (contentLength , isChunked , connectionClose , contentType );
9792 }
9893
94+
95+
9996 @ Nullable
10097 private String readResponseBody (@ NonNull InputStream inputStream , boolean isChunked , Charset bodyCharset , int contentLength , boolean connectionClose ) throws IOException {
10198 String responseBody = null ;
@@ -239,6 +236,10 @@ private String readUntilCloseWithCharset(InputStream inputStream, Charset charse
239236 return new String (bodyBytes .toByteArray (), charset );
240237 }
241238
239+ /**
240+ * Reads a line from the input stream for chunked transfer encoding.
241+ * This is only used for reading chunk size lines and trailers in chunked responses.
242+ */
242243 private String readLineFromStream (InputStream inputStream ) throws IOException {
243244 ByteArrayOutputStream lineBytes = new ByteArrayOutputStream ();
244245 int b ;
0 commit comments