如何解析非标准HTTP响应?

时间:2013-11-04 00:29:10

标签: android apache shoutcast android-4.4-kitkat

我正在努力弄清楚如何解析无标准的HTTP响应。

非标准回复包含ICY 200 OK而不是HTTP 200 OK。以下是发送无标准HTTP响应的示例URL http://50.117.121.162:80

由于Android 4.4 HttpURLConnection将无法再使用这些无标准回复。我尝试过使用Apache的HttpClient,但由于没有标准的HTTP响应,它不起作用。我之后尝试按照指南添加custom response parser,但Android似乎并没有完成所需的所有类。

我真的很难找到解决方案。可能会在HttpClient解析之前修改无标准响应,或者HttpURLConnection可能有效但我不确定这是否可行......

非常感谢任何帮助。

6 个答案:

答案 0 :(得分:3)

经过对一个小/ lite http客户端库的大量研究后,我遇到了这个apache httpclient for android的端口。该库为http连接提供了完整的支持。然后我简单地修改了源代码,特别是BasicLineParser,用HTTP / 1.0替换了ICY。

答案 1 :(得分:2)

我遇到了与KitKat类似的问题,并且成功使用了两个发现here的类来发布http帖子。它们非常易于使用,您也可以轻松修改协议参数。

答案 2 :(得分:1)

在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);

这仍在测试中,但初步结果很有希望。

答案 3 :(得分:1)

感谢@Michael M,您甚至可以通过继承BasicLineParser而不是继承DefaultResponseParser来简化它。

I've uploaded the code into a gist

使用它:

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

答案 4 :(得分:0)

创建一个创建套接字代理的runnable,然后您就可以使用HTTP / 1.0而不是ICY进行响应,然后只需使用您的播放器连接到本地套接字代理

答案 5 :(得分:0)

如果您不想创建大量子类来配置已有的HttpClient类,请在Michal M中修改解决方案。

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过时。