Search in sources :

Example 1 with Header

use of suite.http.Http.Header in project suite by stupidsing.

the class HttpNio method listen.

private void listen(Reg reg) {
    var rw = new Object() {

        // 0 - read, 1 - write, 2 - close after all written
        private int stage = 0;

        private Bytes br = Bytes.empty;

        private Bytes bw = Bytes.empty;

        private Puller<Bytes> write;

        private Source<Boolean> eater = () -> parseLine(line -> handleRequest1stLine(line.trim(), response -> {
            var data = // 
            "HTTP/1.1 " + response.status + "\r\n" + response.headers.streamlet().map((k, v) -> k + ": " + v + "\r\n").toJoinedString() + "\r\n";
            stage = 1;
            if (response.body != null)
                write = Puller.concat(Pull.from(data), response.body);
            else {
                bw = Bytes.of(data.getBytes(Utf8.charset));
                response.write.f(bytes -> {
                    if (bytes != null)
                        bw = bw.append(bytes);
                    else
                        stage = 2;
                    listen();
                });
            }
        }));

        private void listen() {
            if (!bw.isEmpty())
                reg.listenWrite(() -> bw, this::written);
            else if (stage == 0)
                reg.listenRead(in -> {
                    read(in);
                    listen();
                });
            else if (stage == 1 && write != null)
                reg.listenWrite(() -> bw = write.pull(), this::written);
            else if (stage == 2)
                reg.listenWrite(() -> null, null);
            else if (stage == 3)
                reg.listen(0, null, null, null);
        }

        private void read(Bytes in) {
            if (in != null) {
                br = br.append(in);
                while (eater.g()) ;
            } else
                // closes connection
                write = Puller.empty();
        }

        private void written(int n) {
            if (0 <= n)
                bw = bw.range(n);
            else
                stage = 3;
            listen();
        }

        private void handleRequest1stLine(String line, Sink<Response> cb) {
            var hrhl = handleRequestHeaderLine(lines -> handleRequestBody(line, lines, cb));
            eater = () -> parseLine(hrhl);
        }

        private Sink<String> handleRequestHeaderLine(Sink<List<String>> cb) {
            var lines = new ArrayList<String>();
            return line0 -> {
                var line1 = line0.trim();
                if (!line1.isEmpty())
                    lines.add(line1);
                else
                    cb.f(lines);
            };
        }

        private void handleRequestBody(String line0, List<String> headerLines, Sink<Response> cb) {
            eater = () -> // 
            FixieArray.of(// 
            line0.split(" ")).map((method, url, proto) -> handleRequestBody(proto, method, url, headerLines, cb));
        }

        private // 
        boolean handleRequestBody(// 
        String proto, // 
        String method, // 
        String url, // 
        List<String> lines, Sink<Response> cb) {
            var headers = // 
            Read.from(// 
            lines).fold(new Header(), (headers_, line_) -> // 
            Split.strl(line_, // 
            ":").map((k, v) -> headers_.put(k, v)));
            var queue = new ArrayBlockingQueue<Bytes>(Buffer.size);
            Sink<Bytes> offer = queue::add;
            Source<Bytes> take = queue::poll;
            Fun2<String, String, Request> requestFun = (host, pqs) -> Split.strl(pqs, "?").map((path0, query) -> {
                var path1 = path0.startsWith("/") ? path0 : "/" + path0;
                var path2 = ex(() -> URLDecoder.decode(path1, Utf8.charset));
                return // 
                Equals.string(proto, "HTTP/1.1") ? // 
                new Request(method, host, path2, query, headers, Puller.of(take)) : fail("only HTTP/1.1 is supported");
            });
            var pp = Split.string(url, "://");
            var request = pp != null ? Split.strl(pp.v, "/").map(requestFun) : requestFun.apply("", url);
            var cl = request.headers.getOpt("Content-Length").map(Long::parseLong);
            var te = Equals.ab(request.headers.getOpt("Transfer-Encoding"), Opt.of("chunked"));
            Log_.info(request.getLogString());
            if (te)
                eater = handleChunkedRequestBody(request, offer, cb);
            else if (cl.hasValue())
                eater = handleRequestBody(request, offer, cl.g(), cb);
            else
                eater = handleRequestBody(request, offer, 0, cb);
            return true;
        }

        private // 
        Source<Boolean> handleRequestBody(// 
        Request request, // 
        Sink<Bytes> body, // 
        long contentLength, Sink<Response> cb) {
            return new Source<>() {

                private int n;

                public Boolean g() {
                    body.f(br);
                    var isOpen = br != null;
                    if (isOpen) {
                        n += br.size();
                        br = Bytes.empty;
                    }
                    if (!isOpen || contentLength <= n)
                        cb.f(handler.handle(request));
                    return false;
                }
            };
        }

        private Source<Boolean> handleChunkedRequestBody(Request request, Sink<Bytes> body, Sink<Response> cb) {
            return () -> {
                for (var i0 = 0; i0 < br.size(); i0++) if (br.get(i0) == 10) {
                    var line = new String(br.range(0, i0).toArray(), Utf8.charset);
                    var size = Integer.parseInt(line.trim(), 16);
                    for (var i1 = i0 + 1 + size; i1 < br.size(); i1++) if (br.get(i1) == 10) {
                        var chunk = br.range(i0 + 1, i1);
                        br = br.range(i1);
                        body.f(chunk);
                        return true;
                    }
                    if (size == 0)
                        cb.f(handler.handle(request));
                }
                return false;
            };
        }

        private boolean parseLine(Sink<String> handleLine) {
            for (var i = 0; i < br.size(); i++) if (br.get(i) == 10) {
                var line = new String(br.range(0, i).toArray(), Utf8.charset);
                br = br.range(i + 1);
                handleLine.f(line);
                return true;
            }
            return false;
        }
    };
    rw.listen();
}
Also used : FixieArray(primal.adt.FixieArray) Fail.fail(primal.statics.Fail.fail) URLDecoder(java.net.URLDecoder) Reg(suite.os.ListenNio.Reg) Sink(primal.fp.Funs.Sink) ArrayList(java.util.ArrayList) Response(suite.http.Http.Response) Utf8(primal.Nouns.Utf8) Request(suite.http.Http.Request) Split(primal.MoreVerbs.Split) Opt(primal.adt.Opt) Pull(primal.MoreVerbs.Pull) Handler(suite.http.Http.Handler) ListenNio(suite.os.ListenNio) Buffer(primal.Nouns.Buffer) Source(primal.fp.Funs.Source) Header(suite.http.Http.Header) Log_(primal.os.Log_) Read(primal.MoreVerbs.Read) Bytes(primal.primitive.adt.Bytes) ArrayBlockingQueue(java.util.concurrent.ArrayBlockingQueue) List(java.util.List) Rethrow.ex(primal.statics.Rethrow.ex) Fun2(primal.fp.Funs2.Fun2) Equals(primal.Verbs.Equals) Puller(primal.puller.Puller) ArrayList(java.util.ArrayList) Request(suite.http.Http.Request) Source(primal.fp.Funs.Source) Bytes(primal.primitive.adt.Bytes) Sink(primal.fp.Funs.Sink) Header(suite.http.Http.Header) ArrayBlockingQueue(java.util.concurrent.ArrayBlockingQueue) Puller(primal.puller.Puller) ArrayList(java.util.ArrayList) List(java.util.List)

Example 2 with Header

use of suite.http.Http.Header in project suite by stupidsing.

the class HttpHandle method serveDir.

public Handler serveDir(Path root) {
    return request -> ex(() -> {
        var path = root;
        long size;
        for (var p : request.paths) if (!Equals.string(p, ".."))
            path = path.resolve(p);
        var file = path.toFile();
        if (file.exists())
            try (var raf = new RandomAccessFile(file, "r")) {
                size = raf.getChannel().size();
                var array = // 
                request.headers.getOpt(// 
                "Range").map(// 
                range -> range.split(",")).filter(// 
                ranges -> ranges.length == 1).map(// 
                ranges -> ranges[0].split("-")).or(new String[0]);
                if (array.length == 2) {
                    var a0 = array[0];
                    var a1 = array[1];
                    var p0 = max(!a1.isEmpty() ? Long.valueOf(a0) : Long.MIN_VALUE, 0);
                    var px = min(!a1.isEmpty() ? Long.valueOf(a1) : Long.MAX_VALUE, size);
                    var p = LngMutable.of(p0);
                    var empty = // 
                    new Header().put("Content-Range", // 
                    "bytes " + p0 + "-" + px + "/" + size).put("Content-Type", "text/html; charset=UTF-8");
                    return Response.of(Http.S206, empty, Pull.from(new InputStream() {

                        public int read() throws IOException {
                            var pos = p.value();
                            if (pos != px) {
                                raf.seek(p.increment());
                                p.update(pos + 1);
                                return raf.read();
                            } else
                                return -1;
                        }

                        public int read(byte[] b, int off, int len0) throws IOException {
                            var pos = p.value();
                            if (pos != px) {
                                var len1 = min(len0, (int) (px - pos));
                                raf.seek(pos);
                                var n = raf.read(b, off, len1);
                                p.update(pos + n);
                                return n;
                            } else
                                return -1;
                        }
                    }));
                } else
                    return Response.of(Http.S200, Pull.from(Files.newInputStream(path)), size);
            }
        else
            return Http.R404;
    });
}
Also used : PerMap(primal.persistent.PerMap) RandomAccessFile(java.io.RandomAccessFile) Files(java.nio.file.Files) IOException(java.io.IOException) Math.min(java.lang.Math.min) Header(suite.http.Http.Header) Sink(primal.fp.Funs.Sink) Bytes(primal.primitive.adt.Bytes) Response(suite.http.Http.Response) Rethrow.ex(primal.statics.Rethrow.ex) Math.max(java.lang.Math.max) Pull(primal.MoreVerbs.Pull) LngMutable(primal.primitive.adt.LngMutable) Equals(primal.Verbs.Equals) Path(java.nio.file.Path) PerList(primal.persistent.PerList) Handler(suite.http.Http.Handler) InputStream(java.io.InputStream) RandomAccessFile(java.io.RandomAccessFile) Header(suite.http.Http.Header) InputStream(java.io.InputStream) IOException(java.io.IOException)

Example 3 with Header

use of suite.http.Http.Header in project suite by stupidsing.

the class ServerMain method handler.

private Handler handler() {
    BiPredicate<String, String> authenticate = (username, password) -> // 
    Defaults.secrets().prove(Suite.substitute("auth .0 .1", new Str(username), new Str(password)));
    Fun2<String, String, List<String>> authenticateRoles = (username, password) -> {
        return authenticate.test(username, password) ? List.of("role") : null;
    };
    var sseHeaders = new Header(// 
    PerMap.<String, PerList<String>>empty().put("Cache-Control", // 
    PerList.of("no-cache")).put("Content-Type", PerList.of("text/event-stream")));
    Handler handlerDump = request -> Response.of(Pull.from(// 
    "" + // 
    "<html>" + "<br/>method = " + // 
    request.method + "<br/>server = " + // 
    request.server + "<br/>paths = " + // 
    request.paths + "<br/>attrs = " + // 
    HttpHeaderUtil.getAttrs(request.query) + "<br/>headers = " + // 
    request.headers + "</html>"));
    Handler handlerSse = request -> Response.ofWriter(Http.S200, sseHeaders, write -> {
        new Object() {

            private int i = 8;

            private void dispatch() {
                sleep.sink2(1000l, () -> {
                    if (0 < i--) {
                        var event = "event: number\ndata: { \"i\": " + i + " }\n\n";
                        write.f(Bytes.of(event.getBytes(Utf8.charset)));
                        dispatch();
                    } else
                        write.f(null);
                });
            }
        }.dispatch();
    });
    Handler handlerStatus = request -> {
        var cfg = new TradeCfgImpl();
        var summarize = Summarize.of(cfg);
        var sbs = summarize.summarize(trade -> trade.strategy);
        return Response.of(Pull.from("<pre>" + sbs.log + new TreeMap<>(sbs.pnlByKey) + "</pre>"));
    };
    var hh = new HttpHandle();
    var hhsa = new HttpHandleSessionAuth();
    var hhta = new HttpHandleTokenAuth();
    return hh.routeByPath(// 
    PerMap.<String, Handler>empty().put("api", // 
    hhta.applyFilter("role", hh.serveText("in good shape"))).put("hello", // 
    hh.serveText("hello world")).put("html", // 
    hh.serveDir(Paths.get(FileUtil.suiteDir() + "/src/main/html"))).put("path", // 
    hh.serveDir(Tmp.root)).put("site", // 
    hhsa.getHandler(authenticate, handlerDump)).put("sse", // 
    handlerSse).put("status", // 
    handlerStatus).put("token", hh.routeByMethod(// 
    PerMap.<String, Handler>empty().put("PATCH", // 
    hhta.refreshToken(authenticateRoles)).put("POST", hhta.getToken(authenticateRoles)))));
}
Also used : Schedule(suite.os.Schedule) Sink2(primal.fp.Funs2.Sink2) LocalDateTime(java.time.LocalDateTime) Scheduler(suite.os.Scheduler) RunUtil(suite.util.RunUtil) ArrayList(java.util.ArrayList) Defaults(suite.cfg.Defaults) BiPredicate(java.util.function.BiPredicate) Http(suite.http.Http) Response(suite.http.Http.Response) SmtpServer(suite.smtp.SmtpServer) Utf8(primal.Nouns.Utf8) LocalTime(java.time.LocalTime) FileUtil(suite.os.FileUtil) Pull(primal.MoreVerbs.Pull) HttpServe(suite.http.HttpServe) Sleep(primal.Verbs.Sleep) Handler(suite.http.Http.Handler) TelegramBotMain(suite.sample.TelegramBotMain) PerMap(primal.persistent.PerMap) TradeCfgImpl(suite.trade.data.TradeCfgImpl) Files(java.nio.file.Files) HttpNio(suite.http.HttpNio) HttpHandleSessionAuth(suite.http.HttpHandleSessionAuth) Summarize(suite.trade.analysis.Summarize) HttpHandle(suite.http.HttpHandle) Start(primal.Verbs.Start) HttpHeaderUtil(suite.http.HttpHeaderUtil) Header(suite.http.Http.Header) Bytes(primal.primitive.adt.Bytes) List(java.util.List) TreeMap(java.util.TreeMap) Paths(java.nio.file.Paths) HttpHandleTokenAuth(suite.http.HttpHandleTokenAuth) Fun2(primal.fp.Funs2.Fun2) PerList(primal.persistent.PerList) Str(suite.node.Str) Tmp(primal.Nouns.Tmp) HttpHandle(suite.http.HttpHandle) TradeCfgImpl(suite.trade.data.TradeCfgImpl) Handler(suite.http.Http.Handler) Str(suite.node.Str) HttpHandleSessionAuth(suite.http.HttpHandleSessionAuth) Header(suite.http.Http.Header) ArrayList(java.util.ArrayList) List(java.util.List) PerList(primal.persistent.PerList) HttpHandleTokenAuth(suite.http.HttpHandleTokenAuth)

Aggregations

Pull (primal.MoreVerbs.Pull)3 Bytes (primal.primitive.adt.Bytes)3 Handler (suite.http.Http.Handler)3 Header (suite.http.Http.Header)3 Response (suite.http.Http.Response)3 Files (java.nio.file.Files)2 ArrayList (java.util.ArrayList)2 List (java.util.List)2 Utf8 (primal.Nouns.Utf8)2 Equals (primal.Verbs.Equals)2 Sink (primal.fp.Funs.Sink)2 Fun2 (primal.fp.Funs2.Fun2)2 PerList (primal.persistent.PerList)2 PerMap (primal.persistent.PerMap)2 Rethrow.ex (primal.statics.Rethrow.ex)2 IOException (java.io.IOException)1 InputStream (java.io.InputStream)1 RandomAccessFile (java.io.RandomAccessFile)1 Math.max (java.lang.Math.max)1 Math.min (java.lang.Math.min)1