ASF Bugzilla – Attachment 25223 Details for
Bug 47242
request for AJP command line client
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
AJPClient patch
patch-ajpclient.txt (text/plain), 44.32 KB, created by
chamith buddhika
on 2010-04-02 10:01:00 UTC
(
hide
)
Description:
AJPClient patch
Filename:
MIME Type:
Creator:
chamith buddhika
Created:
2010-04-02 10:01:00 UTC
Size:
44.32 KB
patch
obsolete
>Index: AJPClient.java >=================================================================== >--- AJPClient.java (revision 0) >+++ AJPClient.java (revision 0) >@@ -0,0 +1,246 @@ >+/* >+ * Licensed to the Apache Software Foundation (ASF) under one or more >+ * contributor license agreements. See the NOTICE file distributed with >+ * this work for additional information regarding copyright ownership. >+ * The ASF licenses this file to You 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.coyote.ajp.client; >+ >+import java.io.File; >+import java.io.FileNotFoundException; >+import java.io.FileOutputStream; >+import java.io.IOException; >+import java.io.PrintStream; >+import java.text.SimpleDateFormat; >+import java.util.ArrayList; >+import java.util.Date; >+import java.util.Iterator; >+import java.util.Set; >+ >+public class AJPClient { >+ >+ int timeout; >+ >+ File statistics; >+ >+ String bind_address; >+ >+ File header_file; >+ >+ String headers[]; >+ >+ String user_agent; >+ >+ String urls[]; >+ >+ ClientContext cc=new ClientContext(); >+ >+ AjpProcessor[] processors; >+ >+ public static void main(String args[]) throws Exception{ >+ AJPClient client=new AJPClient(); >+ client.parseArguments(args); >+ client.init(); >+ client.run(); >+ client.output(); >+ } >+ >+ private void parseArguments(String args[]){ >+ int index = 0; >+ String arg; >+ ArrayList<String> urlArray=new ArrayList<String>(); >+ >+ while (index < args.length) { >+ arg = args[index++]; >+ String[] tokens=arg.split("="); >+ >+ String name=null; >+ String value=null; >+ boolean isSwitch=false; >+ if(tokens.length == 2){ >+ name=tokens[0]; >+ value=tokens[1]; >+ >+ if (name.equals("-T") || name.equals("--timeout")) { >+ cc.timeout = Double.parseDouble(value); >+ isSwitch=true; >+ } else if (name.equals("-o") || name.equals("--output")) { >+ if(value.equals("-")){ >+ cc.output=System.out; >+ }else{ >+ try{ >+ FileOutputStream fs=new FileOutputStream(value); >+ cc.output= new PrintStream(fs); >+ }catch(FileNotFoundException e){ >+ cc.output=System.out; >+ System.out.println("Failed loading out put file. Output redirected to console..."); >+ } >+ } >+ isSwitch=true; >+ } else if (name.equals("-r") || name.equals("--requests-file")) { >+ cc.requests_file=new File(value); >+ isSwitch=true; >+ } else if (name.equals("-H") || name.equals("--headers")) { >+ String[] tmp=null; >+ if(value.contains("\\")){ >+ tmp=value.split("\\\\"); >+ }else{ >+ tmp=new String[1]; >+ tmp[0]=value; >+ } >+ >+ for(String header:tmp){ >+ String[] tuple=header.split(":"); >+ cc.headers.put(tuple[0], tuple[1]); // Header name and header value >+ } >+ isSwitch=true; >+ } else if (name.equals("--rounds")) { >+ cc.rounds=Integer.parseInt(value); >+ isSwitch=true; >+ } else if (name.equals("--http-version")) { >+ cc.http_version=value; >+ isSwitch=true; >+ } else if (name.equals("--body-file")) { // This switch is not used currently. >+ cc.bodyFile=new File(value); >+ isSwitch=true; >+ } else if (name.equals("--query")) { >+ String[] tmp=null; >+ if(value.contains("\\")){ >+ tmp=value.split("\\\\"); >+ }else{ >+ tmp=new String[1]; >+ tmp[0]=value; >+ } >+ >+ for(String param:tmp){ >+ String[] tuple=param.split(":"); >+ cc.queries.put(tuple[0], tuple[1]); // Parameter name and parameter value >+ } >+ isSwitch=true; >+ } else if (name.equals("-v") || name.equals("--verbose")) { >+ cc.verbose=true; >+ } else if (name.equals("-u") || name.equals("--user-agent")) { >+ cc.headers.put("User-Agent", arg); >+ isSwitch=true; >+ } else if (name.equals("-m") || name.equals("--method")) { >+ String method=value; >+ if(method.trim().equalsIgnoreCase(Constants.POST)){ >+ cc.method=Constants.POST; >+ }else{ >+ cc.method=Constants.GET; >+ } >+ isSwitch=true; >+ } else if (name.equals("-h") || name.equals("--help")) { >+ printUsage(); >+ System.exit(0); >+ } >+ } >+ >+ if(!(arg.startsWith("-")|| arg.startsWith("--"))){ // We have come to the URL section. >+ urlArray.add(arg); >+ } else if(!isSwitch) { >+ printUsage(); >+ System.exit(-1); >+ } >+ } >+ >+ if(cc.method == null){ >+ cc.method=Constants.GET; >+ } >+ >+ if(cc.method.equals(Constants.GET) && cc.bodyFile != null){ // GET cannot have a body >+ error("-m","--bodyFile"); >+ System.exit(-1); >+ } >+ >+ cc.urls=urlArray.toArray(cc.urls); >+ urlArray=null; >+ } >+ >+ private void printUsage(){ >+ System.out.println("java -jar AJPClient.jar [Options] url[1-n]\n"); >+ System.out.println("Options\n"); >+ System.out.println("\t-t\n\t--timeout=seconds\n\t\tSet the read timeout to seconds seconds.\n"); >+ System.out.println("\t-m\n\t--method=method\n\t\tSets the HTTP method." + >+ " Can be either GET or POST. No other methods are supported at present.\n"); >+ System.out.println("\t--rounds=number\n\t\tSets the number of times url[s] given in the command are repeatedly fetched.\n"); >+ System.out.println("\t--http-version=version\n\t\tSets the HTTP version. Can be either 1.0 or 1.1.\n"); >+ System.out.println("\t--query=param_1:paramValue|...|param_n:paramValue\n\t\t" + >+ "Sets the parameters in the query string for the url[s] specified in the command line.\n"); >+ System.out.println("\t-r\n\t--requests-file=file\n\t\tSets the requests.xml configuration file.\n"); >+ System.out.println("\t-o\n\t--output=file\n\t\tSets the output file location. If - is specified then outputs to the console.\n"); >+ System.out.println("\t-H\n\t--headers=header_1:value|....|header_2:value\n\t\t" + >+ "Sets headers to be included in HTTP requests for url[s] specified in the command line.\n"); >+ } >+ >+ private void error(String... switches){ >+ System.out.println("ERROR: Invalid combination of switches. "); >+ for(String ss:switches){ >+ System.out.print(ss+" "); >+ } >+ System.out.println("cannot be used together."); >+ System.out.println(); >+ printUsage(); >+ } >+ >+ private void init() throws Exception{ >+ cc.init(); >+ Set<RequestContext> ctxs=cc.getRequestContexts(); >+ processors=new AjpProcessor[ctxs.size()]; >+ >+ int index=0; >+ for(Iterator<RequestContext> it=ctxs.iterator();it.hasNext();){ >+ AjpProcessor processor=new AjpProcessor(it.next()); >+ processors[index++]=processor; >+ } >+ } >+ >+ private void run(){ >+ for(AjpProcessor processor:processors){ >+ new Thread(processor).start(); >+ } >+ >+ synchronized(cc){ >+ while(cc.getProcessorCount() > 0){ >+ try { >+ cc.wait(); >+ } catch (InterruptedException e) { >+ e.printStackTrace(); >+ } >+ } >+ } >+ } >+ >+ private void output() throws IOException{ >+ ArrayList<Statistics> stats=cc.getStatistics(); >+ PrintStream ps=cc.getOutput(); >+ >+ String format = "|%1$-15s|%2$-20s|%3$-10s|%4$-15s|%5$-10s|%6$-75s\n"; >+ ps.println("\nRun: "+new SimpleDateFormat("yy/MM/dd HH:mm:ss").format(new Date())); >+ ps.println(); >+ ps.format(format, "Connection","StartTime","TimedOut","TimeElapsed(ms)","ReplyCode","URL"); >+ ps.println(); >+ >+ int counter=0; >+ for(Iterator<Statistics> it=stats.iterator();it.hasNext();){ >+ Statistics statistics=it.next(); >+ ps.format(format, counter++,statistics.getDateTime(),statistics.isTimeout(),statistics.getTimeElapsed(), >+ statistics.getReplyCode(),statistics.getUrl()); >+ } >+ >+ ps.flush(); >+ ps.close(); >+ } >+ >+} >Index: AjpProcessor.java >=================================================================== >--- AjpProcessor.java (revision 0) >+++ AjpProcessor.java (revision 0) >@@ -0,0 +1,588 @@ >+/* >+ * Licensed to the Apache Software Foundation (ASF) under one or more >+ * contributor license agreements. See the NOTICE file distributed with >+ * this work for additional information regarding copyright ownership. >+ * The ASF licenses this file to You 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.coyote.ajp.client; >+ >+import java.io.ByteArrayInputStream; >+import java.io.ByteArrayOutputStream; >+import java.io.IOException; >+import java.io.InputStream; >+import java.io.InterruptedIOException; >+import java.io.OutputStream; >+import java.net.Socket; >+import java.net.SocketTimeoutException; >+import java.net.URL; >+import java.text.SimpleDateFormat; >+import java.util.Date; >+import java.util.Map; >+ >+/** >+ * AJP protocol handler class. >+ * For the initial implementation see >+ * http://svn.apache.org/repos/asf/jakarta/jmeter/trunk/src/protocol/http/org/apache/jmeter/protocol/http/sampler/AjpSampler.java >+ * Currently does support GET and POST requests with form data content type application/x-www-form-urlencoded. multipart/form-data >+ * is not currently supported. >+ */ >+public class AjpProcessor implements Runnable{ >+ >+ RequestContext ctx; >+ >+ ClientContext cc; >+ >+ private String responseHeader; >+ >+ private Statistics statistics; >+ >+ /** >+ * Translates integer codes to request header names >+ */ >+ private static final String []headerTransArray = { >+ "accept", //$NON-NLS-1$ >+ "accept-charset", //$NON-NLS-1$ >+ "accept-encoding", //$NON-NLS-1$ >+ "accept-language", //$NON-NLS-1$ >+ "authorization", //$NON-NLS-1$ >+ "connection", //$NON-NLS-1$ >+ "content-type", //$NON-NLS-1$ >+ "content-length", //$NON-NLS-1$ >+ "cookie", //$NON-NLS-1$ >+ "cookie2", //$NON-NLS-1$ >+ "host", //$NON-NLS-1$ >+ "pragma", //$NON-NLS-1$ >+ "referer", //$NON-NLS-1$ >+ "user-agent" //$NON-NLS-1$ >+ }; >+ >+ // Translates integer codes to response header names >+ public static final String []responseTransArray = { >+ "Content-Type", >+ "Content-Language", >+ "Content-Length", >+ "Date", >+ "Last-Modified", >+ "Location", >+ "Set-Cookie", >+ "Set-Cookie2", >+ "Servlet-Engine", >+ "Status", >+ "WWW-Authenticate" >+ }; >+ >+ /** >+ * Base value for translated headers >+ */ >+ >+ private transient Socket channel = null; >+ private int lastPort = -1; >+ private String lastHost = null; >+ private String localName = null; >+ private String localAddress = null; >+ private byte [] inbuf = new byte[8*1024]; >+ private byte [] outbuf = new byte[8*1024]; >+ private transient ByteArrayOutputStream responseData = new ByteArrayOutputStream(); >+ private int inpos = 0; >+ private int outpos = 0; >+ private transient String stringBody = null; >+ private transient InputStream body = null; >+ >+ >+ public AjpProcessor(RequestContext ctx){ >+ this.ctx=ctx; >+ this.cc=ctx.getClientContext(); >+ } >+ >+ public void run(){ >+ process(); >+ } >+ >+ public void process(){ >+ try { >+ int rounds=ctx.getRounds(); >+ >+ while(rounds-- > 0){ >+ setupConnection(); >+ execute(); >+ cleanup(); >+ cc.setStatistics(statistics); >+ } >+ } catch(IOException iex) { >+ if(iex instanceof SocketTimeoutException){ >+ statistics.setTimeout(true); >+ } >+ lastPort = -1; // force reopen on next sample >+ channel = null; >+ //return err; >+ } catch(ProtocolException e){ >+ lastPort = -1; // force reopen on next sample >+ channel = null; >+ } >+ >+ cc.decrementProcessorCount(); >+ } >+ >+ public String getResponseHeader(){ >+ return responseHeader; >+ } >+ >+ public String getResponseData(){ >+ return responseData.toString(); >+ } >+ >+ private void setupConnection() throws IOException { >+ URL url=ctx.getUrl(); >+ statistics=new Statistics(); >+ statistics.setUrl(url); >+ >+ if(ctx.getQueryParams().size() > 0 && ctx.getMethod().equals(Constants.GET) && url.getQuery() != null){ >+ StringBuffer sb=new StringBuffer(url.toString()); >+ sb.append("?"); >+ >+ Map<String,String> params=ctx.getQueryParams(); >+ boolean first=true; >+ >+ for(String param:params.keySet()){ >+ if(first){ >+ first=false; >+ }else{ >+ sb.append("&"); >+ } >+ sb.append(param); >+ sb.append("="); >+ sb.append(params.get(param)); >+ } >+ >+ url=new URL(sb.toString()); >+ ctx.setUrl(url); >+ } >+ >+ String host = url.getHost(); >+ int port = url.getPort(); >+ if(port <= 0 || port == url.getDefaultPort()) { >+ port = 8009; >+ } >+ String scheme = url.getProtocol(); >+ if(channel == null || !host.equals(lastHost) || port != lastPort) { >+ if(channel != null) { >+ channel.close(); >+ } >+ channel = new Socket(host, port); >+ double timeout = cc.getTimeout(); >+ if(timeout > 0) { >+ channel.setSoTimeout((int)timeout*1000); >+ } >+ localAddress = channel.getLocalAddress().getHostAddress(); >+ localName = channel.getLocalAddress().getHostName(); >+ lastHost = host; >+ lastPort = port; >+ } >+ log("Connected to "+host+" at port "+port); >+ >+ outpos = 4; >+ setByte((byte)2); >+ if(ctx.getMethod() != null && ctx.getMethod().equals(Constants.POST)) { >+ setByte((byte)4); >+ } else { >+ setByte((byte)2); >+ } >+ if(cc.getHttpVersion() != null && (cc.getHttpVersion().equals("1.0") || cc.getHttpVersion().equals("1"))) {//$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ >+ setString("HTTP/1.0");//$NON-NLS-1$ >+ } else { >+ setString("HTTP/1.1"); >+ } >+ setString(url.getPath()); >+ setString(localAddress); >+ setString(localName); >+ setString(host); >+ setInt(url.getDefaultPort()); >+ setByte(Constants.PROTOCOL_HTTPS.equalsIgnoreCase(scheme) ? (byte)1 : (byte)0); >+ setInt(getHeaderSize()); >+ setConnectionHeaders(host); >+ String query = url.getQuery(); >+ if (query != null) { >+ setByte((byte)0x05); // Marker for query string attribute >+ setString(query); >+ } >+ setByte((byte)0xff); // More general attributes not supported >+ } >+ >+ private int getHeaderSize() { >+ /* HeaderManager headers = getHeaderManager(); >+ CookieManager cookies = getCookieManager(); >+ AuthManager auth = getAuthManager(); >+ int hsz = 1; // Host always >+ if(method.equals(POST)) { >+ HTTPFileArg[] hfa = getHTTPFiles(); >+ if(hfa.length > 0) { >+ hsz += 3; >+ } else { >+ hsz += 2; >+ } >+ } >+ if(headers != null) { >+ hsz += headers.size(); >+ } >+ if(cookies != null) { >+ hsz += cookies.getCookieCount(); >+ } >+ if(auth != null) { >+ String authHeader = auth.getAuthHeaderForURL(url); >+ if(authHeader != null) { >+ ++hsz; >+ } >+ }*/ >+ //return hsz; >+ int size=1; // For host header which is compulsory. >+ if(ctx.getMethod().endsWith(Constants.POST)){ >+ size+=2; // For content-type and content-length headers. >+ } >+ >+ if(ctx.getHeaders().get("host") != null){ >+ size=size+ctx.getHeaders().size() - 1; // Prevent host header being counted twice >+ }else{ >+ size += ctx.getHeaders().size(); >+ } >+ return size; // For now return size of the header map. Additional header for host header. >+ } >+ >+ >+ private void setConnectionHeaders(String host) throws IOException { >+ /* HeaderManager headers = getHeaderManager(); >+ AuthManager auth = getAuthManager();*/ >+ //StringBuilder hbuf = new StringBuilder(); >+ // Allow Headers to override Host setting >+ //hbuf.append("Host").append(COLON_SPACE).append(host).append(NEWLINE);//$NON-NLS-1$ >+ setInt(0xA00b); //Host >+ setString(host); >+ /*if(headers != null) { >+ CollectionProperty coll = headers.getHeaders(); >+ PropertyIterator i = coll.iterator(); >+ while(i.hasNext()) { >+ Header header = (Header)i.next().getObjectValue(); >+ String n = header.getName(); >+ String v = header.getValue(); >+ //hbuf.append(n).append(COLON_SPACE).append(v).append(NEWLINE); >+ int hc = translateHeader(n); >+ if(hc > 0) { >+ setInt(hc+AJP_HEADER_BASE); >+ } else { >+ setString(n); >+ } >+ setString(v); >+ } >+ }*/ >+ >+ Map<String,String> headers=ctx.getHeaders(); >+ headers.remove("host"); // Remove any repeating header for host. We already set host header. >+ for(String name:headers.keySet()){ >+ String value=headers.get(name); >+ int code=translateHeader(name); >+ if(code > 0){ >+ setInt(code+Constants.AJP_HEADER_BASE); >+ }else{ >+ setString(name); >+ } >+ >+ setString(value); >+ } >+ >+ if(ctx.getMethod().equals(Constants.POST)) { >+ int cl = -1; >+ //HTTPFileArg[] hfa = getHTTPFiles(); >+ if(ctx.getBodyFile() != null) { >+/* File input=ctx.getBodyFile(); >+ cl = (int)input.length(); >+ body = new FileInputStream(input); >+ setString(HEADER_CONTENT_DISPOSITION); >+ setString("form-data; name=\""+encode(fa.getParamName())+ >+ "\"; filename=\"" + encode(fn) +"\""); //$NON-NLS-1$ //$NON-NLS-2$ >+ String mt = fa.getMimeType(); >+ hbuf.append(HEADER_CONTENT_TYPE).append(COLON_SPACE).append(mt).append(NEWLINE); >+ setInt(0xA007); // content-type >+ setString(mt);*/ >+ } else { >+ //hbuf.append(HEADER_CONTENT_TYPE).append(COLON_SPACE).append(APPLICATION_X_WWW_FORM_URLENCODED).append(NEWLINE); >+ setInt(0xA007); // content-type >+ setString(Constants.APPLICATION_X_WWW_FORM_URLENCODED); >+ StringBuilder sb = new StringBuilder(); >+ >+ boolean first = true; >+ Map<String,String> params=ctx.getQueryParams(); >+ >+ for(String param:params.keySet()){ >+ if(first) { >+ first = false; >+ sb.append(param); >+ sb.append("="); >+ } else { >+ sb.append('&'); >+ } >+ sb.append(params.get(param)); >+ } >+ stringBody = sb.toString(); >+ byte [] sbody = stringBody.getBytes(); //FIXME - encoding >+ cl = sbody.length; >+ body = new ByteArrayInputStream(sbody); >+ } >+ //hbuf.append(HEADER_CONTENT_LENGTH).append(COLON_SPACE).append(String.valueOf(cl)).append(NEWLINE); >+ setInt(0xA008); // Content-length >+ setString(String.valueOf(cl)); >+ } >+/* if(auth != null) { >+ String authHeader = auth.getAuthHeaderForURL(url); >+ if(authHeader != null) { >+ setInt(0xA005); // Authorization >+ setString(authHeader); >+ hbuf.append(HEADER_AUTHORIZATION).append(COLON_SPACE).append(authHeader).append(NEWLINE); >+ } >+ } >+ return hbuf.toString();*/ >+ } >+ >+ private String encode(String value) { >+ StringBuilder newValue = new StringBuilder(); >+ char[] chars = value.toCharArray(); >+ for (int i = 0; i < chars.length; i++) >+ { >+ if (chars[i] == '\\')//$NON-NLS-1$ >+ { >+ newValue.append("\\\\");//$NON-NLS-1$ >+ } >+ else >+ { >+ newValue.append(chars[i]); >+ } >+ } >+ return newValue.toString(); >+ } >+ >+ /*private String setConnectionCookies(URL url, CookieManager cookies) { >+ String cookieHeader = null; >+ if(cookies != null) { >+ cookieHeader = cookies.getCookieHeaderForURL(url); >+ CollectionProperty coll = cookies.getCookies(); >+ PropertyIterator i = coll.iterator(); >+ while(i.hasNext()) { >+ Cookie cookie = (Cookie)(i.next().getObjectValue()); >+ setInt(0xA009); // Cookie >+ setString(cookie.getName()+"="+cookie.getValue());//$NON-NLS-1$ >+ } >+ } >+ return cookieHeader; >+ }*/ >+ >+ private int translateHeader(String n) { >+ for(int i=0; i < headerTransArray.length; i++) { >+ if(headerTransArray[i].equalsIgnoreCase(n)) { >+ return i+1; >+ } >+ } >+ return -1; >+ } >+ >+ private void setByte(byte b) { >+ outbuf[outpos++] = b; >+ } >+ >+ private void setInt(int n) { >+ outbuf[outpos++] = (byte)((n >> 8)&0xff); >+ outbuf[outpos++] = (byte) (n&0xff); >+ } >+ >+ private void setString(String s) { >+ if( s == null ) { >+ setInt(0xFFFF); >+ } else { >+ int len = s.length(); >+ setInt(len); >+ for(int i=0; i < len; i++) { >+ setByte((byte)s.charAt(i)); >+ } >+ setByte((byte)0); >+ } >+ } >+ >+ private void send() throws IOException { >+ OutputStream os = channel.getOutputStream(); >+ int len = outpos; >+ outpos = 0; >+ setInt(0x1234); >+ setInt(len-4); >+ os.write(outbuf, 0, len); >+ } >+ >+ private void execute() throws IOException, ProtocolException { >+ String dateTime=new SimpleDateFormat("yy/MM/dd HH:mm:ss").format(new Date()); >+ statistics.setDateTime(dateTime); >+ long start=System.currentTimeMillis(); >+ send(); >+ if(ctx.getMethod() != null && ctx.getMethod().equals(Constants.POST)) { >+ sendPostBody(); >+ } >+ handshake(); >+ long end=System.currentTimeMillis(); >+ statistics.setTimeElapsed(end-start); >+ } >+ >+ private void handshake() throws IOException, ProtocolException { >+ responseData.reset(); >+ int msg = getMessage(); >+ while(msg != 5) { >+ if(msg == 3) { >+ int len = getInt(); >+ responseData.write(inbuf, inpos, len); >+ } else if(msg == 4) { >+ responseHeader=parseHeaders(); >+ } else if(msg == 6) { >+ setNextBodyChunk(); >+ send(); >+ } >+ msg = getMessage(); >+ } >+ } >+ >+ private void sendPostBody() throws IOException { >+ setNextBodyChunk(); >+ send(); >+ } >+ >+ private void setNextBodyChunk() throws IOException { >+ int len = body.available(); >+ if(len < 0) { >+ len = 0; >+ } else if(len > Constants.MAX_SEND_SIZE) { >+ len = Constants.MAX_SEND_SIZE; >+ } >+ outpos = 4; >+ int nr = 0; >+ if(len > 0) { >+ nr = body.read(outbuf, outpos+2, len); >+ } >+ setInt(nr); >+ outpos += nr; >+ } >+ >+ >+ private String parseHeaders() throws IOException { >+ int status = getInt(); >+ statistics.setReplyCode(status); >+ >+ String msg = getString(null); >+ int nh = getInt(); >+ StringBuilder sb = new StringBuilder(); >+ sb.append(Constants.HTTP_1_1 ).append(status).append(" ").append(msg).append(Constants.NEWLINE);//$NON-NLS-1$//$NON-NLS-2$ >+ for(int i=0; i < nh; i++) { >+ // Currently, no Tomcat version sends translated headers >+ String name; >+ int thn = peekInt(); >+ if((thn & 0xff00) == Constants.AJP_HEADER_BASE) { >+ name = responseTransArray[(thn&0xff)-1]; >+ getInt(); >+ } else { >+ name = getString(sb); >+ } >+ String value = getString(sb); >+ /* if(HEADER_CONTENT_TYPE.equalsIgnoreCase(name)) { >+ res.setContentType(value); >+ res.setEncodingAndType(value); >+ } else if(HEADER_SET_COOKIE.equalsIgnoreCase(name)) { >+ CookieManager cookies = getCookieManager(); >+ if(cookies != null) { >+ cookies.addCookieFromHeader(value, res.getURL()); >+ } >+ }*/ >+ sb.append(name).append(Constants.COLON_SPACE).append(value).append(Constants.NEWLINE); >+ } >+ >+ return sb.toString(); >+ } >+ >+ >+ private int getMessage() throws ProtocolException, IOException { >+ InputStream is = channel.getInputStream(); >+ inpos = 0; >+ int nr=0; >+ >+ try{ >+ nr = is.read(inbuf, inpos, 4); >+ }catch(InterruptedIOException e){ >+ throw new SocketTimeoutException(e.getMessage()); >+ } >+ >+ if(nr != 4) { >+ channel.close(); >+ channel = null; >+ throw new ProtocolException("Protocol Error. Unexpected response."); >+ } >+ //int mark = >+ getInt(); >+ int len = getInt(); >+ int toRead = len; >+ int cpos = inpos; >+ while(toRead > 0) { >+ nr = is.read(inbuf, cpos, toRead); >+ cpos += nr; >+ toRead -= nr; >+ } >+ return getByte(); >+ } >+ >+ private byte getByte() { >+ return inbuf[inpos++]; >+ } >+ >+ private int getInt() { >+ int res = (inbuf[inpos++]<<8)&0xff00; >+ res += inbuf[inpos++]&0xff; >+ return res; >+ } >+ >+ private int peekInt() { >+ int res = (inbuf[inpos]<<8)&0xff00; >+ res += inbuf[inpos+1]&0xff; >+ return res; >+ } >+ >+ private String getString(StringBuilder sb) throws IOException { >+ int len = getInt(); >+ String s = new String(inbuf, inpos, len, "iso-8859-1");//$NON-NLS-1$ >+ inpos+= len+1; >+ return s; >+ } >+ >+ private void cleanup(){ >+ responseHeader=null; >+ responseData.reset(); >+ } >+ >+ private void log(String message){ >+ if(cc.isVerbose()){ >+ cc.getOutput().print(message); >+ } >+ } >+ >+ class ProtocolException extends Exception{ >+ >+ private static final long serialVersionUID = 149146185282537074L; >+ >+ public ProtocolException(String message){ >+ super(message); >+ } >+ >+ } >+} >+ >+ >Index: ClientContext.java >=================================================================== >--- ClientContext.java (revision 0) >+++ ClientContext.java (revision 0) >@@ -0,0 +1,346 @@ >+/* >+ * Licensed to the Apache Software Foundation (ASF) under one or more >+ * contributor license agreements. See the NOTICE file distributed with >+ * this work for additional information regarding copyright ownership. >+ * The ASF licenses this file to You 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.coyote.ajp.client; >+ >+import java.io.File; >+import java.io.PrintStream; >+import java.net.URL; >+import java.util.ArrayList; >+import java.util.HashMap; >+import java.util.HashSet; >+import java.util.Map; >+import java.util.Set; >+ >+import javax.xml.parsers.DocumentBuilder; >+import javax.xml.parsers.DocumentBuilderFactory; >+ >+import org.w3c.dom.Document; >+import org.w3c.dom.Element; >+import org.w3c.dom.Node; >+import org.w3c.dom.NodeList; >+ >+public class ClientContext { >+ >+ double timeout; >+ >+ int rounds; >+ >+ int processorCount; >+ >+ boolean verbose; >+ >+ PrintStream output; >+ >+ String bind_address; >+ >+ File requests_file; >+ >+ String user_agent; >+ >+ String urls[]; >+ >+ Map<String,String> headers; >+ >+ Map<String,String> defaultHeaders; >+ >+ Map<String,String> queries; >+ >+ Set<RequestContext> ctxs; >+ >+ String http_version; >+ >+ ArrayList<Statistics> stats; >+ >+ String method; >+ >+ File bodyFile; >+ >+ >+ public ClientContext(){ >+ headers=new HashMap<String,String>(); >+ defaultHeaders=new HashMap<String,String>(); >+ queries=new HashMap<String,String>(); >+ ctxs=new HashSet<RequestContext>(); >+ stats=new ArrayList<Statistics>(); >+ urls=new String[0]; >+ rounds=1; >+ populateDefaultHeaders(); >+ } >+ >+ public void init() throws Exception{ >+ >+ // Set requests from configuration file. >+ try{ >+ if(requests_file!= null){ >+ parse(); >+ } >+ }catch(Exception e){ >+ throw e; >+ } >+ >+ // Set requests from command line. >+ for(int i=0;i<urls.length;i++){ >+ RequestContext ctx=new RequestContext(this); >+ ctx.setUrl(new URL(urls[i])); >+ >+ if(method != null && method.equals("POST")){ >+ ctx.setMethod(method); >+ if(bodyFile != null){ >+ ctx.setBodyFile(bodyFile); >+ } >+ }else{ >+ ctx.setMethod("GET"); >+ } >+ >+ for(String name:headers.keySet()){ >+ ctx.setHeader(name, headers.get(name)); >+ } >+ >+ for(String key:queries.keySet()){ >+ ctx.setQueryParam(key, queries.get(key)); >+ } >+ >+ ctx.setRounds(rounds); >+ >+ if(ctxs.contains(ctx)){ >+ ctxs.remove(ctx); >+ } >+ >+ ctxs.add(ctx); >+ } >+ >+ // Add default headers for generic header types. Doesn't override already set headers. Useful for situations when no headers are explicitly set. >+ for(RequestContext ctx:ctxs){ >+ for(String header:defaultHeaders.keySet()){ >+ ctx.setHeaderIfNotPresent(header, defaultHeaders.get(header)); >+ } >+ } >+ >+ synchronized(this){ >+ processorCount=ctxs.size(); >+ } >+ } >+ >+ public Set<RequestContext> getRequestContexts(){ >+ return ctxs; >+ } >+ >+ public double getTimeout(){ >+ return timeout; >+ } >+ >+ public String getHttpVersion(){ >+ return http_version; >+ } >+ >+ public boolean isVerbose(){ >+ return verbose; >+ } >+ >+ public void setStatistics(Statistics statistics){ >+ synchronized(stats){ >+ stats.add(statistics); >+ } >+ } >+ >+ public ArrayList<Statistics> getStatistics(){ >+ return stats; >+ } >+ >+ public PrintStream getOutput(){ >+ return output; >+ } >+ >+ private void populateDefaultHeaders(){ >+ defaultHeaders.put("From","ajp@test.org"); >+ defaultHeaders.put("User-Agent","AJPClient/1.0"); >+ defaultHeaders.put("Accept-Language","en"); >+ } >+ >+ public synchronized void decrementProcessorCount(){ >+ processorCount--; >+ notifyAll(); >+ } >+ >+ public int getProcessorCount(){ >+ return processorCount; >+ } >+ >+ private void parse(){ >+ try { >+ DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); >+ DocumentBuilder db = dbf.newDocumentBuilder(); >+ Document doc = db.parse(requests_file); >+ doc.getDocumentElement().normalize(); >+ >+ NodeList reqLst = doc.getElementsByTagName("request"); >+ NodeList commonLst=doc.getElementsByTagName("common"); >+ NodeList commonHeaders=null; >+ >+ if(commonLst.getLength() > 0){ >+ Node common=commonLst.item(0); >+ if(common.getNodeType() == Node.ELEMENT_NODE){ >+ Element commonElmnt=(Element)common; >+ commonHeaders=commonElmnt.getElementsByTagName("header"); >+ } >+ } >+ >+ for(int i=0;i< reqLst.getLength();i++){ >+ Node request=reqLst.item(i); >+ if (request.getNodeType() == Node.ELEMENT_NODE) { >+ >+ RequestContext ctx=new RequestContext(this); >+ >+ //Process request URL >+ Element reqElmnt = (Element) request; >+ Node url = reqElmnt.getElementsByTagName("url").item(0); >+ ctx.setUrl(new URL(url.getFirstChild().getTextContent())); >+ >+ //Process common headers. May be overridden later. >+ if(commonHeaders != null){ >+ for(int j=0;j<commonHeaders.getLength();j++){ >+ Node header=commonHeaders.item(j); >+ if(header.getNodeType() == Node.ELEMENT_NODE){ >+ Element headerElmnt=(Element)header; >+ String headerName=headerElmnt.getElementsByTagName("name").item(0).getFirstChild().getTextContent(); >+ String value=headerElmnt.getElementsByTagName("value").item(0).getFirstChild().getTextContent(); >+ ctx.setHeader(headerName, value); >+ } >+ } >+ } >+ >+ //Process headers >+ NodeList headersLst=reqElmnt.getElementsByTagName("headers"); >+ if(headersLst.getLength() > 0){ >+ Node headers=headersLst.item(0); >+ if(headers.getNodeType() == Node.ELEMENT_NODE){ >+ Element headersElmnt=(Element)headers; >+ NodeList headerLst=headersElmnt.getElementsByTagName("header"); >+ >+ for(int j=0;j< headerLst.getLength();j++){ >+ Node header=reqLst.item(i); >+ if (header.getNodeType() == Node.ELEMENT_NODE) { >+ Element headerElmnt=(Element)header; >+ String headerName=headerElmnt.getElementsByTagName("name").item(0).getFirstChild().getTextContent(); >+ String value=headerElmnt.getElementsByTagName("value").item(0).getFirstChild().getTextContent(); >+ >+ ctx.setHeader(headerName, value); >+ } >+ } >+ } >+ } >+ >+ //Process post if present >+ boolean postPresent=false; >+ NodeList postLst=reqElmnt.getElementsByTagName("post"); >+ if(postLst.getLength() > 0){ >+ Node post=postLst.item(0); >+ postPresent=true; >+ ctx.setMethod("POST"); >+ >+ if(post.getNodeType() == Node.ELEMENT_NODE){ >+ Element postElmnt=(Element)post; >+ >+ //Process bodyfile >+ boolean filePresent=false; >+ NodeList fileLst=postElmnt.getElementsByTagName("bodyfile"); >+ if(fileLst.getLength() > 0){ >+ String bodyFile=fileLst.item(0).getFirstChild().getTextContent(); >+ ctx.setBodyFile(new File(bodyFile)); >+ filePresent=true; >+ } >+ >+ //Process query >+ if(!filePresent){ // Gives the precedence to the file if both <bodyfile> and <query> tags are present. >+ NodeList queryLst=postElmnt.getElementsByTagName("query"); >+ if(queryLst.getLength() > 0){ >+ Node query=queryLst.item(0); >+ if(query.getNodeType() == Node.ELEMENT_NODE){ >+ Element queryElmnt=(Element)query; >+ NodeList paramLst=queryElmnt.getElementsByTagName("param"); >+ >+ for(int k=0;k<paramLst.getLength();k++){ >+ Node param=paramLst.item(k); >+ if(param.getNodeType() == Node.ELEMENT_NODE){ >+ Element paramElmnt=(Element)param; >+ String paramName=paramElmnt.getElementsByTagName("name").item(0).getFirstChild().getTextContent(); >+ String value=paramElmnt.getElementsByTagName("value").item(0).getFirstChild().getTextContent(); >+ ctx.setQueryParam(paramName, value); >+ } >+ } >+ } >+ } >+ } >+ } >+ } >+ >+ //Process get if present. Precedence is given to post if both are present. >+ if(!postPresent){ >+ NodeList getLst=reqElmnt.getElementsByTagName("get"); >+ if(getLst.getLength() > 0){ >+ Node get=getLst.item(0); >+ ctx.setMethod("GET"); >+ >+ if(get.getNodeType() == Node.ELEMENT_NODE){ >+ Element getElmnt=(Element)get; >+ NodeList queryLst=getElmnt.getElementsByTagName("query"); >+ >+ if(queryLst.getLength() > 0){ >+ Node query=queryLst.item(0); >+ if(query.getNodeType() == Node.ELEMENT_NODE){ >+ Element queryElmnt=(Element)query; >+ NodeList paramLst=queryElmnt.getElementsByTagName("param"); >+ >+ for(int k=0;k<paramLst.getLength();k++){ >+ Node param=paramLst.item(k); >+ >+ if(param.getNodeType() == Node.ELEMENT_NODE){ >+ Element paramElmnt=(Element)param; >+ String paramName=paramElmnt.getElementsByTagName("name").item(0).getFirstChild().getTextContent(); >+ String value=paramElmnt.getElementsByTagName("value").item(0).getFirstChild().getTextContent(); >+ ctx.setQueryParam(paramName, value); >+ } >+ } >+ } >+ } >+ } >+ } >+ } >+ >+ //Process rounds >+ NodeList roundsLst=reqElmnt.getElementsByTagName("rounds"); >+ if(roundsLst.getLength() > 0 ){ >+ Node rounds=roundsLst.item(0); >+ String roundsStr=rounds.getFirstChild().getTextContent(); >+ ctx.setRounds(Integer.parseInt(roundsStr)); >+ } >+ >+ ctxs.add(ctx); >+ >+ //Default method is GET if no specific method is set >+ if(ctx.getMethod() == null){ >+ ctx.setMethod(Constants.GET); >+ } >+ } >+ } >+ >+ } catch (Exception e) { >+ e.printStackTrace(); >+ } >+ } >+} >Index: Constants.java >=================================================================== >--- Constants.java (revision 0) >+++ Constants.java (revision 0) >@@ -0,0 +1,35 @@ >+/* >+ * Licensed to the Apache Software Foundation (ASF) under one or more >+ * contributor license agreements. See the NOTICE file distributed with >+ * this work for additional information regarding copyright ownership. >+ * The ASF licenses this file to You 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.coyote.ajp.client; >+ >+public class Constants { >+ >+ public static final char NEWLINE = '\n'; >+ public static final String COLON_SPACE = ": ";//$NON-NLS-1$ >+ public static final String POST="POST"; >+ public static final String GET="GET"; >+ public static final String PROTOCOL_HTTPS="https"; >+ public static final String HTTP_1_1="HTTP/1.1"; >+ >+ static final int AJP_HEADER_BASE = 0xA000; >+ >+ static final int MAX_SEND_SIZE = 8*1024 - 4 - 4; >+ >+ static final String APPLICATION_X_WWW_FORM_URLENCODED="application/x-www-form-urlencoded "; >+ >+} >Index: RequestContext.java >=================================================================== >--- RequestContext.java (revision 0) >+++ RequestContext.java (revision 0) >@@ -0,0 +1,127 @@ >+/* >+ * Licensed to the Apache Software Foundation (ASF) under one or more >+ * contributor license agreements. See the NOTICE file distributed with >+ * this work for additional information regarding copyright ownership. >+ * The ASF licenses this file to You 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.coyote.ajp.client; >+ >+import java.io.File; >+import java.net.URL; >+import java.util.HashMap; >+import java.util.Map; >+ >+public class RequestContext { >+ >+ private URL url; >+ private Map<String,String> headers=new HashMap<String,String>(); >+ private Map<String,String> queries=new HashMap<String,String>(); >+ String method; >+ String body; >+ File bodyFile; >+ ClientContext cc; >+ int rounds; >+ >+ public RequestContext(ClientContext cc){ >+ this.cc = cc; >+ rounds=1; >+ } >+ >+ void setUrl(URL url) { >+ this.url = url; >+ } >+ >+ URL getUrl() { >+ return url; >+ } >+ >+ >+ void setHeader(String name,String value){ >+ headers.put(name, value); >+ } >+ >+ void setHeaderIfNotPresent(String name,String value){ >+ if(headers.get(name) == null){ >+ headers.put(name, value); >+ } >+ } >+ >+ void setQueryParam(String name, String value){ >+ queries.put(name, value); >+ } >+ >+ void setBodyFile(File file){ >+ this.bodyFile=file; >+ } >+ >+ void setMethod(String method){ >+ this.method=method; >+ } >+ >+ void setRounds(int rounds){ >+ this.rounds=rounds; >+ } >+ >+ Map<String,String> getHeaders() { >+ return headers; >+ } >+ >+ ClientContext getClientContext(){ >+ return cc; >+ } >+ >+ String getMethod(){ >+ return method; >+ } >+ >+ int getRounds(){ >+ return rounds; >+ } >+ >+ File getBodyFile(){ >+ return bodyFile; >+ } >+ >+ public Map<String,String> getQueryParams(){ >+ return queries; >+ } >+ >+ @Override >+ public int hashCode() { >+ final int prime = 31; >+ int result = 1; >+ result = prime * result + ((url == null) ? 0 : url.hashCode()); >+ return result; >+ } >+ >+ @Override >+ public boolean equals(Object obj) { >+ if (this == obj) >+ return true; >+ if (obj == null) >+ return false; >+ if (getClass() != obj.getClass()) >+ return false; >+ RequestContext other = (RequestContext) obj; >+ if (url == null) { >+ if (other.url != null) >+ return false; >+ } else if (!url.equals(other.url)) >+ return false; >+ return true; >+ } >+ >+ >+ >+} >Index: Statistics.java >=================================================================== >--- Statistics.java (revision 0) >+++ Statistics.java (revision 0) >@@ -0,0 +1,71 @@ >+/* >+ * Licensed to the Apache Software Foundation (ASF) under one or more >+ * contributor license agreements. See the NOTICE file distributed with >+ * this work for additional information regarding copyright ownership. >+ * The ASF licenses this file to You 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.coyote.ajp.client; >+ >+import java.net.URL; >+ >+ >+public class Statistics { >+ >+ private URL url; >+ private boolean timedout; >+ private String dateTime; >+ private long time_elapsed; >+ private int reply_code; >+ >+ void setUrl(URL url) { >+ this.url = url; >+ } >+ >+ URL getUrl() { >+ return url; >+ } >+ >+ void setTimeout(boolean timedout) { >+ this.timedout = timedout; >+ } >+ >+ boolean isTimeout() { >+ return timedout; >+ } >+ >+ void setTimeElapsed(long time_elapsed) { >+ this.time_elapsed = time_elapsed; >+ } >+ >+ long getTimeElapsed() { >+ return time_elapsed; >+ } >+ >+ void setReplyCode(int reply_code) { >+ this.reply_code = reply_code; >+ } >+ >+ int getReplyCode() { >+ return reply_code; >+ } >+ >+ void setDateTime(String dateTime) { >+ this.dateTime = dateTime; >+ } >+ >+ String getDateTime() { >+ return dateTime; >+ } >+ >+}
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 47242
:
25056
|
25106
|
25118
| 25223