【问题标题】:How to parse a none standard HTTP response?如何解析非标准的 HTTP 响应?
【发布时间】:2013-11-04 00:29:09
【问题描述】:

我很难弄清楚如何解析非标准的 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 似乎没有执行此操作所需的所有类。

我真的很难找到解决方案。可能在HttpClientHttpURLConnection 解析之前修改非标准响应可以工作,但我不确定这是否可能......

任何帮助将不胜感激。

【问题讨论】:

  • 我认为这个问题与该帖子无关。我在知道如何流式传输 SHOUTcast 内容和解析元数据等方面没有问题......我已经完成了所有这些并且工作得很好。问题在于最新的 Android 4.4 HttpURLConnection 更加严格,导致问题处于较低级别。
  • 语言错误,但请看一下这里提出的一些解决方案:stackoverflow.com/q/9881305/362536 基本上,我最终编写了到达 HTTP 客户端前面的代码,以将 ICY 重写为 @ 987654332@,然后就不管了。也许您可以为 Android 做类似的事情?
  • 感谢@Brad 的想法。我正在考虑做类似的事情,但我仍在探索最好的方法......我不想中断对其他非冰冷服务器的支持。
  • 如果您检查ICY 作为前4个字节,并且仅在必要时插入HTTP/1.0 ,那么您仍然能够支持正常的HTTP服务器就好了。我已经在 6,000 台各种互联网广播服务器上测试了该方法,并且效果很好。你只需要找到一种在 Android 中实现的方法。

标签: android apache shoutcast android-4.4-kitkat


【解决方案1】:

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

【讨论】:

  • 嗨 Jona 我在使用 InptStream 将数据写入文件时遇到了同样的问题。我得到ch.boye.httpclientandroidlib 库与streamscraper 配合得很好,但我也需要InputStream,因为这是我在使用InputStream 和@ 将数据写入文件时遇到的相同错误(java.net.protocolexception unexpected status line: icy 200) 987654327@ 带有 Shoutcast 流。您能否分享一些您如何完成此操作的代码,或者至少解释一下如何使用InputStreamOutputStream 来实现?我真的迷路了。
  • 您能否详细说明并发布一些代码?特别是修改过的httpclientandroidlib代码。
【解决方案2】:

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

【讨论】:

  • 您好,您能解释一下您是如何为此目的使用这些类的吗?我最难弄清楚这一点。谢谢。
  • 在 HttpClientUtil 更改方法 setHttpProtocolParams 中,您可以指定是否使用 ICY 或其他..
【解决方案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 来简化它。

    I've uploaded the code into a gist

    使用它:

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

    【讨论】:

      【解决方案5】:

      创建一个创建套接字代理的可运行对象,然后您将能够使用 HTTP/1.0 而不是 ICY 进行响应,然后只需使用您的播放器连接到这个本地套接字代理

      【讨论】:

      • 这会增加不必要的开销...我想一个非常简单的 java http 客户端库就可以了,但我似乎找不到... :(
      • 如果您只是缓冲少量数据并将流音乐保存到本地“代理”,它不会使应用程序过载
      • 一旦在特定连接上发生重定向,就会失去控制。我试过做代理服务器,但我确实遇到了重定向问题。
      【解决方案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);
                                  }
                              };
                          }
                      };
                  }
              };
          }
      };
      

      如果可以从DefaultHttpClient 类中以某种方式获取,则可能有一种方法可以使SchemeRegistry 过时。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-12-28
        • 1970-01-01
        • 2012-01-29
        • 2016-06-03
        • 1970-01-01
        • 2015-11-26
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多