• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.android.hotspot2.utils;
2 
3 import android.util.Base64;
4 import android.util.Log;
5 
6 import com.android.hotspot2.osu.OSUManager;
7 
8 import java.io.ByteArrayInputStream;
9 import java.io.EOFException;
10 import java.io.IOException;
11 import java.io.InputStream;
12 import java.nio.ByteBuffer;
13 import java.nio.charset.Charset;
14 import java.nio.charset.StandardCharsets;
15 import java.util.Arrays;
16 import java.util.Collections;
17 import java.util.Iterator;
18 import java.util.LinkedHashMap;
19 import java.util.Map;
20 
21 public class HTTPResponse implements HTTPMessage {
22     private final int mStatusCode;
23     private final Map<String, String> mHeaders = new LinkedHashMap<>();
24     private final ByteBuffer mBody;
25 
26     private static final String csIndicator = "charset=";
27 
HTTPResponse(InputStream in)28     public HTTPResponse(InputStream in) throws IOException {
29         int expected = Integer.MAX_VALUE;
30         int offset = 0;
31         int body = -1;
32         byte[] input = new byte[RX_BUFFER];
33 
34         int statusCode = -1;
35         int bodyPattern = 0;
36 
37         while (offset < expected) {
38             int amount = in.read(input, offset, input.length - offset);
39             Log.d(OSUManager.TAG, String.format("Reading into %d from %d, amount %d -> %d",
40                     input.length, offset, input.length - offset, amount));
41             if (amount < 0) {
42                 throw new EOFException();
43             }
44             //Log.d("ZXZ", "HTTP response: '"
45             // + new String(input, 0, offset + amount, StandardCharsets.ISO_8859_1));
46 
47             if (body < 0) {
48                 for (int n = offset; n < offset + amount; n++) {
49                     bodyPattern = (bodyPattern << 8) | (input[n] & 0xff);
50                     if (bodyPattern == 0x0d0a0d0a) {
51                         body = n + 1;
52                         statusCode = parseHeader(input, body, mHeaders);
53                         expected = calculateLength(body, mHeaders);
54                         if (expected > input.length) {
55                             input = Arrays.copyOf(input, expected);
56                         }
57                         break;
58                     }
59                 }
60             }
61             offset += amount;
62             if (offset < expected && offset == input.length) {
63                 input = Arrays.copyOf(input, input.length * 2);
64             }
65         }
66         mStatusCode = statusCode;
67         mBody = ByteBuffer.wrap(input, body, expected - body);
68     }
69 
parseHeader(byte[] input, int body, Map<String, String> headers)70     private static int parseHeader(byte[] input, int body, Map<String, String> headers)
71             throws IOException {
72         String headerText = new String(input, 0, body - BODY_SEPARATOR_LENGTH,
73                 StandardCharsets.ISO_8859_1);
74         //System.out.println("Received header: " + headerText);
75         Iterator<String> headerLines = Arrays.asList(headerText.split(CRLF)).iterator();
76         if (!headerLines.hasNext()) {
77             throw new IOException("Bad HTTP Request");
78         }
79 
80         int statusCode;
81         String line0 = headerLines.next();
82         String[] status = line0.split(" ");
83         if (status.length != 3 || !"HTTP/1.1".equals(status[0])) {
84             throw new IOException("Bad HTTP Result: " + line0);
85         }
86         try {
87             statusCode = Integer.parseInt(status[1].trim());
88         } catch (NumberFormatException nfe) {
89             throw new IOException("Bad HTTP header line: '" + line0 + "'");
90         }
91 
92         while (headerLines.hasNext()) {
93             String line = headerLines.next();
94             int keyEnd = line.indexOf(':');
95             if (keyEnd < 0) {
96                 throw new IOException("Bad header line: '" + line + "'");
97             }
98             String key = line.substring(0, keyEnd).trim();
99             String value = line.substring(keyEnd + 1).trim();
100             headers.put(key, value);
101         }
102         return statusCode;
103     }
104 
calculateLength(int body, Map<String, String> headers)105     private static int calculateLength(int body, Map<String, String> headers) throws IOException {
106         String contentLength = headers.get(LengthHeader);
107         if (contentLength == null) {
108             throw new IOException("No " + LengthHeader);
109         }
110         try {
111             return body + Integer.parseInt(contentLength);
112         } catch (NumberFormatException nfe) {
113             throw new IOException("Bad " + LengthHeader + ": " + contentLength);
114         }
115     }
116 
getStatusCode()117     public int getStatusCode() {
118         return mStatusCode;
119     }
120 
121     @Override
getHeaders()122     public Map<String, String> getHeaders() {
123         return Collections.unmodifiableMap(mHeaders);
124     }
125 
getHeader(String key)126     public String getHeader(String key) {
127         return mHeaders.get(key);
128     }
129 
130     @Override
getPayloadStream()131     public InputStream getPayloadStream() {
132         return new ByteArrayInputStream(mBody.array(), mBody.position(),
133                 mBody.limit() - mBody.position());
134     }
135 
136     @Override
getPayload()137     public ByteBuffer getPayload() {
138         return mBody.duplicate();
139     }
140 
141     @Override
getBinaryPayload()142     public ByteBuffer getBinaryPayload() {
143         byte[] data = new byte[mBody.remaining()];
144         mBody.duplicate().get(data);
145         byte[] binary = Base64.decode(data, Base64.DEFAULT);
146         return ByteBuffer.wrap(binary);
147     }
148 
149     @Override
toString()150     public String toString() {
151         StringBuilder sb = new StringBuilder();
152         sb.append("Status: ").append(mStatusCode).append(CRLF);
153         for (Map.Entry<String, String> entry : mHeaders.entrySet()) {
154             sb.append(entry.getKey()).append(": ").append(entry.getValue()).append(CRLF);
155         }
156         sb.append(CRLF);
157         Charset charset;
158         try {
159             charset = Charset.forName(getCharset());
160         } catch (IllegalArgumentException iae) {
161             charset = StandardCharsets.ISO_8859_1;
162         }
163         sb.append(new String(mBody.array(), mBody.position(),
164                 mBody.limit() - mBody.position(), charset));
165         return sb.toString();
166     }
167 
getCharset()168     public String getCharset() {
169         String contentType = mHeaders.get(ContentTypeHeader);
170         if (contentType == null) {
171             return null;
172         }
173         int csPos = contentType.indexOf(csIndicator);
174         return csPos < 0 ? null : contentType.substring(csPos + csIndicator.length()).trim();
175     }
176 
equals(byte[] b1, int offset, byte[] pattern)177     private static boolean equals(byte[] b1, int offset, byte[] pattern) {
178         for (int n = 0; n < pattern.length; n++) {
179             if (b1[n + offset] != pattern[n]) {
180                 return false;
181             }
182         }
183         return true;
184     }
185 }
186