/******************************************************************************* * 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.wink.client; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.BindException; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.net.ServerSocketFactory; import javax.net.ssl.SSLServerSocketFactory; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; public class MockHttpServer extends Thread { public static class MockHttpServerResponse { // mock response data private int mockResponseCode = 200; private Map<String, String> mockResponseHeaders = new HashMap<String, String>(); private byte[] mockResponseContent = BaseTest.RECEIVED_MESSAGE.getBytes(); private String mockResponseContentType = "text/plain;charset=utf-8"; private boolean mockResponseContentEchoRequest; public void setMockResponseHeaders(Map<String, String> headers) { mockResponseHeaders.clear(); mockResponseHeaders.putAll(headers); } public void setMockResponseHeader(String name, String value) { mockResponseHeaders.put(name, value); } public Map<String, String> getMockResponseHeaders() { return mockResponseHeaders; } public void setMockResponseCode(int responseCode) { this.mockResponseCode = responseCode; } public int getMockResponseCode() { return mockResponseCode; } public void setMockResponseContent(String content) { mockResponseContent = content.getBytes(); } public void setMockResponseContent(byte[] content) { mockResponseContent = content; } public byte[] getMockResponseContent() { return mockResponseContent; } public void setMockResponseContentType(String type) { mockResponseContentType = type; } public String getMockResponseContentType() { return mockResponseContentType; } public void setMockResponseContentEchoRequest(boolean echo) { mockResponseContentEchoRequest = echo; } public boolean getMockResponseContentEchoRequest() { return mockResponseContentEchoRequest; } } private Thread serverThread = null; private ServerSocket serverSocket = null; private boolean serverStarted = false; private ServerSocketFactory serverSocketFactory = null; private int serverPort; private int readTimeOut = 5000; // 5 // seconds private int delayResponseTime = 0; private static byte[] NEW_LINE = "\r\n".getBytes(); // request data private String requestMethod = null; private String requestUrl = null; private Map<String, List<String>> requestHeaders = new HashMap<String, List<String>>(); private ByteArrayOutputStream requestContent = new ByteArrayOutputStream(); private List<MockHttpServerResponse> mockHttpServerResponses = new ArrayList<MockHttpServerResponse>(); private int responseCounter = 0; public MockHttpServer(int serverPort) { this(serverPort, false); } public MockHttpServer(int serverPort, boolean ssl) { mockHttpServerResponses.add(new MockHttpServerResponse()); // set a // default // response this.serverPort = serverPort; try { serverSocketFactory = ServerSocketFactory.getDefault(); if (ssl) { serverSocketFactory = SSLServerSocketFactory.getDefault(); } while (serverSocket == null) { try { serverSocket = serverSocketFactory.createServerSocket(++this.serverPort); } catch (BindException e) { } } } catch (IOException e) { throw new RuntimeException(e); } } public synchronized void startServer() { if (serverStarted) return; // start the server thread start(); serverStarted = true; // wait for the server thread to start waitForServerToStart(); } private synchronized void waitForServerToStart() { try { wait(5000); } catch (InterruptedException e) { throw new RuntimeException(e); } } private synchronized void waitForServerToStop() { try { wait(5000); } catch (InterruptedException e) { throw new RuntimeException(e); } } public void run() { serverThread = Thread.currentThread(); executeLoop(); } private void executeLoop() { serverStarted(); try { while (true) { Socket socket = serverSocket.accept(); HttpProcessor processor = new HttpProcessor(socket); processor.run(); } } catch (IOException e) { if (e instanceof SocketException) { if (!("Socket closed".equalsIgnoreCase(e.getMessage()) || "Socket is closed" .equalsIgnoreCase(e.getMessage()))) { e.printStackTrace(); throw new RuntimeException(e); } } else { e.printStackTrace(); throw new RuntimeException(e); } } finally { // notify that the server was stopped serverStopped(); } } private synchronized void serverStarted() { // notify the waiting thread that the thread started notifyAll(); } private synchronized void serverStopped() { // notify the waiting thread that the thread started notifyAll(); } public synchronized void stopServer() { if (!serverStarted) return; try { serverStarted = false; // the server may be sleeping somewhere... serverThread.interrupt(); // close the server socket serverSocket.close(); // wait for the server to stop waitForServerToStop(); } catch (IOException e) { e.printStackTrace(); } } private class HttpProcessor { private Socket socket; public HttpProcessor(Socket socket) throws SocketException { // set the read timeout (5 seconds by default) socket.setSoTimeout(readTimeOut); socket.setKeepAlive(false); this.socket = socket; } public void run() { try { processRequest(socket); processResponse(socket); } catch (IOException e) { if (e instanceof SocketException) { if (!("socket closed".equalsIgnoreCase(e.getMessage()))) { e.printStackTrace(); throw new RuntimeException(e); } } else { e.printStackTrace(); throw new RuntimeException(e); } } finally { try { socket.shutdownOutput(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } } private void processRequest(Socket socket) throws IOException { requestContent.reset(); BufferedInputStream is = new BufferedInputStream(socket.getInputStream()); String requestMethodHeader = new String(readLine(is)); if (requestMethodHeader == null) { return; } processRequestMethod(requestMethodHeader); processRequestHeaders(is); processRequestContent(is); } private void processRequestMethod(String requestMethodHeader) { String[] parts = requestMethodHeader.split(" "); if (parts.length < 2) { throw new RuntimeException("illegal http request"); } requestMethod = parts[0]; requestUrl = parts[1]; } private void processRequestHeaders(InputStream is) throws IOException { requestHeaders.clear(); byte[] line = null; while ((line = readLine(is)) != null) { String lineStr = new String(line); // if there are no more headers if ("".equals(lineStr.trim())) { break; } addRequestHeader(lineStr); } } private void processRequestContent(InputStream is) throws NumberFormatException, IOException { if (!("PUT".equals(requestMethod) || "POST".equals(requestMethod))) { return; } List<String> transferEncodingValues = requestHeaders.get("Transfer-Encoding"); String transferEncoding = (transferEncodingValues == null || transferEncodingValues.isEmpty()) ? null : transferEncodingValues.get(0); if ("chunked".equals(transferEncoding)) { processChunkedContent(is); } else { processRegularContent(is); } if (mockHttpServerResponses.get(responseCounter).getMockResponseContentEchoRequest()) { mockHttpServerResponses.get(responseCounter).setMockResponseContent(requestContent .toByteArray()); } } private void processRegularContent(InputStream is) throws IOException { List<String> contentLengthValues = requestHeaders.get("Content-Length"); String contentLength = (contentLengthValues == null || contentLengthValues.isEmpty()) ? null : contentLengthValues.get(0); if (contentLength == null) { return; } int contentLen = Integer.parseInt(contentLength); byte[] bytes = new byte[contentLen]; is.read(bytes); requestContent.write(bytes); } private void processChunkedContent(InputStream is) throws IOException { requestContent.write("".getBytes()); byte[] chunk = null; byte[] line = null; boolean lastChunk = false; // we should exit this loop only after we get to the end of stream while (!lastChunk && (line = readLine(is)) != null) { String lineStr = new String(line); // a chunk is identified as: // 1) not an empty line // 2) not 0. 0 means that there are no more chunks if ("0".equals(lineStr)) { lastChunk = true; } if (!lastChunk) { // get the length of the current chunk (it is in hexadecimal // form) int chunkLen = Integer.parseInt(lineStr, 16); // get the chunk chunk = getChunk(is, chunkLen); // consume the newline after the chunk that separates // between // the chunk content and the next chunk size readLine(is); requestContent.write(chunk); } } // do one last read to consume the empty line after the last chunk if (lastChunk) { readLine(is); } } private byte[] readLine(InputStream is) throws IOException { int n; ByteArrayOutputStream tmpOs = new ByteArrayOutputStream(); while ((n = is.read()) != -1) { if (n == '\r') { n = is.read(); if (n == '\n') { return tmpOs.toByteArray(); } else { tmpOs.write('\r'); if (n != -1) { tmpOs.write(n); } else { return tmpOs.toByteArray(); } } } else if (n == '\n') { return tmpOs.toByteArray(); } else { tmpOs.write(n); } } return tmpOs.toByteArray(); } private byte[] getChunk(InputStream is, int len) throws IOException { ByteArrayOutputStream chunk = new ByteArrayOutputStream(); int read = 0; int totalRead = 0; byte[] bytes = new byte[512]; // read len bytes as the chunk while (totalRead < len) { read = is.read(bytes, 0, Math.min(bytes.length, len - totalRead)); chunk.write(bytes, 0, read); totalRead += read; } return chunk.toByteArray(); } private void addRequestHeader(String line) { String[] parts = line.split(": "); List<String> values = requestHeaders.get(parts[0]); if (values == null) { values = new ArrayList<String>(); requestHeaders.put(parts[0], values); } values.add(parts[1]); } private void processResponse(Socket socket) throws IOException { // if delaying the response failed (because it was interrupted) // then don't send the response if (!delayResponse()) return; OutputStream sos = socket.getOutputStream(); BufferedOutputStream os = new BufferedOutputStream(sos); String reason = ""; Status statusCode = Response.Status.fromStatusCode(mockHttpServerResponses.get(responseCounter) .getMockResponseCode()); if (statusCode != null) { reason = statusCode.toString(); } os.write(("HTTP/1.1 " + mockHttpServerResponses.get(responseCounter) .getMockResponseCode() + " " + reason).getBytes()); os.write(NEW_LINE); processResponseHeaders(os); processResponseContent(os); os.flush(); responseCounter++; } // return: // true - delay was successful // false - delay was unsuccessful private boolean delayResponse() { // delay the response by delayResponseTime milliseconds if (delayResponseTime > 0) { try { Thread.sleep(delayResponseTime); return true; } catch (InterruptedException e) { return false; } } return true; } private void processResponseContent(OutputStream os) throws IOException { if (mockHttpServerResponses.get(responseCounter).getMockResponseContent() == null) { return; } os.write(mockHttpServerResponses.get(responseCounter).getMockResponseContent()); } private void processResponseHeaders(OutputStream os) throws IOException { addServerResponseHeaders(); for (String header : mockHttpServerResponses.get(responseCounter) .getMockResponseHeaders().keySet()) { os.write((header + ": " + mockHttpServerResponses.get(responseCounter) .getMockResponseHeaders().get(header)).getBytes()); os.write(NEW_LINE); } os.write(NEW_LINE); } private void addServerResponseHeaders() { Map<String, String> mockResponseHeaders = mockHttpServerResponses.get(responseCounter).getMockResponseHeaders(); mockResponseHeaders.put("Content-Type", mockHttpServerResponses.get(responseCounter) .getMockResponseContentType()); mockResponseHeaders.put("Content-Length", mockHttpServerResponses.get(responseCounter) .getMockResponseContent().length + ""); mockResponseHeaders.put("Server", "Mock HTTP Server v1.0"); mockResponseHeaders.put("Connection", "closed"); } } public void setReadTimeout(int milliseconds) { readTimeOut = milliseconds; } public void setDelayResponse(int milliseconds) { delayResponseTime = milliseconds; } public String getRequestContentAsString() { return requestContent.toString(); } public byte[] getRequestContent() { return requestContent.toByteArray(); } public Map<String, List<String>> getRequestHeaders() { return requestHeaders; } public String getRequestMethod() { return requestMethod; } public String getRequestUrl() { return requestUrl; } public static void main(String[] args) { MockHttpServer server = null; try { /* * StringReader reader = new StringReader("lalalalala\r\n"); * BufferedReader r = new BufferedReader(reader); String a = * r.readLine(); String b = r.readLine(); System.out.println(a); * System.out.println(b); server = new MockHttpServer(3334); * server.setMockResponseContent("Howdee!"); * server.setMockResponseContentType("text/plain;charset=UTF-8"); * //Map<String,String> maps = new HashMap<String,String>(); * //maps.put("Location", "http://localhost:3333/lalala"); * //server.setMockResponseHeaders(maps); server.startServer(); URL * u = new * URL("http://localhost:3334/qadefect-service/rest/defects/2"); * HttpURLConnection huc = (HttpURLConnection)u.openConnection(); * huc.setRequestMethod("PUT"); huc.setDoOutput(true); * huc.setChunkedStreamingMode(40); * huc.addRequestProperty("Content-Type", "application/xml"); * huc.connect(); OutputStream os = huc.getOutputStream(); * os.write("martinmartinartinmartinmartin".getBytes()); * //os.flush(); * os.write("martinmartinartinmartinmartin".getBytes()); os.flush(); * os.close(); huc.getResponseCode(); huc.getHeaderField("Server"); * InputStream is = huc.getInputStream(); byte[] by = new byte[0]; * byte[] by1 = new byte[5]; int readdd = is.read(by); * System.out.println("readdd = " + readdd); readdd = is.read(by1); * System.out.println("readdd = " + readdd); readdd = is.read(by); * System.out.println("readdd = " + readdd); readdd = is.read(by1); * System.out.println("readdd = " + readdd); readdd = is.read(by); * System.out.println("readdd = " + readdd); huc.disconnect(); */ // RestClient client = new RestClient(); // // // init the resource // String url = // "http://localhost:3333/qadefect-service/rest/defects/2"; // Resource resource = client.newResource(url); // // // get defect // Response<TextEntity> response = resource.doGet(TextEntity.class); // TextEntity d = response.getEntity(); // System.out.println("GET returned content: " + d.getText()); // // TextEntity text = new TextEntity(); // text.setText("Hello"); // response = resource.doPut(text, TextEntity.class); // d = response.getEntity(); // System.out.println("PUT sent content: " + // server.getRequestContent()); // System.out.println("PUT returned content: " + d.getText()); // System.out.println(server.getRequestUrl()); } catch (Exception e1) { e1.printStackTrace(); } finally { if (server != null) { server.stopServer(); } } } public void setMockHttpServerResponses(MockHttpServerResponse... responses) { mockHttpServerResponses.clear(); for (int i = 0; i < responses.length; i++) { mockHttpServerResponses.add(responses[i]); } } public List<MockHttpServerResponse> getMockHttpServerResponses() { return mockHttpServerResponses; } public void setServerPort(int serverPort) { this.serverPort = serverPort; } public int getServerPort() { return serverPort; } }