2013-11-04 21 views
10

Tôi đang gặp khó khăn trong việc tìm ra cách phân tích cú pháp phản hồi HTTP không chuẩn.Làm cách nào để phân tích cú pháp phản hồi HTTP không chuẩn?

Phản hồi tiêu chuẩn không chứa ICY 200 OK thay vì HTTP 200 OK. Đây là URL mẫu gửi phản hồi HTTP không chuẩn.
http://50.117.121.162:80

Vì Android 4.4 HttpURLConnection sẽ không còn hoạt động với những phản hồi tiêu chuẩn này nữa. Tôi đã thử sử dụng Apache HttpClient nhưng nó không hoạt động vì không có phản hồi HTTP chuẩn nào. Sau đó tôi đã thử làm theo hướng dẫn để thêm một custom response parser, nhưng Android dường như không có tất cả các lớp cần thiết để làm điều đó.

Tôi thực sự đang cố gắng tìm ra giải pháp. Có thể sửa đổi phản hồi tiêu chuẩn không có trước khi nó được phân tích cú pháp theo số HttpClient hoặc HttpURLConnection có thể hoạt động nhưng tôi không chắc liệu điều đó có thể thực hiện được hay không ...

Mọi trợ giúp sẽ được đánh giá cao.

+0

Related: http://stackoverflow.com/questions/1963105/listen-to-a-shoutcast-with-android – blahdiblah

+0

Tôi không nghĩ rằng câu hỏi này liên quan đến bài đăng đó. Tôi không có vấn đề với việc biết làm thế nào để dòng nội dung SHOUTcast và phân tích siêu dữ liệu vv ... Tôi có tất cả điều này được thực hiện và làm việc tuyệt vời. Vấn đề là với Android 4.4 mới nhất HttpURLConnection là nghiêm ngặt hơn nhiều gây ra các vấn đề ở cấp độ thấp hơn. – Jona

+0

Sai ngôn ngữ, nhưng hãy xem một số giải pháp được đề xuất ở đây: http://stackoverflow.com/q/9881305/362536 Về cơ bản, tôi đã kết thúc viết mã có mặt trước máy khách HTTP để viết lại 'ICY 'như' HTTP/1.0', và sau đó để nó một mình. Có lẽ bạn có thể làm điều gì đó tương tự cho Android? – Brad

Trả lời

3

của bạn Sau rất nhiều nghiên cứu cho một nhỏ/lite http thư viện khách hàng, tôi chạy vào cổng này của apache httpclient for android. Thư viện cung cấp hỗ trợ đầy đủ cho các kết nối http. Sau đó, tôi chỉ cần sửa đổi mã nguồn, đặc biệt là BasicLineParser để thay thế ICY bằng HTTP/1.0.

+0

Xin chào Jona Tôi có cùng một vấn đề bằng cách sử dụng 'InptStream' để ghi dữ liệu vào một tệp. Tôi đã nhận thư viện' ch.boye.httpclientandroidlib' hoạt động tốt với trình tạo luồng nhưng tôi cũng cần nó bằng 'InputStream' vì đây là nơi tôi gặp lỗi tương tự bạn có ('java.net.protocolexception dòng trạng thái không mong muốn: băng giá 200') khi ghi dữ liệu vào một tệp bằng cách sử dụng' InputStream' và 'OutputStream' với luồng Shoutcast. Bạn có thể chia sẻ một số mã về cách bạn đã thực hiện điều này hay không hoặc ít nhất là giải thích cách thực hiện với 'InputStream' và' OutputStream'? Tôi thực sự bị lạc ở đây. –

+0

Bạn có thể vui lòng xây dựng thêm và đăng một số mã không? Đặc biệt mã httpclientandroidlib đã sửa đổi. – cprcrack

0

Tạo một Runnable tạo Proxy ổ cắm sau đó bạn sẽ có thể phản ứng với HTTP/1.0 thay vì ICY, sau đó chỉ cần kết nối đến proxy ổ cắm địa phương này với máy nghe nhạc

+0

Điều này sẽ thêm một chi phí không cần thiết ... Tôi đoán một thư viện java http khách hàng rất đơn giản sẽ làm nhưng tôi không thể tìm thấy một ... :( – Jona

+0

nếu bạn chỉ cần đệm số lượng nhỏ dữ liệu và tiếp tục phát nhạc đến "Proxy" cục bộ, nó sẽ không quá tải ứng dụng –

+0

Các vấn đề với điều này là mất kiểm soát khi một chuyển hướng xảy ra trên một kết nối cụ thể.Tôi đã cố gắng thực hiện máy chủ proxy nhưng tôi đã gặp sự cố với chuyển hướng – Jona

2

Tôi gặp vấn đề tương tự với KitKat và đã thành công khi sử dụng hai lớp được tìm thấy here cho bài đăng http. Chúng cực kỳ dễ sử dụng và bạn cũng có thể sửa đổi các tham số giao thức một cách dễ dàng.

+0

Xin chào, bạn có thể giải thích cách bạn sử dụng các lớp này cho mục đích này không? Tôi đang có thời gian khó khăn nhất để tìm ra điều này. Cảm ơn bạn. –

+1

Trong phương pháp thay đổi HttpClientUtil setHttpProtocolParams, ở đó bạn có thể chỉ định cho dù bạn sử dụng ICY hay bất cứ điều gì .. – slezadav

1

Có một giải pháp cho vấn đề này trong Android 4.4 nhưng nó yêu cầu sử dụng Apache HttpClient. Điều này dựa trên khả năng cung cấp trình phân tích cú pháp phản hồi tùy chỉnh vào công cụ Apache Http có thể thay đổi ICY 200 OK thành HTTP/1.0 200 OK. Này được dựa trên ý tưởng chung được trình bày trong:

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

Tôi đã sử dụng thành công sau mã.

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

} 

Bây giờ bạn phải mở rộng trình phân tích cú pháp được sử dụng theo mặc định và thêm mã sẽ thay đổi phát lại máy chủ sai để sửa lỗi. Mã thông thường sẽ chặn trên 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); 
} 
} 

Cắm 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); 

này vẫn đang được thử nghiệm nhưng kết quả bước đầu rất hứa hẹn.

1

Cảm ơn @Michael M, bạn thậm chí có thể làm cho nó đơn giản hơn bằng cách phân lớp BasicLineParser thay vì phân lớp DefaultResponseParser.

I've uploaded the code into a gist

Để sử dụng nó:

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

Dưới đây là một biến thể của giải pháp từ Michal M trong trường hợp bạn không muốn tạo nhiều lớp con chỉ để cấu hình đã có sẵn HttpClient lớp.

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

Có lẽ đó là một cách để có được những SchemeRegistry lỗi thời nếu người ta có thể nhận giữ bằng cách nào đó từ bên trong lớp DefaultHttpClient.

Các vấn đề liên quan