Как разобрать стандартный HTTP-ответ?

Мне очень трудно понять, как разобрать стандартный HTTP-ответ.

Ни один стандартный ответ не содержит ICY 200 OK вместо HTTP 200 OK. Вот пример URL-адреса, который отправляет ни один стандартный HTTP-ответ.
http://50.117.121.162:80

Так как Android 4.4 HttpURLConnection больше не будет работать с этими стандартными ответами. Я попытался использовать Apa HttpClient, но он не работает из-за отсутствия стандартного ответа HTTP. Затем я попробовал руководство по добавлению настраиваемого анализатора ответов, но Android, похоже, не имеет всех классов, необходимых для этого.

Я действительно пытаюсь найти решение. Возможно, изменить стандартный ответ до того, как он будет разбираться HttpClient или HttpURLConnection может работать, но я не уверен, что это возможно...

Любая помощь будет принята с благодарностью.

Ответ 1

После большого исследования маленькой/облегченной клиентской библиотеки http я столкнулся с этим портом apache httpclient для android. Библиотека предоставила полную поддержку HTTP-соединений. Затем я просто изменил исходный код, особенно BasicLineParser, чтобы заменить ICY на HTTP/1.0.

Ответ 2

У меня была аналогичная проблема с KitKat, и у меня был успех с использованием двух классов, найденных здесь для http post. Они невероятно просты в использовании, и вы можете легко изменить параметры протокола.

Ответ 3

Существует другое решение этой проблемы в Android 4.4, но для этого требуется использовать Apache HttpClient. Это основано на возможности предоставления настраиваемого парсера ответа в движок Apache Http, который может изменить ICY 200 OK на HTTP/1.0 200 OK. Это основано на общей идее, представленной в:

http://hc.apache.org/httpcomponents-client-4.2.x/tutorial/html/advanced.html

Я успешно использовал следующий код.

public class IcyClientConnection extends DefaultClientConnection{

@Override
protected HttpMessageParser createResponseParser(SessionInputBuffer buffer,
        HttpResponseFactory responseFactory, HttpParams params) {

    return new IcyHttpResponseParser(
            buffer, 
            new BasicLineParser (), 
            responseFactory, 
            params);

}

}


public class IcyClientConnectionOperator extends DefaultClientConnectionOperator {

    public IcyClientConnectionOperator(SchemeRegistry schemes) {
        super(schemes);
    }

    @Override
    public OperatedClientConnection createConnection() {

        return new IcyClientConnection();
    }

}



public class IcyClientConnManager extends SingleClientConnManager  {


    public IcyClientConnManager(HttpParams params, SchemeRegistry schreg) {
        super(params, schreg);
    }

    @Override
    protected ClientConnectionOperator createConnectionOperator(
            SchemeRegistry schreg) {
        return new IcyClientConnectionOperator(schreg);
    }

}

Теперь вам нужно расширить парсер, используемый по умолчанию, и добавить код, который изменит неправильный повтор сервера, чтобы исправить его. Обычно код будет блокироваться на hasProtocolVersion.

public class IcyHttpResponseParser extends DefaultResponseParser{

    private CharArrayBuffer icyLineBuf;

    private int icyMaxGarbageLines = 1000;
    private final HttpResponseFactory icyResponseFactory;




public IcyHttpResponseParser(SessionInputBuffer buffer, LineParser parser,
        HttpResponseFactory responseFactory, HttpParams params) {
    super(buffer, parser, responseFactory, params);

    this.icyLineBuf = new CharArrayBuffer(128);
    icyResponseFactory = responseFactory;
}

@Override
protected HttpMessage parseHead(SessionInputBuffer sessionBuffer)
        throws IOException, HttpException {
    int count = 0;
    ParserCursor cursor = null;
    do {
        // clear the buffer
        this.icyLineBuf.clear();
        final int i = sessionBuffer.readLine(this.icyLineBuf);

        //look for ICY and change to HTTP to provide compatibility with non standard shoutcast servers

        String tmp = icyLineBuf.substring(0, this.icyLineBuf.length());
        if(tmp.contains("ICY ")){
            tmp = tmp.replace("ICY", "HTTP/1.0");
        }
        //copy
        this.icyLineBuf = new CharArrayBuffer(128);
        System.arraycopy(tmp.toCharArray(), 0, icyLineBuf.buffer(), 0, tmp.length());
        icyLineBuf.setLength( tmp.length());


        if (i == -1 && count == 0) {
            // The server just dropped connection on us
            throw new NoHttpResponseException("The target server failed to respond");
        }
        cursor = new ParserCursor(0, this.icyLineBuf.length());
        if (lineParser.hasProtocolVersion(this.icyLineBuf, cursor)) {
            // Got one
            break;
        } else if (i == -1 || count >= this.icyMaxGarbageLines) {
            // Giving up
            throw new ProtocolException("The server failed to respond with a " +
                    "valid HTTP response");
        }
        //if (this.log.isDebugEnabled()) {
        //   this.log.debug("Garbage in response: " + this.lineBuf.toString());
       // }
        count++;
    } while(true);
    //create the status line from the status string
    final StatusLine statusline = lineParser.parseStatusLine(this.icyLineBuf, cursor);
    return this.icyResponseFactory.newHttpResponse(statusline, null);
}
}

Подключить HttpClient:

Scheme http = new Scheme("http", PlainSocketFactory.getSocketFactory(), 80);
Scheme ftp = new Scheme("ftp", PlainSocketFactory.getSocketFactory(), 21);

SchemeRegistry sr = new SchemeRegistry();
sr.register(http);
sr.register(ftp);

HttpClient httpClient = new DefaultHttpClient(new IcyClientConnManager(params, sr), params);

Это все еще тестируется, но исходные результаты являются многообещающими.

Ответ 4

Спасибо @Michael M, вы даже можете сделать это проще, подклассифицируя BasicLineParser вместо подкласса DefaultResponseParser.

Я загрузил код в gist

Чтобы использовать его:

IcyGetRequest request = new IcyGetRequest(urlStr);
HttpResponse response = request.get();
int responseCode = response.getStatusLine().getStatusCode();

Ответ 5

Создайте runnable, который создает прокси-сервер сокета, тогда вы сможете отвечать с помощью HTTP/1.0 вместо ICY, а затем просто подключаться к этому локальному прокси-серверу со своим плеером

Ответ 6

Здесь приведена модификация решения из Michal M, если вам не нравится создавать множество подклассов только для настройки уже доступных классов HttpClient.

final SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
HttpClient httpClient = new DefaultHttpClient() {
    @Override
    protected ClientConnectionManager createClientConnectionManager() {
        return new SingleClientConnManager(getParams(), schemeRegistry) {
            @Override
            protected ClientConnectionOperator createConnectionOperator(SchemeRegistry schreg) {
                return new DefaultClientConnectionOperator(schreg) {
                    @Override
                    public OperatedClientConnection createConnection() {
                        return new DefaultClientConnection() {
                            @Override
                            protected HttpMessageParser createResponseParser(SessionInputBuffer buffer, HttpResponseFactory responseFactory, HttpParams params) {
                                return new IcyHttpResponseParser(buffer, new BasicLineParser(), responseFactory, params);
                            }
                        };
                    }
                };
            }
        };
    }
};

Вероятно, есть способ получить SchemeRegistry obsoleted, если можно каким-то образом ухватиться из класса DefaultHttpClient.