Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
S
spring-cloud-netflix
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
openSource
spring-cloud-netflix
Commits
f46e8e8e
Commit
f46e8e8e
authored
Nov 20, 2015
by
Spencer Gibb
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #543 from ChristianLohmann/create-httpclient-ribbon-command
* create-httpclient-ribbon-command: an Apache http client zuul ribbon command
parents
81b430db
18dff852
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
879 additions
and
0 deletions
+879
-0
RibbonApacheHttpRequest.java
.../cloud/netflix/ribbon/apache/RibbonApacheHttpRequest.java
+92
-0
RibbonApacheHttpResponse.java
...cloud/netflix/ribbon/apache/RibbonApacheHttpResponse.java
+161
-0
RibbonLoadBalancingHttpClient.java
.../netflix/ribbon/apache/RibbonLoadBalancingHttpClient.java
+115
-0
HttpClientRibbonCommand.java
...ix/zuul/filters/route/apache/HttpClientRibbonCommand.java
+123
-0
HttpClientRibbonCommandFactory.java
.../filters/route/apache/HttpClientRibbonCommandFactory.java
+49
-0
SampleZuulProxyAppTestsWithHttpClient.java
...d/netflix/zuul/SampleZuulProxyAppTestsWithHttpClient.java
+339
-0
No files found.
spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/apache/RibbonApacheHttpRequest.java
0 → 100644
View file @
f46e8e8e
/*
* Copyright 2013-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
org
.
springframework
.
cloud
.
netflix
.
ribbon
.
apache
;
import
java.io.InputStream
;
import
java.net.URI
;
import
java.util.List
;
import
org.apache.http.client.config.RequestConfig
;
import
org.apache.http.client.methods.HttpUriRequest
;
import
org.apache.http.client.methods.RequestBuilder
;
import
org.apache.http.entity.BasicHttpEntity
;
import
org.springframework.util.MultiValueMap
;
import
com.netflix.client.ClientRequest
;
import
lombok.Getter
;
/**
* @author Christian Lohmann
*/
@Getter
public
class
RibbonApacheHttpRequest
extends
ClientRequest
implements
Cloneable
{
private
final
String
method
;
private
final
MultiValueMap
<
String
,
String
>
headers
;
private
final
MultiValueMap
<
String
,
String
>
params
;
private
final
InputStream
requestEntity
;
public
RibbonApacheHttpRequest
(
final
String
method
,
final
URI
uri
,
final
Boolean
retryable
,
final
MultiValueMap
<
String
,
String
>
headers
,
final
MultiValueMap
<
String
,
String
>
params
,
final
InputStream
requestEntity
)
{
this
.
method
=
method
;
this
.
uri
=
uri
;
this
.
isRetriable
=
retryable
;
this
.
headers
=
headers
;
this
.
params
=
params
;
this
.
requestEntity
=
requestEntity
;
}
public
HttpUriRequest
toRequest
(
final
RequestConfig
requestConfig
)
{
final
RequestBuilder
builder
=
RequestBuilder
.
create
(
this
.
method
);
builder
.
setUri
(
this
.
uri
);
for
(
final
String
name
:
this
.
headers
.
keySet
())
{
final
List
<
String
>
values
=
this
.
headers
.
get
(
name
);
for
(
final
String
value
:
values
)
{
builder
.
addHeader
(
name
,
value
);
}
}
for
(
final
String
name
:
this
.
params
.
keySet
())
{
final
List
<
String
>
values
=
this
.
params
.
get
(
name
);
for
(
final
String
value
:
values
)
{
builder
.
addParameter
(
name
,
value
);
}
}
if
(
this
.
requestEntity
!=
null
)
{
final
BasicHttpEntity
entity
;
entity
=
new
BasicHttpEntity
();
entity
.
setContent
(
this
.
requestEntity
);
builder
.
setEntity
(
entity
);
}
builder
.
setConfig
(
requestConfig
);
return
builder
.
build
();
}
public
RibbonApacheHttpRequest
withNewUri
(
final
URI
uri
)
{
return
new
RibbonApacheHttpRequest
(
this
.
method
,
uri
,
this
.
isRetriable
,
this
.
headers
,
this
.
params
,
this
.
requestEntity
);
}
}
spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/apache/RibbonApacheHttpResponse.java
0 → 100644
View file @
f46e8e8e
/*
* Copyright 2013-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
org
.
springframework
.
cloud
.
netflix
.
ribbon
.
apache
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.lang.reflect.Type
;
import
java.net.URI
;
import
java.util.ArrayList
;
import
java.util.Collection
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.Map
;
import
org.apache.http.Header
;
import
org.apache.http.HttpResponse
;
import
com.google.common.reflect.TypeToken
;
import
com.netflix.client.ClientException
;
import
com.netflix.client.http.CaseInsensitiveMultiMap
;
import
com.netflix.client.http.HttpHeaders
;
/**
* @author Christian Lohmann
*/
public
class
RibbonApacheHttpResponse
implements
com
.
netflix
.
client
.
http
.
HttpResponse
{
private
HttpResponse
httpResponse
;
private
URI
uri
;
public
RibbonApacheHttpResponse
(
final
HttpResponse
httpResponse
,
final
URI
uri
)
{
this
.
httpResponse
=
httpResponse
;
this
.
uri
=
uri
;
}
@Override
public
Object
getPayload
()
throws
ClientException
{
try
{
return
this
.
httpResponse
.
getEntity
().
getContent
();
}
catch
(
final
IOException
e
)
{
throw
new
ClientException
(
e
.
getMessage
(),
e
);
}
}
@Override
public
boolean
hasPayload
()
{
return
this
.
httpResponse
.
getEntity
()
!=
null
;
}
@Override
public
boolean
isSuccess
()
{
return
this
.
httpResponse
.
getStatusLine
().
getStatusCode
()
==
200
;
}
@Override
public
URI
getRequestedURI
()
{
return
this
.
uri
;
}
public
int
getStatus
()
{
return
httpResponse
.
getStatusLine
().
getStatusCode
();
}
public
String
getStatusLine
()
{
return
httpResponse
.
getStatusLine
().
toString
();
}
@Override
public
Map
<
String
,
Collection
<
String
>>
getHeaders
()
{
final
Map
<
String
,
Collection
<
String
>>
headers
=
new
HashMap
<>();
for
(
final
Header
header
:
this
.
httpResponse
.
getAllHeaders
())
{
if
(
headers
.
containsKey
(
header
.
getName
()))
{
headers
.
get
(
header
.
getName
()).
add
(
header
.
getValue
());
}
else
{
final
List
<
String
>
values
=
new
ArrayList
<>();
values
.
add
(
header
.
getValue
());
headers
.
put
(
header
.
getName
(),
values
);
}
}
return
headers
;
}
@Override
public
HttpHeaders
getHttpHeaders
()
{
final
CaseInsensitiveMultiMap
headers
=
new
CaseInsensitiveMultiMap
();
for
(
final
Header
header
:
httpResponse
.
getAllHeaders
())
{
headers
.
addHeader
(
header
.
getName
(),
header
.
getValue
());
}
return
headers
;
}
@Override
public
void
close
()
{
if
(
this
.
httpResponse
!=
null
&&
this
.
httpResponse
.
getEntity
()
!=
null
)
{
try
{
this
.
httpResponse
.
getEntity
().
getContent
().
close
();
}
catch
(
final
IOException
e
)
{
throw
new
RuntimeException
(
e
.
getMessage
(),
e
);
}
}
}
@Override
public
InputStream
getInputStream
()
{
try
{
return
this
.
httpResponse
.
getEntity
().
getContent
();
}
catch
(
final
IOException
e
)
{
throw
new
RuntimeException
(
e
.
getMessage
(),
e
);
}
}
@Override
public
boolean
hasEntity
()
{
return
hasPayload
();
}
/**
* Not used
*/
@Override
public
<
T
>
T
getEntity
(
final
Class
<
T
>
type
)
throws
Exception
{
return
null
;
}
/**
* Not used
*/
@Override
public
<
T
>
T
getEntity
(
final
Type
type
)
throws
Exception
{
return
null
;
}
/**
* Not used
*/
@Override
public
<
T
>
T
getEntity
(
final
TypeToken
<
T
>
type
)
throws
Exception
{
return
null
;
}
}
spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/apache/RibbonLoadBalancingHttpClient.java
0 → 100644
View file @
f46e8e8e
/*
* Copyright 2013-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
org
.
springframework
.
cloud
.
netflix
.
ribbon
.
apache
;
import
java.net.URI
;
import
org.apache.http.HttpResponse
;
import
org.apache.http.client.HttpClient
;
import
org.apache.http.client.config.RequestConfig
;
import
org.apache.http.client.methods.HttpUriRequest
;
import
org.apache.http.impl.client.HttpClientBuilder
;
import
org.springframework.web.util.UriComponentsBuilder
;
import
com.netflix.client.AbstractLoadBalancerAwareClient
;
import
com.netflix.client.RequestSpecificRetryHandler
;
import
com.netflix.client.RetryHandler
;
import
com.netflix.client.config.CommonClientConfigKey
;
import
com.netflix.client.config.IClientConfig
;
import
com.netflix.loadbalancer.ILoadBalancer
;
import
lombok.Setter
;
/**
* @author Christian Lohmann
*/
public
class
RibbonLoadBalancingHttpClient
extends
AbstractLoadBalancerAwareClient
<
RibbonApacheHttpRequest
,
RibbonApacheHttpResponse
>
{
private
final
HttpClient
delegate
=
HttpClientBuilder
.
create
().
build
();
@Setter
private
int
connectTimeout
;
@Setter
private
int
readTimeout
;
@Setter
private
boolean
secure
;
@Setter
private
IClientConfig
clientConfig
;
public
RibbonLoadBalancingHttpClient
()
{
super
(
null
);
this
.
setRetryHandler
(
RetryHandler
.
DEFAULT
);
}
public
RibbonLoadBalancingHttpClient
(
final
ILoadBalancer
lb
)
{
super
(
lb
);
this
.
setRetryHandler
(
RetryHandler
.
DEFAULT
);
}
@Override
public
RequestSpecificRetryHandler
getRequestSpecificRetryHandler
(
final
RibbonApacheHttpRequest
request
,
final
IClientConfig
requestConfig
)
{
if
(
this
.
clientConfig
.
get
(
CommonClientConfigKey
.
OkToRetryOnAllOperations
,
false
))
{
return
new
RequestSpecificRetryHandler
(
true
,
true
,
this
.
getRetryHandler
(),
requestConfig
);
}
if
(!
request
.
getMethod
().
equals
(
"GET"
))
{
return
new
RequestSpecificRetryHandler
(
true
,
false
,
this
.
getRetryHandler
(),
requestConfig
);
}
else
{
return
new
RequestSpecificRetryHandler
(
true
,
true
,
this
.
getRetryHandler
(),
requestConfig
);
}
}
@Override
public
RibbonApacheHttpResponse
execute
(
RibbonApacheHttpRequest
request
,
final
IClientConfig
configOverride
)
throws
Exception
{
final
RequestConfig
.
Builder
builder
=
RequestConfig
.
custom
();
if
(
configOverride
!=
null
)
{
builder
.
setConnectTimeout
(
configOverride
.
get
(
CommonClientConfigKey
.
ConnectTimeout
,
this
.
connectTimeout
));
builder
.
setConnectionRequestTimeout
(
configOverride
.
get
(
CommonClientConfigKey
.
ReadTimeout
,
this
.
readTimeout
));
}
else
{
builder
.
setConnectTimeout
(
this
.
connectTimeout
);
builder
.
setConnectionRequestTimeout
(
this
.
readTimeout
);
}
final
RequestConfig
requestConfig
=
builder
.
build
();
if
(
isSecure
(
configOverride
))
{
final
URI
secureUri
=
UriComponentsBuilder
.
fromUri
(
request
.
getUri
())
.
scheme
(
"https"
).
build
().
toUri
();
request
=
request
.
withNewUri
(
secureUri
);
}
final
HttpUriRequest
httpUriRequest
=
request
.
toRequest
(
requestConfig
);
final
HttpResponse
httpResponse
=
this
.
delegate
.
execute
(
httpUriRequest
);
return
new
RibbonApacheHttpResponse
(
httpResponse
,
httpUriRequest
.
getURI
());
}
private
boolean
isSecure
(
final
IClientConfig
config
)
{
return
(
config
!=
null
)
?
config
.
get
(
CommonClientConfigKey
.
IsSecure
)
:
secure
;
}
}
spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/route/apache/HttpClientRibbonCommand.java
0 → 100644
View file @
f46e8e8e
/*
* Copyright 2013-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
org
.
springframework
.
cloud
.
netflix
.
zuul
.
filters
.
route
.
apache
;
import
java.io.InputStream
;
import
java.net.URI
;
import
org.springframework.cloud.netflix.ribbon.RibbonHttpResponse
;
import
org.springframework.cloud.netflix.ribbon.apache.RibbonApacheHttpRequest
;
import
org.springframework.cloud.netflix.ribbon.apache.RibbonApacheHttpResponse
;
import
org.springframework.cloud.netflix.ribbon.apache.RibbonLoadBalancingHttpClient
;
import
org.springframework.cloud.netflix.zuul.filters.route.RibbonCommand
;
import
org.springframework.http.client.ClientHttpResponse
;
import
org.springframework.util.MultiValueMap
;
import
com.netflix.config.DynamicIntProperty
;
import
com.netflix.config.DynamicPropertyFactory
;
import
com.netflix.hystrix.HystrixCommand
;
import
com.netflix.hystrix.HystrixCommandGroupKey
;
import
com.netflix.hystrix.HystrixCommandKey
;
import
com.netflix.hystrix.HystrixCommandProperties
;
import
com.netflix.zuul.constants.ZuulConstants
;
import
com.netflix.zuul.context.RequestContext
;
/**
* @author Christian Lohmann
*/
public
class
HttpClientRibbonCommand
extends
HystrixCommand
<
ClientHttpResponse
>
implements
RibbonCommand
{
private
final
RibbonLoadBalancingHttpClient
client
;
private
final
String
method
;
private
final
String
uri
;
private
final
MultiValueMap
<
String
,
String
>
headers
;
private
final
MultiValueMap
<
String
,
String
>
params
;
private
final
InputStream
requestEntity
;
private
final
Boolean
retryable
;
public
HttpClientRibbonCommand
(
final
RibbonLoadBalancingHttpClient
client
,
final
String
method
,
final
String
uri
,
final
MultiValueMap
<
String
,
String
>
headers
,
final
MultiValueMap
<
String
,
String
>
params
,
final
InputStream
requestEntity
,
final
Boolean
retryable
)
{
this
(
"default"
,
client
,
method
,
uri
,
headers
,
params
,
requestEntity
,
retryable
);
}
public
HttpClientRibbonCommand
(
final
String
commandKey
,
final
RibbonLoadBalancingHttpClient
client
,
final
String
method
,
final
String
uri
,
final
MultiValueMap
<
String
,
String
>
headers
,
final
MultiValueMap
<
String
,
String
>
params
,
final
InputStream
requestEntity
,
final
Boolean
retryable
)
{
super
(
getSetter
(
commandKey
));
this
.
client
=
client
;
this
.
method
=
method
;
this
.
uri
=
uri
;
this
.
headers
=
headers
;
this
.
params
=
params
;
this
.
requestEntity
=
requestEntity
;
this
.
retryable
=
retryable
;
}
protected
static
Setter
getSetter
(
final
String
commandKey
)
{
// we want to default to semaphore-isolation since this wraps
// 2 others commands that are already thread isolated
final
String
name
=
ZuulConstants
.
ZUUL_EUREKA
+
commandKey
+
".semaphore.maxSemaphores"
;
final
DynamicIntProperty
value
=
DynamicPropertyFactory
.
getInstance
()
.
getIntProperty
(
name
,
100
);
final
HystrixCommandProperties
.
Setter
setter
=
HystrixCommandProperties
.
Setter
()
.
withExecutionIsolationStrategy
(
HystrixCommandProperties
.
ExecutionIsolationStrategy
.
SEMAPHORE
)
.
withExecutionIsolationSemaphoreMaxConcurrentRequests
(
value
.
get
());
return
Setter
.
withGroupKey
(
HystrixCommandGroupKey
.
Factory
.
asKey
(
"RibbonCommand"
))
.
andCommandKey
(
HystrixCommandKey
.
Factory
.
asKey
(
commandKey
+
"RibbonCommand"
))
.
andCommandPropertiesDefaults
(
setter
);
}
@Override
protected
ClientHttpResponse
run
()
throws
Exception
{
return
forward
();
}
protected
ClientHttpResponse
forward
()
throws
Exception
{
final
RequestContext
context
=
RequestContext
.
getCurrentContext
();
URI
uriInstance
=
new
URI
(
this
.
uri
);
RibbonApacheHttpRequest
request
=
new
RibbonApacheHttpRequest
(
this
.
method
,
uriInstance
,
this
.
retryable
,
this
.
headers
,
this
.
params
,
this
.
requestEntity
);
final
RibbonApacheHttpResponse
response
=
this
.
client
.
executeWithLoadBalancer
(
request
);
context
.
set
(
"ribbonResponse"
,
response
);
// Explicitly close the HttpResponse if the Hystrix command timed out to
// release the underlying HTTP connection held by the response.
//
if
(
this
.
isResponseTimedOut
())
{
if
(
response
!=
null
)
{
response
.
close
();
}
}
return
new
RibbonHttpResponse
(
response
);
}
}
spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/route/apache/HttpClientRibbonCommandFactory.java
0 → 100644
View file @
f46e8e8e
/*
* Copyright 2013-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
org
.
springframework
.
cloud
.
netflix
.
zuul
.
filters
.
route
.
apache
;
import
org.springframework.cloud.netflix.ribbon.SpringClientFactory
;
import
org.springframework.cloud.netflix.ribbon.apache.RibbonLoadBalancingHttpClient
;
import
org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandContext
;
import
org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandFactory
;
import
lombok.RequiredArgsConstructor
;
/**
* @author Christian Lohmann
*/
@RequiredArgsConstructor
public
class
HttpClientRibbonCommandFactory
implements
RibbonCommandFactory
<
HttpClientRibbonCommand
>
{
private
final
SpringClientFactory
clientFactory
;
@Override
public
HttpClientRibbonCommand
create
(
final
RibbonCommandContext
context
)
{
final
String
serviceId
=
context
.
getServiceId
();
final
RibbonLoadBalancingHttpClient
client
=
clientFactory
.
getClient
(
serviceId
,
RibbonLoadBalancingHttpClient
.
class
);
client
.
setLoadBalancer
(
this
.
clientFactory
.
getLoadBalancer
(
serviceId
));
client
.
setClientConfig
(
this
.
clientFactory
.
getClientConfig
(
serviceId
));
final
HttpClientRibbonCommand
httpClientRibbonCommand
=
new
HttpClientRibbonCommand
(
serviceId
,
client
,
context
.
getVerb
(),
context
.
getUri
(),
context
.
getHeaders
(),
context
.
getParams
(),
context
.
getRequestEntity
(),
context
.
getRetryable
());
return
httpClientRibbonCommand
;
}
}
spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/SampleZuulProxyAppTestsWithHttpClient.java
0 → 100644
View file @
f46e8e8e
/*
* Copyright 2013-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
org
.
springframework
.
cloud
.
netflix
.
zuul
;
import
java.util.Arrays
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.Map
;
import
org.junit.Test
;
import
org.junit.runner.RunWith
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.beans.factory.annotation.Value
;
import
org.springframework.boot.SpringApplication
;
import
org.springframework.boot.autoconfigure.EnableAutoConfiguration
;
import
org.springframework.boot.test.IntegrationTest
;
import
org.springframework.boot.test.SpringApplicationConfiguration
;
import
org.springframework.boot.test.TestRestTemplate
;
import
org.springframework.cloud.netflix.ribbon.RibbonClient
;
import
org.springframework.cloud.netflix.ribbon.RibbonClients
;
import
org.springframework.cloud.netflix.ribbon.SpringClientFactory
;
import
org.springframework.cloud.netflix.zuul.filters.ProxyRouteLocator
;
import
org.springframework.cloud.netflix.zuul.filters.ZuulProperties
;
import
org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandFactory
;
import
org.springframework.cloud.netflix.zuul.filters.route.apache.HttpClientRibbonCommandFactory
;
import
org.springframework.context.annotation.Bean
;
import
org.springframework.context.annotation.Configuration
;
import
org.springframework.http.HttpEntity
;
import
org.springframework.http.HttpMethod
;
import
org.springframework.http.HttpStatus
;
import
org.springframework.http.ResponseEntity
;
import
org.springframework.test.annotation.DirtiesContext
;
import
org.springframework.test.context.junit4.SpringJUnit4ClassRunner
;
import
org.springframework.test.context.web.WebAppConfiguration
;
import
org.springframework.util.MultiValueMap
;
import
org.springframework.web.bind.annotation.PathVariable
;
import
org.springframework.web.bind.annotation.RequestBody
;
import
org.springframework.web.bind.annotation.RequestMapping
;
import
org.springframework.web.bind.annotation.RequestMethod
;
import
org.springframework.web.bind.annotation.RequestParam
;
import
org.springframework.web.bind.annotation.RestController
;
import
com.netflix.zuul.ZuulFilter
;
import
com.netflix.zuul.context.RequestContext
;
import
static
org
.
junit
.
Assert
.
assertEquals
;
import
static
org
.
junit
.
Assert
.
assertTrue
;
@RunWith
(
SpringJUnit4ClassRunner
.
class
)
@SpringApplicationConfiguration
(
classes
=
SampleHttpClientZuulProxyApplication
.
class
)
@WebAppConfiguration
@IntegrationTest
({
"server.port: 0"
,
"zuul.routes.other: /test/**=http://localhost:7777/local"
,
"zuul.routes.another: /another/twolevel/**"
,
"zuul.routes.simple: /simple/**"
})
@DirtiesContext
public
class
SampleZuulProxyAppTestsWithHttpClient
{
@Value
(
"${local.server.port}"
)
private
int
port
;
@Autowired
private
ProxyRouteLocator
routes
;
@Autowired
private
RoutesEndpoint
endpoint
;
@Autowired
private
RibbonCommandFactory
ribbonCommandFactory
;
@Test
public
void
bindRouteUsingPhysicalRoute
()
{
assertEquals
(
"http://localhost:7777/local"
,
this
.
routes
.
getRoutes
().
get
(
"/test/**"
));
}
@Test
public
void
bindRouteUsingOnlyPath
()
{
assertEquals
(
"simple"
,
this
.
routes
.
getRoutes
().
get
(
"/simple/**"
));
}
@Test
public
void
getOnSelfViaRibbonRoutingFilter
()
{
ResponseEntity
<
String
>
result
=
new
TestRestTemplate
().
exchange
(
"http://localhost:"
+
this
.
port
+
"/simple/local/1"
,
HttpMethod
.
GET
,
new
HttpEntity
<>((
Void
)
null
),
String
.
class
);
assertEquals
(
HttpStatus
.
OK
,
result
.
getStatusCode
());
assertEquals
(
"Gotten 1!"
,
result
.
getBody
());
}
@Test
public
void
patchOnSelfViaRibbonRoutingFilter
()
{
ResponseEntity
<
String
>
result
=
new
TestRestTemplate
().
exchange
(
"http://localhost:"
+
this
.
port
+
"/simple/local/1"
,
HttpMethod
.
PATCH
,
new
HttpEntity
<>(
"TestPatch"
),
String
.
class
);
assertEquals
(
HttpStatus
.
OK
,
result
.
getStatusCode
());
assertEquals
(
"Patched 1!"
,
result
.
getBody
());
}
@Test
public
void
postOnSelfViaRibbonRoutingFilter
()
{
ResponseEntity
<
String
>
result
=
new
TestRestTemplate
().
exchange
(
"http://localhost:"
+
this
.
port
+
"/simple/local/1"
,
HttpMethod
.
POST
,
new
HttpEntity
<>(
"TestPost"
),
String
.
class
);
assertEquals
(
HttpStatus
.
OK
,
result
.
getStatusCode
());
assertEquals
(
"Posted 1!"
,
result
.
getBody
());
}
@Test
public
void
deleteOnSelfViaRibbonRoutingFilter
()
{
ResponseEntity
<
String
>
result
=
new
TestRestTemplate
().
exchange
(
"http://localhost:"
+
this
.
port
+
"/simple/local/1"
,
HttpMethod
.
DELETE
,
new
HttpEntity
<>((
Void
)
null
),
String
.
class
);
assertEquals
(
HttpStatus
.
OK
,
result
.
getStatusCode
());
assertEquals
(
"Deleted 1!"
,
result
.
getBody
());
}
@Test
public
void
deleteOnSelfViaSimpleHostRoutingFilter
()
{
this
.
routes
.
addRoute
(
"/self/**"
,
"http://localhost:"
+
this
.
port
+
"/local"
);
this
.
endpoint
.
reset
();
ResponseEntity
<
String
>
result
=
new
TestRestTemplate
().
exchange
(
"http://localhost:"
+
this
.
port
+
"/self/1"
,
HttpMethod
.
DELETE
,
new
HttpEntity
<>((
Void
)
null
),
String
.
class
);
assertEquals
(
HttpStatus
.
OK
,
result
.
getStatusCode
());
assertEquals
(
"Deleted 1!"
,
result
.
getBody
());
}
@Test
public
void
patchOnSelfViaSimpleHostRoutingFilter
()
{
this
.
routes
.
addRoute
(
"/self/**"
,
"http://localhost:"
+
this
.
port
+
"/local"
);
this
.
endpoint
.
reset
();
ResponseEntity
<
String
>
result
=
new
TestRestTemplate
().
exchange
(
"http://localhost:"
+
this
.
port
+
"/self/1"
,
HttpMethod
.
PATCH
,
new
HttpEntity
<>(
"TestPatch"
),
String
.
class
);
assertEquals
(
HttpStatus
.
OK
,
result
.
getStatusCode
());
assertEquals
(
"Patched 1!"
,
result
.
getBody
());
}
@Test
public
void
stripPrefixFalseAppendsPath
()
{
this
.
routes
.
addRoute
(
new
ZuulProperties
.
ZuulRoute
(
"strip"
,
"/strip/**"
,
"strip"
,
"http://localhost:"
+
this
.
port
+
"/local"
,
false
,
false
));
this
.
endpoint
.
reset
();
ResponseEntity
<
String
>
result
=
new
TestRestTemplate
().
exchange
(
"http://localhost:"
+
this
.
port
+
"/strip"
,
HttpMethod
.
GET
,
new
HttpEntity
<>((
Void
)
null
),
String
.
class
);
assertEquals
(
HttpStatus
.
OK
,
result
.
getStatusCode
());
// Prefix not stripped to it goes to /local/strip
assertEquals
(
"Gotten strip!"
,
result
.
getBody
());
}
@Test
public
void
testNotFound
()
{
ResponseEntity
<
String
>
result
=
new
TestRestTemplate
().
exchange
(
"http://localhost:"
+
this
.
port
+
"/myinvalidpath"
,
HttpMethod
.
GET
,
new
HttpEntity
<>((
Void
)
null
),
String
.
class
);
assertEquals
(
HttpStatus
.
NOT_FOUND
,
result
.
getStatusCode
());
}
@Test
public
void
getSecondLevel
()
{
ResponseEntity
<
String
>
result
=
new
TestRestTemplate
().
exchange
(
"http://localhost:"
+
this
.
port
+
"/another/twolevel/local/1"
,
HttpMethod
.
GET
,
new
HttpEntity
<>((
Void
)
null
),
String
.
class
);
assertEquals
(
HttpStatus
.
OK
,
result
.
getStatusCode
());
assertEquals
(
"Gotten 1!"
,
result
.
getBody
());
}
@Test
public
void
ribbonRouteWithSpace
()
{
ResponseEntity
<
String
>
result
=
new
TestRestTemplate
().
exchange
(
"http://localhost:"
+
this
.
port
+
"/simple/spa ce"
,
HttpMethod
.
GET
,
new
HttpEntity
<>((
Void
)
null
),
String
.
class
);
assertEquals
(
HttpStatus
.
OK
,
result
.
getStatusCode
());
assertEquals
(
"Hello space"
,
result
.
getBody
());
}
@Test
public
void
simpleHostRouteWithSpace
()
{
routes
.
addRoute
(
"/self/**"
,
"http://localhost:"
+
this
.
port
);
this
.
endpoint
.
reset
();
ResponseEntity
<
String
>
result
=
new
TestRestTemplate
().
exchange
(
"http://localhost:"
+
this
.
port
+
"/self/spa ce"
,
HttpMethod
.
GET
,
new
HttpEntity
<>((
Void
)
null
),
String
.
class
);
assertEquals
(
HttpStatus
.
OK
,
result
.
getStatusCode
());
assertEquals
(
"Hello space"
,
result
.
getBody
());
}
@Test
public
void
simpleHostRouteWithOriginalQString
()
{
routes
.
addRoute
(
"/self/**"
,
"http://localhost:"
+
this
.
port
);
this
.
endpoint
.
reset
();
ResponseEntity
<
String
>
result
=
new
TestRestTemplate
().
exchange
(
"http://localhost:"
+
this
.
port
+
"/self/qstring?original=value1&original=value2"
,
HttpMethod
.
GET
,
new
HttpEntity
<>((
Void
)
null
),
String
.
class
);
assertEquals
(
HttpStatus
.
OK
,
result
.
getStatusCode
());
assertEquals
(
"Received {original=[value1, value2]}"
,
result
.
getBody
());
}
@Test
public
void
simpleHostRouteWithOverriddenQString
()
{
routes
.
addRoute
(
"/self/**"
,
"http://localhost:"
+
this
.
port
);
this
.
endpoint
.
reset
();
ResponseEntity
<
String
>
result
=
new
TestRestTemplate
().
exchange
(
"http://localhost:"
+
this
.
port
+
"/self/qstring?override=true&different=key"
,
HttpMethod
.
GET
,
new
HttpEntity
<>((
Void
)
null
),
String
.
class
);
assertEquals
(
HttpStatus
.
OK
,
result
.
getStatusCode
());
assertEquals
(
"Received {key=[overridden]}"
,
result
.
getBody
());
}
@Test
public
void
ribbonCommandFactoryOverridden
()
{
assertTrue
(
"ribbonCommandFactory not a MyRibbonCommandFactory"
,
ribbonCommandFactory
instanceof
HttpClientRibbonCommandFactory
);
}
}
// Don't use @SpringBootApplication because we don't want to component scan
@Configuration
@EnableAutoConfiguration
@RestController
@EnableZuulProxy
@RibbonClients
({
@RibbonClient
(
name
=
"simple"
,
configuration
=
SimpleRibbonClientConfiguration
.
class
),
@RibbonClient
(
name
=
"another"
,
configuration
=
AnotherRibbonClientConfiguration
.
class
)
})
class
SampleHttpClientZuulProxyApplication
{
public
static
void
main
(
final
String
[]
args
)
{
SpringApplication
.
run
(
SampleZuulProxyApplication
.
class
,
args
);
}
@RequestMapping
(
"/testing123"
)
public
String
testing123
()
{
throw
new
RuntimeException
(
"myerror"
);
}
@RequestMapping
(
"/local"
)
public
String
local
()
{
return
"Hello local"
;
}
@RequestMapping
(
value
=
"/local/{id}"
,
method
=
RequestMethod
.
DELETE
)
public
String
delete
(
@PathVariable
final
String
id
)
{
return
"Deleted "
+
id
+
"!"
;
}
@RequestMapping
(
value
=
"/local/{id}"
,
method
=
RequestMethod
.
PATCH
)
public
String
patch
(
@PathVariable
final
String
id
,
@RequestBody
final
String
body
)
{
return
"Patched "
+
id
+
"!"
;
}
@RequestMapping
(
value
=
"/local/{id}"
,
method
=
RequestMethod
.
GET
)
public
String
get
(
@PathVariable
final
String
id
)
{
return
"Gotten "
+
id
+
"!"
;
}
@RequestMapping
(
value
=
"/local/{id}"
,
method
=
RequestMethod
.
POST
)
public
String
post
(
@PathVariable
final
String
id
,
@RequestBody
final
String
body
)
{
return
"Posted "
+
id
+
"!"
;
}
@RequestMapping
(
value
=
"/qstring"
)
public
String
qstring
(
@RequestParam
final
MultiValueMap
<
String
,
String
>
params
)
{
return
"Received "
+
params
.
toString
();
}
@RequestMapping
(
"/"
)
public
String
home
()
{
return
"Hello world"
;
}
@RequestMapping
(
"/spa ce"
)
public
String
space
()
{
return
"Hello space"
;
}
@Bean
public
RibbonCommandFactory
ribbonCommandFactory
(
final
SpringClientFactory
clientFactory
)
{
return
new
HttpClientRibbonCommandFactory
(
clientFactory
);
}
@Bean
public
ZuulFilter
sampleFilter
()
{
return
new
ZuulFilter
()
{
@Override
public
String
filterType
()
{
return
"pre"
;
}
@Override
public
boolean
shouldFilter
()
{
return
true
;
}
@Override
public
Object
run
()
{
if
(
RequestContext
.
getCurrentContext
().
getRequest
().
getParameterMap
()
.
containsKey
(
"override"
))
{
Map
<
String
,
List
<
String
>>
overridden
=
new
HashMap
<>();
overridden
.
put
(
"key"
,
Arrays
.
asList
(
"overridden"
));
RequestContext
.
getCurrentContext
().
setRequestQueryParams
(
overridden
);
}
return
null
;
}
@Override
public
int
filterOrder
()
{
return
0
;
}
};
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment