Как проанализировать стандартный HTTP-ответ none?


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

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

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

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

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

6 ответов:

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

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

Есть еще одно решение этой проблемы в Android 4.4, но оно требует использования Apache HttpClient. Это основано на возможности предоставления пользовательского парсера ответа в Apache Http engine, который может изменить 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);
Это все еще тестируется, но первоначальные результаты многообещающи.

Спасибо @Михаил м, вы даже можете сделать его проще путем создания подклассов BasicLineParser вместо наследованием DefaultResponseParser.

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

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

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

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

Здесь модификация решения из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 устаревший, если бы можно было каким-то образом получить доступ из класса DefaultHttpClient.