/*=============================================================================*
* Copyright 2004 The Apache Software Foundation
*
* 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 org.apache.ws.util.test.axis;
import org.apache.axis.AxisFault;
import org.apache.axis.Constants;
import org.apache.axis.Message;
import org.apache.axis.MessageContext;
import org.apache.axis.description.OperationDesc;
import org.apache.axis.description.ServiceDesc;
import org.apache.axis.encoding.Base64;
import org.apache.axis.message.SOAPEnvelope;
import org.apache.axis.message.SOAPFault;
import org.apache.axis.server.AxisServer;
import org.apache.axis.transport.http.HTTPConstants;
import org.apache.axis.transport.http.NonBlockingBufferedInputStream;
import org.apache.axis.transport.http.SimpleAxisServer;
import org.apache.axis.transport.http.SimpleAxisWorker;
import org.apache.axis.utils.Messages;
import org.apache.axis.utils.XMLUtils;
import org.apache.ws.util.IoUtils;
import org.w3c.dom.Document;
import javax.xml.namespace.QName;
import javax.xml.soap.MimeHeader;
import javax.xml.soap.MimeHeaders;
import javax.xml.soap.SOAPMessage;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Iterator;
/**
* Web server that supports SOAP POST requests and arbitrary GET requests.
*
* @author Ian P. Springer
*/
public class NotSoSimpleAxisWorker
extends SimpleAxisWorker
{
private static final byte[] TO_LOWER = new byte[256];
static
{
for ( int i = 0; i < 256; i++ )
{
TO_LOWER[i] = (byte) i;
}
for ( int lc = 'a'; lc <= 'z'; lc++ )
{
TO_LOWER[( lc + 'A' ) - 'a'] = (byte) lc;
}
}
// buffer for IO
private static final int BUFSIZ = 4096;
// Axis specific constants
private static String TRANSPORT_NAME = "SimpleHTTP";
// header ender
//private static final byte[] HEADER_ENDER = ": ".getBytes( );
// HTTP status codes
private static byte[] OK = ( "200 " + Messages.getMessage( "ok00" ) ).getBytes( );
private static byte[] NOCONTENT = ( "202 " + Messages.getMessage( "ok00" ) + "\n\n" ).getBytes( );
private static byte[] UNAUTH = ( "401 " + Messages.getMessage( "unauth00" ) ).getBytes( );
private static byte[] SENDER = "400".getBytes( );
private static byte[] ISE = ( "500 " + Messages.getMessage( "internalError01" ) ).getBytes( );
// HTTP prefix
private static byte[] HTTP = "HTTP/1.0 ".getBytes( );
// Standard MIME headers for XML payload
private static byte[] XML_MIME_STUFF =
( "\r\nContent-Type: text/xml; charset=utf-8\r\n" + "Content-Length: " ).getBytes( );
// Standard MIME headers for HTML payload
private static byte[] HTML_MIME_STUFF =
( "\r\nContent-Type: text/html; charset=utf-8\r\n" + "Content-Length: " ).getBytes( );
// Mime/Content separator
private static byte[] SEPARATOR = "\r\n\r\n".getBytes( );
// mime header for content length
private static final byte[] lenHeader = "content-length: ".getBytes( );
private static final int lenLen = lenHeader.length;
// mime header for content type
private static final byte[] typeHeader =
( HTTPConstants.HEADER_CONTENT_TYPE.toLowerCase( ) + ": " ).getBytes( );
private static final int typeLen = typeHeader.length;
// mime header for content location
private static final byte[] locationHeader =
( HTTPConstants.HEADER_CONTENT_LOCATION.toLowerCase( ) + ": " ).getBytes( );
private static final int locationLen = locationHeader.length;
// mime header for soap action
private static final byte[] actionHeader = "soapaction: ".getBytes( );
private static final int actionLen = actionHeader.length;
// mime header for cookie
private static final byte[] cookieHeader = "cookie: ".getBytes( );
private static final int cookieLen = cookieHeader.length;
// mime header for cookie2
private static final byte[] cookie2Header = "cookie2: ".getBytes( );
private static final int cookie2Len = cookie2Header.length;
// HTTP header for authentication
private static final byte[] authHeader = "authorization: ".getBytes( );
private static final int authLen = authHeader.length;
// mime header for GET
private static final byte[] getHeader = "GET".getBytes( );
// mime header for POST
private static final byte[] postHeader = "POST".getBytes( );
// header ender
private static final byte[] headerEnder = ": ".getBytes( );
// "Basic" auth string
private static final byte[] basicAuth = "basic ".getBytes( );
private File m_docRootDir;
private NotSoSimpleAxisServer m_server;
private Socket m_socket;
/**
* Creates a new {@link NotSoSimpleAxisWorker} object.
*
* @param server DOCUMENT_ME
* @param socket DOCUMENT_ME
* @param docRootDir DOCUMENT_ME
*/
public NotSoSimpleAxisWorker( NotSoSimpleAxisServer server,
Socket socket,
File docRootDir )
{
super( server, socket );
m_server = server;
m_socket = socket;
m_docRootDir = docRootDir;
}
/**
* The main workhorse method.
*/
public void execute( )
{
byte[] buf = new byte[BUFSIZ];
// create an Axis server
AxisServer engine = m_server.getAxisServer( );
// create and initialize a message context
MessageContext msgContext = new MessageContext( engine );
Message requestMsg = null;
// Reusuable, buffered, content length controlled, InputStream
NonBlockingBufferedInputStream is = new NonBlockingBufferedInputStream( );
// buffers for the headers we care about
StringBuffer soapAction = new StringBuffer( );
StringBuffer httpRequest = new StringBuffer( );
StringBuffer fileName = new StringBuffer( );
StringBuffer cookie = new StringBuffer( );
StringBuffer cookie2 = new StringBuffer( );
StringBuffer authInfo = new StringBuffer( );
StringBuffer contentType = new StringBuffer( );
StringBuffer contentLocation = new StringBuffer( );
Message responseMsg = null;
// prepare request (do as much as possible while waiting for the
// next connection). Note the next two statements are commented
// out. Uncomment them if you experience any problems with not
// resetting state between requests:
// msgContext = new MessageContext();
// requestMsg = new Message("", "String");
//msgContext.setProperty("transport", "HTTPTransport");
msgContext.setTransportName( TRANSPORT_NAME );
responseMsg = null;
try
{
// assume the best
byte[] status = OK;
// assume we're not getting WSDL
boolean doWsdl = false;
// cookie for this session, if any
String cooky = null;
String methodName = null;
try
{
// wipe cookies if we're doing sessions
if ( m_server.isSessionUsed( ) )
{
cookie.delete( 0,
cookie.length( ) );
cookie2.delete( 0,
cookie2.length( ) );
}
authInfo.delete( 0,
authInfo.length( ) );
// read headers
is.setInputStream( m_socket.getInputStream( ) );
// parse all headers into hashtable
MimeHeaders requestHeaders = new MimeHeaders( );
int contentLength =
parseHeaders( is, buf, contentType, contentLocation, soapAction, httpRequest, fileName, cookie,
cookie2, authInfo, requestHeaders );
is.setContentLength( contentLength );
int paramIdx = fileName.toString( ).indexOf( '?' );
if ( paramIdx != -1 )
{
// Got params
String params = fileName.substring( paramIdx + 1 );
fileName.setLength( paramIdx );
log.debug( Messages.getMessage( "filename00",
fileName.toString( ) ) );
log.debug( Messages.getMessage( "params00", params ) );
if ( "wsdl".equalsIgnoreCase( params ) )
{
doWsdl = true;
}
if ( params.startsWith( "method=" ) )
{
methodName = params.substring( 7 );
}
}
// Real and relative paths are the same for the
// SimpleAxisServer
msgContext.setProperty( Constants.MC_REALPATH,
fileName.toString( ) );
msgContext.setProperty( Constants.MC_RELATIVE_PATH,
fileName.toString( ) );
msgContext.setProperty( Constants.MC_JWS_CLASSDIR, "jwsClasses" );
msgContext.setProperty( Constants.MC_HOME_DIR, "." );
// !!! Fix string concatenation
String url =
"http://" + getLocalHost( ) + ":" + m_server.getServerSocket( ).getLocalPort( ) + "/"
+ fileName.toString( );
msgContext.setProperty( MessageContext.TRANS_URL, url );
String filePart = fileName.toString( );
if ( filePart.startsWith( "axis/services/" ) )
{
String servicePart = filePart.substring( 14 );
int separator = servicePart.indexOf( '/' );
if ( separator > -1 )
{
msgContext.setProperty( "objectID",
servicePart.substring( separator + 1 ) );
servicePart = servicePart.substring( 0, separator );
}
msgContext.setTargetService( servicePart );
}
if ( authInfo.length( ) > 0 )
{
// Process authentication info
//authInfo = new StringBuffer("dXNlcjE6cGFzczE=");
byte[] decoded = Base64.decode( authInfo.toString( ) );
StringBuffer userBuf = new StringBuffer( );
StringBuffer pwBuf = new StringBuffer( );
StringBuffer authBuf = userBuf;
for ( int i = 0; i < decoded.length; i++ )
{
if ( (char) ( decoded[i] & 0x7f ) == ':' )
{
authBuf = pwBuf;
continue;
}
authBuf.append( (char) ( decoded[i] & 0x7f ) );
}
if ( log.isDebugEnabled( ) )
{
log.debug( Messages.getMessage( "user00",
userBuf.toString( ) ) );
}
msgContext.setUsername( userBuf.toString( ) );
msgContext.setPassword( pwBuf.toString( ) );
}
// if get, then return simpleton document as response
if ( httpRequest.toString( ).equals( "GET" ) )
{
OutputStream out = m_socket.getOutputStream( );
out.write( HTTP );
if ( fileName.length( ) == 0 )
{
out.write( "301 Redirect\nLocation: /axis/\n\n".getBytes( ) );
out.flush( );
return;
}
out.write( status );
if ( methodName != null )
{
String body = "<" + methodName + ">" +
// args +
"</" + methodName + ">";
String msgtxt =
"<SOAP-ENV:Envelope" + " xmlns:SOAP-ENV=\"" + Constants.URI_SOAP12_ENV + "\">"
+ "<SOAP-ENV:Body>" + body + "</SOAP-ENV:Body>" + "</SOAP-ENV:Envelope>";
ByteArrayInputStream istream = new ByteArrayInputStream( msgtxt.getBytes( ) );
requestMsg = new Message( istream );
}
else if ( doWsdl )
{
engine.generateWSDL( msgContext );
Document doc = (Document) msgContext.getProperty( "WSDL" );
if ( doc != null )
{
XMLUtils.normalize( doc.getDocumentElement( ) );
String response = XMLUtils.PrettyDocumentToString( doc );
byte[] respBytes = response.getBytes( );
out.write( XML_MIME_STUFF );
putInt( buf, out, respBytes.length );
out.write( SEPARATOR );
out.write( respBytes );
out.flush( );
return;
}
}
else if ( fileName.equals( "/" ) )
{
StringBuffer sb = new StringBuffer( );
sb.append( "<h2>And now... Some Services</h2>\n" );
Iterator i = engine.getConfig( ).getDeployedServices( );
sb.append( "<ul>\n" );
while ( i.hasNext( ) )
{
ServiceDesc sd = (ServiceDesc) i.next( );
sb.append( "<li>\n" );
sb.append( sd.getName( ) );
sb.append( " <a href=\"services/" );
sb.append( sd.getName( ) );
sb.append( "?wsdl\"><i>(wsdl)</i></a></li>\n" );
ArrayList operations = sd.getOperations( );
if ( !operations.isEmpty( ) )
{
sb.append( "<ul>\n" );
for ( Iterator it = operations.iterator( ); it.hasNext( ); )
{
OperationDesc desc = (OperationDesc) it.next( );
sb.append( "<li>" + desc.getName( ) );
}
sb.append( "</ul>\n" );
}
}
sb.append( "</ul>\n" );
byte[] bytes = sb.toString( ).getBytes( );
out.write( HTML_MIME_STUFF );
putInt( buf, out, bytes.length );
out.write( SEPARATOR );
out.write( bytes );
out.flush( );
return;
}
else
{
String filePath = fileName.toString( );
if ( filePath.startsWith( "/" ) )
{
filePath = filePath.substring( 1 );
}
File realPath = new File( m_docRootDir, filePath );
InputStream in = new BufferedInputStream( new FileInputStream( realPath ) );
//System.out.println( "Serving up " + realPath + " on a silver platter..." );
IoUtils.copy( in, out );
in.close( );
out.flush( );
return;
}
}
else
{
// this may be "" if either SOAPAction: "" or if no SOAPAction at all.
// for now, do not complain if no SOAPAction at all
String soapActionString = soapAction.toString( );
if ( soapActionString != null )
{
msgContext.setUseSOAPAction( true );
msgContext.setSOAPActionURI( soapActionString );
}
requestMsg = new Message( is,
false,
contentType.toString( ),
contentLocation.toString( ) );
}
// Transfer HTTP headers to MIME headers for request message.
MimeHeaders requestMimeHeaders = requestMsg.getMimeHeaders( );
for ( Iterator i = requestHeaders.getAllHeaders( ); i.hasNext( ); )
{
MimeHeader requestHeader = (MimeHeader) i.next( );
requestMimeHeaders.addHeader( requestHeader.getName( ),
requestHeader.getValue( ) );
}
msgContext.setRequestMessage( requestMsg );
// put character encoding of request to message context
// in order to reuse it during the whole process.
String requestEncoding = (String) requestMsg.getProperty( SOAPMessage.CHARACTER_SET_ENCODING );
if ( requestEncoding != null )
{
msgContext.setProperty( SOAPMessage.CHARACTER_SET_ENCODING, requestEncoding );
}
// set up session, if any
if ( m_server.isSessionUsed( ) )
{
// did we get a cookie?
if ( cookie.length( ) > 0 )
{
cooky = cookie.toString( ).trim( );
}
else if ( cookie2.length( ) > 0 )
{
cooky = cookie2.toString( ).trim( );
}
// if cooky is null, cook up a cooky
if ( cooky == null )
{
// fake one up!
// make it be an arbitrarily increasing number
// (no this is not thread safe because ++ isn't atomic)
int i = SimpleAxisServer.sessionIndex++;
cooky = "" + i;
}
msgContext.setSession( m_server.createSession( cooky ) );
}
// invoke the Axis engine
engine.invoke( msgContext );
// Retrieve the response from Axis
responseMsg = msgContext.getResponseMessage( );
if ( responseMsg == null )
{
status = NOCONTENT;
}
}
catch ( Exception e )
{
AxisFault af;
if ( e instanceof AxisFault )
{
af = (AxisFault) e;
log.debug( Messages.getMessage( "serverFault00" ),
af );
QName faultCode = af.getFaultCode( );
if ( Constants.FAULT_SOAP12_SENDER.equals( faultCode ) )
{
status = SENDER;
}
else if ( "Server.Unauthorized".equals( af.getFaultCode( ).getLocalPart( ) ) )
{
status = UNAUTH; // SC_UNAUTHORIZED
}
else
{
status = ISE; // SC_INTERNAL_SERVER_ERROR
}
}
else
{
status = ISE; // SC_INTERNAL_SERVER_ERROR
af = AxisFault.makeFault( e );
}
// There may be headers we want to preserve in the
// response message - so if it's there, just add the
// FaultElement to it. Otherwise, make a new one.
responseMsg = msgContext.getResponseMessage( );
if ( responseMsg == null )
{
responseMsg = new Message( af );
responseMsg.setMessageContext( msgContext );
}
else
{
try
{
SOAPEnvelope env = responseMsg.getSOAPEnvelope( );
env.clearBody( );
env.addBodyElement( new SOAPFault( (AxisFault) e ) );
}
catch ( AxisFault fault )
{
// Should never reach here!
}
}
}
// synchronize the character encoding of request and response
String responseEncoding = (String) msgContext.getProperty( SOAPMessage.CHARACTER_SET_ENCODING );
if ( ( responseEncoding != null ) && ( responseMsg != null ) )
{
responseMsg.setProperty( SOAPMessage.CHARACTER_SET_ENCODING, responseEncoding );
}
// Send it on its way...
OutputStream out = m_socket.getOutputStream( );
out.write( HTTP );
out.write( status );
if ( responseMsg != null )
{
if ( m_server.isSessionUsed( ) && ( null != cooky ) && ( 0 != cooky.trim( ).length( ) ) )
{
// write cookie headers, if any
// don't sweat efficiency *too* badly
// optimize at will
StringBuffer cookieOut = new StringBuffer( );
cookieOut.append( "\r\nSet-Cookie: " ).append( cooky ).append( "\r\nSet-Cookie2: " ).append( cooky );
// OH, THE HUMILITY! yes this is inefficient.
out.write( cookieOut.toString( ).getBytes( ) );
}
//out.write(XML_MIME_STUFF);
out.write( ( "\r\n" + HTTPConstants.HEADER_CONTENT_TYPE + ": "
+ responseMsg.getContentType( msgContext.getSOAPConstants( ) ) ).getBytes( ) );
// Writing the length causes the entire message to be decoded twice.
//out.write(("\r\n" + HTTPConstants.HEADER_CONTENT_LENGTH + ": " + responseMsg.getContentLength()).getBytes());
// putInt(out, response.length);
// Transfer MIME headers to HTTP headers for response message.
for ( Iterator i = responseMsg.getMimeHeaders( ).getAllHeaders( ); i.hasNext( ); )
{
MimeHeader responseHeader = (MimeHeader) i.next( );
out.write( '\r' );
out.write( '\n' );
out.write( responseHeader.getName( ).getBytes( ) );
out.write( headerEnder );
out.write( responseHeader.getValue( ).getBytes( ) );
}
out.write( SEPARATOR );
responseMsg.writeTo( out );
}
// out.write(response);
out.flush( );
}
catch ( Exception e )
{
log.info( Messages.getMessage( "exception00" ),
e );
}
finally
{
try
{
if ( m_socket != null )
{
m_socket.close( );
}
}
catch ( Exception e )
{
}
}
if ( msgContext.getProperty( MessageContext.QUIT_REQUESTED ) != null )
{
// why then, quit!
try
{
m_server.stop( );
}
catch ( Exception e )
{
}
}
}
/**
* Read all mime headers, returning the value of Content-Length and SOAPAction.
*
* @param is InputStream to read from
* @param contentType The content type.
* @param contentLocation The content location
* @param soapAction StringBuffer to return the soapAction into
* @param httpRequest StringBuffer for GET / POST
* @param cookie first cookie header (if doSessions)
* @param cookie2 second cookie header (if doSessions)
* @param headers HTTP headers to transfer to MIME headers
*
* @return Content-Length
*/
private int parseHeaders( NonBlockingBufferedInputStream is,
byte[] buf,
StringBuffer contentType,
StringBuffer contentLocation,
StringBuffer soapAction,
StringBuffer httpRequest,
StringBuffer fileName,
StringBuffer cookie,
StringBuffer cookie2,
StringBuffer authInfo,
MimeHeaders headers )
throws java.io.IOException
{
int n;
int len = 0;
// parse first line as GET or POST
n = this.readLine( is, buf, 0, buf.length );
if ( n < 0 )
{
// nothing!
throw new java.io.IOException( Messages.getMessage( "unexpectedEOS00" ) );
}
// which does it begin with?
httpRequest.delete( 0,
httpRequest.length( ) );
fileName.delete( 0,
fileName.length( ) );
contentType.delete( 0,
contentType.length( ) );
contentLocation.delete( 0,
contentLocation.length( ) );
if ( buf[0] == getHeader[0] )
{
httpRequest.append( "GET" );
for ( int i = 0; i < ( n - 5 ); i++ )
{
char c = (char) ( buf[i + 5] & 0x7f );
if ( c == ' ' )
{
break;
}
fileName.append( c );
}
log.debug( Messages.getMessage( "filename01",
"SimpleAxisServer",
fileName.toString( ) ) );
return 0;
}
else if ( buf[0] == postHeader[0] )
{
httpRequest.append( "POST" );
for ( int i = 0; i < ( n - 6 ); i++ )
{
char c = (char) ( buf[i + 6] & 0x7f );
if ( c == ' ' )
{
break;
}
fileName.append( c );
}
log.debug( Messages.getMessage( "filename01",
"SimpleAxisServer",
fileName.toString( ) ) );
}
else
{
throw new java.io.IOException( Messages.getMessage( "badRequest00" ) );
}
while ( ( n = readLine( is, buf, 0, buf.length ) ) > 0 )
{
if ( ( n <= 2 ) && ( ( buf[0] == '\n' ) || ( buf[0] == '\r' ) ) && ( len > 0 ) )
{
break;
}
// RobJ gutted the previous logic; it was too hard to extend for more headers.
// Now, all it does is search forwards for ": " in the buf,
// then do a length / byte compare.
// Hopefully this is still somewhat efficient (Sam is watching!).
// First, search forwards for ": "
int endHeaderIndex = 0;
while ( ( endHeaderIndex < n ) && ( TO_LOWER[buf[endHeaderIndex]] != headerEnder[0] ) )
{
endHeaderIndex++;
}
endHeaderIndex += 2;
// endHeaderIndex now points _just past_ the ": ", and is
// comparable to the various lenLen, actionLen, etc. values
// convenience; i gets pre-incremented, so initialize it to one less
int i = endHeaderIndex - 1;
// which header did we find?
if ( ( endHeaderIndex == lenLen ) && matches( buf, lenHeader ) )
{
// parse content length
while ( ( ++i < n ) && ( buf[i] >= '0' ) && ( buf[i] <= '9' ) )
{
len = ( len * 10 ) + ( buf[i] - '0' );
}
headers.addHeader( HTTPConstants.HEADER_CONTENT_LENGTH,
String.valueOf( len ) );
}
else if ( ( endHeaderIndex == actionLen ) && matches( buf, actionHeader ) )
{
soapAction.delete( 0,
soapAction.length( ) );
// skip initial '"'
i++;
while ( ( ++i < n ) && ( buf[i] != '"' ) )
{
soapAction.append( (char) ( buf[i] & 0x7f ) );
}
headers.addHeader( HTTPConstants.HEADER_SOAP_ACTION, "\"" + soapAction.toString( ) + "\"" );
}
else if ( m_server.isSessionUsed( ) && ( endHeaderIndex == cookieLen ) && matches( buf, cookieHeader ) )
{
// keep everything up to first ;
while ( ( ++i < n ) && ( buf[i] != ';' ) && ( buf[i] != '\r' ) && ( buf[i] != '\n' ) )
{
cookie.append( (char) ( buf[i] & 0x7f ) );
}
headers.addHeader( "Set-Cookie",
cookie.toString( ) );
}
else if ( m_server.isSessionUsed( ) && ( endHeaderIndex == cookie2Len ) && matches( buf, cookie2Header ) )
{
// keep everything up to first ;
while ( ( ++i < n ) && ( buf[i] != ';' ) && ( buf[i] != '\r' ) && ( buf[i] != '\n' ) )
{
cookie2.append( (char) ( buf[i] & 0x7f ) );
}
headers.addHeader( "Set-Cookie2",
cookie.toString( ) );
}
else if ( ( endHeaderIndex == authLen ) && matches( buf, authHeader ) )
{
if ( matches( buf, endHeaderIndex, basicAuth ) )
{
i += basicAuth.length;
while ( ( ++i < n ) && ( buf[i] != '\r' ) && ( buf[i] != '\n' ) )
{
if ( buf[i] == ' ' )
{
continue;
}
authInfo.append( (char) ( buf[i] & 0x7f ) );
}
headers.addHeader( HTTPConstants.HEADER_AUTHORIZATION,
new String( basicAuth ) + authInfo.toString( ) );
}
else
{
throw new java.io.IOException( Messages.getMessage( "badAuth00" ) );
}
}
else if ( ( endHeaderIndex == locationLen ) && matches( buf, locationHeader ) )
{
while ( ( ++i < n ) && ( buf[i] != '\r' ) && ( buf[i] != '\n' ) )
{
if ( buf[i] == ' ' )
{
continue;
}
contentLocation.append( (char) ( buf[i] & 0x7f ) );
}
headers.addHeader( HTTPConstants.HEADER_CONTENT_LOCATION,
contentLocation.toString( ) );
}
else if ( ( endHeaderIndex == typeLen ) && matches( buf, typeHeader ) )
{
while ( ( ++i < n ) && ( buf[i] != '\r' ) && ( buf[i] != '\n' ) )
{
if ( buf[i] == ' ' )
{
continue;
}
contentType.append( (char) ( buf[i] & 0x7f ) );
}
headers.addHeader( HTTPConstants.HEADER_CONTENT_TYPE,
contentLocation.toString( ) );
}
else
{
String customHeaderName = new String( buf, 0, endHeaderIndex - 2 );
StringBuffer customHeaderValue = new StringBuffer( );
while ( ( ++i < n ) && ( buf[i] != '\r' ) && ( buf[i] != '\n' ) )
{
if ( buf[i] == ' ' )
{
continue;
}
customHeaderValue.append( (char) ( buf[i] & 0x7f ) );
}
headers.addHeader( customHeaderName,
customHeaderValue.toString( ) );
}
}
return len;
}
/**
* output an integer into the output stream
*
* @param out OutputStream to be written to
* @param value Integer value to be written.
*/
private void putInt( byte[] buf,
OutputStream out,
int value )
throws java.io.IOException
{
int len = 0;
int offset = buf.length;
// negative numbers
if ( value < 0 )
{
buf[--offset] = (byte) '-';
value = -value;
len++;
}
// zero
if ( value == 0 )
{
buf[--offset] = (byte) '0';
len++;
}
// positive numbers
while ( value > 0 )
{
buf[--offset] = (byte) ( ( value % 10 ) + '0' );
value = value / 10;
len++;
}
// write the result
out.write( buf, offset, len );
}
/**
* Read a single line from the input stream
*
* @param is inputstream to read from
* @param b byte array to read into
* @param off starting offset into the byte array
* @param len maximum number of bytes to read
*/
private int readLine( NonBlockingBufferedInputStream is,
byte[] b,
int off,
int len )
throws java.io.IOException
{
int count = 0;
int c;
while ( ( c = is.read( ) ) != -1 )
{
if ( ( c != '\n' ) && ( c != '\r' ) )
{
b[off++] = (byte) c;
count++;
}
if ( count == len )
{
break;
}
if ( '\n' == c )
{
int peek = is.peek( ); //If the next line begins with tab or space then this is a continuation.
if ( ( peek != ' ' ) && ( peek != '\t' ) )
{
break;
}
}
}
return ( count > 0 ) ? count : ( -1 );
}
}