Java tutorial
// Copyright (c) 2010 Mahesh Sharma,Matt MacDonald // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package org.prx.prp; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketTimeoutException; import java.net.UnknownHostException; import java.util.StringTokenizer; import org.apache.http.Header; import org.apache.http.HttpRequest; import org.apache.http.HttpResponse; import org.apache.http.HttpResponseFactory; import org.apache.http.ParseException; import org.apache.http.ProtocolVersion; import org.apache.http.RequestLine; import org.apache.http.StatusLine; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.methods.HttpGet; import org.apache.http.conn.ClientConnectionOperator; import org.apache.http.conn.OperatedClientConnection; import org.apache.http.conn.scheme.PlainSocketFactory; import org.apache.http.conn.scheme.Scheme; import org.apache.http.conn.scheme.SchemeRegistry; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.conn.DefaultClientConnection; import org.apache.http.impl.conn.DefaultClientConnectionOperator; import org.apache.http.impl.conn.DefaultResponseParser; import org.apache.http.impl.conn.SingleClientConnManager; import org.apache.http.io.HttpMessageParser; import org.apache.http.io.SessionInputBuffer; import org.apache.http.message.BasicHttpRequest; import org.apache.http.message.BasicHttpResponse; import org.apache.http.message.BasicLineParser; import org.apache.http.message.ParserCursor; import org.apache.http.params.HttpParams; import org.apache.http.protocol.HTTP; import org.apache.http.util.CharArrayBuffer; import android.content.Context; import android.content.Intent; import android.media.MediaPlayer; import android.util.Log; public class StreamProxy implements Runnable { private int port = 0; MediaPlayer m; Context mContext; public int getPort() { return port; } private boolean isRunning = true; private ServerSocket socket; private Thread thread; public void init(MediaPlayer m, Context mContext) { try { this.m = m; this.mContext = mContext; socket = new ServerSocket(port, 0, InetAddress.getLocalHost()); socket.setSoTimeout(5000); port = socket.getLocalPort(); Log.d(getClass().getName(), "port " + port + " obtained"); } catch (UnknownHostException e) { Log.e(getClass().getName(), "Error initializing server", e); } catch (IOException e) { Log.e(getClass().getName(), "Error initializing server", e); } } public void start() { thread = new Thread(this); thread.start(); } public void stop() { isRunning = false; thread.interrupt(); try { thread.join(5000); } catch (InterruptedException e) { e.printStackTrace(); } } public void run() { Log.d(getClass().getName(), "running"); while (isRunning) { try { Socket client = socket.accept(); if (client == null) { continue; } Log.d(getClass().getName(), "client connected"); HttpRequest request = readRequest(client); processRequest(request, client); } catch (java.net.UnknownHostException e1) { Log.d("PRPAND", "asdasd"); } catch (SocketTimeoutException e) { // Do nothing } catch (IOException e) { Log.e(getClass().getName(), "Error connecting to client", e); } } Log.d(getClass().getName(), "Proxy interrupted. Shutting down."); } private HttpRequest readRequest(Socket client) { HttpRequest request = null; InputStream is; String firstLine; try { is = client.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(is)); firstLine = reader.readLine(); } catch (IOException e) { Log.e(getClass().getName(), "Error parsing request", e); return request; } StringTokenizer st = new StringTokenizer(firstLine); String method = st.nextToken(); String uri = st.nextToken(); Log.d(getClass().getName(), uri); String realUri = uri.substring(1); Log.d(getClass().getName(), realUri); request = new BasicHttpRequest(method, realUri); return request; } private HttpResponse download(String url) { DefaultHttpClient seed = new DefaultHttpClient(); SchemeRegistry registry = new SchemeRegistry(); registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); SingleClientConnManager mgr = new MyClientConnManager(seed.getParams(), registry); DefaultHttpClient http = new DefaultHttpClient(mgr, seed.getParams()); HttpGet method = new HttpGet(url); HttpResponse response = null; try { Log.d(getClass().getName(), "starting download"); response = http.execute(method); Log.d(getClass().getName(), "downloaded"); } catch (java.net.UnknownHostException e) { Intent i = new Intent("org.prx.errorInStream"); mContext.sendBroadcast(i); } catch (ClientProtocolException e) { Log.e(getClass().getName(), "Error downloading", e); } catch (IOException e) { Log.e(getClass().getName(), "Error downloading", e); } return response; } private void processRequest(HttpRequest request, Socket client) throws IllegalStateException, IOException { if (request == null) { return; } Log.d(getClass().getName(), "processing"); String url = request.getRequestLine().getUri(); HttpResponse realResponse = download(url); if (realResponse == null) { return; } Log.d(getClass().getName(), "downloading..."); InputStream data = realResponse.getEntity().getContent(); StatusLine line = realResponse.getStatusLine(); HttpResponse response = new BasicHttpResponse(line); response.setHeaders(realResponse.getAllHeaders()); Log.d(getClass().getName(), "reading headers"); StringBuilder httpString = new StringBuilder(); httpString.append(response.getStatusLine().toString()); httpString.append("\n"); for (Header h : response.getAllHeaders()) { httpString.append(h.getName()).append(": ").append(h.getValue()).append("\n"); } httpString.append("\n"); Log.d(getClass().getName(), "headers done"); //Log.d("PRPAND","Response "+httpString); try { byte[] buffer = httpString.toString().getBytes(); int readBytes = -1; Log.d(getClass().getName(), "writing to client"); client.getOutputStream().write(buffer, 0, buffer.length); // Start streaming content. byte[] buff = new byte[1024 * 50]; while (isRunning && (readBytes = data.read(buff, 0, buff.length)) != -1) { client.getOutputStream().write(buff, 0, readBytes); } } catch (Exception e) { Log.e("", e.getMessage(), e); } finally { if (data != null) { data.close(); } client.close(); } } private class IcyLineParser extends BasicLineParser { private static final String ICY_PROTOCOL_NAME = "ICY"; private IcyLineParser() { super(); } @Override public boolean hasProtocolVersion(CharArrayBuffer buffer, ParserCursor cursor) { boolean superFound = super.hasProtocolVersion(buffer, cursor); if (superFound) { return true; } int index = cursor.getPos(); final int protolength = ICY_PROTOCOL_NAME.length(); if (buffer.length() < protolength) return false; // not long enough for "HTTP/1.1" if (index < 0) { // end of line, no tolerance for trailing whitespace // this works only for single-digit major and minor version index = buffer.length() - protolength; } else if (index == 0) { // beginning of line, tolerate leading whitespace while ((index < buffer.length()) && HTTP.isWhitespace(buffer.charAt(index))) { index++; } } // else within line, don't tolerate whitespace if (index + protolength > buffer.length()) return false; return buffer.substring(index, index + protolength).equals(ICY_PROTOCOL_NAME); } @Override public Header parseHeader(CharArrayBuffer buffer) throws ParseException { return super.parseHeader(buffer); } @Override public ProtocolVersion parseProtocolVersion(CharArrayBuffer buffer, ParserCursor cursor) throws ParseException { if (buffer == null) { throw new IllegalArgumentException("Char array buffer may not be null"); } if (cursor == null) { throw new IllegalArgumentException("Parser cursor may not be null"); } final int protolength = ICY_PROTOCOL_NAME.length(); int indexFrom = cursor.getPos(); int indexTo = cursor.getUpperBound(); skipWhitespace(buffer, cursor); int i = cursor.getPos(); // long enough for "HTTP/1.1"? if (i + protolength + 4 > indexTo) { throw new ParseException("Not a valid protocol version: " + buffer.substring(indexFrom, indexTo)); } if (!buffer.substring(i, i + protolength).equals(ICY_PROTOCOL_NAME)) { return super.parseProtocolVersion(buffer, cursor); } cursor.updatePos(i + protolength); return createProtocolVersion(1, 0); } @Override public RequestLine parseRequestLine(CharArrayBuffer buffer, ParserCursor cursor) throws ParseException { return super.parseRequestLine(buffer, cursor); } @Override public StatusLine parseStatusLine(CharArrayBuffer buffer, ParserCursor cursor) throws ParseException { StatusLine superLine = super.parseStatusLine(buffer, cursor); return superLine; } } class MyClientConnection extends DefaultClientConnection { @Override protected HttpMessageParser createResponseParser(final SessionInputBuffer buffer, final HttpResponseFactory responseFactory, final HttpParams params) { return new DefaultResponseParser(buffer, new IcyLineParser(), responseFactory, params); } } class MyClientConnectionOperator extends DefaultClientConnectionOperator { public MyClientConnectionOperator(final SchemeRegistry sr) { super(sr); } @Override public OperatedClientConnection createConnection() { return new MyClientConnection(); } } class MyClientConnManager extends SingleClientConnManager { private MyClientConnManager(HttpParams params, SchemeRegistry schreg) { super(params, schreg); } @Override protected ClientConnectionOperator createConnectionOperator(final SchemeRegistry sr) { return new MyClientConnectionOperator(sr); } } }