Простой HTTP-сервер в Java с использованием только Java SE API

Есть ли способ создать очень простой HTTP-сервер (поддерживающий только GET/POST) в Java, используя только API Java SE, без написания кода для ручного анализа HTTP-запросов и отформатирования HTTP-ответов вручную? Java SE API прекрасно инкапсулирует функциональность HTTP-клиента в HttpURLConnection, но существует ли аналоговый для HTTP-сервер функционал?

Просто, чтобы быть ясным, проблема, с которой я сталкиваюсь с множеством примеров ServerSocket, которые я видел в Интернете, заключается в том, что они выполняют собственный алгоритм синтаксического анализа/отклика запросов и обработку ошибок, что является утомительным, подверженным ошибкам и вряд ли быть всеобъемлющим, и я стараюсь избегать этого по этим причинам.

В качестве примера ручной HTTP-манипуляции, которую я пытаюсь избежать:

http://java.sun.com/developer/technicalArticles/Networking/Webserver/WebServercode.html

Ответ 1

Начиная с Java SE 6 в Sun Oracle JRE имеется встроенный HTTP-сервер. В сводке пакета com.sun.net.httpserver описаны участвующие классы и приведены примеры.

Вот начальный пример, скопированный из их документов (тем не менее, для всех, кто пытается его редактировать, потому что это ужасный кусок кода, пожалуйста, не копируйте, а не мой, более того, вы никогда не должны редактировать цитаты, если они не изменились. в первоисточнике). Вы можете просто скопировать и запустить его на Java 6+.

package com.stackoverflow.q3732109;

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 Test {

    public static void main(String[] args) throws Exception {
        HttpServer server = HttpServer.create(new InetSocketAddress(8000), 0);
        server.createContext("/test", new MyHandler());
        server.setExecutor(null); // creates a default executor
        server.start();
    }

    static class MyHandler implements HttpHandler {
        @Override
        public void handle(HttpExchange t) throws IOException {
            String response = "This is the response";
            t.sendResponseHeaders(200, response.length());
            OutputStream os = t.getResponseBody();
            os.write(response.getBytes());
            os.close();
        }
    }

}

Следует отметить, что часть response.length() в их примере плохая, это должна быть response.getBytes().length. Даже в этом getBytes() метод getBytes() должен явно указывать кодировку, которую вы затем указываете в заголовке ответа. Увы, хотя и вводящий в заблуждение начинающих, это всего лишь базовый пример.

Выполните его и перейдите по адресу http://localhost: 8000/test, и вы увидите следующий ответ:

Это ответ


Что касается использования com.sun.*, Обратите внимание, что это, в отличие от того, что думают некоторые разработчики, абсолютно не запрещено общеизвестными часто задаваемыми вопросами. Почему разработчики не должны писать программы, называющие "солнечные" пакеты. Этот sun.misc.BASE64Encoder часто задаваемых вопросов касается пакета sun.* (Такого как sun.misc.BASE64Encoder) для внутреннего использования Oracle JRE (который, таким образом, убьет ваше приложение при запуске его на другом JRE), а не пакет com.sun.*. Sun/Oracle также просто разрабатывает программное обеспечение поверх API Java SE, как и любая другая компания, такая как Apache и так далее. Использование com.sun.* рекомендуется (но не запрещено), когда это касается реализации определенного Java API, такого как GlassFish (Java EE impl), Mojarra (JSF impl), Джерси (JAX-RS impl) и т.д.,

Ответ 2

Отъезд NanoHttpd

"NanoHTTPD - это легкий HTTP-сервер, предназначенный для встраивания в другие приложения, выпущенные под лицензией Modified BSD.

Он разрабатывается в Github и использует Apache Maven для сборки и модульного тестирования.

Ответ 3

Решение com.sun.net.httpserver не переносится через JRE. Лучше использовать официальный API веб-сервисов в javax.xml.ws для загрузки минимального HTTP-сервера...

import java.io._
import javax.xml.ws._
import javax.xml.ws.http._
import javax.xml.transform._
import javax.xml.transform.stream._

@WebServiceProvider
@ServiceMode(value=Service.Mode.PAYLOAD) 
class P extends Provider[Source] {
  def invoke(source: Source) = new StreamSource( new StringReader("<p>Hello There!</p>"));
}

val address = "http://127.0.0.1:8080/"
Endpoint.create(HTTPBinding.HTTP_BINDING, new P()).publish(address)

println("Service running at "+address)
println("Type [CTRL]+[C] to quit!")

Thread.sleep(Long.MaxValue)

EDIT: это действительно работает! Вышеприведенный код выглядит как Groovy или что-то в этом роде. Вот перевод на Java, который я тестировал:

import java.io.*;
import javax.xml.ws.*;
import javax.xml.ws.http.*;
import javax.xml.transform.*;
import javax.xml.transform.stream.*;

@WebServiceProvider
@ServiceMode(value = Service.Mode.PAYLOAD)
public class Server implements Provider<Source> {

    public Source invoke(Source request) {
        return  new StreamSource(new StringReader("<p>Hello There!</p>"));
    }

    public static void main(String[] args) throws InterruptedException {

        String address = "http://127.0.0.1:8080/";
        Endpoint.create(HTTPBinding.HTTP_BINDING, new Server()).publish(address);

        System.out.println("Service running at " + address);
        System.out.println("Type [CTRL]+[C] to quit!");

        Thread.sleep(Long.MAX_VALUE);
    }
}

Ответ 4

Посмотрите на веб-сервер "Jetty" Jetty. Превосходная часть программного обеспечения с открытым исходным кодом, которая, похоже, отвечает всем вашим требованиям.

Если вы настаиваете на сворачивании своих собственных, посмотрите на класс "httpMessage".

Ответ 5

Мне нравится этот вопрос, потому что это область, где постоянно развиваются инновации, и всегда нужно иметь легкий сервер, особенно когда речь идет о встроенных серверах на небольших (er) устройствах. Я думаю, что ответы делятся на две широкие группы.

  • Thin-server: статический контент на сервере с минимальной обработкой, обработкой контекста или сеансом.
  • Малый сервер: якобы a имеет много http-подобных свойств сервера с небольшим размером, поскольку вы можете уйти.

Хотя я мог бы подумать о библиотеках HTTP, таких как: Jetty, Apache Http Components, Netty и другие, чтобы быть более похожими на необработанные средства обработки HTTP. Маркировка очень субъективна и зависит от того, что вы делали для небольших сайтов. Я делаю это различие в духе вопроса, особенно замечание о...

  • "... без написания кода для ручного анализа HTTP-запросов и вручную отклика HTTP-ответов..."

Эти исходные инструменты позволяют делать это (как описано в других ответах). Они действительно не поддаются готовому дизайну, создавая легкий, встроенный или мини-сервер. Мини-сервер - это то, что может дать вам аналогичную функциональность полнофункциональному веб-серверу (например, Tomcat) без колоколов и свистов, низкий объем, хорошая производительность в 99% случаев. Тонкий сервер кажется ближе к оригинальной фразе, чуть более сырой, возможно, с ограниченным набором функций, достаточным для того, чтобы вы выглядели неплохо в 90% случаев. Мое представление о сыром будет заставлять меня выглядеть хорошо 75% - 89% времени без дополнительного дизайна и кодирования. Я думаю, что если/когда вы достигнете уровня WAR файлов, мы оставили "маленький" для серверов bonsi, который выглядит как все, что делает большой сервер.

Параметры тонкого сервера

Параметры мини-сервера:

  • Spark Java... Хорошие вещи возможны благодаря множеству вспомогательных конструкций, таких как фильтры, шаблоны и т.д.
  • MadVoc... стремится быть бонсай и вполне может быть таким; -)

Среди других вещей, которые следует учитывать, я бы включил аутентификацию, валидацию, интернационализацию, используя что-то вроде FreeMaker или другой инструмент шаблона для рендеринга страницы вывод. В противном случае управление редактированием и параметризацией HTML, вероятно, приведет к тому, что работа с HTTP будет выглядеть как noughts-n-crosses. Естественно, все зависит от того, насколько вы гибки. Если это факсимильная машина с меню, она может быть очень простой. Чем больше взаимодействий, тем "более толстой" должна быть ваша структура. Хороший вопрос, удачи!

Ответ 6

Когда-то я искал нечто похожее - легкий, но полностью функциональный HTTP-сервер, который я мог бы легко встроить и настроить. Я нашел два типа потенциальных решений:

  • Полные серверы, которые не такие легкие или простые (для экстремального определения легкости).
  • Поистине легкие серверы, которые не совсем HTTP-серверы, но прославленные примеры ServerSocket, которые даже не поддерживают RFC-совместимость и не поддерживают обычно необходимые базовые функции.

Итак... Я решил написать JLHTTP - легкий HTTP-сервер Java.

Вы можете встраивать его в любой проект в виде одиночного (если довольно длинного) исходного файла или в виде баннера размером ~ 50 КБ (~ 35 КБ) без каких-либо зависимостей. Он стремится быть совместимым с RFC и включает в себя обширную документацию и множество полезных функций при сохранении раздувания до минимума.

Возможности включают в себя: виртуальные хосты, файлы с диска, сопоставления типа mime через стандартный файл mime.types, генерацию индекса каталога, приветственные файлы, поддержку всех HTTP-методов, условные ETags и поддержку заголовков If- *, кодирование с кодировкой передачи, gzip/сбрасывать сжатие, базовые HTTPS (как предоставлено JVM), частичный контент (продолжение загрузки), обработку мультифайлов/форм файлов для загрузки файлов, несколько обработчиков контекстов через API или аннотации, разбор параметров (строка запроса или x-www-form- urlencoded body) и т.д.

Я надеюсь, что другие считают это полезным :-)

Ответ 7

Spark является самым простым, вот краткое руководство: http://sparkjava.com/

Ответ 9

Возможно создание httpserver, обеспечивающего базовую поддержку сервлетов J2EE только с JDK и сервлета api всего за несколько строк кода.

Я нашел это очень полезным для сервлетов для тестирования модулей, поскольку он запускается намного быстрее, чем другие легкие контейнеры (мы используем причал для производства).

Большинство очень легких httpservers не обеспечивают поддержку сервлетов, но мы нуждаемся в них, поэтому я думал, что буду делиться.

В приведенном ниже примере представлена ​​базовая поддержка сервлетов или выбрасывается и UnsupportedOperationException для тех вещей, которые еще не реализованы. Он использует com.sun.net.httpserver.HttpServer для базовой поддержки http.

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());
    }
}

Ответ 10

Я настоятельно рекомендую изучить Simple, особенно если вам не нужны возможности сервлета, а просто доступ к объектам запроса/ответа, Если вам нужен REST, вы можете положить Джерси поверх него, если вам нужно вывести HTML или аналогичный там Freemarker. Мне очень нравится то, что вы можете сделать с этой комбинацией, и относительно немного API для изучения.

Ответ 12

Этот код лучше нашего, вам нужно только добавить 2 библиотеки: javax.servelet.jar и org.mortbay.jetty.jar.

Причал класса:

package jetty;

import java.util.logging.Level;
import java.util.logging.Logger;
import org.mortbay.http.SocketListener;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.servlet.ServletHttpContext;

public class Jetty {

    public static void main(String[] args) {
        try {
            Server server = new Server();
            SocketListener listener = new SocketListener();      

            System.out.println("Max Thread :" + listener.getMaxThreads() + " Min Thread :" + listener.getMinThreads());

            listener.setHost("localhost");
            listener.setPort(8070);
            listener.setMinThreads(5);
            listener.setMaxThreads(250);
            server.addListener(listener);            

            ServletHttpContext context = (ServletHttpContext) server.getContext("/");
            context.addServlet("/MO", "jetty.HelloWorldServlet");

            server.start();
            server.join();

        /*//We will create our server running at http://localhost:8070
        Server server = new Server();
        server.addListener(":8070");

        //We will deploy our servlet to the server at the path '/'
        //it will be available at http://localhost:8070
        ServletHttpContext context = (ServletHttpContext) server.getContext("/");
        context.addServlet("/MO", "jetty.HelloWorldServlet");

        server.start();
        */

        } catch (Exception ex) {
            Logger.getLogger(Jetty.class.getName()).log(Level.SEVERE, null, ex);
        }

    }
} 

Класс сервлета:

package jetty;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class HelloWorldServlet extends HttpServlet
{
    @Override
    protected void doGet(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws ServletException, IOException
    {
        String appid = httpServletRequest.getParameter("appid");
        String conta = httpServletRequest.getParameter("conta");

        System.out.println("Appid : "+appid);
        System.out.println("Conta : "+conta);

        httpServletResponse.setContentType("text/plain");
        PrintWriter out = httpServletResponse.getWriter();
        out.println("Hello World!");
        out.close();
    }
}

Ответ 13

checkout Simple. его довольно простой встраиваемый сервер со встроенной поддержкой для целого ряда операций. Мне особенно нравится его резьбовая модель.

Потрясающе!

Ответ 15

Как насчет проекта Apache Commons HttpCore?

С веб-сайта:... Цели HttpCore

  • Реализация самых основных аспектов транспорта HTTP
  • Баланс между хорошей производительностью и ясностью и выразительностью API
  • Малый (прогнозируемый) объем памяти памяти
  • Автономная библиотека (без внешних зависимостей за пределами JRE)

Ответ 16

Все вышеперечисленное подробно описывает ответы на один основной обработчик запросов.

установка:

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

Позволяет выполнять несколько запросов через несколько потоков, используя службу executor.

Таким образом, код конца будет примерно таким:

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();
        }
    }
}

Ответ 17

Вы можете написать довольно простой встроенный сервер Jetty Java.

Embedded Jetty означает, что сервер (Jetty) отправлен вместе с приложением в отличие от развертывания приложения на внешнем сервере Jetty.

Итак, если в не встроенном подходе ваш webapp встроен в файл WAR, который развернут на какой-то внешний сервер (Tomcat/Jetty/etc), в встроенном Jetty вы пишете webapp и создаете экземпляр сервера причалов в той же кодовой базе.

Пример для встроенного сервера Jetty Java можно git clone и использовать: https://github.com/stas-slu/embedded-jetty-java-server-example

Ответ 18

Попробуйте это https://github.com/devashish234073/Java-Socket-Http-Server/blob/master/README.md

Этот API создает HTTP-сервер с использованием сокетов.

  1. Получает запрос от браузера в виде текста
  2. Анализирует его, чтобы получить информацию об URL, метод, атрибуты и т.д.
  3. Создает динамический ответ, используя определенное сопоставление URL
  4. Отправляет ответ в браузер.

Например, вот как конструктор в классе Response.java преобразует необработанный ответ в ответ http:

public Response(String resp){
    Date date = new Date();
    String start = "HTTP/1.1 200 OK\r\n";
    String header = "Date: "+date.toString()+"\r\n";
    header+= "Content-Type: text/html\r\n";
    header+= "Content-length: "+resp.length()+"\r\n";
    header+="\r\n";
    this.resp=start+header+resp;
}