/* package */ Boundary(String boundary) { if (TextUtils.isEmpty(boundary)) { boundary = generateBoundary(); } this.boundary = boundary; final String starting = "--" + boundary + MultipartEntity.CRLF; // $NON-NLS-1$ final String closing = "--" + boundary + "--" + MultipartEntity.CRLF; // $NON-NLS-1$ startingBoundary = EncodingUtils.getAsciiBytes(starting); closingBoundary = EncodingUtils.getAsciiBytes(closing); }
/** * Creates a random cnonce value based on the current time. * * @return The cnonce value as String. * @throws UnsupportedDigestAlgorithmException if MD5 algorithm is not supported. */ public static String createCnonce() { String cnonce; MessageDigest md5Helper = createMessageDigest("MD5"); cnonce = Long.toString(System.currentTimeMillis()); cnonce = encode(md5Helper.digest(EncodingUtils.getAsciiBytes(cnonce))); return cnonce; }
void sendToServer(String strMsg) { byte[] msg = EncodingUtils.getAsciiBytes(strMsg); if (msg.length > buffer.capacity()) // check if buffer long enough buffer = ByteBuffer.wrap(msg); buffer.clear(); buffer.put(msg); buffer.flip(); try { GlobalData.socketChannel.write(buffer); } catch (IOException e) { Log.d(TAG, "error on write: " + e.getMessage()); } }
/** * Returns the MIME boundary string that is used to demarcate boundaries of this part. The first * call to this method will implicitly create a new boundary string. To create a boundary string * first the HttpMethodParams.MULTIPART_BOUNDARY parameter is considered. Otherwise a random one * is generated. * * @return The boundary string of this entity in ASCII encoding. */ protected byte[] getMultipartBoundary() { if (multipartBoundary == null) { String temp = null; if (params != null) { temp = (String) params.getParameter(MULTIPART_BOUNDARY); } if (temp != null) { multipartBoundary = EncodingUtils.getAsciiBytes(temp); } else { multipartBoundary = generateMultipartBoundary(); } } return multipartBoundary; }
/** * Handles the given exception and generates an HTTP response to be sent back to the client to * inform about the exceptional condition encountered in the course of the request processing. * * @param ex the exception. * @param response the HTTP response. */ protected void handleException(final HttpException ex, final HttpResponse response) { if (ex instanceof MethodNotSupportedException) { response.setStatusCode(HttpStatus.SC_NOT_IMPLEMENTED); } else if (ex instanceof UnsupportedHttpVersionException) { response.setStatusCode(HttpStatus.SC_HTTP_VERSION_NOT_SUPPORTED); } else if (ex instanceof ProtocolException) { response.setStatusCode(HttpStatus.SC_BAD_REQUEST); } else { response.setStatusCode(HttpStatus.SC_INTERNAL_SERVER_ERROR); } byte[] msg = EncodingUtils.getAsciiBytes(ex.getMessage()); ByteArrayEntity entity = new ByteArrayEntity(msg); entity.setContentType("text/plain; charset=US-ASCII"); response.setEntity(entity); }
@Test public void testDecodingFileWithOffsetAndBufferedSessionData() throws Exception { final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] {"stuff; ", "more stuff; ", "a lot more stuff!"}, Consts.ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, Consts.ASCII); final HttpTransportMetricsImpl metrics = new HttpTransportMetricsImpl(); final IdentityDecoder decoder = new IdentityDecoder(channel, inbuf, metrics); final int i = inbuf.fill(channel); Assert.assertEquals(7, i); final byte[] beginning = EncodingUtils.getAsciiBytes("beginning; "); createTempFile(); RandomAccessFile testfile = new RandomAccessFile(this.tmpfile, "rw"); try { testfile.write(beginning); } finally { testfile.close(); } testfile = new RandomAccessFile(this.tmpfile, "rw"); try { final FileChannel fchannel = testfile.getChannel(); long pos = beginning.length; while (!decoder.isCompleted()) { if (testfile.length() < pos) { testfile.setLength(pos); } final long bytesRead = decoder.transfer(fchannel, pos, 10); if (bytesRead > 0) { pos += bytesRead; } } // count everything except the initial 7 bytes that went to the session buffer Assert.assertEquals(testfile.length() - 7 - beginning.length, metrics.getBytesTransferred()); } finally { testfile.close(); } Assert.assertEquals( "beginning; stuff; more stuff; a lot more stuff!", CodecTestUtils.readFromFile(this.tmpfile)); }
protected void handleException(HttpException var1, HttpResponse var2) { if (var1 instanceof MethodNotSupportedException) { var2.setStatusCode(501); } else if (var1 instanceof UnsupportedHttpVersionException) { var2.setStatusCode(505); } else if (var1 instanceof ProtocolException) { var2.setStatusCode(400); } else { var2.setStatusCode(500); } String var4 = var1.getMessage(); String var3 = var4; if (var4 == null) { var3 = var1.toString(); } ByteArrayEntity var5 = new ByteArrayEntity(EncodingUtils.getAsciiBytes(var3)); var5.setContentType("text/plain; charset=US-ASCII"); var2.setEntity(var5); }
/** * This test case executes a series of simple POST requests that do not meet the target server * expectations. */ @Test public void testHttpPostsWithExpectationVerification() throws Exception { final int reqNo = 3; // Initialize the server-side request handler this.server.registerHandler( "*", new HttpRequestHandler() { public void handle( final HttpRequest request, final HttpResponse response, final HttpContext context) throws HttpException, IOException { final StringEntity outgoing = new StringEntity("No content"); response.setEntity(outgoing); } }); this.server.setExpectationVerifier( new HttpExpectationVerifier() { public void verify( final HttpRequest request, final HttpResponse response, final HttpContext context) throws HttpException { final Header someheader = request.getFirstHeader("Secret"); if (someheader != null) { int secretNumber; try { secretNumber = Integer.parseInt(someheader.getValue()); } catch (final NumberFormatException ex) { response.setStatusCode(HttpStatus.SC_BAD_REQUEST); return; } if (secretNumber < 2) { response.setStatusCode(HttpStatus.SC_EXPECTATION_FAILED); final ByteArrayEntity outgoing = new ByteArrayEntity(EncodingUtils.getAsciiBytes("Wrong secret number")); response.setEntity(outgoing); } } } }); this.server.start(); final DefaultBHttpClientConnection conn = client.createConnection(); final HttpHost host = new HttpHost("localhost", this.server.getPort()); try { for (int r = 0; r < reqNo; r++) { if (!conn.isOpen()) { client.connect(host, conn); } final BasicHttpEntityEnclosingRequest post = new BasicHttpEntityEnclosingRequest("POST", "/"); post.addHeader("Secret", Integer.toString(r)); final ByteArrayEntity outgoing = new ByteArrayEntity(EncodingUtils.getAsciiBytes("No content " + r)); post.setEntity(outgoing); final HttpResponse response = this.client.execute(post, host, conn); final HttpEntity entity = response.getEntity(); Assert.assertNotNull(entity); EntityUtils.consume(entity); if (r < 2) { Assert.assertEquals( HttpStatus.SC_EXPECTATION_FAILED, response.getStatusLine().getStatusCode()); } else { Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode()); } if (!this.client.keepAlive(response)) { conn.close(); } } // Verify the connection metrics final HttpConnectionMetrics cm = conn.getMetrics(); Assert.assertEquals(reqNo, cm.getRequestCount()); Assert.assertEquals(reqNo, cm.getResponseCount()); } finally { conn.close(); this.server.shutdown(); } }
/** * Implements a request entity suitable for an HTTP multipart POST method. * * <p>The HTTP multipart POST method is defined in section 3.3 of <a * href="http://www.ietf.org/rfc/rfc1867.txt">RFC1867</a>: * * <blockquote> * * The media-type multipart/form-data follows the rules of all multipart MIME data streams as * outlined in RFC 1521. The multipart/form-data contains a series of parts. Each part is expected * to contain a content-disposition header where the value is "form-data" and a name attribute * specifies the field name within the form, e.g., 'content-disposition: form-data; name="xxxxx"', * where xxxxx is the field name corresponding to that field. Field names originally in non-ASCII * character sets may be encoded using the method outlined in RFC 1522. * * </blockquote> * * <p>This entity is designed to be used in conjunction with the {@link org.apache.http.HttpRequest} * to provide multipart posts. Example usage: * * <pre> * File f = new File("/path/fileToUpload.txt"); * HttpRequest request = new HttpRequest("http://host/some_path"); * Part[] parts = { * new StringPart("param_name", "value"), * new FilePart(f.getName(), f) * }; * filePost.setEntity( * new MultipartRequestEntity(parts, filePost.getParams()) * ); * HttpClient client = new HttpClient(); * int status = client.executeMethod(filePost); * </pre> * * @since 3.0 */ public class MultipartEntity extends AbstractHttpEntity { // private static final Log log = LogFactory.getLog(MultipartEntity.class); /** The Content-Type for multipart/form-data. */ private static final String MULTIPART_FORM_CONTENT_TYPE = "multipart/form-data"; /** * Sets the value to use as the multipart boundary. * * <p>This parameter expects a value if type {@link String}. */ public static final String MULTIPART_BOUNDARY = "http.method.multipart.boundary"; /** The pool of ASCII chars to be used for generating a multipart boundary. */ private static byte[] MULTIPART_CHARS = EncodingUtils.getAsciiBytes( "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); /** Generates a random multipart boundary string. */ public static byte[] generateMultipartBoundary() { Random rand = new Random(); byte[] bytes = new byte[rand.nextInt(11) + 30]; // a random size from 30 to 40 for (int i = 0; i < bytes.length; i++) { bytes[i] = MULTIPART_CHARS[rand.nextInt(MULTIPART_CHARS.length)]; } return bytes; } /** The MIME parts as set by the constructor */ protected Part[] parts; private byte[] multipartBoundary; private HttpParams params; private boolean contentConsumed = false; /** * Creates a new multipart entity containing the given parts. * * @param parts The parts to include. * @param params The params of the HttpMethod using this entity. */ public MultipartEntity(Part[] parts, HttpParams params) { if (parts == null) { throw new IllegalArgumentException("parts cannot be null"); } if (params == null) { throw new IllegalArgumentException("params cannot be null"); } this.parts = parts; this.params = params; } public MultipartEntity(Part[] parts) { setContentType(MULTIPART_FORM_CONTENT_TYPE); if (parts == null) { throw new IllegalArgumentException("parts cannot be null"); } this.parts = parts; this.params = null; } /** * Returns the MIME boundary string that is used to demarcate boundaries of this part. The first * call to this method will implicitly create a new boundary string. To create a boundary string * first the HttpMethodParams.MULTIPART_BOUNDARY parameter is considered. Otherwise a random one * is generated. * * @return The boundary string of this entity in ASCII encoding. */ protected byte[] getMultipartBoundary() { if (multipartBoundary == null) { String temp = null; if (params != null) { temp = (String) params.getParameter(MULTIPART_BOUNDARY); } if (temp != null) { multipartBoundary = EncodingUtils.getAsciiBytes(temp); } else { multipartBoundary = generateMultipartBoundary(); } } return multipartBoundary; } /** Returns <code>true</code> if all parts are repeatable, <code>false</code> otherwise. */ public boolean isRepeatable() { for (int i = 0; i < parts.length; i++) { if (!parts[i].isRepeatable()) { return false; } } return true; } /* (non-Javadoc) */ public void writeTo(OutputStream out) throws IOException { // if (out != null) { // android.util.Log.v("multipartEntity", "out != null"); // } Part.sendParts(out, parts, getMultipartBoundary()); } /* (non-Javadoc) * @see org.apache.commons.http.AbstractHttpEntity.#getContentType() */ @Override public Header getContentType() { StringBuffer buffer = new StringBuffer(MULTIPART_FORM_CONTENT_TYPE); buffer.append("; boundary="); buffer.append(EncodingUtils.getAsciiString(getMultipartBoundary())); return new BasicHeader(HTTP.CONTENT_TYPE, buffer.toString()); } /* (non-Javadoc) */ public long getContentLength() { try { return Part.getLengthOfParts(parts, getMultipartBoundary()); } catch (Exception e) { // log.error("An exception occurred while getting the length of the parts", e); return 0; } } public InputStream getContent() throws IOException, IllegalStateException { if (!isRepeatable() && this.contentConsumed) { throw new IllegalStateException("Content has been consumed"); } this.contentConsumed = true; ByteArrayOutputStream baos = new ByteArrayOutputStream(); Part.sendParts(baos, this.parts, this.multipartBoundary); ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); return bais; } public boolean isStreaming() { return false; } }
/** * Creates digest-response header as defined in RFC2617. * * @param credentials User credentials * @return The digest-response as String. */ private Header createDigestHeader(final Credentials credentials) throws AuthenticationException { String uri = getParameter("uri"); String realm = getParameter("realm"); String nonce = getParameter("nonce"); String opaque = getParameter("opaque"); String method = getParameter("methodname"); String algorithm = getParameter("algorithm"); if (uri == null) { throw new IllegalStateException("URI may not be null"); } if (realm == null) { throw new IllegalStateException("Realm may not be null"); } if (nonce == null) { throw new IllegalStateException("Nonce may not be null"); } // TODO: add support for QOP_INT int qop = QOP_UNKNOWN; String qoplist = getParameter("qop"); if (qoplist != null) { StringTokenizer tok = new StringTokenizer(qoplist, ","); while (tok.hasMoreTokens()) { String variant = tok.nextToken().trim(); if (variant.equals("auth")) { qop = QOP_AUTH; break; } } } else { qop = QOP_MISSING; } if (qop == QOP_UNKNOWN) { throw new AuthenticationException("None of the qop methods is supported: " + qoplist); } // If an algorithm is not specified, default to MD5. if (algorithm == null) { algorithm = "MD5"; } // If an charset is not specified, default to ISO-8859-1. String charset = getParameter("charset"); if (charset == null) { charset = "ISO-8859-1"; } String digAlg = algorithm; if (digAlg.equalsIgnoreCase("MD5-sess")) { digAlg = "MD5"; } MessageDigest digester; try { digester = createMessageDigest(digAlg); } catch (UnsupportedDigestAlgorithmException ex) { throw new AuthenticationException("Unsuppported digest algorithm: " + digAlg); } String uname = credentials.getUserPrincipal().getName(); String pwd = credentials.getPassword(); if (nonce.equals(this.lastNonce)) { nounceCount++; } else { nounceCount = 1; cnonce = null; lastNonce = nonce; } StringBuilder sb = new StringBuilder(256); Formatter formatter = new Formatter(sb, Locale.US); formatter.format("%08x", nounceCount); String nc = sb.toString(); if (cnonce == null) { cnonce = createCnonce(); } a1 = null; a2 = null; // 3.2.2.2: Calculating digest if (algorithm.equalsIgnoreCase("MD5-sess")) { // H( unq(username-value) ":" unq(realm-value) ":" passwd ) // ":" unq(nonce-value) // ":" unq(cnonce-value) // calculated one per session sb.setLength(0); sb.append(uname).append(':').append(realm).append(':').append(pwd); String checksum = encode(digester.digest(EncodingUtils.getBytes(sb.toString(), charset))); sb.setLength(0); sb.append(checksum).append(':').append(nonce).append(':').append(cnonce); a1 = sb.toString(); } else { // unq(username-value) ":" unq(realm-value) ":" passwd sb.setLength(0); sb.append(uname).append(':').append(realm).append(':').append(pwd); a1 = sb.toString(); } String hasha1 = encode(digester.digest(EncodingUtils.getBytes(a1, charset))); if (qop == QOP_AUTH) { // Method ":" digest-uri-value a2 = method + ':' + uri; } else if (qop == QOP_AUTH_INT) { // Method ":" digest-uri-value ":" H(entity-body) // TODO: calculate entity hash if entity is repeatable throw new AuthenticationException("qop-int method is not suppported"); } else { a2 = method + ':' + uri; } String hasha2 = encode(digester.digest(EncodingUtils.getBytes(a2, charset))); // 3.2.2.1 String digestValue; if (qop == QOP_MISSING) { sb.setLength(0); sb.append(hasha1).append(':').append(nonce).append(':').append(hasha2); digestValue = sb.toString(); } else { sb.setLength(0); sb.append(hasha1) .append(':') .append(nonce) .append(':') .append(nc) .append(':') .append(cnonce) .append(':') .append(qop == QOP_AUTH_INT ? "auth-int" : "auth") .append(':') .append(hasha2); digestValue = sb.toString(); } String digest = encode(digester.digest(EncodingUtils.getAsciiBytes(digestValue))); CharArrayBuffer buffer = new CharArrayBuffer(128); if (isProxy()) { buffer.append(AUTH.PROXY_AUTH_RESP); } else { buffer.append(AUTH.WWW_AUTH_RESP); } buffer.append(": Digest "); List<BasicNameValuePair> params = new ArrayList<BasicNameValuePair>(20); params.add(new BasicNameValuePair("username", uname)); params.add(new BasicNameValuePair("realm", realm)); params.add(new BasicNameValuePair("nonce", nonce)); params.add(new BasicNameValuePair("uri", uri)); params.add(new BasicNameValuePair("response", digest)); if (qop != QOP_MISSING) { params.add(new BasicNameValuePair("qop", qop == QOP_AUTH_INT ? "auth-int" : "auth")); params.add(new BasicNameValuePair("nc", nc)); params.add(new BasicNameValuePair("cnonce", cnonce)); } if (algorithm != null) { params.add(new BasicNameValuePair("algorithm", algorithm)); } if (opaque != null) { params.add(new BasicNameValuePair("opaque", opaque)); } for (int i = 0; i < params.size(); i++) { BasicNameValuePair param = params.get(i); if (i > 0) { buffer.append(", "); } boolean noQuotes = "nc".equals(param.getName()) || "qop".equals(param.getName()); BasicHeaderValueFormatter.DEFAULT.formatNameValuePair(buffer, param, !noQuotes); } return new BufferedHeader(buffer); }
/** * Creates an MD5 response digest. * * @return The created digest as string. This will be the response tag's value in the * Authentication HTTP header. * @throws AuthenticationException when MD5 is an unsupported algorithm */ private String createDigest(final Credentials credentials) throws AuthenticationException { // Collecting required tokens String uri = getParameter("uri"); String realm = getParameter("realm"); String nonce = getParameter("nonce"); String method = getParameter("methodname"); String algorithm = getParameter("algorithm"); if (uri == null) { throw new IllegalStateException("URI may not be null"); } if (realm == null) { throw new IllegalStateException("Realm may not be null"); } if (nonce == null) { throw new IllegalStateException("Nonce may not be null"); } // If an algorithm is not specified, default to MD5. if (algorithm == null) { algorithm = "MD5"; } // If an charset is not specified, default to ISO-8859-1. String charset = getParameter("charset"); if (charset == null) { charset = "ISO-8859-1"; } if (qopVariant == QOP_AUTH_INT) { throw new AuthenticationException("Unsupported qop in HTTP Digest authentication"); } MessageDigest md5Helper = createMessageDigest("MD5"); String uname = credentials.getUserPrincipal().getName(); String pwd = credentials.getPassword(); // 3.2.2.2: Calculating digest StringBuilder tmp = new StringBuilder(uname.length() + realm.length() + pwd.length() + 2); tmp.append(uname); tmp.append(':'); tmp.append(realm); tmp.append(':'); tmp.append(pwd); // unq(username-value) ":" unq(realm-value) ":" passwd String a1 = tmp.toString(); // a1 is suitable for MD5 algorithm if (algorithm.equals("MD5-sess")) { // H( unq(username-value) ":" unq(realm-value) ":" passwd ) // ":" unq(nonce-value) // ":" unq(cnonce-value) String cnonce = getCnonce(); String tmp2 = encode(md5Helper.digest(EncodingUtils.getBytes(a1, charset))); StringBuilder tmp3 = new StringBuilder(tmp2.length() + nonce.length() + cnonce.length() + 2); tmp3.append(tmp2); tmp3.append(':'); tmp3.append(nonce); tmp3.append(':'); tmp3.append(cnonce); a1 = tmp3.toString(); } else if (!algorithm.equals("MD5")) { throw new AuthenticationException("Unhandled algorithm " + algorithm + " requested"); } String md5a1 = encode(md5Helper.digest(EncodingUtils.getBytes(a1, charset))); String a2 = null; if (qopVariant == QOP_AUTH_INT) { // Unhandled qop auth-int // we do not have access to the entity-body or its hash // TODO: add Method ":" digest-uri-value ":" H(entity-body) } else { a2 = method + ':' + uri; } String md5a2 = encode(md5Helper.digest(EncodingUtils.getAsciiBytes(a2))); // 3.2.2.1 String serverDigestValue; if (qopVariant == QOP_MISSING) { StringBuilder tmp2 = new StringBuilder(md5a1.length() + nonce.length() + md5a2.length()); tmp2.append(md5a1); tmp2.append(':'); tmp2.append(nonce); tmp2.append(':'); tmp2.append(md5a2); serverDigestValue = tmp2.toString(); } else { String qopOption = getQopVariantString(); String cnonce = getCnonce(); StringBuilder tmp2 = new StringBuilder( md5a1.length() + nonce.length() + NC.length() + cnonce.length() + qopOption.length() + md5a2.length() + 5); tmp2.append(md5a1); tmp2.append(':'); tmp2.append(nonce); tmp2.append(':'); tmp2.append(NC); tmp2.append(':'); tmp2.append(cnonce); tmp2.append(':'); tmp2.append(qopOption); tmp2.append(':'); tmp2.append(md5a2); serverDigestValue = tmp2.toString(); } String serverDigest = encode(md5Helper.digest(EncodingUtils.getAsciiBytes(serverDigestValue))); return serverDigest; }