Home | History | Annotate | Download | only in utils
      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 
     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 
     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 
    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 
    113     public int getStatusCode() {
    114         return mStatusCode;
    115     }
    116 
    117     @Override
    118     public Map<String, String> getHeaders() {
    119         return Collections.unmodifiableMap(mHeaders);
    120     }
    121 
    122     public String getHeader(String key) {
    123         return mHeaders.get(key);
    124     }
    125 
    126     @Override
    127     public InputStream getPayloadStream() {
    128         return new ByteArrayInputStream(mBody.array(), mBody.position(),
    129                 mBody.limit() - mBody.position());
    130     }
    131 
    132     @Override
    133     public ByteBuffer getPayload() {
    134         return mBody.duplicate();
    135     }
    136 
    137     @Override
    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
    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 
    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 
    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