Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle primitive HTTP client responses with MessageBodyReader #10699

Merged
merged 4 commits into from
Apr 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
package io.micronaut.http.body

import io.micronaut.context.annotation.Property
import io.micronaut.context.annotation.Requires
import io.micronaut.core.convert.TypeConverterRegistrar
import io.micronaut.core.type.Argument
import io.micronaut.http.MediaType
import io.micronaut.http.annotation.Body
import io.micronaut.http.annotation.Controller
import io.micronaut.http.annotation.Get
import io.micronaut.http.annotation.Post
import io.micronaut.http.client.annotation.Client
import io.micronaut.runtime.server.EmbeddedServer
import io.micronaut.test.annotation.MockBean
import io.micronaut.test.extensions.spock.annotation.MicronautTest
import jakarta.inject.Inject
import spock.lang.Issue
import spock.lang.Specification

@MicronautTest
@Property(name = "spec.name", value = "ConversionPrimitiveHandlerSpec")
@Issue('https://github.com/micronaut-projects/micronaut-core/issues/10698')
class MessageBodyReaderForPrimitivesSpec extends Specification {

@Inject EmbeddedServer server
@Inject MessageBodyHandlerRegistry messageBodyHandlerRegistry
@Inject TestClient client

@MockBean(bean = TypeConverterRegistrar, named = "NettyConverters")
TypeConverterRegistrar nettyConverters() {
// block loading the Netty converters, which is analogous to http-server-netty not being loaded
Mock(TypeConverterRegistrar.class)
}

def 'test boolean output'() {
expect:
client.testBoolean()
}

def 'test boolean input and output'() {
expect:
!client.testBoolean(false)
client.testBoolean(true)
}

def 'test long output'() {
expect:
client.testLong() == 1L
}

def 'test long input and output'() {
expect:
client.testLong(2L) == 2L
}

def 'test int output'() {
expect:
client.testInt() == 3
}

def 'test int input and output'() {
expect:
client.testInt(4) == 4
}

def 'test MessageBodyReader for boolean exists'() {
when:
def reader =
messageBodyHandlerRegistry
.findReader(Argument.of(boolean.class), [MediaType.APPLICATION_JSON_TYPE])

then:
reader.isPresent()
}

def 'test MessageBodyReader for long exists'() {
when:
def reader =
messageBodyHandlerRegistry
.findReader(Argument.of(long.class), [MediaType.APPLICATION_JSON_TYPE])

then:
reader.isPresent()
}

def 'test MessageBodyReader for int exists'() {
when:
def reader =
messageBodyHandlerRegistry
.findReader(Argument.of(int.class), [MediaType.APPLICATION_JSON_TYPE])

then:
reader.isPresent()
}

@Client("/primitives")
@Requires(property = "spec.name", value = "ConversionPrimitiveHandlerSpec")
static interface TestClient {

@Get("boolean")
boolean testBoolean();

@Post("boolean")
boolean testBoolean(@Body boolean value);

@Get("long")
long testLong();

@Post("long")
long testLong(@Body long value);

@Get("int")
int testInt();

@Post("int")
int testInt(@Body int value);

}

@Controller("/primitives")
@Requires(property = "spec.name", value = "ConversionPrimitiveHandlerSpec")
static class TestController {

@Get("boolean")
boolean testBoolean() {
return true;
}

@Post("boolean")
boolean testBoolean(@Body boolean value) {
return value;
}

@Get("long")
long testLong() {
return 1L;
}

@Post("long")
long testLong(@Body long value) {
return value;
}

@Get("int")
int testInt() {
return 3;
}

@Post("int")
int testInt(@Body int value) {
return value;
}

}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package io.micronaut.http.server.netty

import groovy.transform.InheritConstructors
import io.micronaut.context.annotation.Property
import io.micronaut.context.annotation.Requires
import io.micronaut.core.type.Argument
import io.micronaut.http.HttpRequest
import io.micronaut.http.HttpResponse
Expand Down Expand Up @@ -28,6 +30,7 @@ import static io.micronaut.http.server.netty.ContentNegotiationSpec.NegotiatingC
import static io.micronaut.http.server.netty.ContentNegotiationSpec.NegotiatingController.XML

@MicronautTest
@Property(name = "spec.name", value = "ContentNegotiationSpec")
class ContentNegotiationSpec extends Specification {

@Inject
Expand Down Expand Up @@ -171,6 +174,7 @@ class ContentNegotiationSpec extends Specification {
}

@Controller("/negotiate")
@Requires(property = "spec.name", value = "ContentNegotiationSpec")
static class NegotiatingController {

public static final String XML = "<hello>world</hello>"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.micronaut.http.server.netty

import io.micronaut.context.ApplicationContext
import io.micronaut.context.annotation.Requires
import io.micronaut.core.annotation.NonNull
import io.micronaut.core.async.annotation.SingleResult
import io.micronaut.http.HttpRequest
Expand Down Expand Up @@ -61,8 +62,13 @@ import java.util.concurrent.CopyOnWriteArrayList

class MaxRequestSizeSpec extends Specification {

static final String SPEC_NAME = 'MaxRequestSizeSpec'

void "test max request size default processor"() {
EmbeddedServer embeddedServer = ApplicationContext.run(EmbeddedServer, ['micronaut.server.maxRequestSize': '10KB'])
EmbeddedServer embeddedServer = ApplicationContext.run(EmbeddedServer, [
'micronaut.server.maxRequestSize': '10KB',
'spec.name': SPEC_NAME
])
HttpClient client = embeddedServer.applicationContext.createBean(HttpClient, embeddedServer.getURL())

when:
Expand All @@ -86,7 +92,10 @@ class MaxRequestSizeSpec extends Specification {
}

void "test max request size json processor"() {
EmbeddedServer embeddedServer = ApplicationContext.run(EmbeddedServer, ['micronaut.server.maxRequestSize': '10KB'])
EmbeddedServer embeddedServer = ApplicationContext.run(EmbeddedServer, [
'micronaut.server.maxRequestSize': '10KB',
'spec.name': SPEC_NAME
])
HttpClient client = embeddedServer.applicationContext.createBean(HttpClient, embeddedServer.getURL())

when:
Expand All @@ -111,7 +120,10 @@ class MaxRequestSizeSpec extends Specification {

@Ignore("Whether or not the exception is thrown is inconsistent. I don't think there is anything we can do to ensure its consistency")
void "test max request size multipart processor"() {
EmbeddedServer embeddedServer = ApplicationContext.run(EmbeddedServer, ['micronaut.server.maxRequestSize': '10KB'])
EmbeddedServer embeddedServer = ApplicationContext.run(EmbeddedServer, [
'micronaut.server.maxRequestSize': '10KB',
'spec.name': SPEC_NAME
])
HttpClient client = embeddedServer.applicationContext.createBean(HttpClient, embeddedServer.getURL())

when:
Expand Down Expand Up @@ -149,7 +161,8 @@ class MaxRequestSizeSpec extends Specification {

void "test max part size multipart processor"() {
EmbeddedServer embeddedServer = ApplicationContext.run(EmbeddedServer, [
'micronaut.server.multipart.maxFileSize': '1KB'
'micronaut.server.multipart.maxFileSize': '1KB',
'spec.name': SPEC_NAME
])
HttpClient client = embeddedServer.applicationContext.createBean(HttpClient, embeddedServer.getURL())

Expand Down Expand Up @@ -188,7 +201,8 @@ class MaxRequestSizeSpec extends Specification {

void "test max part size multipart body binder"() {
EmbeddedServer embeddedServer = ApplicationContext.run(EmbeddedServer, [
'micronaut.server.multipart.maxFileSize': '1KB'
'micronaut.server.multipart.maxFileSize': '1KB',
'spec.name': SPEC_NAME
])
HttpClient client = embeddedServer.applicationContext.createBean(HttpClient, embeddedServer.getURL())

Expand Down Expand Up @@ -228,7 +242,8 @@ class MaxRequestSizeSpec extends Specification {
void "test content length exceeded with different disk storage"() {
EmbeddedServer embeddedServer = ApplicationContext.run(EmbeddedServer, [
'micronaut.server.maxRequestSize': '10KB',
'micronaut.server.multipart.disk': true
'micronaut.server.multipart.disk': true,
'spec.name': SPEC_NAME
])
HttpClient client = embeddedServer.applicationContext.createBean(HttpClient, embeddedServer.getURL())

Expand All @@ -254,7 +269,8 @@ class MaxRequestSizeSpec extends Specification {
given:
EmbeddedServer embeddedServer = ApplicationContext.run(EmbeddedServer, [
'micronaut.server.maxRequestSize': '10KB',
'micronaut.server.port': -1
'micronaut.server.port': -1,
'spec.name': SPEC_NAME
])

def responses = new CopyOnWriteArrayList<FullHttpResponse>()
Expand Down Expand Up @@ -334,7 +350,8 @@ class MaxRequestSizeSpec extends Specification {
'micronaut.server.ssl.enabled': true,
'micronaut.server.netty.log-level': 'TRACE',
'micronaut.server.ssl.port': -1,
'micronaut.server.ssl.buildSelfSigned': true
'micronaut.server.ssl.buildSelfSigned': true,
'spec.name': SPEC_NAME
])

def request1 = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, '/test-max-size/multipart-body')
Expand Down Expand Up @@ -433,6 +450,7 @@ class MaxRequestSizeSpec extends Specification {
}

@Controller("/test-max-size")
@Requires(property = "spec.name", value = "MaxRequestSizeSpec")
static class TestController {

@Post(uri = "/text", consumes = MediaType.TEXT_PLAIN)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.micronaut.http.server.netty.binding

import groovy.json.JsonSlurper
import io.micronaut.context.annotation.Requires
import io.micronaut.http.HttpRequest
import io.micronaut.http.HttpStatus
import io.micronaut.http.annotation.Body
Expand Down Expand Up @@ -36,6 +37,7 @@ class DefaultJsonErrorHandlingSpec extends AbstractMicronautSpec {
}

@Controller("/errors")
@Requires(property = "spec.name", value = "DefaultJsonErrorHandlingSpec")
static class ErrorsController {
@Post("/string")
String string(@Body String text) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package io.micronaut.http.server.netty.binding

import groovy.transform.EqualsAndHashCode
import io.micronaut.context.annotation.Requires
import io.micronaut.core.async.annotation.SingleResult
import io.micronaut.http.HttpRequest
import io.micronaut.http.HttpResponse
Expand Down Expand Up @@ -260,6 +261,7 @@ class FormDataBindingSpec extends AbstractMicronautSpec {
}

@Controller(value = '/form', consumes = MediaType.APPLICATION_FORM_URLENCODED)
@Requires(property = "spec.name", value = "FormDataBindingSpec")
static class FormController {

@Post('/string')
Expand Down Expand Up @@ -296,6 +298,7 @@ class FormDataBindingSpec extends AbstractMicronautSpec {
}

@Controller('/form/saml/test')
@Requires(property = "spec.name", value = "FormDataBindingSpec")
static class MainController {

@Post(consumes = MediaType.APPLICATION_FORM_URLENCODED)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.micronaut.http.server.netty.binding

import io.micronaut.context.ApplicationContext
import io.micronaut.context.annotation.Requires
import io.micronaut.http.HttpRequest
import io.micronaut.http.HttpResponse
import io.micronaut.http.HttpStatus
Expand All @@ -22,7 +23,8 @@ class FormDataDiskSpec extends Specification {
'micronaut.server.multipart.disk': true,
'micronaut.server.multipart.mixed': true,
'micronaut.server.thread-selection': 'IO',
'netty.resource-leak-detector-level': 'paranoid'
'netty.resource-leak-detector-level': 'paranoid',
"spec.name": "FormDataDiskSpec"
])
def client = server.applicationContext.createBean(HttpClient, server.URI)

Expand All @@ -47,7 +49,8 @@ class FormDataDiskSpec extends Specification {
def server = (EmbeddedServer) ApplicationContext.run(EmbeddedServer, [
'micronaut.server.multipart.mixed': true,
'micronaut.server.thread-selection': 'IO',
'netty.resource-leak-detector-level': 'paranoid'
'netty.resource-leak-detector-level': 'paranoid',
"spec.name": "FormDataDiskSpec"
])
def client = server.applicationContext.createBean(HttpClient, server.URI)

Expand All @@ -67,6 +70,7 @@ class FormDataDiskSpec extends Specification {
}

@Controller(value = '/form-disk', consumes = MediaType.APPLICATION_FORM_URLENCODED)
@Requires(property = "spec.name", value = "FormDataDiskSpec")
static class FormController {
@Post('/object')
Object object(@Body Object object) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package io.micronaut.http.server.netty.binding

import com.fasterxml.jackson.annotation.JsonCreator
import groovy.json.JsonSlurper
import io.micronaut.context.annotation.Requires
import io.micronaut.core.annotation.Introspected
import io.micronaut.core.async.annotation.SingleResult
import io.micronaut.http.HttpHeaders
Expand All @@ -26,6 +27,11 @@ import java.util.concurrent.CompletableFuture

class JsonBodyBindingSpec extends AbstractMicronautSpec {

@Override
Map<String, Object> getConfiguration() {
return super.getConfiguration() + ['test.controller': 'JsonController']
}

void "test JSON is not parsed when the body is a raw body type"() {
when:
String json = '{"title":"The Stand"'
Expand Down Expand Up @@ -304,6 +310,7 @@ class JsonBodyBindingSpec extends AbstractMicronautSpec {
}

@Controller(value = "/json", produces = io.micronaut.http.MediaType.APPLICATION_JSON)
@Requires(property = "test.controller", value = "JsonController")
static class JsonController {

@Post("/params")
Expand Down
Loading
Loading