410 Stimmen

Einfacher HTTP-Server in Java, der nur die Java SE API verwendet

Gibt es eine Möglichkeit, einen sehr einfachen HTTP-Server (der nur GET/POST unterstützt) in Java zu erstellen, indem man nur die Java SE API verwendet, ohne Code zu schreiben, um HTTP-Anfragen manuell zu parsen und HTTP-Antworten manuell zu formatieren? Die Java SE API kapselt die HTTP-Client-Funktionalität sehr gut in HttpURLConnection aber gibt es ein Analogon für die HTTP-Server-Funktionalität?

Nur um das klarzustellen: Das Problem, das ich mit einer Menge von ServerSocket Die Beispiele, die ich online gesehen habe, zeigen, dass sie ihre eigene Anfrageanalyse/Antwortformatierung und Fehlerbehandlung durchführen, was mühsam, fehleranfällig und wahrscheinlich nicht umfassend ist, und aus diesen Gründen versuche ich, es zu vermeiden.

21voto

amichair Punkte 3905

Vor einiger Zeit war ich auf der Suche nach etwas Ähnlichem - einem leichtgewichtigen und dennoch voll funktionsfähigen HTTP-Server, den ich leicht einbetten und anpassen konnte. Ich fand zwei Arten von möglichen Lösungen:

  • Vollständige Server, die nicht gerade leichtgewichtig oder einfach sind (für eine extreme Definition von leichtgewichtig).
  • Wirklich leichtgewichtige Server, die nicht wirklich HTTP-Server sind, sondern glorifizierte ServerSocket-Beispiele, die nicht einmal im Entferntesten RFC-konform sind und allgemein benötigte Grundfunktionen nicht unterstützen.

Also... habe ich mir vorgenommen, zu schreiben JLHTTP - Der Java-Leichtgewicht-HTTP-Server .

Sie können es in jedes Projekt als eine einzige (wenn auch recht lange) Quelldatei oder als ~50K jar (~35K stripped) ohne Abhängigkeiten einbetten. Es ist bestrebt, RFC-konform zu sein und enthält eine ausführliche Dokumentation und viele nützliche Funktionen, während es gleichzeitig so wenig wie möglich aufgebläht ist.

Zu den Funktionen gehören: virtuelle Hosts, File Serving von der Festplatte, Mime-Type-Mappings über die Standard-Mime. Typen-Datei, Verzeichnisindex-Generierung, Willkommensdateien, Unterstützung für alle HTTP-Methoden, bedingte ETags und If-*-Header-Unterstützung, Chunked-Transfer-Codierung, gzip/deflate-Komprimierung, grundlegendes HTTPS (wie von der JVM bereitgestellt), partieller Inhalt (Download-Fortsetzung), Multipart/Form-Data-Behandlung für Dateiuploads, mehrere Kontext-Handler über API oder Anmerkungen, Parameter-Parsing (Abfrage-String oder x-www-form-urlencoded body) usw.

Ich hoffe, andere finden es nützlich :-)

12voto

Laercio Metzner Punkte 1323

Spark ist am einfachsten, hier ist eine Schnellstartanleitung: http://sparkjava.com/

10voto

Balu mallisetty Punkte 543

Alle oben genannten Antworten enthalten Details über Single Main Threaded Request Handler.

Umgebung:

 server.setExecutor(java.util.concurrent.Executors.newCachedThreadPool());

Ermöglicht die Bearbeitung mehrerer Anfragen über mehrere Threads unter Verwendung des Executor-Dienstes.

Der endgültige Code sieht dann etwa wie folgt aus:

import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
public class App {
    public static void main(String[] args) throws Exception {
        HttpServer server = HttpServer.create(new InetSocketAddress(8000), 0);
        server.createContext("/test", new MyHandler());
        //Thread control is given to executor service.
        server.setExecutor(java.util.concurrent.Executors.newCachedThreadPool());
        server.start();
    }
    static class MyHandler implements HttpHandler {
        @Override
        public void handle(HttpExchange t) throws IOException {
            String response = "This is the response";
            long threadId = Thread.currentThread().getId();
            System.out.println("I am thread " + threadId );
            response = response + "Thread Id = "+threadId;
            t.sendResponseHeaders(200, response.length());
            OutputStream os = t.getResponseBody();
            os.write(response.getBytes());
            os.close();
        }
    }
}

10voto

Mahozad Punkte 10508

Beginnend in Java 18 können Sie mit der Java-Standardbibliothek einfache Webserver erstellen:

class Main {
    public static void main(String[] args) {
        var port = 8000;
        var rootDirectory = Path.of("C:/Users/Mahozad/Desktop/");
        var outputLevel = OutputLevel.VERBOSE;
        var server = SimpleFileServer.createFileServer(
                new InetSocketAddress(port),
                rootDirectory,
                outputLevel
        );
        server.start();
    }
}

Dies zeigt standardmäßig eine Verzeichnisliste des von Ihnen angegebenen Stammverzeichnisses an. Sie können ein index.html Datei (und andere Assets wie CSS- und JS-Dateien) in diesem Verzeichnis, um sie stattdessen anzuzeigen.

Beispiel (ich habe diese in Schreibtisch das oben als mein Verzeichnis Root angegeben wurde):

index.html :

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Java 18 Simple Web Server</title>
  <link rel="stylesheet" href="styles.css">
  <script src="scripts.js" defer></script>
</head>
<body>
  <h1>I'm <i>index.html</i> in the root directory.</h1>
</body>
</html>

styles.css :

h1 { color: blue; }

skripte.js :

let element = document.getElementsByTagName("h1")[0];
element.style.fontSize = "48px";

Sidenote

Für die Java-Standardbibliothek HTTP-Client, siehe diese Stelle .

9voto

f.carlsen Punkte 445

Es ist möglich, einen httpserver zu erstellen, der grundlegende Unterstützung für J2EE-Servlets mit nur dem JDK und der Servlet-Api in nur wenigen Zeilen Code bietet.

Ich habe festgestellt, dass dies für Unit-Tests von Servlets sehr nützlich ist, da es viel schneller startet als andere leichtgewichtige Container (wir verwenden Jetty für die Produktion).

Die meisten sehr leichtgewichtigen Httpserver bieten keine Unterstützung für Servlets, aber wir brauchen sie, also dachte ich, ich teile sie.

Das folgende Beispiel bietet grundlegende Servlet-Unterstützung oder wirft eine UnsupportedOperationException für Dinge, die noch nicht implementiert sind. Es verwendet den com.sun.net.httpserver.HttpServer für die grundlegende http-Unterstützung.

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<String, String[]> postData;
        private final ServletInputStream is;
        private final Map<String, Object> attributes = new HashMap<>();

        private RequestWrapper(HttpServletRequest request, HttpExchange ex, Map<String, String[]> 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<String> getHeaders(String name) {
            return new Vector<String>(ex.getRequestHeaders().get(name)).elements();
        }

        @Override
        public Enumeration<String> getHeaderNames() {
            return new Vector<String>(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<String> getAttributeNames() {
            return new Vector<String>(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<String, String[]> getParameterMap() {
            return postData;
        }

        @Override
        public Enumeration<String> getParameterNames() {
            return new Vector<String>(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<String, String[]> 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<String, String[]> 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> T createUnimplementAdapter(Class<T> 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());
    }
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X