[vert.x] Vert.x core examples - 2. Net Example

2016. 3. 25. 11:57Java/Vert.x

Net Example

이 예제들은 Vert.x net servers 와 clients 의 사용에 대해 설명합니다.
TCP(그리고 SSL) 서버와 클라이언트에 사용되었습니다.

Echo

이 예제는 TCP connections를 제공하고 간단하에 무엇을 받던지 간에 해당 연결로 되돌려 주는, echo server verticle 로 구성되어 있습니다.
말 그대로 에코..메아리 입니다.

에코 서버를 시작가능한데 그 서버에 접속하기 위해 콘솔에다가 telent localhost 1234 로 연결합니다.
몇가지를 타이핑하면, 그것이 바로 에코되어 그걸 볼수 있답니다.

에코 클라이언트도 포함되었습니다. 
서버에 연결을 만들고, 데이터를 보내고, 되돌려 받는 로그를 출력합니다.
여러분은 텔넷을 통해 연결한곳에 서버든 클라이언트로 사용할수 있습니다. 



public class Server extends AbstractVerticle {
    //Convenience method so you can run it in your IDE
    public static void main(String[] args) {
        Runner.runExample(Server.class);
    }

    @Override
    public void start() throws Exception {
        vertx.createNetServer().connectHandler(sock -> {
            // Create a pump
            Pump.pump(socksock).start();
        }).listen(1234);
        System.out.println("Echo server is now listening");
    }
}



public class Client  extends AbstractVerticle{
    //Convenience method so you can run it in your IDE
    public static void main(String[] args) {
        Runner.runExample(Client.class);
    }

    @Override
    public void start() throws Exception {
        vertx.createNetClient().connect(1234,"localhost",res ->{
            if(res.succeeded()){
                NetSocket socket = res.result();
                socket.handler(buffer-> {
                    System.out.println("Net client receiving : " + buffer.toString("UTF-8"));
                });
                //Now send some data
                for(int i = i < 10 i++){
                    String str = "hello " + i +"\n";
                    System.out.println("Net client sending: " + str);
                    socket.write(str);
                }
            }else{
                System.out.println("Failed to connect" + res.cause());
            }
        });
    }
}


Echo SSL

Echo 예시와 같습니다만, connection을 암호화(encrypt) 하기 위해 SSL을 사용합니다.



//Add SSL Option
NetServerOptions options = new NetServerOptions()
        .setSsl(true).setKeyStoreOptions(new JksOptions().setPath("server-keystore.jks").setPassword("wibble"));




//Add SSL Option
NetClientOptions options = new NetClientOptions().setSsl(true).setTrustAll(true);




HTTP examples

이번 예제에서는 Vert.x를 이용해서 HTTP를 사용하는 것을 설명합니다.

Simple

같은 요청에 항상 같은 응답을 하는 매우 간단한 HTTP 서버 


@Override
public void start() throws Exception {
    vertx.createHttpServer().requestHandler(req -> {
        req.response().putHeader("content-type""text/html").end("<html><body><h1>Hello from vert.x!</h1></body></html>");
    }).listen(8080);
}

가동시기키고 나서 http://localhost:8080 에서 보시면 됩니다.
그리고 이 서버에 요청을 만드는 HTTP client 입니다.


@Override
public void start() throws Exception {
    vertx.createHttpClient().getNow(8080"localhost""/"resp -> {
        System.out.println("Got response " + resp.statusCode());
        resp.bodyHandler(body -> {
            System.out.println("Got data " + body.toString("ISO-8859-1"));
        });
    });
}

HTTPS examples

위의 예제와 별다르진 않지만, HTTP 대신에 HTTPS 를 썼다라는 점?


추가적으로 server-keystore.jks 를 넣어주세요.
@Override
public void start() throws Exception {
    HttpServer server =
            vertx.createHttpServer(new HttpServerOptions().setSsl(true).setKeyStoreOptions(
                    new JksOptions().setPath("server-keystore.jks").setPassword("wibble")
            ));
    server.requestHandler(req -> {
        req.response().putHeader("content-type""text/html").end("<html><body><h1>Hello from vert.x!</h1></body></html>");
    }).listen(4443);
}

http://localhost:4443  으로 열어서 사용해보시면 됩니다.
HTTPS 서버에 요청하는 HTTPS 클라이언트.



@Override
public void start() throws Exception {
    // Note! in real-life you wouldn't often set trust all to true as it could leave you open to man in the middle attacks.

    vertx.createHttpClient(new HttpClientOptions().setSsl(true).setTrustAll(true)).getNow(4443"localhost""/"resp -> {
        System.out.println("Got response " + resp.statusCode());
        resp.bodyHandler(body -> System.out.println("Got data " + body.toString("ISO-8859-1")));
    });
}

Proxy examples

간단한 toy HTTP proxy.
proxy는 요청들을 받고 , endpoint 서버에 그 요청들을 향하게 하며, 다른 서버로 부터 응답을 받고 클라이언트에게 그 요청을 전달한다.

Server(8282) 가 있고, Proxy(8080) 이 있고, 클라이언트는 우선 Proxy(8080)에 요청을 합니다.



@Override
public void start() throws Exception {
    vertx.createHttpServer().requestHandler(req -> {
        System.out.println("Got request " + req.uri());
        for (String name : req.headers().names()) {
            System.out.println(name + ": " + req.headers().get(name));
        }
        req.handler(data -> System.out.println("Got data " + data.toString("ISO-8859-1")));
        req.endHandler(v -> {
            // Now send back a response
            req.response().setChunked(true);
            for (int i = 0i < 10i++) {
                req.response().write("server-data-chunk-" + i);
            }
            req.response().end();
        });
    }).listen(8282);
}


@Override
public void start() throws Exception {
    HttpClient client = vertx.createHttpClient(new HttpClientOptions());
    vertx.createHttpServer().requestHandler(req -> {
        System.out.println("Proxying request: " + req.uri());
        HttpClientRequest c_req = client.request(req.method()8282"localhost"req.uri()c_res -> {
            System.out.println("Proxying response: " + c_res.statusCode());
            req.response().setChunked(true);
            req.response().setStatusCode(c_res.statusCode());
            req.response().headers().setAll(c_res.headers());
            c_res.handler(data -> {
                System.out.println("Proxying response body: " + data.toString("ISO-8859-1"));
                req.response().write(data);
            });
            c_res.endHandler((v) -> req.response().end());
        });
        c_req.setChunked(true);
        c_req.headers().setAll(req.headers());
        req.handler(data -> {
            System.out.println("Proxying request body " + data.toString("ISO-8859-1"));
            c_req.write(data);
        });
        req.endHandler((v) -> c_req.end());
    }).listen(8080);
}


@Override
public void start() throws Exception {
    HttpClientRequest request = vertx.createHttpClient(new HttpClientOptions()).put(8080"localhost""/"resp -> {
        System.out.println("Got response " + resp.statusCode());
        resp.bodyHandler(body -> System.out.println("Got data " + body.toString("ISO-8859-1")));
    });
    request.setChunked(true);
    for (int i = 0i < 10i++) {
        request.write("client-chunk-" + i);
    }
    request.end();
}

간단하게 끝...

Sendfile

이 예제에서는 어떻게 정적인 파일들을 Vert.x http Server를 사용해서 디스크에서 제공해줄지를 설명합니다. 


주석에도 나왔다 시피, 이와 같은 것은 Vert.x-web 를 사용하는 것을 강력 추천하고 있으니, 참고로만 확인하세요.
@Override
public void start() throws Exception{

    // In reality it's highly recommend you use Vert.x-Web for applications like this.

    vertx.createHttpServer().requestHandler(req -> {
        String filename = null;
        if (req.path().equals("/")) {
            filename = "index.html";
       else if (req.path().equals("/page1.html")) {
            filename = "page1.html";
       else if (req.path().equals("/page2.html")) {
            filename = "page2.html";
       else {
            req.response().setStatusCode(404).end();
        }
        if (filename != null) {
            req.response().sendFile(filename);
        }
    }).listen(8080);
}

확인은 언제나, localhost:8080 입니다.

Note
실제로는 이렇게 로우 레벨상의 웹 서버를 만들어 쓰는 것 보다는 차라리 Vert.x-Web 을 사용할 것입니다.
이것과 같이, 수동으로 파일들을 제공하는 것은 여러분들이 보안 취약점에 노출되도록 할 수 있답니다.
e.g. 클라이언트들에 의해 허용되어진 지역 밖의 리소스에 접근 하도록 URI 경로 조작하는 것.
Vert-x-Web 은 이런 종류의 악용하는 것을 피하기 위한 URI 경로 normalisation 을 제공하고, 캐싱 헤더들과 여러분이 아마도 웹 어플리케이션에서 정적 파일들을 제공할 때 아마도 원했던 다른 기능들을 다루는 정적 파일 핸들러를 제공합니다.


Simple form

서버에 HTML form를 다룰수 있는 법을 설명합니다.



@Override
public void start() throws Exception {
   vertx.createHttpServer().requestHandler(req->{
       if(req.uri().equals("/")){
           //serve the index page
           req.response().sendFile("index.html");
       }else if(req.uri().equals("/form")){
           req.response().setChunked(true);
           req.setExpectMultipart(true);
           req.endHandler((v)->{
              for(String attr : req.formAttributes().names()){
                  req.response().write("Got attr " + attr + " : " + req.formAttributes().get(attr) + "\n");
              }
               req.response().end();
           });
       }else{
           req.response().setStatusCode(404).end();
       }
   }).listen(8080);
}

Simple form file upload

HTML form 제출(submission) 으로 부터 파일 업로드을 다룰수 있는 법을 설명합니다.


* 여기서도 파일업로드는 Vert.x-Web 을 사용하시라고 하네요.

@Override
public void start() throws Exception {
    vertx.createHttpServer().requestHandler(req -> {
        if (req.uri().equals("/")) {
            // Serve the index page
            req.response().sendFile("index.html");
       else if (req.uri().startsWith("/form")) {
            req.setExpectMultipart(true);
            req.uploadHandler(upload -> {
                upload.exceptionHandler(cause -> {
                    req.response().setChunked(true).end("Upload failed");
                });

                upload.endHandler(v -> {
                    req.response().setChunked(true).end("Successfully uploaded to " + upload.filename());
                });

                // FIXME - Potential security exploit! In a real system you must check this filename
                // to make sure you're not saving to a place where you don't want!
                // Or better still, just use Vert.x-Web which controls the upload area.
                upload.streamToFileSystem(upload.filename());
            });
       else {
            req.response().setStatusCode(404);
            req.response().end();
        }
    }).listen(8080);
}

Note 
실제로는 이런 로우레벨상의 서버를 작성하는 것 보단, Vert.x-Web을 사용할 것입니다.
Vert-x.Web은 HTML forms 와 file uploads에 지원하도록 구축되어 제공되고, 조작한 URI 경로를보안 이슈를 피합니다. 


Http request body upload

이 예제는 요청을 받고, 요청 바디를 메모리상 전체 request body를 완전하게 저장하는 저장하는 것 없이,디스크상에 파일에 펌핑하는 HTTP Server를 설명합니다. ?? 뭔말이래(pumping the request body to a file on disk without ever storing the entire request body fully in memory.)

서버에 요청을 보내고 파일을 디스크에서 HTTP Request body에 펌프하는 클라이언트도 있습니다. 
파일이 매우 클지라도(GigaBytes), 그 파일은 성공적으로 업로드되어 집니다.



@Override
public void start() throws Exception {
    vertx.createHttpServer().requestHandler(req -> {
        req.pause();
        String filename = UUID.randomUUID() + ".uploaded";
        vertx.fileSystem().open(filename, new OpenOptions()ares -> {
            AsyncFile file = ares.result();
            Pump pump = Pump.pump(reqfile);
            req.endHandler(v1 -> file.close(v2 -> {
                System.out.println("Uploaded to " + filename);
                req.response().end();
            }));
            pump.start();
            req.resume();
        });
    }).listen(8080);
}



@Override
public void start() throws Exception {
    HttpClientRequest req = vertx.createHttpClient(new HttpClientOptions()).put(8080,"localhost","/someurl",resq ->{
        System.out.println("Response " + resq.statusCode());
    });

    String filename = "Client.java";
    FileSystem fs = vertx.fileSystem();

    fs.props(filenameares -> {
        FileProps props = ares.result();
        System.out.println("props is " + props);
        long size = props.size();
        System.out.println("props size is " + size);
        req.headers().set("content-length",String.valueOf(size));
        fs.open(filename, new OpenOptions()ares2 -> {
            AsyncFile file = ares2.result();
            Pump pump = Pump.pump(file,req);
            file.endHandler(v->{
               req.end();
            });
            pump.start();
        });
    });
}

구현 해보니, 클라이언트에서 특정 파일에 내용을 서버에 전송을 합니다.
그 내용을 서버에 받아서, 파일로 생성을 해주는 것입니다. 


HTTP Server Sharing

몇몇의 verticle들이 같은 포트상에 HTTP server을 열었을때, vert.x에 의해서 구현된 라운드 로빈 방식을 묘사하는 서버.


@Override
public void start() throws Exception {
    vertx.deployVerticle(
            "io.vertx.example.core.http.sharing.HttpServerVerticle",
            new DeploymentOptions().setInstances(2));
}

서버 공유와 라운드 로빈을 묘사하는 예제 입니다.
서버들은 ID 를 사용하여 구분되어 집니다.
HTTP Server Verticle은 개발 옵션상에 두번 초기화 됩니다.



public class HttpServerVerticle extends AbstractVerticle {
    @Override
    public void start() throws Exception {
        vertx.createHttpServer().requestHandler(req -> {
            req.response()
                    .putHeader("content-type""text/html")
                    .end("<html><body><h1>Hello from " this "</h1></body></html>");
        }).listen(8080);
    }
}

별다른것 없는 서버입니다. 

서버를 가동하고, 브라우저를 열고 http://localhost:8080 으로 접속해보세요.
요청들은 다른것 후에 하나의 인스턴스에 의해서 다뤄질 것입니다. 

클라이언트는 정기적으로 서버에 요청에 의해 라운드 로빈을 묘사하고 응답 컨텐츠을 보여줍니다.


직접 HTTPServerVerticle를 올릴수가 있죠, vertx run 명령을 사용해서,
그땐, 여러분은 여러분이 원하는 인스턴스 숫자를 세팅할수 있어요.


vertx run io.vertx.example.core.http.sharing.HttpServerVerticle -instance 4


WebSockets echo example

이 예제는 웹소켓 연결을 다루는 Vert.x HTTP 서버를 보여줍니다. 
이 예제는 단순히 클라이언트에게 되돌려 줍니다. 웹소켓에서 무엇을 받던지 간에 말이죠.

서버에 접속하는 클라이언트 역시 있답니다. 어떤 데이터를 보내고, 받을것을 로그로 찍어줍니다.



@Override
public void start() throws Exception {
    vertx.createHttpServer().websocketHandler(ws -> ws.handler(ws::writeBinaryMessage)).requestHandler(req -> {
        if (req.uri().equals("/")) req.response().sendFile("ws.html");
    }).listen(8080);
}




@Override
public void start() throws Exception {
    HttpClient client = vertx.createHttpClient();

    client.websocket(8080"localhost""/some-uri"websocket -> {
        websocket.handler(data -> {
            System.out.println("Received data " + data.toString("ISO-8859-1"));
            client.close();
        });
        websocket.writeBinaryMessage(Buffer.buffer("Hello world"));
    });
}




<html>
<head><title>Web Socket Test</title></head>
<body>
<script>
    var socket;
    if (window.WebSocket) {
        socket = new WebSocket("ws://localhost:8080/myapp");
        socket.onmessage function (event) {
            alert("Received data from websocket: " + event.data);
        }
        socket.onopen function (event) {
            alert("Web Socket opened!");
        };
        socket.onclose function (event) {
            alert("Web Socket closed.");
        };
   else {
        alert("Your browser does not support Websockets. (Use Chrome)");
    }
    function send(message) {
        if (!window.WebSocket) {
            return;
        }
        if (socket.readyState == WebSocket.OPEN) {
            socket.send(message);
       else {
            alert("The socket is not open.");
        }
    }
</script>
<form onsubmit="return false;">
    <input type="text" name="message" value="Hello, World!"/>
    <input type="button" value="Send Web Socket Data" onclick="send(this.form.message.value)"/>
</form>
</body>
</html>


서버를 가동하고, 브라우저 열고, http://localhost:8080 으로 가보시죠.

Note
실제로는 WebSockets를 사용하는 web application을 구축하기 위해 Vert.x-Web 을 사용할 것 입니다.