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