Skip to content

Commit

Permalink
form/urlencoded support
Browse files Browse the repository at this point in the history
  • Loading branch information
spencergibb committed Feb 8, 2017
1 parent 7a8d76d commit d121101
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 30 deletions.
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package org.springframework.cloud.gateway.filter;

import java.net.URI;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ServerWebExchange;
Expand All @@ -21,6 +24,7 @@
import reactor.core.publisher.Mono;
import reactor.ipc.netty.NettyPipeline;
import reactor.ipc.netty.http.client.HttpClient;
import reactor.ipc.netty.http.client.HttpClientRequest;

/**
* @author Spencer Gibb
Expand Down Expand Up @@ -53,26 +57,39 @@ public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
final DefaultHttpHeaders httpHeaders = new DefaultHttpHeaders();
request.getHeaders().forEach(httpHeaders::set);

return this.httpClient.request(method, url, req ->
req.options(NettyPipeline.SendOptions::flushOnEach)
.headers(httpHeaders)
.sendHeaders()
.send(request.getBody()
.map(DataBuffer::asByteBuffer)
.map(Unpooled::wrappedBuffer)))
.then(res -> {
ServerHttpResponse response = exchange.getResponse();
// put headers and status so filters can modify the response
final HttpHeaders headers = new HttpHeaders();
res.responseHeaders().forEach(entry -> headers.add(entry.getKey(), entry.getValue()));

response.getHeaders().putAll(headers);
response.setStatusCode(HttpStatus.valueOf(res.status().code()));

// Defer committing the response until all route filters have run
// Put client response as ServerWebExchange attribute and write response later WriteResponseFilter
exchange.getAttributes().put(CLIENT_RESPONSE_ATTR, res);
return Mono.empty();
}).then(chain.filter(exchange));
return this.httpClient.request(method, url, req -> {
final HttpClientRequest proxyRequest = req.options(NettyPipeline.SendOptions::flushOnEach)
.headers(httpHeaders);

if (MediaType.APPLICATION_FORM_URLENCODED.includes(request.getHeaders().getContentType())) {
return exchange.getFormData()
.then(map -> proxyRequest.sendForm(form -> {
for (Map.Entry<String, List<String>> entry: map.entrySet()) {
for (String value : entry.getValue()) {
form.attr(entry.getKey(), value);
}
}
}).then())
.then(chain.filter(exchange));
}

return proxyRequest.sendHeaders()
.send(request.getBody()
.map(DataBuffer::asByteBuffer)
.map(Unpooled::wrappedBuffer));
}).then(res -> {
ServerHttpResponse response = exchange.getResponse();
// put headers and status so filters can modify the response
HttpHeaders headers = new HttpHeaders();
res.responseHeaders().forEach(entry -> headers.add(entry.getKey(), entry.getValue()));

response.getHeaders().putAll(headers);
response.setStatusCode(HttpStatus.valueOf(res.status().code()));

// Defer committing the response until all route filters have run
// Put client response as ServerWebExchange attribute and write response later WriteResponseFilter
exchange.getAttributes().put(CLIENT_RESPONSE_ATTR, res);
return Mono.empty();
}).then(chain.filter(exchange));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.server.ServerWebExchange;
Expand All @@ -45,6 +48,7 @@

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = RANDOM_PORT)
@DirtiesContext//(methodMode = DirtiesContext.MethodMode.AFTER_METHOD)
@SuppressWarnings("unchecked")
public class GatewayIntegrationTests {

Expand Down Expand Up @@ -246,6 +250,28 @@ public void loadBalancerFilterWorks() {
.verify(DURATION);
}

@Test
public void formUrlencodedWorks() {
LinkedMultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
formData.add("foo", "bar");
formData.add("baz", "bam");

Mono<Map> result = webClient.post()
.uri("/post")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.exchange(BodyInserters.fromFormData(formData))
.then(response -> response.body(toMono(Map.class)));

StepVerifier.create(result)
.consumeNextWith(map -> {
Map<String, Object> form = getMap(map, "form");
assertThat(form).containsEntry("foo", "bar");
assertThat(form).containsEntry("baz", "bam");
})
.expectComplete()
.verify(DURATION);
}

@Test
public void postWorks() {
Mono<Map> result = webClient.post()
Expand Down
11 changes: 2 additions & 9 deletions src/test/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,6 @@ spring:
filters:
- AddResponseHeader=X-Request-Foo, Bar

# =====================================
- id: complex_content_type_test
uri: http://httpbin.org:80
predicates:
- Host=**.complexcontenttype.org
- Url=/headers

# =====================================
- id: hystrix_failure_test
uri: http://httpbin.org:80
Expand Down Expand Up @@ -181,8 +174,8 @@ myservice:
logging:
level:
org.springframework.cloud.gateway: TRACE
# org.springframework.http.server.reactive: DEBUG
# reactor.ipc.netty: DEBUG
org.springframework.http.server.reactive: DEBUG
reactor.ipc.netty: DEBUG

management:
context-path: /admin
Expand Down

0 comments on commit d121101

Please sign in to comment.