From d25d967332c9f280bcd29a635a8fbd6cdf4e773f Mon Sep 17 00:00:00 2001 From: Hannes Janetzek Date: Sun, 17 Feb 2013 17:24:05 +0100 Subject: [PATCH] extract LwHttp from MapDatabase --- src/org/oscim/database/oscimap/LwHttp.java | 431 ++++++++++++++++ .../oscim/database/oscimap/MapDatabase.java | 480 ++---------------- 2 files changed, 480 insertions(+), 431 deletions(-) create mode 100644 src/org/oscim/database/oscimap/LwHttp.java diff --git a/src/org/oscim/database/oscimap/LwHttp.java b/src/org/oscim/database/oscimap/LwHttp.java new file mode 100644 index 00000000..d1d3ebc4 --- /dev/null +++ b/src/org/oscim/database/oscimap/LwHttp.java @@ -0,0 +1,431 @@ +/* + * Copyright 2013 OpenScienceMap + * + * This program is free software: you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with + * this program. If not, see . + */ +package org.oscim.database.oscimap; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.net.MalformedURLException; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.URL; + +import org.oscim.core.Tile; + +import android.os.SystemClock; +import android.util.Log; + +public class LwHttp { + private static final String TAG = LwHttp.class.getName(); + + private String mHost; + private int mPort; + private long mContentLenth; + private InputStream mInputStream; + + private final int BUFFER_SIZE = 65536; + + byte[] buffer = new byte[BUFFER_SIZE]; + // position in buffer + int bufferPos; + + // bytes available in buffer + int bufferFill; + + // overall bytes of content read + private int mReadPos; + + private int mMaxReq = 0; + private Socket mSocket; + private OutputStream mCommandStream; + private InputStream mResponseStream; + long mLastRequest = 0; + private SocketAddress mSockAddr; + + private final static byte[] RESPONSE_HTTP_OK = "HTTP/1.1 200 OK".getBytes(); + private final static int RESPONSE_EXPECTED_LIVES = 100; + private final static int RESPONSE_EXPECTED_TIMEOUT = 10000; + + private byte[] REQUEST_GET_START; + private byte[] REQUEST_GET_END; + + private byte[] mRequestBuffer; + + boolean setServer(String urlString) { + URL url; + try { + url = new URL(urlString); + } catch (MalformedURLException e) { + + e.printStackTrace(); + return false; + //return new OpenResult("invalid url: " + options.get("url")); + } + + int port = url.getPort(); + if (port < 0) + port = 80; + + String host = url.getHost(); + String path = url.getPath(); + Log.d(TAG, "open oscim database: " + host + " " + port + " " + path); + + REQUEST_GET_START = ("GET " + path).getBytes(); + REQUEST_GET_END = (".osmtile HTTP/1.1\n" + + "Host: " + host + "\n" + + "Connection: Keep-Alive\n\n").getBytes(); + + mHost = host; + mPort = port; + + mRequestBuffer = new byte[1024]; + System.arraycopy(REQUEST_GET_START, 0, + mRequestBuffer, 0, REQUEST_GET_START.length); + return true; + } + + void close() { + if (mSocket != null) { + try { + mSocket.close(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + mSocket = null; + } + } + } + + int readHeader() throws IOException { + InputStream is = mResponseStream; + + byte[] buf = buffer; + boolean first = true; + int read = 0; + int pos = 0; + int end = 0; + int len = 0; + + // header cannot be larger than BUFFER_SIZE for this to work + for (; pos < read || (len = is.read(buf, read, BUFFER_SIZE - read)) >= 0; len = 0) { + read += len; + while (end < read && (buf[end] != '\n')) + end++; + + if (buf[end] == '\n') { + if (first) { + // check only for OK + first = false; + if (!compareBytes(buf, pos, end, RESPONSE_HTTP_OK, 15)) + return -1; + + } else if (end - pos == 1) { + // check empty line (header end) + end += 1; + break; + } + + // String line = new String(buf, pos, end - pos - 1); + // Log.d(TAG, ">" + line + "< " + resp_len); + + pos += (end - pos) + 1; + end = pos; + } + } + + // check 4 bytes available.. + while ((read - end) < 4 && (len = is.read(buf, read, BUFFER_SIZE - read)) >= 0) + read += len; + + if (read - len < 4) + return -1; + + int contentLength = decodeInt(buf, end); + mContentLenth = contentLength; + + // buffer fill + bufferFill = read; + // start of content + bufferPos = end + 4; + // bytes of content already read into buffer + mReadPos = read - bufferPos; + + mInputStream = mResponseStream; + + return contentLength; + } + + boolean sendRequest(Tile tile) throws IOException { + + bufferFill = 0; + bufferPos = 0; + mReadPos = 0; + mCacheFile = null; + + if (mSocket != null && ((mMaxReq-- <= 0) + || (SystemClock.elapsedRealtime() - mLastRequest + > RESPONSE_EXPECTED_TIMEOUT))) { + try { + mSocket.close(); + } catch (IOException e) { + + } + + // Log.d(TAG, "not alive - recreate connection " + mMaxReq); + mSocket = null; + } + + if (mSocket == null) { + lwHttpConnect(); + // we know our server + mMaxReq = RESPONSE_EXPECTED_LIVES; + // Log.d(TAG, "create connection"); + } else { + // should not be needed + int avail = mResponseStream.available(); + if (avail > 0) { + Log.d(TAG, "Consume left-over bytes: " + avail); + mResponseStream.read(buffer, 0, avail); + } + } + + byte[] request = mRequestBuffer; + int pos = REQUEST_GET_START.length; + + pos = writeInt(tile.zoomLevel, pos, request); + request[pos++] = '/'; + pos = writeInt(tile.tileX, pos, request); + request[pos++] = '/'; + pos = writeInt(tile.tileY, pos, request); + + int len = REQUEST_GET_END.length; + System.arraycopy(REQUEST_GET_END, 0, request, pos, len); + len += pos; + + // this does the same but with a few more allocations: + // byte[] request = String.format(REQUEST, + // Integer.valueOf(tile.zoomLevel), + // Integer.valueOf(tile.tileX), Integer.valueOf(tile.tileY)).getBytes(); + + try { + mCommandStream.write(request, 0, len); + mCommandStream.flush(); + return true; + } catch (IOException e) { + Log.d(TAG, "recreate connection"); + } + + lwHttpConnect(); + + mCommandStream.write(request, 0, len); + mCommandStream.flush(); + + return true; + } + + private boolean lwHttpConnect() throws IOException { + if (mSockAddr == null) + mSockAddr = new InetSocketAddress(mHost, mPort); + + mSocket = new Socket(); + mSocket.connect(mSockAddr, 30000); + mSocket.setTcpNoDelay(true); + + mCommandStream = mSocket.getOutputStream(); //new BufferedOutputStream(); + mResponseStream = mSocket.getInputStream(); + + return true; + } + + // write (positive) integer as char sequence to buffer + private static int writeInt(int val, int pos, byte[] buf) { + if (val == 0) { + buf[pos] = '0'; + return pos + 1; + } + + int i = 0; + for (int n = val; n > 0; n = n / 10, i++) + buf[pos + i] = (byte) ('0' + n % 10); + + // reverse bytes + for (int j = pos, end = pos + i - 1, mid = pos + i / 2; j < mid; j++, end--) { + byte tmp = buf[j]; + buf[j] = buf[end]; + buf[end] = tmp; + } + + return pos + i; + } + + private static boolean compareBytes(byte[] buffer, int position, int available, + byte[] string, int length) { + + if (available - position < length) + return false; + + for (int i = 0; i < length; i++) + if (buffer[position + i] != string[i]) + return false; + + return true; + } + + static int decodeInt(byte[] buffer, int offset) { + return buffer[offset] << 24 | (buffer[offset + 1] & 0xff) << 16 + | (buffer[offset + 2] & 0xff) << 8 + | (buffer[offset + 3] & 0xff); + } + + void readBuffer(int size) throws IOException { + + // check if buffer already contains the request bytes + if (bufferPos + size < bufferFill) + return; + + // check if inputstream is read to the end + if (mReadPos == mContentLenth) + return; + int maxSize = buffer.length; + + if (size > maxSize) { + Log.d(TAG, "increase read buffer to " + size + " bytes"); + maxSize = size; + byte[] tmp = new byte[maxSize]; + + bufferFill -= bufferPos; + System.arraycopy(buffer, bufferPos, tmp, 0, bufferFill); + bufferPos = 0; + buffer = tmp; + } + + if (bufferFill == bufferPos) { + bufferPos = 0; + bufferFill = 0; + } else if (bufferPos + size > maxSize) { + // copy bytes left to the beginning of buffer + bufferFill -= bufferPos; + System.arraycopy(buffer, bufferPos, buffer, 0, bufferFill); + bufferPos = 0; + } + + int max = maxSize - bufferFill; + + while ((bufferFill - bufferPos) < size && max > 0) { + + max = maxSize - bufferFill; + if (max > mContentLenth - mReadPos) + max = (int) (mContentLenth - mReadPos); + + // read until requested size is available in buffer + int len = mInputStream.read(buffer, bufferFill, max); + + if (len < 0) { + // finished reading, mark end + buffer[bufferFill] = 0; + break; + } + + mReadPos += len; + + // if (mCacheFile != null) + // mCacheFile.write(mReadBuffer, mBufferFill, len); + + if (mReadPos == mContentLenth) + break; + + bufferFill += len; + } + } + + private FileOutputStream mCacheFile; + + boolean cacheRead(Tile tile, File f) { + if (f.exists() && f.length() > 0) { + FileInputStream in; + + try { + in = new FileInputStream(f); + + mContentLenth = f.length(); + Log.d(TAG, tile + " - using cache: " + mContentLenth); + mInputStream = in; + + //decode(); + in.close(); + + return true; + + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (Exception ex) { + ex.printStackTrace(); + } + + f.delete(); + return false; + } + + return false; + } + + boolean cacheBegin(Tile tile, File f) { + if (MapDatabase.USE_CACHE) { + try { + Log.d(TAG, tile + " - writing cache"); + mCacheFile = new FileOutputStream(f); + + if (mReadPos > 0) { + try { + mCacheFile.write(buffer, bufferPos, + bufferFill - bufferPos); + + } catch (IOException e) { + e.printStackTrace(); + mCacheFile = null; + return false; + } + } + } catch (FileNotFoundException e) { + e.printStackTrace(); + mCacheFile = null; + return false; + } + } + return true; + } + + void cacheFinish(Tile tile, File file, boolean success) { + if (MapDatabase.USE_CACHE) { + if (success) { + try { + mCacheFile.flush(); + mCacheFile.close(); + Log.d(TAG, tile + " - cache written " + file.length()); + } catch (IOException e) { + e.printStackTrace(); + } + } else { + file.delete(); + } + } + mCacheFile = null; + } +} diff --git a/src/org/oscim/database/oscimap/MapDatabase.java b/src/org/oscim/database/oscimap/MapDatabase.java index b37d11a0..a50fe07b 100644 --- a/src/org/oscim/database/oscimap/MapDatabase.java +++ b/src/org/oscim/database/oscimap/MapDatabase.java @@ -15,19 +15,9 @@ package org.oscim.database.oscimap; import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.InetSocketAddress; -import java.net.MalformedURLException; -import java.net.Socket; -import java.net.SocketAddress; import java.net.SocketException; import java.net.SocketTimeoutException; -import java.net.URL; import java.net.UnknownHostException; import java.util.Arrays; @@ -52,48 +42,42 @@ import android.util.Log; * */ public class MapDatabase implements IMapDatabase { - private static final String TAG = "MapDatabase"; + private static final String TAG = MapDatabase.class.getName(); + + static final boolean USE_CACHE = false; private static final MapInfo mMapInfo = new MapInfo(new BoundingBox(-180, -90, 180, 90), new Byte((byte) 4), new GeoPoint(53.11, 8.85), null, 0, 0, 0, "de", "comment", "author", null); - private boolean mOpenFile = false; - private static final boolean USE_CACHE = false; private static final String CACHE_DIRECTORY = "/Android/data/org.oscim.app/cache/"; private static final String CACHE_FILE = "%d-%d-%d.tile"; - //private static String SERVER_ADDR = "city.informatik.uni-bremen.de"; - //private static int PORT = 80; - //private static final String URL = "/osci/map-live/"; - //private static String TILE_URL = "/osci/oscim/"; private final static float REF_TILE_SIZE = 4096.0f; + // 'open' state + private boolean mOpen = false; private static File cacheDir; - private int MAX_TILE_TAGS = 100; + private final int MAX_TILE_TAGS = 100; private Tag[] curTags = new Tag[MAX_TILE_TAGS]; private int mCurTagCnt; private IMapDatabaseCallback mMapGenerator; private float mScaleFactor; private JobTile mTile; - private FileOutputStream mCacheFile; - private String mHost; - private int mPort; private long mContentLenth; - private InputStream mInputStream; private final boolean debug = false; + private LwHttp lwHttp; @Override public QueryResult executeQuery(JobTile tile, IMapDatabaseCallback mapDatabaseCallback) { QueryResult result = QueryResult.SUCCESS; - mCacheFile = null; mTile = tile; @@ -104,24 +88,20 @@ public class MapDatabase implements IMapDatabase { File f = null; - mBufferFill = 0; - mBufferPos = 0; - mReadPos = 0; - if (USE_CACHE) { - f = new File(cacheDir, String.format(CACHE_FILE, + f = new File(cacheDir, String.format(CACHE_FILE, Integer.valueOf(tile.zoomLevel), Integer.valueOf(tile.tileX), Integer.valueOf(tile.tileY))); - if (cacheRead(tile, f)) + if (lwHttp.cacheRead(tile, f)) return QueryResult.SUCCESS; } try { - if (lwHttpSendRequest(tile) && lwHttpReadHeader() >= 0) { - cacheBegin(tile, f); + if (lwHttp.sendRequest(tile) && (mContentLenth = lwHttp.readHeader()) >= 0) { + lwHttp.cacheBegin(tile, f); decode(); } else { Log.d(TAG, tile + " Network Error"); @@ -141,22 +121,14 @@ public class MapDatabase implements IMapDatabase { result = QueryResult.FAILED; } - mLastRequest = SystemClock.elapsedRealtime(); + lwHttp.mLastRequest = SystemClock.elapsedRealtime(); if (result == QueryResult.SUCCESS) { - cacheFinish(tile, f, true); + lwHttp.cacheFinish(tile, f, true); } else { - cacheFinish(tile, f, false); - if (mSocket != null) { - // clear connection, TODO cleanup properly - try { - mSocket.close(); - } catch (IOException e) { - e.printStackTrace(); - } - mSocket = null; - } + lwHttp.cacheFinish(tile, f, false); + lwHttp.close(); } return result; } @@ -173,47 +145,23 @@ public class MapDatabase implements IMapDatabase { @Override public boolean isOpen() { - return mOpenFile; + return mOpen; } @Override public OpenResult open(MapOptions options) { - if (mOpenFile) + if (mOpen) return OpenResult.SUCCESS; if (options == null || !options.containsKey("url")) return new OpenResult("options missing"); - URL url; - try { - url = new URL(options.get("url")); - } catch (MalformedURLException e) { + lwHttp = new LwHttp(); - e.printStackTrace(); + if (!lwHttp.setServer(options.get("url"))){ return new OpenResult("invalid url: " + options.get("url")); } - int port = url.getPort(); - if (port < 0) - port = 80; - - String host = url.getHost(); - String path = url.getPath(); - Log.d(TAG, "open oscim database: " + host + " " + port + " " + path); - - REQUEST_GET_START = ("GET " + path).getBytes(); - REQUEST_GET_END = (".osmtile HTTP/1.1\n" + - "Host: " + host + "\n" + - "Connection: Keep-Alive\n\n").getBytes(); - - mHost = host; - mPort = port; - //mSockAddr = new InetSocketAddress(host, port); - - mRequestBuffer = new byte[1024]; - System.arraycopy(REQUEST_GET_START, 0, - mRequestBuffer, 0, REQUEST_GET_START.length); - if (USE_CACHE) { if (cacheDir == null) { String externalStorageDirectory = Environment @@ -224,7 +172,7 @@ public class MapDatabase implements IMapDatabase { } } - mOpenFile = true; + mOpen = true; initDecorder(); @@ -233,17 +181,9 @@ public class MapDatabase implements IMapDatabase { @Override public void close() { - mOpenFile = false; + mOpen = false; - if (mSocket != null) { - try { - mSocket.close(); - } catch (IOException e) { - e.printStackTrace(); - } finally { - mSocket = null; - } - } + lwHttp.close(); if (USE_CACHE) { cacheDir = null; @@ -270,21 +210,20 @@ public class MapDatabase implements IMapDatabase { // /////////////// hand sewed tile protocol buffers decoder /////////////// private final int MAX_WAY_COORDS = 16384; - private final int BUFFER_SIZE = 65536; - private byte[] mReadBuffer = new byte[BUFFER_SIZE]; + //private final byte[] mReadBuffer = new byte[BUFFER_SIZE]; // position in buffer - private int mBufferPos; +// private int mBufferPos; // bytes available in buffer - private int mBufferFill; +// private int mBufferFill; // overall bytes of content processed private int mBytesProcessed; // overall bytes of content read - private int mReadPos; +// private int mReadPos; private static final int TAG_TILE_NUM_TAGS = 1; private static final int TAG_TILE_TAG_KEYS = 2; @@ -305,7 +244,7 @@ public class MapDatabase implements IMapDatabase { private short[] mTmpKeys = new short[100]; private short[] mIndices = new short[10]; - private Tag[] mTmpTags = new Tag[20]; + private final Tag[] mTmpTags = new Tag[20]; private float[] mTmpCoords; @@ -533,17 +472,17 @@ public class MapDatabase implements IMapDatabase { private int decodeWayCoordinates(boolean skip, int nodes) throws IOException { int bytes = decodeVarint32(); - readBuffer(bytes); + lwHttp.readBuffer(bytes); if (skip) { - mBufferPos += bytes; + lwHttp.bufferPos += bytes; return nodes; } - int pos = mBufferPos; + int pos = lwHttp.bufferPos; int end = pos + bytes; float[] coords = mTmpCoords; - byte[] buf = mReadBuffer; + byte[] buf = lwHttp.buffer; int cnt = 0; int result; @@ -606,73 +545,12 @@ public class MapDatabase implements IMapDatabase { } } - mBufferPos = pos; + lwHttp.bufferPos = pos; mBytesProcessed += bytes; return cnt; } - private void readBuffer(int size) throws IOException { - - // check if buffer already contains the request bytes - if (mBufferPos + size < mBufferFill) - return; - - // check if inputstream is read to the end - if (mReadPos == mContentLenth) - return; - int maxSize = mReadBuffer.length; - - if (size > maxSize) { - Log.d(TAG, mTile + "increase read buffer to " + size + " bytes"); - maxSize = size; - byte[] tmp = new byte[maxSize]; - - mBufferFill -= mBufferPos; - System.arraycopy(mReadBuffer, mBufferPos, tmp, 0, mBufferFill); - mBufferPos = 0; - mReadBuffer = tmp; - } - - if (mBufferFill == mBufferPos) { - mBufferPos = 0; - mBufferFill = 0; - } else if (mBufferPos + size > maxSize) { - // copy bytes left to the beginning of buffer - mBufferFill -= mBufferPos; - System.arraycopy(mReadBuffer, mBufferPos, mReadBuffer, 0, mBufferFill); - mBufferPos = 0; - } - - int max = maxSize - mBufferFill; - - while ((mBufferFill - mBufferPos) < size && max > 0) { - - max = maxSize - mBufferFill; - if (max > mContentLenth - mReadPos) - max = (int) (mContentLenth - mReadPos); - - // read until requested size is available in buffer - int len = mInputStream.read(mReadBuffer, mBufferFill, max); - - if (len < 0) { - // finished reading, mark end - mReadBuffer[mBufferFill] = 0; - break; - } - - mReadPos += len; - - if (mCacheFile != null) - mCacheFile.write(mReadBuffer, mBufferFill, len); - - if (mReadPos == mContentLenth) - break; - - mBufferFill += len; - } - } - private short[] decodeShortArray(int num, short[] array) throws IOException { int bytes = decodeVarint32(); @@ -681,13 +559,13 @@ public class MapDatabase implements IMapDatabase { index = new short[num]; } - readBuffer(bytes); + lwHttp.readBuffer(bytes); int cnt = 0; - int pos = mBufferPos; + int pos = lwHttp.bufferPos; int end = pos + bytes; - byte[] buf = mReadBuffer; + byte[] buf = lwHttp.buffer; int result; while (pos < end) { @@ -730,40 +608,40 @@ public class MapDatabase implements IMapDatabase { index[cnt++] = (short) result; } - mBufferPos = pos; + lwHttp.bufferPos = pos; mBytesProcessed += bytes; return index; } private int decodeVarint32() throws IOException { - int pos = mBufferPos; + int pos = lwHttp.bufferPos; - if (pos + 10 > mBufferFill) { - readBuffer(4096); - pos = mBufferPos; + if (pos + 10 > lwHttp.bufferFill) { + lwHttp.readBuffer(4096); + pos = lwHttp.bufferPos; } - byte[] buf = mReadBuffer; + byte[] buf =lwHttp.buffer; if (buf[pos] >= 0) { - mBufferPos += 1; + lwHttp.bufferPos += 1; mBytesProcessed += 1; return buf[pos]; } else if (buf[pos + 1] >= 0) { - mBufferPos += 2; + lwHttp.bufferPos += 2; mBytesProcessed += 2; return (buf[pos] & 0x7f) | (buf[pos + 1]) << 7; } else if (buf[pos + 2] >= 0) { - mBufferPos += 3; + lwHttp.bufferPos += 3; mBytesProcessed += 3; return (buf[pos] & 0x7f) | (buf[pos + 1] & 0x7f) << 7 | (buf[pos + 2]) << 14; } else if (buf[pos + 3] >= 0) { - mBufferPos += 4; + lwHttp.bufferPos += 4; mBytesProcessed += 4; return (buf[pos] & 0x7f) | (buf[pos + 1] & 0x7f) << 7 @@ -788,7 +666,7 @@ public class MapDatabase implements IMapDatabase { if (read == 10) throw new IOException("X malformed VarInt32 in " + mTile); - mBufferPos += read; + lwHttp.bufferPos += read; mBytesProcessed += read; return result; @@ -796,10 +674,10 @@ public class MapDatabase implements IMapDatabase { private String decodeString() throws IOException { final int size = decodeVarint32(); - readBuffer(size); - final String result = new String(mReadBuffer, mBufferPos, size, "UTF-8"); + lwHttp.readBuffer(size); + final String result = new String(lwHttp.buffer, lwHttp.bufferPos, size, "UTF-8"); - mBufferPos += size; + lwHttp.bufferPos += size; mBytesProcessed += size; return result; @@ -811,264 +689,4 @@ public class MapDatabase implements IMapDatabase { | (buffer[offset + 3] & 0xff); } - // ///////////////////////// Lightweight HttpClient /////////////////////// - - private int mMaxReq = 0; - private Socket mSocket; - private OutputStream mCommandStream; - private InputStream mResponseStream; - private long mLastRequest = 0; - private SocketAddress mSockAddr; - - private final static byte[] RESPONSE_HTTP_OK = "HTTP/1.1 200 OK".getBytes(); - private final static int RESPONSE_EXPECTED_LIVES = 100; - private final static int RESPONSE_EXPECTED_TIMEOUT = 10000; - - private byte[] REQUEST_GET_START; - private byte[] REQUEST_GET_END; - - private byte[] mRequestBuffer; - - int lwHttpReadHeader() throws IOException { - InputStream is = mResponseStream; - - byte[] buf = mReadBuffer; - boolean first = true; - int read = 0; - int pos = 0; - int end = 0; - int len = 0; - - // header cannot be larger than BUFFER_SIZE for this to work - for (; pos < read || (len = is.read(buf, read, BUFFER_SIZE - read)) >= 0; len = 0) { - read += len; - while (end < read && (buf[end] != '\n')) - end++; - - if (buf[end] == '\n') { - if (first) { - // check only for OK - first = false; - if (!compareBytes(buf, pos, end, RESPONSE_HTTP_OK, 15)) - return -1; - - } else if (end - pos == 1) { - // check empty line (header end) - end += 1; - break; - } - - // String line = new String(buf, pos, end - pos - 1); - // Log.d(TAG, ">" + line + "< " + resp_len); - - pos += (end - pos) + 1; - end = pos; - } - } - - // check 4 bytes available.. - while ((read - end) < 4 && (len = is.read(buf, read, BUFFER_SIZE - read)) >= 0) - read += len; - - if (read - len < 4) - return -1; - - mContentLenth = decodeInt(buf, end); - - // buffer fill - mBufferFill = read; - // start of content - mBufferPos = end + 4; - // bytes of content already read into buffer - mReadPos = read - mBufferPos; - - mInputStream = mResponseStream; - - return 1; - } - - private boolean lwHttpSendRequest(Tile tile) throws IOException { - - if (mSocket != null && ((mMaxReq-- <= 0) - || (SystemClock.elapsedRealtime() - mLastRequest - > RESPONSE_EXPECTED_TIMEOUT))) { - try { - mSocket.close(); - } catch (IOException e) { - - } - - // Log.d(TAG, "not alive - recreate connection " + mMaxReq); - mSocket = null; - } - - if (mSocket == null) { - lwHttpConnect(); - // we know our server - mMaxReq = RESPONSE_EXPECTED_LIVES; - // Log.d(TAG, "create connection"); - } else { - // should not be needed - int avail = mResponseStream.available(); - if (avail > 0) { - Log.d(TAG, "Consume left-over bytes: " + avail); - mResponseStream.read(mReadBuffer, 0, avail); - } - } - - byte[] request = mRequestBuffer; - int pos = REQUEST_GET_START.length; - - pos = writeInt(tile.zoomLevel, pos, request); - request[pos++] = '/'; - pos = writeInt(tile.tileX, pos, request); - request[pos++] = '/'; - pos = writeInt(tile.tileY, pos, request); - - int len = REQUEST_GET_END.length; - System.arraycopy(REQUEST_GET_END, 0, request, pos, len); - len += pos; - - // this does the same but with a few more allocations: - // byte[] request = String.format(REQUEST, - // Integer.valueOf(tile.zoomLevel), - // Integer.valueOf(tile.tileX), Integer.valueOf(tile.tileY)).getBytes(); - - try { - mCommandStream.write(request, 0, len); - mCommandStream.flush(); - return true; - } catch (IOException e) { - Log.d(TAG, mTile + " recreate connection"); - } - - lwHttpConnect(); - - mCommandStream.write(request, 0, len); - mCommandStream.flush(); - - return true; - } - - private boolean lwHttpConnect() throws IOException { - if (mSockAddr == null) - mSockAddr = new InetSocketAddress(mHost, mPort); - - mSocket = new Socket(); - mSocket.connect(mSockAddr, 30000); - mSocket.setTcpNoDelay(true); - - mCommandStream = mSocket.getOutputStream(); //new BufferedOutputStream(); - mResponseStream = mSocket.getInputStream(); - - return true; - } - - // write (positive) integer as char sequence to buffer - private static int writeInt(int val, int pos, byte[] buf) { - if (val == 0) { - buf[pos] = '0'; - return pos + 1; - } - - int i = 0; - for (int n = val; n > 0; n = n / 10, i++) - buf[pos + i] = (byte) ('0' + n % 10); - - // reverse bytes - for (int j = pos, end = pos + i - 1, mid = pos + i / 2; j < mid; j++, end--) { - byte tmp = buf[j]; - buf[j] = buf[end]; - buf[end] = tmp; - } - - return pos + i; - } - - private static boolean compareBytes(byte[] buffer, int position, int available, - byte[] string, int length) { - - if (available - position < length) - return false; - - for (int i = 0; i < length; i++) - if (buffer[position + i] != string[i]) - return false; - - return true; - } - - // ///////////////////////// Tile cache ///////////////////////////////// - - private boolean cacheRead(Tile tile, File f) { - if (f.exists() && f.length() > 0) { - FileInputStream in; - - try { - in = new FileInputStream(f); - - mContentLenth = f.length(); - Log.d(TAG, tile + " - using cache: " + mContentLenth); - mInputStream = in; - - decode(); - in.close(); - - return true; - - } catch (FileNotFoundException e) { - e.printStackTrace(); - } catch (Exception ex) { - ex.printStackTrace(); - } - - f.delete(); - return false; - } - - return false; - } - - private boolean cacheBegin(Tile tile, File f) { - if (USE_CACHE) { - try { - Log.d(TAG, tile + " - writing cache"); - mCacheFile = new FileOutputStream(f); - - if (mReadPos > 0) { - try { - mCacheFile.write(mReadBuffer, mBufferPos, - mBufferFill - mBufferPos); - - } catch (IOException e) { - e.printStackTrace(); - mCacheFile = null; - return false; - } - } - } catch (FileNotFoundException e) { - e.printStackTrace(); - mCacheFile = null; - return false; - } - } - return true; - } - - private void cacheFinish(Tile tile, File file, boolean success) { - if (USE_CACHE) { - if (success) { - try { - mCacheFile.flush(); - mCacheFile.close(); - Log.d(TAG, tile + " - cache written " + file.length()); - } catch (IOException e) { - e.printStackTrace(); - } - } else { - file.delete(); - } - } - mCacheFile = null; - } }