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 if (amount < 0) { 40 throw new EOFException(); 41 } 42 43 if (body < 0) { 44 for (int n = offset; n < offset + amount; n++) { 45 bodyPattern = (bodyPattern << 8) | (input[n] & 0xff); 46 if (bodyPattern == 0x0d0a0d0a) { 47 body = n + 1; 48 statusCode = parseHeader(input, body, mHeaders); 49 expected = calculateLength(body, mHeaders); 50 if (expected > input.length) { 51 input = Arrays.copyOf(input, expected); 52 } 53 break; 54 } 55 } 56 } 57 offset += amount; 58 if (offset < expected && offset == input.length) { 59 input = Arrays.copyOf(input, input.length * 2); 60 } 61 } 62 mStatusCode = statusCode; 63 mBody = ByteBuffer.wrap(input, body, expected - body); 64 } 65 parseHeader(byte[] input, int body, Map<String, String> headers)66 private static int parseHeader(byte[] input, int body, Map<String, String> headers) 67 throws IOException { 68 String headerText = new String(input, 0, body - BODY_SEPARATOR_LENGTH, 69 StandardCharsets.ISO_8859_1); 70 //System.out.println("Received header: " + headerText); 71 Iterator<String> headerLines = Arrays.asList(headerText.split(CRLF)).iterator(); 72 if (!headerLines.hasNext()) { 73 throw new IOException("Bad HTTP Request"); 74 } 75 76 int statusCode; 77 String line0 = headerLines.next(); 78 String[] status = line0.split(" "); 79 if (status.length != 3 || !"HTTP/1.1".equals(status[0])) { 80 throw new IOException("Bad HTTP Result: " + line0); 81 } 82 try { 83 statusCode = Integer.parseInt(status[1].trim()); 84 } catch (NumberFormatException nfe) { 85 throw new IOException("Bad HTTP header line: '" + line0 + "'"); 86 } 87 88 while (headerLines.hasNext()) { 89 String line = headerLines.next(); 90 int keyEnd = line.indexOf(':'); 91 if (keyEnd < 0) { 92 throw new IOException("Bad header line: '" + line + "'"); 93 } 94 String key = line.substring(0, keyEnd).trim(); 95 String value = line.substring(keyEnd + 1).trim(); 96 headers.put(key, value); 97 } 98 return statusCode; 99 } 100 calculateLength(int body, Map<String, String> headers)101 private static int calculateLength(int body, Map<String, String> headers) throws IOException { 102 String contentLength = headers.get(LengthHeader); 103 if (contentLength == null) { 104 throw new IOException("No " + LengthHeader); 105 } 106 try { 107 return body + Integer.parseInt(contentLength); 108 } catch (NumberFormatException nfe) { 109 throw new IOException("Bad " + LengthHeader + ": " + contentLength); 110 } 111 } 112 getStatusCode()113 public int getStatusCode() { 114 return mStatusCode; 115 } 116 117 @Override getHeaders()118 public Map<String, String> getHeaders() { 119 return Collections.unmodifiableMap(mHeaders); 120 } 121 getHeader(String key)122 public String getHeader(String key) { 123 return mHeaders.get(key); 124 } 125 126 @Override getPayloadStream()127 public InputStream getPayloadStream() { 128 return new ByteArrayInputStream(mBody.array(), mBody.position(), 129 mBody.limit() - mBody.position()); 130 } 131 132 @Override getPayload()133 public ByteBuffer getPayload() { 134 return mBody.duplicate(); 135 } 136 137 @Override getBinaryPayload()138 public ByteBuffer getBinaryPayload() { 139 byte[] data = new byte[mBody.remaining()]; 140 mBody.duplicate().get(data); 141 byte[] binary = Base64.decode(data, Base64.DEFAULT); 142 return ByteBuffer.wrap(binary); 143 } 144 145 @Override toString()146 public String toString() { 147 StringBuilder sb = new StringBuilder(); 148 sb.append("Status: ").append(mStatusCode).append(CRLF); 149 for (Map.Entry<String, String> entry : mHeaders.entrySet()) { 150 sb.append(entry.getKey()).append(": ").append(entry.getValue()).append(CRLF); 151 } 152 sb.append(CRLF); 153 Charset charset; 154 try { 155 charset = Charset.forName(getCharset()); 156 } catch (IllegalArgumentException iae) { 157 charset = StandardCharsets.ISO_8859_1; 158 } 159 sb.append(new String(mBody.array(), mBody.position(), 160 mBody.limit() - mBody.position(), charset)); 161 return sb.toString(); 162 } 163 getCharset()164 public String getCharset() { 165 String contentType = mHeaders.get(ContentTypeHeader); 166 if (contentType == null) { 167 return null; 168 } 169 int csPos = contentType.indexOf(csIndicator); 170 return csPos < 0 ? null : contentType.substring(csPos + csIndicator.length()).trim(); 171 } 172 equals(byte[] b1, int offset, byte[] pattern)173 private static boolean equals(byte[] b1, int offset, byte[] pattern) { 174 for (int n = 0; n < pattern.length; n++) { 175 if (b1[n + offset] != pattern[n]) { 176 return false; 177 } 178 } 179 return true; 180 } 181 } 182