我正在努力弄清楚如何解析无标准的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可能有效但我不确定这是否可行......
非常感谢任何帮助。
答案 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
过时。