simple HTTP server in Java using only Java SE API

前端 未结 17 1983
无人共我
无人共我 2020-11-22 13:28

Is there a way to create a very basic HTTP server (supporting only GET/POST) in Java using just the Java SE API, without writing code to manually parse HTTP requests and man

17条回答
  •  我在风中等你
    2020-11-22 14:24

    It's possible to create an httpserver that provides basic support for J2EE servlets with just the JDK and the servlet api in a just a few lines of code.

    I've found this very useful for unit testing servlets, as it starts much faster than other lightweight containers (we use jetty for production).

    Most very lightweight httpservers do not provide support for servlets, but we need them, so I thought I'd share.

    The below example provides basic servlet support, or throws and UnsupportedOperationException for stuff not yet implemented. It uses the com.sun.net.httpserver.HttpServer for basic http support.

    import java.io.*;
    import java.lang.reflect.*;
    import java.net.InetSocketAddress;
    import java.util.*;
    
    import javax.servlet.*;
    import javax.servlet.http.*;
    
    import com.sun.net.httpserver.HttpExchange;
    import com.sun.net.httpserver.HttpHandler;
    import com.sun.net.httpserver.HttpServer;
    
    @SuppressWarnings("deprecation")
    public class VerySimpleServletHttpServer {
        HttpServer server;
        private String contextPath;
        private HttpHandler httpHandler;
    
        public VerySimpleServletHttpServer(String contextPath, HttpServlet servlet) {
            this.contextPath = contextPath;
            httpHandler = new HttpHandlerWithServletSupport(servlet);
        }
    
        public void start(int port) throws IOException {
            InetSocketAddress inetSocketAddress = new InetSocketAddress(port);
            server = HttpServer.create(inetSocketAddress, 0);
            server.createContext(contextPath, httpHandler);
            server.setExecutor(null);
            server.start();
        }
    
        public void stop(int secondsDelay) {
            server.stop(secondsDelay);
        }
    
        public int getServerPort() {
            return server.getAddress().getPort();
        }
    
    }
    
    final class HttpHandlerWithServletSupport implements HttpHandler {
    
        private HttpServlet servlet;
    
        private final class RequestWrapper extends HttpServletRequestWrapper {
            private final HttpExchange ex;
            private final Map postData;
            private final ServletInputStream is;
            private final Map attributes = new HashMap<>();
    
            private RequestWrapper(HttpServletRequest request, HttpExchange ex, Map postData, ServletInputStream is) {
                super(request);
                this.ex = ex;
                this.postData = postData;
                this.is = is;
            }
    
            @Override
            public String getHeader(String name) {
                return ex.getRequestHeaders().getFirst(name);
            }
    
            @Override
            public Enumeration getHeaders(String name) {
                return new Vector(ex.getRequestHeaders().get(name)).elements();
            }
    
            @Override
            public Enumeration getHeaderNames() {
                return new Vector(ex.getRequestHeaders().keySet()).elements();
            }
    
            @Override
            public Object getAttribute(String name) {
                return attributes.get(name);
            }
    
            @Override
            public void setAttribute(String name, Object o) {
                this.attributes.put(name, o);
            }
    
            @Override
            public Enumeration getAttributeNames() {
                return new Vector(attributes.keySet()).elements();
            }
    
            @Override
            public String getMethod() {
                return ex.getRequestMethod();
            }
    
            @Override
            public ServletInputStream getInputStream() throws IOException {
                return is;
            }
    
            @Override
            public BufferedReader getReader() throws IOException {
                return new BufferedReader(new InputStreamReader(getInputStream()));
            }
    
            @Override
            public String getPathInfo() {
                return ex.getRequestURI().getPath();
            }
    
            @Override
            public String getParameter(String name) {
                String[] arr = postData.get(name);
                return arr != null ? (arr.length > 1 ? Arrays.toString(arr) : arr[0]) : null;
            }
    
            @Override
            public Map getParameterMap() {
                return postData;
            }
    
            @Override
            public Enumeration getParameterNames() {
                return new Vector(postData.keySet()).elements();
            }
        }
    
        private final class ResponseWrapper extends HttpServletResponseWrapper {
            final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            final ServletOutputStream servletOutputStream = new ServletOutputStream() {
    
                @Override
                public void write(int b) throws IOException {
                    outputStream.write(b);
                }
            };
    
            private final HttpExchange ex;
            private final PrintWriter printWriter;
            private int status = HttpServletResponse.SC_OK;
    
            private ResponseWrapper(HttpServletResponse response, HttpExchange ex) {
                super(response);
                this.ex = ex;
                printWriter = new PrintWriter(servletOutputStream);
            }
    
            @Override
            public void setContentType(String type) {
                ex.getResponseHeaders().add("Content-Type", type);
            }
    
            @Override
            public void setHeader(String name, String value) {
                ex.getResponseHeaders().add(name, value);
            }
    
            @Override
            public javax.servlet.ServletOutputStream getOutputStream() throws IOException {
                return servletOutputStream;
            }
    
            @Override
            public void setContentLength(int len) {
                ex.getResponseHeaders().add("Content-Length", len + "");
            }
    
            @Override
            public void setStatus(int status) {
                this.status = status;
            }
    
            @Override
            public void sendError(int sc, String msg) throws IOException {
                this.status = sc;
                if (msg != null) {
                    printWriter.write(msg);
                }
            }
    
            @Override
            public void sendError(int sc) throws IOException {
                sendError(sc, null);
            }
    
            @Override
            public PrintWriter getWriter() throws IOException {
                return printWriter;
            }
    
            public void complete() throws IOException {
                try {
                    printWriter.flush();
                    ex.sendResponseHeaders(status, outputStream.size());
                    if (outputStream.size() > 0) {
                        ex.getResponseBody().write(outputStream.toByteArray());
                    }
                    ex.getResponseBody().flush();
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    ex.close();
                }
            }
        }
    
        public HttpHandlerWithServletSupport(HttpServlet servlet) {
            this.servlet = servlet;
        }
    
        @SuppressWarnings("deprecation")
        @Override
        public void handle(final HttpExchange ex) throws IOException {
            byte[] inBytes = getBytes(ex.getRequestBody());
            ex.getRequestBody().close();
            final ByteArrayInputStream newInput = new ByteArrayInputStream(inBytes);
            final ServletInputStream is = new ServletInputStream() {
    
                @Override
                public int read() throws IOException {
                    return newInput.read();
                }
            };
    
            Map parsePostData = new HashMap<>();
    
            try {
                parsePostData.putAll(HttpUtils.parseQueryString(ex.getRequestURI().getQuery()));
    
                // check if any postdata to parse
                parsePostData.putAll(HttpUtils.parsePostData(inBytes.length, is));
            } catch (IllegalArgumentException e) {
                // no postData - just reset inputstream
                newInput.reset();
            }
            final Map postData = parsePostData;
    
            RequestWrapper req = new RequestWrapper(createUnimplementAdapter(HttpServletRequest.class), ex, postData, is);
    
            ResponseWrapper resp = new ResponseWrapper(createUnimplementAdapter(HttpServletResponse.class), ex);
    
            try {
                servlet.service(req, resp);
                resp.complete();
            } catch (ServletException e) {
                throw new IOException(e);
            }
        }
    
        private static byte[] getBytes(InputStream in) throws IOException {
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            while (true) {
                int r = in.read(buffer);
                if (r == -1)
                    break;
                out.write(buffer, 0, r);
            }
            return out.toByteArray();
        }
    
        @SuppressWarnings("unchecked")
        private static  T createUnimplementAdapter(Class httpServletApi) {
            class UnimplementedHandler implements InvocationHandler {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    throw new UnsupportedOperationException("Not implemented: " + method + ", args=" + Arrays.toString(args));
                }
            }
    
            return (T) Proxy.newProxyInstance(UnimplementedHandler.class.getClassLoader(),
                    new Class[] { httpServletApi },
                    new UnimplementedHandler());
        }
    }
    

提交回复
热议问题