Git Product home page Git Product logo

vlingo / xoom-http Goto Github PK

View Code? Open in Web Editor NEW
51.0 13.0 15.0 1.41 MB

The VLINGO XOOM platform SDK for Reactive, scalable, high-throughput, and resilient HTTP server supporting RESTful services running on XOOM LATTICE and XOOM ACTORS.

Home Page: https://vlingo.io

License: Mozilla Public License 2.0

Java 99.92% CSS 0.02% HTML 0.02% JavaScript 0.04% Vue 0.01%
actors actor-model jvm jvm-languages reactive-programming http http-server http-services restful

xoom-http's Introduction

xoom-http

Javadocs Build Download Gitter chat

The VLINGO XOOM platform SDK Reactive, scalable, high-throughput, and resilient HTTP server supporting REST (RESTful) services running on XOOM LATTICE and XOOM ACTORS.

Docs: https://docs.vlingo.io/xoom-http

Installation

  <dependencies>
    <dependency>
      <groupId>io.vlingo.xoom</groupId>
      <artifactId>xoom-http</artifactId>
      <version>1.11.1</version>
      <scope>compile</scope>
    </dependency>
  </dependencies>
dependencies {
    compile 'io.vlingo.xoom:xoom-http:1.11.1'
}

Usage

Reactive, scalable, and resilient HTTP servers and RESTful services running on XOOM LATTICE and XOOM ACTORS.

  1. Feature complete
  • Fully actor-based asynchronous requests and responses.
  • Request handling is resource based.
  • Requests that require message body content are auto-mapped to simple Java objects.
  • Supports Media Types, Filters
  • Supports Server-Sent Events See SSE on Wikipedia
  1. To run the Server:
  1. See the following for usage examples:

License (See LICENSE file for full license)

Copyright © 2012-2023 VLINGO LABS. All rights reserved.

This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.

xoom-http's People

Contributors

ahus1 avatar aleixmorgadas avatar alexguzun avatar brian-wehrle-roche avatar buritos avatar bwehrle avatar d-led avatar danilo-ambrosio avatar davemuirhead avatar dependabot[bot] avatar florian-schoenherr avatar gregloring avatar hamzajg avatar hurelhuyag avatar jakzal avatar kbastani avatar kmruiz avatar pflueras avatar vaughnvernon avatar vlingo-java avatar wwerner avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

xoom-http's Issues

Race Condition On Heavy Load

Cause is unprotected integer increments.

16:02:01.606 [main] INFO  i.v.w.f.b.n.s.NettyServerChannelActor - Netty server running on Mac OS X
16:02:01.609 [main] INFO  i.v.w.f.b.n.s.NettyServerChannelActor - Netty server using NioEventLoopGroup
16:02:01.715 [main] INFO  i.v.w.f.b.n.s.NettyServerChannelActor - Netty server using NioEventLoopGroup 10
16:02:01.719 [main] INFO  i.v.w.f.b.n.s.NettyServerChannelActor - Netty server using NioServerSocketChannel
16:02:01.866 [main] INFO  i.v.w.f.b.n.s.NettyServerChannelActor - Netty server server-request-response-channel actor started
16:02:01.884 [pool-2-thread-9] INFO  io.vlingo.http.resource.ServerActor - Server vlingo-http-server is listening on port: 9105 started in 284 ms
16:02:01.884 [pool-2-thread-9] INFO  io.vlingo.http.resource.ServerActor - Resource: Event API
16:02:01.884 [pool-2-thread-9] INFO  io.vlingo.http.resource.ServerActor - Action: id=0, method=GET, uri=/events/{name}, to=dynamic1(String name)
16:02:06.042 [nioEventLoopGroup-3-2] WARN  io.netty.channel.ChannelInitializer - Failed to initialize a channel. Closing: [id: 0x8a146849, L:/127.0.0.1:9105 - R:/127.0.0.1:60077]
java.lang.ArrayIndexOutOfBoundsException: 2
	at io.vlingo.http.resource.ServerActor$ServerDispatcherPool.dispatcher(ServerActor.java:266) ~[vlingo-http-1.4.1-SNAPSHOT.jar:na]
	at io.vlingo.http.resource.ServerActor.httpRequestChannelConsumer(ServerActor.java:172) ~[vlingo-http-1.4.1-SNAPSHOT.jar:na]
	at io.vlingo.http.resource.ServerActor.requestChannelConsumer(ServerActor.java:167) ~[vlingo-http-1.4.1-SNAPSHOT.jar:na]
	at io.vlingo.wire.fdx.bidirectional.netty.server.NettyInboundHandler.<init>(NettyInboundHandler.java:53) ~[vlingo-wire-1.4.1-20201119.202301-6.jar:na]
	at io.vlingo.wire.fdx.bidirectional.netty.server.NettyServerChannelActor$1.initChannel(NettyServerChannelActor.java:82) ~[vlingo-wire-1.4.1-20201119.202301-6.jar:na]
	at io.vlingo.wire.fdx.bidirectional.netty.server.NettyServerChannelActor$1.initChannel(NettyServerChannelActor.java:78) ~[vlingo-wire-1.4.1-20201119.202301-6.jar:na]
	at io.netty.channel.ChannelInitializer.initChannel(ChannelInitializer.java:129) [netty-all-4.1.49.Final.jar:4.1.49.Final]
	at io.netty.channel.ChannelInitializer.handlerAdded(ChannelInitializer.java:112) [netty-all-4.1.49.Final.jar:4.1.49.Final]
	at io.netty.channel.AbstractChannelHandlerContext.callHandlerAdded(AbstractChannelHandlerContext.java:938) [netty-all-4.1.49.Final.jar:4.1.49.Final]
	at io.netty.channel.DefaultChannelPipeline.callHandlerAdded0(DefaultChannelPipeline.java:609) [netty-all-4.1.49.Final.jar:4.1.49.Final]
	at io.netty.channel.DefaultChannelPipeline.access$100(DefaultChannelPipeline.java:46) [netty-all-4.1.49.Final.jar:4.1.49.Final]
	at io.netty.channel.DefaultChannelPipeline$PendingHandlerAddedTask.execute(DefaultChannelPipeline.java:1463) [netty-all-4.1.49.Final.jar:4.1.49.Final]
	at io.netty.channel.DefaultChannelPipeline.callHandlerAddedForAllHandlers(DefaultChannelPipeline.java:1115) [netty-all-4.1.49.Final.jar:4.1.49.Final]
	at io.netty.channel.DefaultChannelPipeline.invokeHandlerAddedIfNeeded(DefaultChannelPipeline.java:650) [netty-all-4.1.49.Final.jar:4.1.49.Final]
	at io.netty.channel.AbstractChannel$AbstractUnsafe.register0(AbstractChannel.java:502) [netty-all-4.1.49.Final.jar:4.1.49.Final]
	at io.netty.channel.AbstractChannel$AbstractUnsafe.access$200(AbstractChannel.java:417) [netty-all-4.1.49.Final.jar:4.1.49.Final]
	at io.netty.channel.AbstractChannel$AbstractUnsafe$1.run(AbstractChannel.java:474) [netty-all-4.1.49.Final.jar:4.1.49.Final]
	at io.netty.util.concurrent.AbstractEventExecutor.safeExecute$$$capture(AbstractEventExecutor.java:164) [netty-all-4.1.49.Final.jar:4.1.49.Final]
	at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java) [netty-all-4.1.49.Final.jar:4.1.49.Final]
	at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:472) [netty-all-4.1.49.Final.jar:4.1.49.Final]
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:500) [netty-all-4.1.49.Final.jar:4.1.49.Final]
	at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989) [netty-all-4.1.49.Final.jar:4.1.49.Final]
	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) [netty-all-4.1.49.Final.jar:4.1.49.Final]
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) [netty-all-4.1.49.Final.jar:4.1.49.Final]
	at java.lang.Thread.run(Thread.java:748) [na:1.8.0_192]

Security issue: caller can cause DoS via EOM error on server due to no limit on incoming sizes

There is no checks on post body size. This can easily overwhelm services when multiple callers post huge requests.
This cap on body size should exist like it does in Tomcat, et. al. and be set to a very small default value like 1 megabyte.
Public facing services should have this tuned to a value more appropriate for their needs and as small as possible. It is is a best practice to use a web application firewall to mitigate DDoS surface area.

Might this be handled by Netty already? This should be clarified as discussed below.

Mitigation:
VLINGO-HTTP servers can be placed behind a reverse proxy or application gateway which limits incoming content size. Nginx supports this- see http://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size.

Gson deserialize without mappers

Hi,
I noticed that if I made a class such as

class Bar {
  private final int val;
 public Bar(int val) {this.val = val;}
}

I could create a Resource like this:

post("/foo").body(Bar.class).handle(this::createFoo),

with

void createFoo(Bar b) {
}

Now, I can make a post that will be accepted with the data:

{val: "12345"}

This works as expected.
However, if I do the following:

{somekey: "12345"}

I will get a Bar with a val == 0. I would expect the resource handler to automatically throw a 404, since the data does not conform to the expected requirements (my class cannot be constructed with the data presented).

I see there is a provision for adding mappers, which I assume solves this problem, but this default case is not safe; the client code cannot determine if the value was actually "0", and in fact my ctor for Bar is never called, due to how gson is implemented.

Leaving the methods without mappers the use, in my opinion, is presenting an unsafe interface, given how gson works by default via "unsafe" object construction. So, in other terms, this default behaviour by-passes the invariance rules in my value objects. I question whether it is useful the developer to expose these interfaces as-is, since it is not evident this problem can arise.

https://medium.com/@programmerr47/gson-unsafe-problem-d1ff29d4696f

Request and Response Content-Length Must Use Bytes Count

There is a bug lurking in each of these because currently the Content-Length is determined by the string length rather than the encoded bytes count. The current way works for all single byte UTF-8 characters, but will be wrong with all wider character encodings.

The implementation should auto generate the header after converting to bytes. To avoid encoding twice the encoding use to determine Content-Length should be cached and reused when creating the ByteBuffer for the full Request and Response.

See the new tests for RequestParser and ResponseParser for examples.

ResourceBuilder should treat trailing slash optional

I believe that trailing slash "/" character should be optional in every request URL. For instance below example URL should treated same.

http://localhost:18080/veterinarians/{id}/
http://localhost:18080/veterinarians/{id}

Currently if I use route like below snippet and call this endpoint with http://localhost:18080/veterinarians/abc/. this::veterinarian method receiving variable id=abc/. It must be id=abc (without trailing slash)

  @Override
  public Resource<?> routes() {
     return resource("VeterinarianResource",
        io.vlingo.http.resource.ResourceBuilder.get("/veterinarians/{id}")
            .param(String.class)
            .handle(this::veterinarian)
     );
  }

Feature: Endpoint sorting

Allow unsorted endpoints for the developer (from specific to generic is required now).

Current Behaviour

Defined the next endpoints in the specific order

  1. GET /users
  2. GET /users/all

When it's performed the call

  • GET /users/all, the 1. method is resolved first

Desired behaviour

It should be called the 2. method resolver

Smart Content-Type header

@bwehrle said on #24:

The other idea I had was smartly setting the the content-type header. Currently the header is not set smartly, meaning, the mapper knows the output format but does not set the header appropriately. This would be easy to change and add to the mapper interface. When writing my tests using RestAssured I noticed that the Content-Type header was not being set (it complains when it finds these situation).

Feature

Smart Content-Type based on Mapping interfeace.

Further discussion

Should we have fluent mapping depending on requested Content-Type? Example:

Same endpoint:

  • Content-Type: application/json → use Gson mapper instance
  • Content-Type: application/xml → use another mapper instance
  • undefined Content-Type → json as default mapper instance

Does it make sense?

Feature: Param Aggregation Draft

#10 shares the problem when there's a meaningful amount of parameters on the handler function, this feature aims to reduce the number of parameters aggregating them into new objects.

Current:

get("/user/{userId}")
    .param(String.class)
    .query("page", Integer.class)
    .query("limit", Integer.class)
    .query("order", String.class)
    .handle((userId, page, limit, order) -> { /* */ });

with param aggregation:

get("/user/{userId}")
    .param(String.class)
    .aggregate().query("page", Integer.class).query("limit", Integer.class).query("order", String.class).as(Pagination::new)
    .handle((userId, pagination) -> { /* */ });

Generic exception handling support

Hi @aleixmorgadas,
When an actor throws an exception, the resource code bails and the client code hangs. This is easy to repo by putting a throw or NPE into the actor code. I propose that the Resource handling system support new behavior such that I can add in to manage exceptional cases or where exceptions can be handled... This is where an IllegalStateException could be turned into a 404 response generically for different routes.

For example:

post( url ).body( RequestData.class  ).errorHandler(  errorHandler   ); 

SocketChannel looping infinitely while handling probe interval

The SockeChannelSelectionProcessorActor loops infinitely while trying to perform some Rest operations on the calculator app, which is a recent implemented vlingo-xoom example running on Heroku environment.

The following thread dump was captured from Heroku's jStack when the issue occurred.

"pool-3-thread-11" #29 prio=5 os_prio=0 tid=0x00007f1e68ac3000 nid=0x6a runnable [0x00007f1e381de000]
   java.lang.Thread.State: RUNNABLE
	at sun.nio.ch.EPollArrayWrapper.epollWait(Native Method)
	at sun.nio.ch.EPollArrayWrapper.poll(EPollArrayWrapper.java:269)
	at sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:93)
	at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:86)
	- locked <0x00000000fe8ac860> (a sun.nio.ch.Util$3)
	- locked <0x00000000fe8ac850> (a java.util.Collections$UnmodifiableSet)
	- locked <0x00000000fe8ac870> (a sun.nio.ch.EPollSelectorImpl)
	at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:97)
	at io.vlingo.wire.channel.SocketChannelSelectionProcessorActor.probeChannel(SocketChannelSelectionProcessorActor.java:156)
	at io.vlingo.wire.channel.SocketChannelSelectionProcessorActor.intervalSignal(SocketChannelSelectionProcessorActor.java:105)
	at io.vlingo.common.Scheduled__Proxy.lambda$intervalSignal$0(Scheduled__Proxy.java:31)
	at io.vlingo.common.Scheduled__Proxy$$Lambda$373/1199491717.accept(Unknown Source)
	at io.vlingo.actors.LocalMessage.internalDeliver(LocalMessage.java:115)
	at io.vlingo.actors.LocalMessage.deliver(LocalMessage.java:47)
	at io.vlingo.actors.plugin.mailbox.concurrentqueue.ConcurrentQueueMailbox.run(ConcurrentQueueMailbox.java:101)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)

The app is using Java 8 and running on Ubuntu-based Heroku's OS.

Response Without a Body Fails

When a Response doesn’t have a body then the Response seems to get stuck in the server or maybe the browser is rejecting the response because it may be malformed when there is no body.

  • There may simply be a newline missing following the headers, if any.
  • Currently unknown if this fails when there are no response headers.

Support for compressed body POST/PUT media types

Support for these media types is needed:
application/x-tar
application/zip & application/x-zip-compressed

This will only work the handler accepts a body with a single parameter, of type TBD, in which case that value will contain the compressed data. The receiver will need to decompress the data. This is not the same as gzip transfer encoding (which is also need because JSON is horribly verbose)

Questions:

  • Do you need to support Content-Disposition? Is there a need to handle a file name?
  • Do you need to support binary transfer encoding? Assume yes; Assume the receiver will be getting a binary buffer.

The Fluent API with DynamicResource Is Not Correctly Creating Pool Instances

The ResourceHandler pools are not being created with operable instances, which causes all requests to be handled by a single (actor backed) ResourceHandler instance.

See io.vlingo.http.resource.DynamicResource.SpecificResourceHandler, which is basically an inoperable instance of a ResourceHandler.

Somehow we must instantiate an actual ResourceHandler when requested of protected ResourceHandler resourceHandlerInstance(final Stage stage).

Looking at io.vlingo.http.resource.ConfigurationResource implementation of resourceHandlerInstance() it seems that it could be moved back to Resource<T> and it would be used by both ConfigurationResource and DynamicResource. At least for the interim it would seem to solve the problem. This would have to be tested carefully to ensure that all instances share the same routes() definitions.

Request handler withFailure causes blocked response

The code block below creates a new Vlingo Boot HTTP server with the request handler containing an OK HTTP body of Hello.

public class VlingoApplication {
    public static void main(String[] args) {
        VlingoServer.create("Vlingo Application",
                ResourceBuilder.resource("Hello", get("/hello")
                        .handle(() -> withFailure(of(Ok, "Hello")))));
    }
}

When using withFailure or throwing a runtime exception in the handler, the server will indefinitely block a response to the client.

curl localhost:8080/hello

This curl request will hang until the server is shut down. At that point the server will not return a reply, but the socket will be closed and the consumer will receive a message...

curl: (52) Empty reply from server

Feature: Optional Mapper

abstract

Fluent API should accept Mappers for scoped resources or specific endpoint.

specification

The user should be able to specify a concrete Mapper for

  • Resources
  • Resource Handler

When Action.disallowPathParametersWithSlash is set to false, incorrect URIs are matched

Given

  1. A URI template like /a/{x}/b/{y}/foo and
  2. A URI template like /a/{x}/b/{y}/c/{z}/foo and
  3. A DynamicResource registered on 1. and
  4. A DynamicResource registered on 2.

When matching /a/1/b/2/c/3/foo
Then

  • Resource 1. is called
  • {y} is expanded to `2/c/3/``

Expected:

  • Resource 2. is called
  • {y} is expanded to 2
  • {z} is expanded to 3

This behaviour goes away if Action.disallowPathParametersWithSlash is set to true.

Details on how this came up: vlingo/xoom-schemata#63

Server shutdown() always fails

The test code in ServerTest.java apparently hints at this case. The code will always return false and spend as much time as the specified await time. Probably better to remove this and just have people call server.stop(), or fix. There should be some documentation that informs of the benefit of using shutdown (graceful completion of in-flight work?)

  @After
  public void tearDown() {
    client.close();
    server.shutDown(); // TODO: wait
//    if (!server.shutDown().await(2000)) {
//      System.out.println("Server did not shut down properly.");
//    }
    super.tearDown();
  }

Request response of exotic unicode characters seems broken

I have tested my xoom-application using the latest vlingo-http-1.6.1-SNAPSHOT (locally build and after fix of vlingo/xoom-designer#54). And now it does accept POST requests containing 'exotic' characters like æ, ø and å. However, when querying the value with a subsequent GET-method, it is no longer the same character that gets back, for instance 'Å' comes back as 'Ã�' (second whitespace-character is presented as 'NEL' in my IDE). The response literally crashed my Sapper server with an ERR_STREAM_WRITE_AFTER_END error. I reckon the same would happen for the example app "vlingo-e2e-sys-airline-ops", but I haven't had time to try that out.

I tried to inspect the values on the 'inside' of my xoom application, and it appears to have 'arrived' correctly, so I guess this time it has to do with serializing the response. Or maybe the response is lacking some encoding hints to the receiver!?

Allow RequestHandler* to return subclasses of Response

With complex APIs is desirable to have more typesafe way of handling responses, because the response type can be seen as documentation. Consider this request handler:

Completes<Response> createSchema(Object requiredParam) {
   return schemaRegistry.createSchema(requiredParam).andThen(this::buildResponse);
}

It's would be impossible to know the response type before digging into the implementation (and taking a look at buildResponse). The suggested improvement would make explicit the response type:

// CreatedSchemaResponse.java
public class CreatedSchemaResponse extends Response {
   public CreatedSchemaResponse(String location) { 
      super(ResponseStatus.Ok, Headers.of("Location", location)); // convenience constructor
   }
}

// SchemaResource.java
Completes<CreatedSchemaResponse> createSchema(...) {
}

WDYT?

Eliminate Bare TestUntil Uses

The following tests must be converted to use AccessSafely rather than bare TestUntil:

  • MockResponseSenderChannel (mock used by tests)
  • SseFeedTest
  • SseStreamResourceTest
  • ClientTest
  • ConfigurationResourceTest
  • MockCompletesEventuallyResponse (mock used by tests)
  • ServerTest
  • TestResponseChannelConsumer (used by tests)

Content-Type text/plain is not supported

Sending a POST/PUT/PATCH request w/ Content-Type: text/plain leads to

io.vlingo.http.resource.MediaTypeNotSupportedException: No mapper registered for the following media mimeType: text/plain
	at io.vlingo.http.resource.MediaTypeMapper.from(MediaTypeMapper.java:20) ~[classes/:na]
	at io.vlingo.http.resource.ParameterResolver.lambda$body$2(ParameterResolver.java:46) ~[classes/:na]
	at io.vlingo.http.resource.ParameterResolver.apply(ParameterResolver.java:91) ~[classes/:na]
	at io.vlingo.http.resource.RequestHandler6.execute(RequestHandler6.java:115) ~[classes/:na]
	at io.vlingo.http.resource.ResourceRequestHandlerActor.lambda$handleFor$0(ResourceRequestHandlerActor.java:44) ~[classes/:na]
	at io.vlingo.http.resource.ResourceRequestHandlerActor.handleFor(ResourceRequestHandlerActor.java:30) ~[classes/:na]
	at io.vlingo.http.resource.ResourceRequestHandlerActor.handleFor(ResourceRequestHandlerActor.java:47) ~[classes/:na]
	at io.vlingo.http.resource.ResourceRequestHandler__Proxy.lambda$handleFor$1(ResourceRequestHandler__Proxy.java:47) ~[classes/:na]
	at io.vlingo.actors.LocalMessage.internalDeliver(LocalMessage.java:115) ~[classes/:na]
	at io.vlingo.actors.LocalMessage.deliver(LocalMessage.java:47) ~[classes/:na]
	at io.vlingo.actors.plugin.mailbox.concurrentqueue.ConcurrentQueueMailbox.run(ConcurrentQueueMailbox.java:101) ~[classes/:na]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) ~[na:1.8.0_212]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) ~[na:1.8.0_212]
	at java.lang.Thread.run(Thread.java:748) ~[na:1.8.0_212]

Connection-based Async Client

Must support async request-response that does not roundtrip the X-Correlation-ID header.

Serialize each request-response over a pool of actors. Provide three strategies:

  • LoadBalanced
  • Partitioned
  • Round Robin

Empty response causes hang in client response

When creating a Resource class, if the Response Body is empty, the HTTP client will hang.

 public Completes<Response> noOpSuccess(final String param) {
        return Completes.withSuccess(
                Response.of(Created,
                        headers(of(Location,"/")), Body.from("")));
    }

This can be seen in the following branch using the example code in CartResourceShould class in the "vlingo-examples/vlingo-ecommerce" sample project I was working on. I was looking at the http code to find a solution, but I haven't found the issue yet.

bwehrle/vlingo-examples@6772b2c

DSL

Continuation of #1.

The DSL should help developers define the Resources in a friendly & type safe DSL.

Suggestion example:

final Resource resource = resource("userResource",
        get("/helloWorld").handle(() -> Response.of(Ok, serialized("Hello World"))),
        post("/post/{postId}")
          .param(String.class)
          .body(PostData.class)
          .handle((postId, userData) -> Response.of(Ok, serialized(postId)))
);

It allows to map the handler function into class methods.

Similar to

class HelloResource {
    private final String NAME = "HelloResource";

    public Response helloWorld() {
        return Response.of(Ok, serialized("Hello World"));
    }

    public Response createPost(final String postId, final PostData postData) {
        // Do stuff
        return Response.of(Ok, serialized(postData));
    }

    public static Resource routes() {
        final HelloResource helloResource = new HelloResource();
        return resource(HelloResource.NAME,
            get("/helloworld", helloResource::helloWorld),
            post("/post/{postId}")
                .param(String.class)
                .body(PostData.class)
                .handle(helloResource::createPost)
        );
    }
}

The DSL maps the different HTTP parameters as:

  • path parameters: param(Class)
  • headers: header(Header) & header(String) for non standard Headers
  • body: body(Class)
  • query parameters: query(String name, Class type) & query(String name, defaultValue, Class type)

Each of those methods adds a new parameter to the handler function, it might cause a very long function signature. To reduce it, a way to aggregate different parameters into DTOs will be created. A first code example:

⚠️ Syntax to be defined

class Pagination {
    int page;
    int pageSize;

    public Pagination(int page, int pageSize) {
        this.page = page;
        this.pageSize = pageSize;
    }
}

return resource(HelloResource.NAME,
    get("/post/{postId}")
        .param(String.class)
        .combine(query("page", Integer.class), query("pageSize", Integer.class)).as(Pagination::new)
        .handle((postId, pagination) -> Response.of(Ok, serialized("[]")));
);

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.