📄 httpdatachannel.java
字号:
/* * Copyright (C) 2007-2008 Esmertec AG. * Copyright (C) 2007-2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package com.android.im.imps;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.InputStream;import java.net.HttpURLConnection;import java.net.URI;import java.net.URISyntaxException;import java.util.concurrent.LinkedBlockingQueue;import java.util.concurrent.TimeUnit;import java.util.concurrent.atomic.AtomicBoolean;import org.apache.http.Header;import org.apache.http.HttpEntity;import org.apache.http.HttpResponse;import org.apache.http.StatusLine;import org.apache.http.client.methods.HttpPost;import org.apache.http.entity.ByteArrayEntity;import org.apache.http.message.BasicHeader;import org.apache.http.params.HttpConnectionParams;import org.apache.http.params.HttpParams;import android.net.http.AndroidHttpClient;import android.os.SystemClock;import android.util.Log;import com.android.im.engine.ImErrorInfo;import com.android.im.engine.ImException;import com.android.im.imps.Primitive.TransactionMode;/** * The <code>HttpDataChannel</code> is an implementation of IMPS data channel * in which the protocol binding is HTTP. */class HttpDataChannel extends DataChannel implements Runnable { private static final int MAX_RETRY_COUNT = 10; private static final int INIT_RETRY_DELAY_MS = 5000; private static final int MAX_RETRY_DELAY_MS = 300 * 1000; private Thread mSendThread; private boolean mStopped; private boolean mConnected; private boolean mStopRetry; private Object mRetryLock = new Object(); private LinkedBlockingQueue<Primitive> mSendQueue; private LinkedBlockingQueue<Primitive> mReceiveQueue; private long mLastActive; private int mKeepAliveMillis; private Primitive mKeepAlivePrimitive; private AtomicBoolean mHasPendingPolling = new AtomicBoolean(false); private final AndroidHttpClient mHttpClient; private final Header mContentTypeHeader; private final Header mMsisdnHeader; private URI mPostUri; private ImpsTransactionManager mTxManager; /** * Constructs a new HttpDataChannel for a connection. * * @param connection the connection which uses the data channel. */ public HttpDataChannel(ImpsConnection connection) throws ImException { super(connection); mTxManager = connection.getTransactionManager(); ImpsConnectionConfig cfg = connection.getConfig(); try { mPostUri = new URI(cfg.getHost()); if (mPostUri.getPath() == null || "".equals(mPostUri.getPath())) { mPostUri = new URI(cfg.getHost() + "/"); } if (!"http".equalsIgnoreCase(mPostUri.getScheme()) && !"https".equalsIgnoreCase(mPostUri.getScheme())) { throw new ImException(ImErrorInfo.INVALID_HOST_NAME, "Non HTTP/HTTPS host name."); } mHttpClient = AndroidHttpClient.newInstance("Android-Imps/0.1"); HttpParams params = mHttpClient.getParams(); HttpConnectionParams.setConnectionTimeout(params, cfg.getReplyTimeout()); HttpConnectionParams.setSoTimeout(params, cfg.getReplyTimeout()); } catch (URISyntaxException e) { throw new ImException(ImErrorInfo.INVALID_HOST_NAME, e.getLocalizedMessage()); } mContentTypeHeader = new BasicHeader("Content-Type", cfg.getTransportContentType()); String msisdn = cfg.getMsisdn(); mMsisdnHeader = (msisdn != null) ? new BasicHeader("MSISDN", msisdn) : null; } @Override public void connect() throws ImException { if (mConnected) { throw new ImException("Already connected"); } mStopped = false; mStopRetry = false; mSendQueue = new LinkedBlockingQueue<Primitive>(); mReceiveQueue = new LinkedBlockingQueue<Primitive>(); mSendThread = new Thread(this, "HttpDataChannel"); mSendThread.setDaemon(true); mSendThread.start(); mConnected = true; } @Override public void shutdown() { // Stop the sending thread mStopped = true; mSendThread.interrupt(); mConnected = false; } @Override public void sendPrimitive(Primitive p) { if (!mConnected || mStopped) { ImpsLog.log("DataChannel not connected, ignore primitive " + p.getType()); return; } if (ImpsTags.Polling_Request.equals(p.getType())) { if (!mHasPendingPolling.compareAndSet(false, true)) { ImpsLog.log("HttpDataChannel: Ignoring Polling-Request"); return; } } else if (ImpsTags.Logout_Request.equals(p.getType())) { mStopRetry = true; synchronized (mRetryLock) { mRetryLock.notify(); } } if (!mSendQueue.offer(p)) { // This is almost impossible for a LinkedBlockingQueue. We don't // even bother to assign an error code for this. ;) mTxManager.notifyErrorResponse(p.getTransactionID(), ImErrorInfo.UNKNOWN_ERROR, "sending queue full"); } } @Override public Primitive receivePrimitive() throws InterruptedException { if (!mConnected || mStopped) { throw new IllegalStateException(); } return mReceiveQueue.take(); } @Override public void startKeepAlive(long interval) { if (!mConnected || mStopped) { throw new IllegalStateException(); } if (interval <= 0) { interval = mConnection.getConfig().getDefaultKeepAliveInterval(); } mKeepAliveMillis = (int)(interval * 1000 - 100); if (mKeepAliveMillis < 0) { ImpsLog.log("Negative keep alive time. Won't send keep-alive"); } mKeepAlivePrimitive = new Primitive(ImpsTags.KeepAlive_Request); } @Override public long getLastActiveTime() { return mLastActive; } @Override public boolean isSendingQueueEmpty() { if (!mConnected || mStopped) { throw new IllegalStateException(); } return mSendQueue.isEmpty(); } public void run() { boolean needKeepAlive = false; while (!mStopped) { if (needKeepAlive) { sendKeepAlive(); needKeepAlive = false; } Primitive primitive;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -