blob: fc441e0e165c75351a6493fe4c1f8a5f10c143ef [file] [log] [blame]
Nick Pelly9439a7f2009-06-30 12:04:36 -07001/*
2 * Copyright (c) 2008-2009, Motorola, Inc.
3 *
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * - Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 *
12 * - Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
15 *
16 * - Neither the name of the Motorola, Inc. nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33package javax.obex;
34
35import java.io.IOException;
36import java.io.InputStream;
37import java.io.DataInputStream;
38import java.io.OutputStream;
39import java.io.DataOutputStream;
40import java.io.ByteArrayOutputStream;
41
42/**
43 * This class implements the Operation interface for server side connections.
44 * <P>
Tao Liejun05ff98bb2009-07-13 15:57:11 -070045 * <STRONG>Request Codes</STRONG> There are four different request codes that
46 * are in this class. 0x02 is a PUT request that signals that the request is not
47 * complete and requires an additional OBEX packet. 0x82 is a PUT request that
48 * says that request is complete. In this case, the server can begin sending the
49 * response. The 0x03 is a GET request that signals that the request is not
50 * finished. When the server receives a 0x83, the client is signaling the server
51 * that it is done with its request. TODO: Extend the ClientOperation and reuse
52 * the methods defined TODO: in that class.
Nick Pelly2e0da962009-06-30 16:28:54 -070053 * @hide
Nick Pelly9439a7f2009-06-30 12:04:36 -070054 */
Tao Liejun3998bf02009-07-02 19:29:09 +080055public final class ServerOperation implements Operation, BaseStream {
Nick Pelly9439a7f2009-06-30 12:04:36 -070056
Tao Liejun3998bf02009-07-02 19:29:09 +080057 public boolean isAborted;
Nick Pelly9439a7f2009-06-30 12:04:36 -070058
Tao Liejun3998bf02009-07-02 19:29:09 +080059 public HeaderSet requestHeader;
Nick Pelly9439a7f2009-06-30 12:04:36 -070060
Tao Liejun3998bf02009-07-02 19:29:09 +080061 public HeaderSet replyHeader;
Nick Pelly9439a7f2009-06-30 12:04:36 -070062
Tao Liejun3998bf02009-07-02 19:29:09 +080063 public boolean finalBitSet;
Nick Pelly9439a7f2009-06-30 12:04:36 -070064
Tao Liejun3998bf02009-07-02 19:29:09 +080065 private InputStream mInput;
Nick Pelly9439a7f2009-06-30 12:04:36 -070066
Tao Liejun3998bf02009-07-02 19:29:09 +080067 private ServerSession mParent;
Nick Pelly9439a7f2009-06-30 12:04:36 -070068
Tao Liejun3998bf02009-07-02 19:29:09 +080069 private int mMaxPacketLength;
Nick Pelly2e0da962009-06-30 16:28:54 -070070
Tao Liejun3998bf02009-07-02 19:29:09 +080071 private int mResponseSize;
Nick Pelly9439a7f2009-06-30 12:04:36 -070072
Tao Liejun3998bf02009-07-02 19:29:09 +080073 private boolean mClosed;
Nick Pelly9439a7f2009-06-30 12:04:36 -070074
Tao Liejun3998bf02009-07-02 19:29:09 +080075 private boolean mGetOperation;
Nick Pelly9439a7f2009-06-30 12:04:36 -070076
Tao Liejun3998bf02009-07-02 19:29:09 +080077 private PrivateInputStream mPrivateInput;
Nick Pelly9439a7f2009-06-30 12:04:36 -070078
Tao Liejun3998bf02009-07-02 19:29:09 +080079 private PrivateOutputStream mPrivateOutput;
Nick Pelly9439a7f2009-06-30 12:04:36 -070080
Tao Liejun3998bf02009-07-02 19:29:09 +080081 private boolean mPrivateOutputOpen;
Nick Pelly9439a7f2009-06-30 12:04:36 -070082
Tao Liejun3998bf02009-07-02 19:29:09 +080083 private String mExceptionString;
Nick Pelly9439a7f2009-06-30 12:04:36 -070084
Tao Liejun3998bf02009-07-02 19:29:09 +080085 private ServerRequestHandler mListener;
Nick Pelly9439a7f2009-06-30 12:04:36 -070086
Tao Liejun3998bf02009-07-02 19:29:09 +080087 private boolean mRequestFinished;
Nick Pelly9439a7f2009-06-30 12:04:36 -070088
Tao Liejun3998bf02009-07-02 19:29:09 +080089 private boolean mHasBody;
Nick Pelly9439a7f2009-06-30 12:04:36 -070090
Matthew Xiefe3807a2013-07-18 17:31:50 -070091 private boolean mSendBodyHeader = true;
92
Nick Pelly9439a7f2009-06-30 12:04:36 -070093 /**
Tao Liejun3998bf02009-07-02 19:29:09 +080094 * Creates new ServerOperation
Nick Pelly9439a7f2009-06-30 12:04:36 -070095 * @param p the parent that created this object
Nick Pelly9439a7f2009-06-30 12:04:36 -070096 * @param in the input stream to read from
Nick Pelly9439a7f2009-06-30 12:04:36 -070097 * @param out the output stream to write to
Nick Pelly9439a7f2009-06-30 12:04:36 -070098 * @param request the initial request that was received from the client
Nick Pelly9439a7f2009-06-30 12:04:36 -070099 * @param maxSize the max packet size that the client will accept
Nick Pelly9439a7f2009-06-30 12:04:36 -0700100 * @param listen the listener that is responding to the request
Nick Pelly2e0da962009-06-30 16:28:54 -0700101 * @throws IOException if an IO error occurs
Nick Pelly9439a7f2009-06-30 12:04:36 -0700102 */
103 public ServerOperation(ServerSession p, InputStream in, int request, int maxSize,
104 ServerRequestHandler listen) throws IOException {
105
106 isAborted = false;
Tao Liejun3998bf02009-07-02 19:29:09 +0800107 mParent = p;
108 mInput = in;
109 mMaxPacketLength = maxSize;
110 mClosed = false;
111 requestHeader = new HeaderSet();
112 replyHeader = new HeaderSet();
113 mPrivateInput = new PrivateInputStream(this);
114 mResponseSize = 3;
115 mListener = listen;
116 mRequestFinished = false;
117 mPrivateOutputOpen = false;
118 mHasBody = false;
Nick Pelly9439a7f2009-06-30 12:04:36 -0700119 int bytesReceived;
120
121 /*
122 * Determine if this is a PUT request
123 */
124 if ((request == 0x02) || (request == 0x82)) {
125 /*
126 * It is a PUT request.
127 */
Tao Liejun3998bf02009-07-02 19:29:09 +0800128 mGetOperation = false;
Lixin Yue65208312009-11-11 00:51:22 +0800129
130 /*
131 * Determine if the final bit is set
132 */
133 if ((request & 0x80) == 0) {
134 finalBitSet = false;
135 } else {
136 finalBitSet = true;
137 mRequestFinished = true;
138 }
139 } else if ((request == 0x03) || (request == 0x83)) {
Nick Pelly9439a7f2009-06-30 12:04:36 -0700140 /*
141 * It is a GET request.
142 */
Tao Liejun3998bf02009-07-02 19:29:09 +0800143 mGetOperation = true;
Nick Pelly9439a7f2009-06-30 12:04:36 -0700144
Lixin Yue65208312009-11-11 00:51:22 +0800145 // For Get request, final bit set is decided by server side logic
Nick Pelly9439a7f2009-06-30 12:04:36 -0700146 finalBitSet = false;
Lixin Yue65208312009-11-11 00:51:22 +0800147
148 if (request == 0x83) {
149 mRequestFinished = true;
150 }
Nick Pelly9439a7f2009-06-30 12:04:36 -0700151 } else {
Lixin Yue65208312009-11-11 00:51:22 +0800152 throw new IOException("ServerOperation can not handle such request");
Nick Pelly9439a7f2009-06-30 12:04:36 -0700153 }
154
155 int length = in.read();
156 length = (length << 8) + in.read();
157
158 /*
159 * Determine if the packet length is larger than this device can receive
160 */
Nick Pelly2e0da962009-06-30 16:28:54 -0700161 if (length > ObexHelper.MAX_PACKET_SIZE_INT) {
Tao Liejun3998bf02009-07-02 19:29:09 +0800162 mParent.sendResponse(ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE, null);
Nick Pelly9439a7f2009-06-30 12:04:36 -0700163 throw new IOException("Packet received was too large");
164 }
165
166 /*
167 * Determine if any headers were sent in the initial request
168 */
169 if (length > 3) {
170 byte[] data = new byte[length - 3];
171 bytesReceived = in.read(data);
172
173 while (bytesReceived != data.length) {
174 bytesReceived += in.read(data, bytesReceived, data.length - bytesReceived);
175 }
176
Tao Liejun3998bf02009-07-02 19:29:09 +0800177 byte[] body = ObexHelper.updateHeaderSet(requestHeader, data);
Nick Pelly9439a7f2009-06-30 12:04:36 -0700178
179 if (body != null) {
Tao Liejun3998bf02009-07-02 19:29:09 +0800180 mHasBody = true;
Nick Pelly9439a7f2009-06-30 12:04:36 -0700181 }
182
Tao Liejune80534f2009-09-09 17:18:49 +0800183 if (mListener.getConnectionId() != -1 && requestHeader.mConnectionID != null) {
Tao Liejun3998bf02009-07-02 19:29:09 +0800184 mListener.setConnectionId(ObexHelper.convertToLong(requestHeader.mConnectionID));
Nick Pelly9439a7f2009-06-30 12:04:36 -0700185 } else {
Tao Liejune80534f2009-09-09 17:18:49 +0800186 mListener.setConnectionId(1);
Nick Pelly9439a7f2009-06-30 12:04:36 -0700187 }
188
Tao Liejun3998bf02009-07-02 19:29:09 +0800189 if (requestHeader.mAuthResp != null) {
190 if (!mParent.handleAuthResp(requestHeader.mAuthResp)) {
191 mExceptionString = "Authentication Failed";
192 mParent.sendResponse(ResponseCodes.OBEX_HTTP_UNAUTHORIZED, null);
193 mClosed = true;
194 requestHeader.mAuthResp = null;
Nick Pelly9439a7f2009-06-30 12:04:36 -0700195 return;
196 }
197 }
198
Tao Liejun3998bf02009-07-02 19:29:09 +0800199 if (requestHeader.mAuthChall != null) {
200 mParent.handleAuthChall(requestHeader);
Nick Pelly9439a7f2009-06-30 12:04:36 -0700201 // send the authResp to the client
Tao Liejun3998bf02009-07-02 19:29:09 +0800202 replyHeader.mAuthResp = new byte[requestHeader.mAuthResp.length];
203 System.arraycopy(requestHeader.mAuthResp, 0, replyHeader.mAuthResp, 0,
204 replyHeader.mAuthResp.length);
205 requestHeader.mAuthResp = null;
206 requestHeader.mAuthChall = null;
Nick Pelly9439a7f2009-06-30 12:04:36 -0700207
208 }
209
210 if (body != null) {
Tao Liejun3998bf02009-07-02 19:29:09 +0800211 mPrivateInput.writeBytes(body, 1);
Nick Pelly9439a7f2009-06-30 12:04:36 -0700212 } else {
Tao Liejun3998bf02009-07-02 19:29:09 +0800213 while ((!mGetOperation) && (!finalBitSet)) {
214 sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
215 if (mPrivateInput.available() > 0) {
Nick Pelly9439a7f2009-06-30 12:04:36 -0700216 break;
217 }
218 }
Tao Liejun3998bf02009-07-02 19:29:09 +0800219 }
220 }
Nick Pelly9439a7f2009-06-30 12:04:36 -0700221
Tao Liejun3998bf02009-07-02 19:29:09 +0800222 while ((!mGetOperation) && (!finalBitSet) && (mPrivateInput.available() == 0)) {
223 sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
224 if (mPrivateInput.available() > 0) {
Nick Pelly9439a7f2009-06-30 12:04:36 -0700225 break;
226 }
227 }
228
229 // wait for get request finished !!!!
Lixin Yue65208312009-11-11 00:51:22 +0800230 while (mGetOperation && !mRequestFinished) {
Tao Liejun3998bf02009-07-02 19:29:09 +0800231 sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
Nick Pelly9439a7f2009-06-30 12:04:36 -0700232 }
Nick Pelly9439a7f2009-06-30 12:04:36 -0700233 }
234
Tao Liejun3998bf02009-07-02 19:29:09 +0800235 public boolean isValidBody() {
236 return mHasBody;
Nick Pelly9439a7f2009-06-30 12:04:36 -0700237 }
238
239 /**
Tao Liejun05ff98bb2009-07-13 15:57:11 -0700240 * Determines if the operation should continue or should wait. If it should
241 * continue, this method will continue the operation.
Nick Pelly9439a7f2009-06-30 12:04:36 -0700242 * @param sendEmpty if <code>true</code> then this will continue the
Tao Liejun05ff98bb2009-07-13 15:57:11 -0700243 * operation even if no headers will be sent; if <code>false</code>
244 * then this method will only continue the operation if there are
245 * headers to send
246 * @param inStream if<code>true</code> the stream is input stream, otherwise
247 * output stream
Nick Pelly9439a7f2009-06-30 12:04:36 -0700248 * @return <code>true</code> if the operation was completed;
Tao Liejun05ff98bb2009-07-13 15:57:11 -0700249 * <code>false</code> if no operation took place
Nick Pelly9439a7f2009-06-30 12:04:36 -0700250 */
251 public synchronized boolean continueOperation(boolean sendEmpty, boolean inStream)
252 throws IOException {
Tao Liejun3998bf02009-07-02 19:29:09 +0800253 if (!mGetOperation) {
Nick Pelly9439a7f2009-06-30 12:04:36 -0700254 if (!finalBitSet) {
255 if (sendEmpty) {
Tao Liejun3998bf02009-07-02 19:29:09 +0800256 sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
Nick Pelly9439a7f2009-06-30 12:04:36 -0700257 return true;
258 } else {
Tao Liejun3998bf02009-07-02 19:29:09 +0800259 if ((mResponseSize > 3) || (mPrivateOutput.size() > 0)) {
260 sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
Nick Pelly9439a7f2009-06-30 12:04:36 -0700261 return true;
262 } else {
263 return false;
264 }
265 }
266 } else {
267 return false;
268 }
269 } else {
Tao Liejun3998bf02009-07-02 19:29:09 +0800270 sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
Nick Pelly9439a7f2009-06-30 12:04:36 -0700271 return true;
272 }
273 }
274
275 /**
Tao Liejun05ff98bb2009-07-13 15:57:11 -0700276 * Sends a reply to the client. If the reply is a OBEX_HTTP_CONTINUE, it
Nick Pelly9439a7f2009-06-30 12:04:36 -0700277 * will wait for a response from the client before ending.
Nick Pelly9439a7f2009-06-30 12:04:36 -0700278 * @param type the response code to send back to the client
Nick Pelly9439a7f2009-06-30 12:04:36 -0700279 * @return <code>true</code> if the final bit was not set on the reply;
Tao Liejun05ff98bb2009-07-13 15:57:11 -0700280 * <code>false</code> if no reply was received because the operation
281 * ended, an abort was received, or the final bit was set in the
282 * reply
Nick Pelly2e0da962009-06-30 16:28:54 -0700283 * @throws IOException if an IO error occurs
Nick Pelly9439a7f2009-06-30 12:04:36 -0700284 */
Tao Liejun3998bf02009-07-02 19:29:09 +0800285 public synchronized boolean sendReply(int type) throws IOException {
Nick Pelly9439a7f2009-06-30 12:04:36 -0700286 ByteArrayOutputStream out = new ByteArrayOutputStream();
287 int bytesReceived;
288
Tao Liejun3998bf02009-07-02 19:29:09 +0800289 long id = mListener.getConnectionId();
Nick Pelly9439a7f2009-06-30 12:04:36 -0700290 if (id == -1) {
Tao Liejun3998bf02009-07-02 19:29:09 +0800291 replyHeader.mConnectionID = null;
Nick Pelly9439a7f2009-06-30 12:04:36 -0700292 } else {
Tao Liejun3998bf02009-07-02 19:29:09 +0800293 replyHeader.mConnectionID = ObexHelper.convertToByteArray(id);
Nick Pelly9439a7f2009-06-30 12:04:36 -0700294 }
295
Tao Liejun3998bf02009-07-02 19:29:09 +0800296 byte[] headerArray = ObexHelper.createHeader(replyHeader, true);
Nick Pelly9439a7f2009-06-30 12:04:36 -0700297 int bodyLength = -1;
298 int orginalBodyLength = -1;
299
Tao Liejun3998bf02009-07-02 19:29:09 +0800300 if (mPrivateOutput != null) {
301 bodyLength = mPrivateOutput.size();
Nick Pelly9439a7f2009-06-30 12:04:36 -0700302 orginalBodyLength = bodyLength;
303 }
304
Tao Liejun3998bf02009-07-02 19:29:09 +0800305 if ((ObexHelper.BASE_PACKET_LENGTH + headerArray.length) > mMaxPacketLength) {
Nick Pelly9439a7f2009-06-30 12:04:36 -0700306
307 int end = 0;
308 int start = 0;
309
310 while (end != headerArray.length) {
Tao Liejun3998bf02009-07-02 19:29:09 +0800311 end = ObexHelper.findHeaderEnd(headerArray, start, mMaxPacketLength
312 - ObexHelper.BASE_PACKET_LENGTH);
Nick Pelly9439a7f2009-06-30 12:04:36 -0700313 if (end == -1) {
314
Tao Liejun3998bf02009-07-02 19:29:09 +0800315 mClosed = true;
Nick Pelly9439a7f2009-06-30 12:04:36 -0700316
Tao Liejun3998bf02009-07-02 19:29:09 +0800317 if (mPrivateInput != null) {
318 mPrivateInput.close();
Nick Pelly9439a7f2009-06-30 12:04:36 -0700319 }
320
Tao Liejun3998bf02009-07-02 19:29:09 +0800321 if (mPrivateOutput != null) {
322 mPrivateOutput.close();
Nick Pelly9439a7f2009-06-30 12:04:36 -0700323 }
Tao Liejun3998bf02009-07-02 19:29:09 +0800324 mParent.sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
Nick Pelly9439a7f2009-06-30 12:04:36 -0700325 throw new IOException("OBEX Packet exceeds max packet size");
326 }
327 byte[] sendHeader = new byte[end - start];
328 System.arraycopy(headerArray, start, sendHeader, 0, sendHeader.length);
329
Tao Liejun3998bf02009-07-02 19:29:09 +0800330 mParent.sendResponse(type, sendHeader);
Nick Pelly9439a7f2009-06-30 12:04:36 -0700331 start = end;
332 }
333
334 if (bodyLength > 0) {
335 return true;
336 } else {
337 return false;
338 }
339
340 } else {
341 out.write(headerArray);
342 }
343
Lixin Yue65208312009-11-11 00:51:22 +0800344 // For Get operation: if response code is OBEX_HTTP_OK, then this is the
345 // last packet; so set finalBitSet to true.
346 if (mGetOperation && type == ResponseCodes.OBEX_HTTP_OK) {
347 finalBitSet = true;
348 }
349
Tao Liejun3998bf02009-07-02 19:29:09 +0800350 if ((finalBitSet) || (headerArray.length < (mMaxPacketLength - 20))) {
Nick Pelly9439a7f2009-06-30 12:04:36 -0700351 if (bodyLength > 0) {
352 /*
353 * Determine if I can send the whole body or just part of
354 * the body. Remember that there is the 3 bytes for the
355 * response message and 3 bytes for the header ID and length
356 */
Tao Liejun3998bf02009-07-02 19:29:09 +0800357 if (bodyLength > (mMaxPacketLength - headerArray.length - 6)) {
358 bodyLength = mMaxPacketLength - headerArray.length - 6;
Nick Pelly9439a7f2009-06-30 12:04:36 -0700359 }
360
Tao Liejun3998bf02009-07-02 19:29:09 +0800361 byte[] body = mPrivateOutput.readBytes(bodyLength);
Nick Pelly9439a7f2009-06-30 12:04:36 -0700362
363 /*
364 * Since this is a put request if the final bit is set or
365 * the output stream is closed we need to send the 0x49
366 * (End of Body) otherwise, we need to send 0x48 (Body)
367 */
Tao Liejun3998bf02009-07-02 19:29:09 +0800368 if ((finalBitSet) || (mPrivateOutput.isClosed())) {
Matthew Xiefe3807a2013-07-18 17:31:50 -0700369 if(mSendBodyHeader == true) {
370 out.write(0x49);
371 bodyLength += 3;
372 out.write((byte)(bodyLength >> 8));
373 out.write((byte)bodyLength);
374 out.write(body);
375 }
Nick Pelly9439a7f2009-06-30 12:04:36 -0700376 } else {
Matthew Xiefe3807a2013-07-18 17:31:50 -0700377 if(mSendBodyHeader == true) {
Nick Pelly9439a7f2009-06-30 12:04:36 -0700378 out.write(0x48);
Matthew Xiefe3807a2013-07-18 17:31:50 -0700379 bodyLength += 3;
380 out.write((byte)(bodyLength >> 8));
381 out.write((byte)bodyLength);
382 out.write(body);
383 }
Nick Pelly9439a7f2009-06-30 12:04:36 -0700384 }
385
Nick Pelly9439a7f2009-06-30 12:04:36 -0700386 }
387 }
388
389 if ((finalBitSet) && (type == ResponseCodes.OBEX_HTTP_OK) && (orginalBodyLength <= 0)) {
Matthew Xiefe3807a2013-07-18 17:31:50 -0700390 if(mSendBodyHeader == true) {
391 out.write(0x49);
392 orginalBodyLength = 3;
393 out.write((byte)(orginalBodyLength >> 8));
394 out.write((byte)orginalBodyLength);
395 }
Nick Pelly9439a7f2009-06-30 12:04:36 -0700396 }
397
Tao Liejun3998bf02009-07-02 19:29:09 +0800398 mResponseSize = 3;
399 mParent.sendResponse(type, out.toByteArray());
Nick Pelly9439a7f2009-06-30 12:04:36 -0700400
Tao Liejun3998bf02009-07-02 19:29:09 +0800401 if (type == ResponseCodes.OBEX_HTTP_CONTINUE) {
402 int headerID = mInput.read();
403 int length = mInput.read();
404 length = (length << 8) + mInput.read();
405 if ((headerID != ObexHelper.OBEX_OPCODE_PUT)
406 && (headerID != ObexHelper.OBEX_OPCODE_PUT_FINAL)
407 && (headerID != ObexHelper.OBEX_OPCODE_GET)
408 && (headerID != ObexHelper.OBEX_OPCODE_GET_FINAL)) {
Nick Pelly9439a7f2009-06-30 12:04:36 -0700409
410 if (length > 3) {
Anders Petersson4507b172010-10-27 08:38:30 +0200411 byte[] temp = new byte[length - 3];
412 // First three bytes already read, compensating for this
Tao Liejun3998bf02009-07-02 19:29:09 +0800413 bytesReceived = mInput.read(temp);
Nick Pelly9439a7f2009-06-30 12:04:36 -0700414
Anders Petersson4507b172010-10-27 08:38:30 +0200415 while (bytesReceived != temp.length) {
416 bytesReceived += mInput.read(temp, bytesReceived,
417 temp.length - bytesReceived);
Nick Pelly9439a7f2009-06-30 12:04:36 -0700418 }
419 }
420
421 /*
422 * Determine if an ABORT was sent as the reply
423 */
Tao Liejun3998bf02009-07-02 19:29:09 +0800424 if (headerID == ObexHelper.OBEX_OPCODE_ABORT) {
425 mParent.sendResponse(ResponseCodes.OBEX_HTTP_OK, null);
426 mClosed = true;
Nick Pelly9439a7f2009-06-30 12:04:36 -0700427 isAborted = true;
Tao Liejun3998bf02009-07-02 19:29:09 +0800428 mExceptionString = "Abort Received";
Nick Pelly9439a7f2009-06-30 12:04:36 -0700429 throw new IOException("Abort Received");
430 } else {
Tao Liejun3998bf02009-07-02 19:29:09 +0800431 mParent.sendResponse(ResponseCodes.OBEX_HTTP_BAD_REQUEST, null);
432 mClosed = true;
433 mExceptionString = "Bad Request Received";
Nick Pelly9439a7f2009-06-30 12:04:36 -0700434 throw new IOException("Bad Request Received");
435 }
436 } else {
437
Lixin Yue65208312009-11-11 00:51:22 +0800438 if ((headerID == ObexHelper.OBEX_OPCODE_PUT_FINAL)) {
Nick Pelly9439a7f2009-06-30 12:04:36 -0700439 finalBitSet = true;
Lixin Yue65208312009-11-11 00:51:22 +0800440 } else if (headerID == ObexHelper.OBEX_OPCODE_GET_FINAL) {
441 mRequestFinished = true;
Nick Pelly9439a7f2009-06-30 12:04:36 -0700442 }
443
444 /*
445 * Determine if the packet length is larger then this device can receive
446 */
Nick Pelly2e0da962009-06-30 16:28:54 -0700447 if (length > ObexHelper.MAX_PACKET_SIZE_INT) {
Tao Liejun3998bf02009-07-02 19:29:09 +0800448 mParent.sendResponse(ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE, null);
Nick Pelly9439a7f2009-06-30 12:04:36 -0700449 throw new IOException("Packet received was too large");
450 }
451
452 /*
453 * Determine if any headers were sent in the initial request
454 */
455 if (length > 3) {
456 byte[] data = new byte[length - 3];
Tao Liejun3998bf02009-07-02 19:29:09 +0800457 bytesReceived = mInput.read(data);
Nick Pelly9439a7f2009-06-30 12:04:36 -0700458
459 while (bytesReceived != data.length) {
Tao Liejun3998bf02009-07-02 19:29:09 +0800460 bytesReceived += mInput.read(data, bytesReceived, data.length
Nick Pelly9439a7f2009-06-30 12:04:36 -0700461 - bytesReceived);
462 }
Tao Liejun3998bf02009-07-02 19:29:09 +0800463 byte[] body = ObexHelper.updateHeaderSet(requestHeader, data);
Nick Pelly9439a7f2009-06-30 12:04:36 -0700464 if (body != null) {
Tao Liejun3998bf02009-07-02 19:29:09 +0800465 mHasBody = true;
Nick Pelly9439a7f2009-06-30 12:04:36 -0700466 }
Tao Liejune80534f2009-09-09 17:18:49 +0800467 if (mListener.getConnectionId() != -1 && requestHeader.mConnectionID != null) {
Tao Liejun3998bf02009-07-02 19:29:09 +0800468 mListener.setConnectionId(ObexHelper
469 .convertToLong(requestHeader.mConnectionID));
Nick Pelly9439a7f2009-06-30 12:04:36 -0700470 } else {
Tao Liejun3998bf02009-07-02 19:29:09 +0800471 mListener.setConnectionId(1);
Nick Pelly9439a7f2009-06-30 12:04:36 -0700472 }
473
Tao Liejun3998bf02009-07-02 19:29:09 +0800474 if (requestHeader.mAuthResp != null) {
475 if (!mParent.handleAuthResp(requestHeader.mAuthResp)) {
476 mExceptionString = "Authentication Failed";
477 mParent.sendResponse(ResponseCodes.OBEX_HTTP_UNAUTHORIZED, null);
478 mClosed = true;
479 requestHeader.mAuthResp = null;
Nick Pelly9439a7f2009-06-30 12:04:36 -0700480 return false;
481 }
Tao Liejun3998bf02009-07-02 19:29:09 +0800482 requestHeader.mAuthResp = null;
Nick Pelly9439a7f2009-06-30 12:04:36 -0700483 }
484
Tao Liejun3998bf02009-07-02 19:29:09 +0800485 if (requestHeader.mAuthChall != null) {
486 mParent.handleAuthChall(requestHeader);
Nick Pelly9439a7f2009-06-30 12:04:36 -0700487 // send the auhtResp to the client
Tao Liejun3998bf02009-07-02 19:29:09 +0800488 replyHeader.mAuthResp = new byte[requestHeader.mAuthResp.length];
489 System.arraycopy(requestHeader.mAuthResp, 0, replyHeader.mAuthResp, 0,
490 replyHeader.mAuthResp.length);
491 requestHeader.mAuthResp = null;
492 requestHeader.mAuthChall = null;
Nick Pelly9439a7f2009-06-30 12:04:36 -0700493 }
494
495 if (body != null) {
Tao Liejun3998bf02009-07-02 19:29:09 +0800496 mPrivateInput.writeBytes(body, 1);
Nick Pelly9439a7f2009-06-30 12:04:36 -0700497 }
498 }
499 }
500 return true;
501 } else {
502 return false;
503 }
504 }
505
506 /**
Tao Liejun05ff98bb2009-07-13 15:57:11 -0700507 * Sends an ABORT message to the server. By calling this method, the
Nick Pelly9439a7f2009-06-30 12:04:36 -0700508 * corresponding input and output streams will be closed along with this
509 * object.
Tao Liejun05ff98bb2009-07-13 15:57:11 -0700510 * @throws IOException if the transaction has already ended or if an OBEX
511 * server called this method
Nick Pelly9439a7f2009-06-30 12:04:36 -0700512 */
513 public void abort() throws IOException {
514 throw new IOException("Called from a server");
515 }
516
517 /**
518 * Returns the headers that have been received during the operation.
Tao Liejun05ff98bb2009-07-13 15:57:11 -0700519 * Modifying the object returned has no effect on the headers that are sent
520 * or retrieved.
Nick Pelly9439a7f2009-06-30 12:04:36 -0700521 * @return the headers received during this <code>Operation</code>
Nick Pelly2e0da962009-06-30 16:28:54 -0700522 * @throws IOException if this <code>Operation</code> has been closed
Nick Pelly9439a7f2009-06-30 12:04:36 -0700523 */
Tao Liejun3998bf02009-07-02 19:29:09 +0800524 public HeaderSet getReceivedHeader() throws IOException {
Nick Pelly9439a7f2009-06-30 12:04:36 -0700525 ensureOpen();
Tao Liejun3998bf02009-07-02 19:29:09 +0800526 return requestHeader;
Nick Pelly9439a7f2009-06-30 12:04:36 -0700527 }
528
529 /**
530 * Specifies the headers that should be sent in the next OBEX message that
531 * is sent.
Nick Pelly9439a7f2009-06-30 12:04:36 -0700532 * @param headers the headers to send in the next message
Tao Liejun05ff98bb2009-07-13 15:57:11 -0700533 * @throws IOException if this <code>Operation</code> has been closed or the
534 * transaction has ended and no further messages will be exchanged
Nick Pelly2e0da962009-06-30 16:28:54 -0700535 * @throws IllegalArgumentException if <code>headers</code> was not created
Tao Liejun05ff98bb2009-07-13 15:57:11 -0700536 * by a call to <code>ServerRequestHandler.createHeaderSet()</code>
Nick Pelly9439a7f2009-06-30 12:04:36 -0700537 */
538 public void sendHeaders(HeaderSet headers) throws IOException {
539 ensureOpen();
540
541 if (headers == null) {
Tao Liejun3998bf02009-07-02 19:29:09 +0800542 throw new IOException("Headers may not be null");
Nick Pelly9439a7f2009-06-30 12:04:36 -0700543 }
544
545 int[] headerList = headers.getHeaderList();
546 if (headerList != null) {
547 for (int i = 0; i < headerList.length; i++) {
Tao Liejun3998bf02009-07-02 19:29:09 +0800548 replyHeader.setHeader(headerList[i], headers.getHeader(headerList[i]));
Nick Pelly9439a7f2009-06-30 12:04:36 -0700549 }
550
551 }
552 }
553
554 /**
Tao Liejun05ff98bb2009-07-13 15:57:11 -0700555 * Retrieves the response code retrieved from the server. Response codes are
556 * defined in the <code>ResponseCodes</code> interface.
Nick Pelly9439a7f2009-06-30 12:04:36 -0700557 * @return the response code retrieved from the server
Nick Pelly2e0da962009-06-30 16:28:54 -0700558 * @throws IOException if an error occurred in the transport layer during
Tao Liejun05ff98bb2009-07-13 15:57:11 -0700559 * the transaction; if this method is called on a
560 * <code>HeaderSet</code> object created by calling
561 * <code>createHeaderSet</code> in a <code>ClientSession</code>
562 * object; if this is called from a server
Nick Pelly9439a7f2009-06-30 12:04:36 -0700563 */
564 public int getResponseCode() throws IOException {
565 throw new IOException("Called from a server");
566 }
567
568 /**
569 * Always returns <code>null</code>
Nick Pelly9439a7f2009-06-30 12:04:36 -0700570 * @return <code>null</code>
571 */
572 public String getEncoding() {
573 return null;
574 }
575
576 /**
577 * Returns the type of content that the resource connected to is providing.
578 * E.g. if the connection is via HTTP, then the value of the content-type
579 * header field is returned.
Nick Pelly9439a7f2009-06-30 12:04:36 -0700580 * @return the content type of the resource that the URL references, or
Tao Liejun05ff98bb2009-07-13 15:57:11 -0700581 * <code>null</code> if not known
Nick Pelly9439a7f2009-06-30 12:04:36 -0700582 */
583 public String getType() {
584 try {
Tao Liejun3998bf02009-07-02 19:29:09 +0800585 return (String)requestHeader.getHeader(HeaderSet.TYPE);
Nick Pelly9439a7f2009-06-30 12:04:36 -0700586 } catch (IOException e) {
587 return null;
588 }
589 }
590
591 /**
592 * Returns the length of the content which is being provided. E.g. if the
Tao Liejun05ff98bb2009-07-13 15:57:11 -0700593 * connection is via HTTP, then the value of the content-length header field
594 * is returned.
Nick Pelly9439a7f2009-06-30 12:04:36 -0700595 * @return the content length of the resource that this connection's URL
Tao Liejun05ff98bb2009-07-13 15:57:11 -0700596 * references, or -1 if the content length is not known
Nick Pelly9439a7f2009-06-30 12:04:36 -0700597 */
598 public long getLength() {
599 try {
Tao Liejun3998bf02009-07-02 19:29:09 +0800600 Long temp = (Long)requestHeader.getHeader(HeaderSet.LENGTH);
Nick Pelly9439a7f2009-06-30 12:04:36 -0700601
602 if (temp == null) {
603 return -1;
604 } else {
605 return temp.longValue();
606 }
607 } catch (IOException e) {
608 return -1;
609 }
610 }
611
612 public int getMaxPacketSize() {
Lixin Yue65208312009-11-11 00:51:22 +0800613 return mMaxPacketLength - 6 - getHeaderLength();
614 }
615
616 public int getHeaderLength() {
617 long id = mListener.getConnectionId();
618 if (id == -1) {
619 replyHeader.mConnectionID = null;
620 } else {
621 replyHeader.mConnectionID = ObexHelper.convertToByteArray(id);
622 }
623
624 byte[] headerArray = ObexHelper.createHeader(replyHeader, false);
625
626 return headerArray.length;
Nick Pelly9439a7f2009-06-30 12:04:36 -0700627 }
628
629 /**
630 * Open and return an input stream for a connection.
Nick Pelly9439a7f2009-06-30 12:04:36 -0700631 * @return an input stream
Nick Pelly2e0da962009-06-30 16:28:54 -0700632 * @throws IOException if an I/O error occurs
Nick Pelly9439a7f2009-06-30 12:04:36 -0700633 */
634 public InputStream openInputStream() throws IOException {
635 ensureOpen();
Tao Liejun3998bf02009-07-02 19:29:09 +0800636 return mPrivateInput;
Nick Pelly9439a7f2009-06-30 12:04:36 -0700637 }
638
639 /**
640 * Open and return a data input stream for a connection.
Nick Pelly9439a7f2009-06-30 12:04:36 -0700641 * @return an input stream
Nick Pelly2e0da962009-06-30 16:28:54 -0700642 * @throws IOException if an I/O error occurs
Nick Pelly9439a7f2009-06-30 12:04:36 -0700643 */
644 public DataInputStream openDataInputStream() throws IOException {
645 return new DataInputStream(openInputStream());
646 }
647
648 /**
649 * Open and return an output stream for a connection.
Nick Pelly9439a7f2009-06-30 12:04:36 -0700650 * @return an output stream
Nick Pelly2e0da962009-06-30 16:28:54 -0700651 * @throws IOException if an I/O error occurs
Nick Pelly9439a7f2009-06-30 12:04:36 -0700652 */
653 public OutputStream openOutputStream() throws IOException {
654 ensureOpen();
655
Tao Liejun3998bf02009-07-02 19:29:09 +0800656 if (mPrivateOutputOpen) {
Nick Pelly9439a7f2009-06-30 12:04:36 -0700657 throw new IOException("no more input streams available, stream already opened");
Nick Pelly9439a7f2009-06-30 12:04:36 -0700658 }
Tao Liejun3998bf02009-07-02 19:29:09 +0800659
660 if (!mRequestFinished) {
661 throw new IOException("no output streams available ,request not finished");
662 }
663
664 if (mPrivateOutput == null) {
Lixin Yue65208312009-11-11 00:51:22 +0800665 mPrivateOutput = new PrivateOutputStream(this, getMaxPacketSize());
Tao Liejun3998bf02009-07-02 19:29:09 +0800666 }
667 mPrivateOutputOpen = true;
668 return mPrivateOutput;
Nick Pelly9439a7f2009-06-30 12:04:36 -0700669 }
670
671 /**
672 * Open and return a data output stream for a connection.
Nick Pelly9439a7f2009-06-30 12:04:36 -0700673 * @return an output stream
Nick Pelly2e0da962009-06-30 16:28:54 -0700674 * @throws IOException if an I/O error occurs
Nick Pelly9439a7f2009-06-30 12:04:36 -0700675 */
676 public DataOutputStream openDataOutputStream() throws IOException {
677 return new DataOutputStream(openOutputStream());
678 }
679
680 /**
681 * Closes the connection and ends the transaction
Nick Pelly2e0da962009-06-30 16:28:54 -0700682 * @throws IOException if the operation has already ended or is closed
Nick Pelly9439a7f2009-06-30 12:04:36 -0700683 */
684 public void close() throws IOException {
685 ensureOpen();
Tao Liejun3998bf02009-07-02 19:29:09 +0800686 mClosed = true;
Nick Pelly9439a7f2009-06-30 12:04:36 -0700687 }
688
689 /**
690 * Verifies that the connection is open and no exceptions should be thrown.
Nick Pelly2e0da962009-06-30 16:28:54 -0700691 * @throws IOException if an exception needs to be thrown
Nick Pelly9439a7f2009-06-30 12:04:36 -0700692 */
693 public void ensureOpen() throws IOException {
Tao Liejun3998bf02009-07-02 19:29:09 +0800694 if (mExceptionString != null) {
695 throw new IOException(mExceptionString);
Nick Pelly9439a7f2009-06-30 12:04:36 -0700696 }
Tao Liejun3998bf02009-07-02 19:29:09 +0800697 if (mClosed) {
Nick Pelly9439a7f2009-06-30 12:04:36 -0700698 throw new IOException("Operation has already ended");
699 }
700 }
701
702 /**
Tao Liejun05ff98bb2009-07-13 15:57:11 -0700703 * Verifies that additional information may be sent. In other words, the
Nick Pelly9439a7f2009-06-30 12:04:36 -0700704 * operation is not done.
705 * <P>
Tao Liejun05ff98bb2009-07-13 15:57:11 -0700706 * Included to implement the BaseStream interface only. It does not do
Nick Pelly9439a7f2009-06-30 12:04:36 -0700707 * anything on the server side since the operation of the Operation object
708 * is not done until after the handler returns from its method.
Nick Pelly2e0da962009-06-30 16:28:54 -0700709 * @throws IOException if the operation is completed
Nick Pelly9439a7f2009-06-30 12:04:36 -0700710 */
711 public void ensureNotDone() throws IOException {
712 }
713
714 /**
Tao Liejun05ff98bb2009-07-13 15:57:11 -0700715 * Called when the output or input stream is closed. It does not do anything
716 * on the server side since the operation of the Operation object is not
717 * done until after the handler returns from its method.
Nick Pelly9439a7f2009-06-30 12:04:36 -0700718 * @param inStream <code>true</code> if the input stream is closed;
Tao Liejun05ff98bb2009-07-13 15:57:11 -0700719 * <code>false</code> if the output stream is closed
Nick Pelly2e0da962009-06-30 16:28:54 -0700720 * @throws IOException if an IO error occurs
Nick Pelly9439a7f2009-06-30 12:04:36 -0700721 */
722 public void streamClosed(boolean inStream) throws IOException {
723
724 }
Matthew Xiefe3807a2013-07-18 17:31:50 -0700725
726 public void noBodyHeader(){
727 mSendBodyHeader = false;
728 }
Nick Pelly9439a7f2009-06-30 12:04:36 -0700729}