web-dev-qa-db-ja.com

java SE APIのみを使用したJavaの単純なHTTPサーバー

手動でHTTP要求を解析して手動でHTTP応答をフォーマットするコードを書かずに、Java SE APIだけを使用して、非常に基本的なHTTPサーバー(GET/POSTのみをサポート)をJavaで作成する方法はありますか? Java SE APIはHttpURLConnectionのHTTPクライアント機能をうまくカプセル化していますが、HTTPサーバー機能の類似物はありますか?

明確にするために、私がオンラインで見た多くのServerSocketの例で私が抱えている問題は、それらが彼ら自身のリクエスト解析/レスポンスフォーマットとエラー処理をするということです。そして私はそれらの理由でそれを避けようとしています。

私が避けようとしている手動のHTTP操作の例として:

http://Java.Sun.com/developer/technicalArticles/Networking/Webserver/WebServercode.html

299
asker

Java SE 6以降、組み込みのHTTPサーバーがあります 太陽 Oracle JRE。 com.Sun.net.httpserver package summary は、関係するクラスの概要を示し、例を含みます。

これは、ドキュメントからのコピー例copypastedです(ただし、編集しようとするすべての人に、これはいコードなので、これはしないでください)コピーペーストは私のものではなく、さらに、元のソースで変更されていない限り、引用を編集しないでください)。 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()メソッドは、応答ヘッダーで指定する文字セットを明示的に指定する必要があります。悲しいかな、初心者に誤解を与えているとはいえ、結局のところ、これは単なる基本的なキックオフの例です。

実行して http:// localhost:8000/test に移動すると、次の応答が表示されます。

これは応答です


com.Sun.*クラスの使用に関しては、これは、一部の開発者が考えることとは反対に、よく知られているFAQ によって絶対に禁止されていないことに注意してください「Sun」パッケージを呼び出すプログラム 。そのFAQは、Oracle JREによる内部使用のためのSun.*パッケージ(Sun.misc.BASE64Encoderなど)に関係します(したがって、異なるJREで実行するとアプリケーションが強制終了されます) com.Sun.*パッケージ。また、Sun/Oracleは、Apacheなどの他のすべての会社と同様に、Java SE API上でソフトウェアを開発するだけです。 com.Sun.*クラスの使用は、特定のJava AP​​Iの実装に関係する場合にのみ推奨されます(ただし禁止されません)。 GlassFish(Java EE impl)、Mojarra(JSF impl)、Jersey(JAX-RS impl)など。

444
BalusC

チェックアウト NanoHttpd

「NanoHTTPDは、Modified BSDライセンスの下でリリースされた、他のアプリケーションへの組み込み用に設計された軽量のHTTPサーバーです。

それはGithubで開発されていて、ビルドと単体テストのためにApache Mavenを使います」

41
letronje

com.Sun.net.httpserver ソリューションはJRE間で移植性がありません。最小のHTTPサーバーをブートストラップするには、 javax.xml.ws の公式WebサービスAPIを使用する方が良いでしょう。

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)

編集:これは実際に動作します!上記のコードは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);
    }
}
24
gruenewa

"Jetty" Webサーバー Jetty を見てください。あなたのすべての要求を満たすように思われるオープンソースソフトウェアのすばらしい作品。

あなたが自分自身を転がすことを主張するならば、それから "httpMessage"クラスを見てください。

21
James Anderson

私はこの質問が好きです。なぜならこれは絶え間ない技術革新があり、特に小型(er)デバイスの組み込みサーバーについて話すときは常に軽量サーバーを持つ必要がある分野です。答えは2つの大きなグループに分けられると思います。

  1. Thin-server :最小限の処理、コンテキスト、またはセッション処理でサーバーアップ静的コンテンツ。
  2. Small-server :明らかにaはあなたが逃げることができるのと同じくらい小さい足跡で多くのhttpDのようなサーバー品質を持っています。

JettyApache HttpコンポーネントNetty などのHTTPライブラリを、生のHTTP処理機能のようなものと考えることもできます。ラベリングは非常に主観的なもので、小規模サイト用に提供するためにあなたが要求してきたものの種類によって異なります。私は質問の精神、特に...についての発言でこの区別をしています。

  • 「... HTTPリクエストを手動で解析し、HTTPレスポンスを手動でフォーマットするためのコードを記述せずに...」

これらの生のツールはあなたにそれをさせます(他の答えで説明されるように)。彼らは、軽量、組み込み、またはミニサーバーを作るという既成のスタイルには向いていません。ミニサーバーとは、フル機能のWebサーバー( Tomcat など)に似たような機能を、鐘を鳴らすことなく、小音量で、99%のパフォーマンスで実現できるものです。シンサーバーは元のフレーズに近いと思われますが、おそらくサブセット機能が限られていることで、生のものよりほんの少しだけ上手くいきます。私の生の考えは、余分なデザインやコーディングをしなくても、75% - 89%見栄えがよくなることです。あなたがWARファイルのレベルに到達したとき、私たちは大きなサーバーがすべて小さくなるようなbonsiサーバー用の「小さい」を残しましたか。

シンサーバーオプション

ミニサーバーオプション:

  • Spark Java ...フィルタ、テンプレートなどのようなたくさんのヘルパー構成体で良いことが可能です。
  • MadVoc ...盆栽になることを目的としており、そのようなこともありえます;-)

考慮すべき他のものの中には、認証、検証、国際化、 FreeMaker またはその他のテンプレートツールを使用したページ出力のレンダリングなどがあります。そうでなければ、HTMLの編集とパラメータ化を管理することで、HTTPを扱うのが難しくなります。当然、それはすべてあなたがどれだけ柔軟になる必要があるかに依存します。それがメニュー方式のファクシミリ装置であるならば、それは非常に簡単でありえる。相互作用が多いほど、フレームワークは '厚い' 'である必要があります。良い質問、頑張ってください!

17
will

かつて私は似たようなものを探していました - 私は簡単に埋め込んでカスタマイズすることができた軽量だが十分に機能的なHTTPサーバーです。私は2つのタイプの潜在的な解決策を見つけました:

  • それほど軽量でも単純でもないフルサーバー(軽量の極端な定義用)。
  • HTTPサーバーではない、本当に軽量なサーバーですが、リモートでもRFCに準拠しておらず、一般的に必要な基本機能をサポートしていないServerSocketの例が見事に示されています。

それで... JLHTTP - JavaライトウェイトHTTPサーバーを書くことにしました

単一の(かなり長い場合)ソースファイルとして、または依存関係のない〜50K jar(〜35K削除)として、任意のプロジェクトに埋め込むことができます。これは、RFCに準拠することを目指しており、膨大な量を最小限に抑えながら、豊富なドキュメントと多くの便利な機能を含んでいます。

機能は次のとおりです。仮想ホスト、ディスクからのファイル配信、標準mime.typesファイルによるMIMEタイプマッピング、ディレクトリインデックス生成、ウェルカムファイル、すべてのHTTPメソッドのサポート、条件付きETagおよびIf- *ヘッダーサポート、チャンク転送エンコード、gzip/deflate圧縮、基本HTTPS(JVM提供)、部分コンテンツ(ダウンロード継続)、ファイルアップロード用のマルチパート/フォームデータ処理、APIまたはアノテーションを介したマルチコンテキストハンドラ、パラメータ解析(クエリ文字列またはx-www-form-urlencoded)体)など.

私は他の人がそれを役に立つと思ってくれることを願っています:-)

16
amichair

スパークは最も簡単です、ここにクイックスタートガイドがあります。 http://sparkjava.com/ /

9
Laercio Metzner
8
Nikos

わずか数行のコードで、JDKとサーブレットAPIだけでJ2EEサーブレットの基本的なサポートを提供するhttpserverを作成することが可能です。

他の軽量コンテナよりもはるかに速く起動するので、私はこれを単体テストサーブレットに非常に役立つと思いました(プロダクションにはJettyを使用します)。

ほとんどの非常に軽量のhttpserverはサーブレットのサポートを提供していませんが、それらが必要なので、私は共有すると思いました。

以下の例は、基本的なサーブレットのサポートを提供するか、まだ実装されていないものについてはUnsupportedOperationExceptionをスローします。それは基本的なhttpサポートのためにcom.Sun.net.httpserver.HttpServerを使います。

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

特にサーブレット機能を必要とせず、単に要求/応答オブジェクトにアクセスする必要がある場合は、 Simple を調べることを強くお勧めします。あなたがRESTを必要とするなら、あなたはそれの上にJerseyを置くことができます、あなたがHTMLまたは同様のものを出力する必要があるならばFreemarkerがあります。私はあなたがこの組み合わせでできることが本当に大好きです、そして学ぶべきAPIは比較的少ないです。

6
Waldheinz

このコードは私たちのものより優れています、あなたは2つのlibsを追加する必要があるだけです: 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();
    }
}
5
leandro

また、次のようなNIOアプリケーションフレームワークを見ることもできます。

  1. Netty: http://jboss.org/netty
  2. Apache Mina: http://mina.Apache.org/ またはそのサブプロジェクトAsyncWeb: http://mina.Apache.org/asyncweb/
5
ThiamTeck

takesを調べてください。 https://github.com/yegor256/takes を見てください。

3
George

チェックアウト シンプル 。かなりシンプルな組み込み可能なサーバーで、さまざまな操作をサポートしています。私は特にそのスレッドモデルが大好きです。

すごい!

3
Olu Smith

上記のすべての回答が、シングルメインスレッドリクエストハンドラに関する詳細です。

設定:

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

Apache Commons HttpCore projectはどうですか。

Webサイトから:... HttpCore Goals

  • 最も基本的なHTTPトランスポートの側面の実装
  • 優れたパフォーマンスとAPIの明確さと表現力のバランス
  • 小さな(予測可能な)メモリ使用量
  • 自己完結型ライブラリ(JRE以外に外部の依存関係はありません)
2
I. Joseph

あなたはかなり単純な 埋め込まれたJetty Javaサーバーを書くことができます。

Embedded Jettyとは、外部Jettyサーバーにアプリケーションをデプロイするのではなく、サーバー(Jetty)がアプリケーションと一緒に出荷されることを意味します。

つまり、埋め込みでないアプローチでWebアプリケーションが外部サーバー( Tomcat / Jetty/etc)にデプロイされたWARファイルに組み込まれている場合は、埋め込みJettyでWebアプリケーションを作成し、同じコードベースでjettyサーバーをインスタンス化します。

組み込みJetty Javaサーバーの例では、 git clone を使用できます。 https://github.com/stas-slu/embedded-jetty-Java-server-example

1
Stas

これを試してみてください 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;
}