Remove JSONP support
CORS is now widely supported and should be used instead for cross-domain requests. Issue: SPR-16914
This commit is contained in:
parent
19dc981685
commit
ac37b678a3
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2018 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.
|
||||
|
|
@ -95,21 +95,6 @@ public class MappingJackson2HttpMessageConverter extends AbstractJackson2HttpMes
|
|||
if (this.jsonPrefix != null) {
|
||||
generator.writeRaw(this.jsonPrefix);
|
||||
}
|
||||
String jsonpFunction =
|
||||
(object instanceof MappingJacksonValue ? ((MappingJacksonValue) object).getJsonpFunction() : null);
|
||||
if (jsonpFunction != null) {
|
||||
generator.writeRaw("/**/");
|
||||
generator.writeRaw(jsonpFunction + "(");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeSuffix(JsonGenerator generator, Object object) throws IOException {
|
||||
String jsonpFunction =
|
||||
(object instanceof MappingJacksonValue ? ((MappingJacksonValue) object).getJsonpFunction() : null);
|
||||
if (jsonpFunction != null) {
|
||||
generator.writeRaw(");");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2018 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.
|
||||
|
|
@ -45,9 +45,6 @@ public class MappingJacksonValue {
|
|||
@Nullable
|
||||
private FilterProvider filters;
|
||||
|
||||
@Nullable
|
||||
private String jsonpFunction;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new instance wrapping the given POJO to be serialized.
|
||||
|
|
@ -113,19 +110,4 @@ public class MappingJacksonValue {
|
|||
return this.filters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the JSONP function name.
|
||||
*/
|
||||
public void setJsonpFunction(@Nullable String functionName) {
|
||||
this.jsonpFunction = functionName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the configured JSONP function name.
|
||||
*/
|
||||
@Nullable
|
||||
public String getJsonpFunction() {
|
||||
return this.jsonpFunction;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -394,39 +394,6 @@ public class MappingJackson2HttpMessageConverterTests {
|
|||
assertThat(result, not(containsString("\"property2\":\"value\"")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void jsonp() throws Exception {
|
||||
MappingJacksonValue jacksonValue = new MappingJacksonValue("foo");
|
||||
jacksonValue.setSerializationView(MyJacksonView1.class);
|
||||
jacksonValue.setJsonpFunction("callback");
|
||||
|
||||
MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
|
||||
this.converter.writeInternal(jacksonValue, null, outputMessage);
|
||||
|
||||
assertEquals("/**/callback(\"foo\");", outputMessage.getBodyAsString(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void jsonpAndJsonView() throws Exception {
|
||||
MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
|
||||
JacksonViewBean bean = new JacksonViewBean();
|
||||
bean.setWithView1("with");
|
||||
bean.setWithView2("with");
|
||||
bean.setWithoutView("without");
|
||||
|
||||
MappingJacksonValue jacksonValue = new MappingJacksonValue(bean);
|
||||
jacksonValue.setSerializationView(MyJacksonView1.class);
|
||||
jacksonValue.setJsonpFunction("callback");
|
||||
this.converter.writeInternal(jacksonValue, null, outputMessage);
|
||||
|
||||
String result = outputMessage.getBodyAsString(StandardCharsets.UTF_8);
|
||||
assertThat(result, startsWith("/**/callback("));
|
||||
assertThat(result, endsWith(");"));
|
||||
assertThat(result, containsString("\"withView1\":\"with\""));
|
||||
assertThat(result, not(containsString("\"withView2\":\"with\"")));
|
||||
assertThat(result, not(containsString("\"withoutView\":\"without\"")));
|
||||
}
|
||||
|
||||
@Test // SPR-13318
|
||||
public void writeSubType() throws Exception {
|
||||
MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
|
||||
|
|
|
|||
|
|
@ -1,114 +0,0 @@
|
|||
/*
|
||||
* Copyright 2002-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.web.servlet.mvc.method.annotation;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.json.MappingJacksonValue;
|
||||
import org.springframework.http.server.ServerHttpRequest;
|
||||
import org.springframework.http.server.ServerHttpResponse;
|
||||
import org.springframework.http.server.ServletServerHttpRequest;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* A convenient base class for a {@code ResponseBodyAdvice} to instruct the
|
||||
* {@link org.springframework.http.converter.json.MappingJackson2HttpMessageConverter}
|
||||
* to serialize with JSONP formatting.
|
||||
*
|
||||
* <p>Sub-classes must specify the query parameter name(s) to check for the name
|
||||
* of the JSONP callback function.
|
||||
*
|
||||
* <p>Sub-classes are likely to be annotated with the {@code @ControllerAdvice}
|
||||
* annotation and auto-detected or otherwise must be registered directly with the
|
||||
* {@code RequestMappingHandlerAdapter} and {@code ExceptionHandlerExceptionResolver}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.1
|
||||
*/
|
||||
public abstract class AbstractJsonpResponseBodyAdvice extends AbstractMappingJacksonResponseBodyAdvice {
|
||||
|
||||
/**
|
||||
* Pattern for validating jsonp callback parameter values.
|
||||
*/
|
||||
private static final Pattern CALLBACK_PARAM_PATTERN = Pattern.compile("[0-9A-Za-z_\\.]*");
|
||||
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final String[] jsonpQueryParamNames;
|
||||
|
||||
|
||||
protected AbstractJsonpResponseBodyAdvice(String... queryParamNames) {
|
||||
Assert.isTrue(!ObjectUtils.isEmpty(queryParamNames), "At least one query param name is required");
|
||||
this.jsonpQueryParamNames = queryParamNames;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void beforeBodyWriteInternal(MappingJacksonValue bodyContainer, MediaType contentType,
|
||||
MethodParameter returnType, ServerHttpRequest request, ServerHttpResponse response) {
|
||||
|
||||
HttpServletRequest servletRequest = ((ServletServerHttpRequest) request).getServletRequest();
|
||||
|
||||
for (String name : this.jsonpQueryParamNames) {
|
||||
String value = servletRequest.getParameter(name);
|
||||
if (value != null) {
|
||||
if (!isValidJsonpQueryParam(value)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Ignoring invalid jsonp parameter value: " + value);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
MediaType contentTypeToUse = getContentType(contentType, request, response);
|
||||
response.getHeaders().setContentType(contentTypeToUse);
|
||||
bodyContainer.setJsonpFunction(value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the jsonp query parameter value. The default implementation
|
||||
* returns true if it consists of digits, letters, or "_" and ".".
|
||||
* Invalid parameter values are ignored.
|
||||
* @param value the query param value, never {@code null}
|
||||
* @since 4.1.8
|
||||
*/
|
||||
protected boolean isValidJsonpQueryParam(String value) {
|
||||
return CALLBACK_PARAM_PATTERN.matcher(value).matches();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the content type to set the response to.
|
||||
* This implementation always returns "application/javascript".
|
||||
* @param contentType the content type selected through content negotiation
|
||||
* @param request the current request
|
||||
* @param response the current response
|
||||
* @return the content type to set the response to
|
||||
*/
|
||||
protected MediaType getContentType(MediaType contentType, ServerHttpRequest request, ServerHttpResponse response) {
|
||||
return new MediaType("application", "javascript");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2018 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.
|
||||
|
|
@ -17,15 +17,10 @@
|
|||
package org.springframework.web.servlet.view.json;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonView;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
|
|
@ -33,10 +28,8 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
|||
import com.fasterxml.jackson.databind.ser.FilterProvider;
|
||||
|
||||
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
|
||||
import org.springframework.http.converter.json.MappingJacksonValue;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.servlet.View;
|
||||
|
||||
|
|
@ -67,17 +60,6 @@ public class MappingJackson2JsonView extends AbstractJackson2View {
|
|||
*/
|
||||
public static final String DEFAULT_CONTENT_TYPE = "application/json";
|
||||
|
||||
/**
|
||||
* Default content type for JSONP: "application/javascript".
|
||||
*/
|
||||
public static final String DEFAULT_JSONP_CONTENT_TYPE = "application/javascript";
|
||||
|
||||
/**
|
||||
* Pattern for validating jsonp callback parameter values.
|
||||
*/
|
||||
private static final Pattern CALLBACK_PARAM_PATTERN = Pattern.compile("[0-9A-Za-z_\\.]*");
|
||||
|
||||
|
||||
@Nullable
|
||||
private String jsonPrefix;
|
||||
|
||||
|
|
@ -86,9 +68,6 @@ public class MappingJackson2JsonView extends AbstractJackson2View {
|
|||
|
||||
private boolean extractValueFromSingleKeyModel = false;
|
||||
|
||||
@Nullable
|
||||
private Set<String> jsonpParameterNames = new LinkedHashSet<>(Arrays.asList("jsonp", "callback"));
|
||||
|
||||
|
||||
/**
|
||||
* Construct a new {@code MappingJackson2JsonView} using default configuration
|
||||
|
|
@ -166,49 +145,6 @@ public class MappingJackson2JsonView extends AbstractJackson2View {
|
|||
this.extractValueFromSingleKeyModel = extractValueFromSingleKeyModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set JSONP request parameter names. Each time a request has one of those
|
||||
* parameters, the resulting JSON will be wrapped into a function named as
|
||||
* specified by the JSONP request parameter value.
|
||||
* <p>The parameter names configured by default are "jsonp" and "callback".
|
||||
* @since 4.1
|
||||
* @see <a href="http://en.wikipedia.org/wiki/JSONP">JSONP Wikipedia article</a>
|
||||
*/
|
||||
public void setJsonpParameterNames(Set<String> jsonpParameterNames) {
|
||||
this.jsonpParameterNames = jsonpParameterNames;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String getJsonpParameterValue(HttpServletRequest request) {
|
||||
if (this.jsonpParameterNames != null) {
|
||||
for (String name : this.jsonpParameterNames) {
|
||||
String value = request.getParameter(name);
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
continue;
|
||||
}
|
||||
if (!isValidJsonpQueryParam(value)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Ignoring invalid jsonp parameter value: " + value);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the jsonp query parameter value. The default implementation
|
||||
* returns true if it consists of digits, letters, or "_" and ".".
|
||||
* Invalid parameter values are ignored.
|
||||
* @param value the query param value, never {@code null}
|
||||
* @since 4.1.8
|
||||
*/
|
||||
protected boolean isValidJsonpQueryParam(String value) {
|
||||
return CALLBACK_PARAM_PATTERN.matcher(value).matches();
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter out undesired attributes from the given model.
|
||||
* The return value can be either another {@link Map} or a single value object.
|
||||
|
|
@ -231,58 +167,11 @@ public class MappingJackson2JsonView extends AbstractJackson2View {
|
|||
return (this.extractValueFromSingleKeyModel && result.size() == 1 ? result.values().iterator().next() : result);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object filterAndWrapModel(Map<String, Object> model, HttpServletRequest request) {
|
||||
Object value = super.filterAndWrapModel(model, request);
|
||||
String jsonpParameterValue = getJsonpParameterValue(request);
|
||||
if (jsonpParameterValue != null) {
|
||||
if (value instanceof MappingJacksonValue) {
|
||||
((MappingJacksonValue) value).setJsonpFunction(jsonpParameterValue);
|
||||
}
|
||||
else {
|
||||
MappingJacksonValue container = new MappingJacksonValue(value);
|
||||
container.setJsonpFunction(jsonpParameterValue);
|
||||
value = container;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writePrefix(JsonGenerator generator, Object object) throws IOException {
|
||||
if (this.jsonPrefix != null) {
|
||||
generator.writeRaw(this.jsonPrefix);
|
||||
}
|
||||
|
||||
String jsonpFunction = null;
|
||||
if (object instanceof MappingJacksonValue) {
|
||||
jsonpFunction = ((MappingJacksonValue) object).getJsonpFunction();
|
||||
}
|
||||
if (jsonpFunction != null) {
|
||||
generator.writeRaw("/**/");
|
||||
generator.writeRaw(jsonpFunction + "(");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeSuffix(JsonGenerator generator, Object object) throws IOException {
|
||||
String jsonpFunction = null;
|
||||
if (object instanceof MappingJacksonValue) {
|
||||
jsonpFunction = ((MappingJacksonValue) object).getJsonpFunction();
|
||||
}
|
||||
if (jsonpFunction != null) {
|
||||
generator.writeRaw(");");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setResponseContentType(HttpServletRequest request, HttpServletResponse response) {
|
||||
if (getJsonpParameterValue(request) != null) {
|
||||
response.setContentType(DEFAULT_JSONP_CONTENT_TYPE);
|
||||
}
|
||||
else {
|
||||
super.setResponseContentType(request, response);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2018 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.
|
||||
|
|
@ -255,25 +255,6 @@ public class RequestMappingHandlerAdapterTests {
|
|||
assertEquals("{\"status\":400,\"message\":\"body\"}", this.response.getContentAsString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void jsonpResponseBodyAdvice() throws Exception {
|
||||
|
||||
List<HttpMessageConverter<?>> converters = new ArrayList<>();
|
||||
converters.add(new MappingJackson2HttpMessageConverter());
|
||||
this.handlerAdapter.setMessageConverters(converters);
|
||||
|
||||
this.webAppContext.registerSingleton("jsonpAdvice", JsonpAdvice.class);
|
||||
this.webAppContext.refresh();
|
||||
|
||||
testJsonp("callback", true);
|
||||
testJsonp("_callback", true);
|
||||
testJsonp("_Call.bAcK", true);
|
||||
testJsonp("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_.", true);
|
||||
|
||||
testJsonp("<script>", false);
|
||||
testJsonp("!foo!bar", false);
|
||||
}
|
||||
|
||||
private HandlerMethod handlerMethod(Object handler, String methodName, Class<?>... paramTypes) throws Exception {
|
||||
Method method = handler.getClass().getDeclaredMethod(methodName, paramTypes);
|
||||
return new InvocableHandlerMethod(handler, method);
|
||||
|
|
@ -399,12 +380,4 @@ public class RequestMappingHandlerAdapterTests {
|
|||
}
|
||||
}
|
||||
|
||||
@ControllerAdvice
|
||||
private static class JsonpAdvice extends AbstractJsonpResponseBodyAdvice {
|
||||
|
||||
public JsonpAdvice() {
|
||||
super("c");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2018 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.
|
||||
|
|
@ -322,17 +322,6 @@ public class MappingJackson2JsonViewTests {
|
|||
assertFalse(content.contains(FilterProvider.class.getName()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void renderWithJsonp() throws Exception {
|
||||
testJsonp("jsonp", "callback", true);
|
||||
testJsonp("jsonp", "_callback", true);
|
||||
testJsonp("jsonp", "_Call.bAcK", true);
|
||||
testJsonp("jsonp", "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_.", true);
|
||||
|
||||
testJsonp("jsonp", "<script>", false);
|
||||
testJsonp("jsonp", "!foo!bar", false);
|
||||
}
|
||||
|
||||
private void validateResult() throws Exception {
|
||||
String json = response.getContentAsString();
|
||||
DirectFieldAccessor viewAccessor = new DirectFieldAccessor(view);
|
||||
|
|
@ -346,26 +335,6 @@ public class MappingJackson2JsonViewTests {
|
|||
assertEquals("application/json", response.getContentType());
|
||||
}
|
||||
|
||||
private void testJsonp(String paramName, String paramValue, boolean validValue) throws Exception {
|
||||
Map<String, Object> model = new HashMap<>();
|
||||
model.put("foo", "bar");
|
||||
|
||||
this.request = new MockHttpServletRequest();
|
||||
this.request.addParameter("otherparam", "value");
|
||||
this.request.addParameter(paramName, paramValue);
|
||||
this.response = new MockHttpServletResponse();
|
||||
|
||||
this.view.render(model, this.request, this.response);
|
||||
|
||||
String content = this.response.getContentAsString();
|
||||
if (validValue) {
|
||||
assertEquals("/**/" + paramValue + "({\"foo\":\"bar\"});", content);
|
||||
}
|
||||
else {
|
||||
assertEquals("{\"foo\":\"bar\"}", content);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public interface MyJacksonView1 {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* Copyright 2002-2018 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.
|
||||
|
|
@ -48,7 +48,7 @@ public interface StompWebSocketEndpointRegistration {
|
|||
* {@code Origin} header value.
|
||||
*
|
||||
* <p>When SockJS is enabled and origins are restricted, transport types that do not
|
||||
* allow to check request origin (JSONP and Iframe based transports) are disabled.
|
||||
* allow to check request origin (Iframe based transports) are disabled.
|
||||
* As a consequence, IE 6 to 9 are not supported when origins are restricted.
|
||||
*
|
||||
* <p>Each provided allowed origin must start by "http://", "https://" or be "*"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* Copyright 2002-2018 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.
|
||||
|
|
@ -50,7 +50,7 @@ public interface WebSocketHandlerRegistration {
|
|||
* {@code Origin} header value.
|
||||
*
|
||||
* <p>When SockJS is enabled and origins are restricted, transport types that do not
|
||||
* allow to check request origin (JSONP and Iframe based transports) are disabled.
|
||||
* allow to check request origin (Iframe based transports) are disabled.
|
||||
* As a consequence, IE 6 to 9 are not supported when origins are restricted.
|
||||
*
|
||||
* <p>Each provided allowed origin must start by "http://", "https://" or be "*"
|
||||
|
|
|
|||
|
|
@ -298,9 +298,9 @@ public abstract class AbstractSockJsService implements SockJsService, CorsConfig
|
|||
* designed for browsers. There is nothing preventing other types of client
|
||||
* to modify the {@code Origin} header value.
|
||||
* <p>When SockJS is enabled and origins are restricted, transport types
|
||||
* that do not allow to check request origin (JSONP and Iframe based
|
||||
* transports) are disabled. As a consequence, IE 6 to 9 are not supported
|
||||
* when origins are restricted.
|
||||
* that do not allow to check request origin (Iframe based transports)
|
||||
* are disabled. As a consequence, IE 6 to 9 are not supported when origins
|
||||
* are restricted.
|
||||
* <p>Each provided allowed origin must have a scheme, and optionally a port
|
||||
* (e.g. "http://example.org", "http://example.org:9090"). An allowed origin
|
||||
* string may also be "*" in which case all origins are allowed.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2018 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.
|
||||
|
|
@ -40,10 +40,6 @@ public enum TransportType {
|
|||
|
||||
XHR_SEND("xhr_send", HttpMethod.POST, "cors", "jsessionid", "no_cache"),
|
||||
|
||||
JSONP("jsonp", HttpMethod.GET, "jsessionid", "no_cache"),
|
||||
|
||||
JSONP_SEND("jsonp_send", HttpMethod.POST, "jsessionid", "no_cache"),
|
||||
|
||||
XHR_STREAMING("xhr_streaming", HttpMethod.POST, "cors", "jsessionid", "no_cache"),
|
||||
|
||||
EVENT_SOURCE("eventsource", HttpMethod.GET, "origin", "jsessionid", "no_cache"),
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2018 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.
|
||||
|
|
@ -46,7 +46,7 @@ public abstract class AbstractHttpSendingTransportHandler extends AbstractTransp
|
|||
implements SockJsSessionFactory {
|
||||
|
||||
/**
|
||||
* Pattern for validating jsonp callback parameter values.
|
||||
* Pattern for validating callback parameter values.
|
||||
*/
|
||||
private static final Pattern CALLBACK_PARAM_PATTERN = Pattern.compile("[0-9A-Za-z_\\.]*");
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2018 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.
|
||||
|
|
@ -84,8 +84,6 @@ public class DefaultSockJsService extends TransportHandlingSockJsService impleme
|
|||
result.add(new XhrPollingTransportHandler());
|
||||
result.add(new XhrReceivingTransportHandler());
|
||||
result.add(new XhrStreamingTransportHandler());
|
||||
result.add(new JsonpPollingTransportHandler());
|
||||
result.add(new JsonpReceivingTransportHandler());
|
||||
result.add(new EventSourceTransportHandler());
|
||||
result.add(new HtmlFileTransportHandler());
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -1,102 +0,0 @@
|
|||
/*
|
||||
* Copyright 2002-2016 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.web.socket.sockjs.transport.handler;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.server.ServerHttpRequest;
|
||||
import org.springframework.http.server.ServerHttpResponse;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.socket.CloseStatus;
|
||||
import org.springframework.web.socket.WebSocketHandler;
|
||||
import org.springframework.web.socket.sockjs.SockJsException;
|
||||
import org.springframework.web.socket.sockjs.SockJsTransportFailureException;
|
||||
import org.springframework.web.socket.sockjs.frame.DefaultSockJsFrameFormat;
|
||||
import org.springframework.web.socket.sockjs.frame.SockJsFrameFormat;
|
||||
import org.springframework.web.socket.sockjs.transport.SockJsSession;
|
||||
import org.springframework.web.socket.sockjs.transport.TransportType;
|
||||
import org.springframework.web.socket.sockjs.transport.session.AbstractHttpSockJsSession;
|
||||
import org.springframework.web.socket.sockjs.transport.session.PollingSockJsSession;
|
||||
import org.springframework.web.util.JavaScriptUtils;
|
||||
|
||||
/**
|
||||
* A TransportHandler that sends messages via JSONP polling.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.0
|
||||
*/
|
||||
public class JsonpPollingTransportHandler extends AbstractHttpSendingTransportHandler {
|
||||
|
||||
@Override
|
||||
public TransportType getTransportType() {
|
||||
return TransportType.JSONP;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MediaType getContentType() {
|
||||
return new MediaType("application", "javascript", StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkSessionType(SockJsSession session) {
|
||||
return session instanceof PollingSockJsSession;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PollingSockJsSession createSession(
|
||||
String sessionId, WebSocketHandler handler, Map<String, Object> attributes) {
|
||||
|
||||
return new PollingSockJsSession(sessionId, getServiceConfig(), handler, attributes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleRequestInternal(ServerHttpRequest request, ServerHttpResponse response,
|
||||
AbstractHttpSockJsSession sockJsSession) throws SockJsException {
|
||||
|
||||
try {
|
||||
String callback = getCallbackParam(request);
|
||||
if (!StringUtils.hasText(callback)) {
|
||||
response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
response.getBody().write("\"callback\" parameter required".getBytes(StandardCharsets.UTF_8));
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
sockJsSession.tryCloseWithSockJsTransportError(ex, CloseStatus.SERVER_ERROR);
|
||||
throw new SockJsTransportFailureException("Failed to send error", sockJsSession.getId(), ex);
|
||||
}
|
||||
|
||||
super.handleRequestInternal(request, response, sockJsSession);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SockJsFrameFormat getFrameFormat(ServerHttpRequest request) {
|
||||
// We already validated the parameter above...
|
||||
String callback = getCallbackParam(request);
|
||||
|
||||
return new DefaultSockJsFrameFormat("/**/" + callback + "(\"%s\");\r\n") {
|
||||
@Override
|
||||
protected String preProcessContent(String content) {
|
||||
return JavaScriptUtils.javaScriptEscape(content);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,85 +0,0 @@
|
|||
/*
|
||||
* Copyright 2002-2016 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.web.socket.sockjs.transport.handler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.FormHttpMessageConverter;
|
||||
import org.springframework.http.server.ServerHttpRequest;
|
||||
import org.springframework.http.server.ServerHttpResponse;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.socket.WebSocketHandler;
|
||||
import org.springframework.web.socket.sockjs.SockJsException;
|
||||
import org.springframework.web.socket.sockjs.frame.SockJsMessageCodec;
|
||||
import org.springframework.web.socket.sockjs.transport.TransportHandler;
|
||||
import org.springframework.web.socket.sockjs.transport.TransportType;
|
||||
import org.springframework.web.socket.sockjs.transport.session.AbstractHttpSockJsSession;
|
||||
|
||||
/**
|
||||
* A {@link TransportHandler} that receives messages over HTTP.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class JsonpReceivingTransportHandler extends AbstractHttpReceivingTransportHandler {
|
||||
|
||||
private final FormHttpMessageConverter formConverter = new FormHttpMessageConverter();
|
||||
|
||||
|
||||
@Override
|
||||
public TransportType getTransportType() {
|
||||
return TransportType.JSONP_SEND;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleRequestInternal(ServerHttpRequest request, ServerHttpResponse response,
|
||||
WebSocketHandler wsHandler, AbstractHttpSockJsSession sockJsSession) throws SockJsException {
|
||||
|
||||
super.handleRequestInternal(request, response, wsHandler, sockJsSession);
|
||||
try {
|
||||
response.getBody().write("ok".getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new SockJsException("Failed to write to the response body", sockJsSession.getId(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
protected String[] readMessages(ServerHttpRequest request) throws IOException {
|
||||
SockJsMessageCodec messageCodec = getServiceConfig().getMessageCodec();
|
||||
MediaType contentType = request.getHeaders().getContentType();
|
||||
if (contentType != null && MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(contentType)) {
|
||||
MultiValueMap<String, String> map = this.formConverter.read(null, request);
|
||||
String d = map.getFirst("d");
|
||||
return (StringUtils.hasText(d) ? messageCodec.decode(d) : null);
|
||||
}
|
||||
else {
|
||||
return messageCodec.decodeInputStream(request.getBody());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HttpStatus getResponseStatus() {
|
||||
return HttpStatus.OK;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* Copyright 2002-2018 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.
|
||||
|
|
@ -55,8 +55,6 @@ import org.springframework.web.socket.sockjs.transport.TransportType;
|
|||
import org.springframework.web.socket.sockjs.transport.handler.DefaultSockJsService;
|
||||
import org.springframework.web.socket.sockjs.transport.handler.EventSourceTransportHandler;
|
||||
import org.springframework.web.socket.sockjs.transport.handler.HtmlFileTransportHandler;
|
||||
import org.springframework.web.socket.sockjs.transport.handler.JsonpPollingTransportHandler;
|
||||
import org.springframework.web.socket.sockjs.transport.handler.JsonpReceivingTransportHandler;
|
||||
import org.springframework.web.socket.sockjs.transport.handler.WebSocketTransportHandler;
|
||||
import org.springframework.web.socket.sockjs.transport.handler.XhrPollingTransportHandler;
|
||||
import org.springframework.web.socket.sockjs.transport.handler.XhrReceivingTransportHandler;
|
||||
|
|
@ -178,8 +176,6 @@ public class HandlersBeanDefinitionParserTests {
|
|||
containsInAnyOrder(
|
||||
instanceOf(XhrPollingTransportHandler.class),
|
||||
instanceOf(XhrReceivingTransportHandler.class),
|
||||
instanceOf(JsonpPollingTransportHandler.class),
|
||||
instanceOf(JsonpReceivingTransportHandler.class),
|
||||
instanceOf(XhrStreamingTransportHandler.class),
|
||||
instanceOf(EventSourceTransportHandler.class),
|
||||
instanceOf(HtmlFileTransportHandler.class),
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2018 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.
|
||||
|
|
@ -30,8 +30,6 @@ public class TransportTypeTests {
|
|||
assertEquals(TransportType.WEBSOCKET, TransportType.fromValue("websocket"));
|
||||
assertEquals(TransportType.XHR, TransportType.fromValue("xhr"));
|
||||
assertEquals(TransportType.XHR_SEND, TransportType.fromValue("xhr_send"));
|
||||
assertEquals(TransportType.JSONP, TransportType.fromValue("jsonp"));
|
||||
assertEquals(TransportType.JSONP_SEND, TransportType.fromValue("jsonp_send"));
|
||||
assertEquals(TransportType.XHR_STREAMING, TransportType.fromValue("xhr_streaming"));
|
||||
assertEquals(TransportType.EVENT_SOURCE, TransportType.fromValue("eventsource"));
|
||||
assertEquals(TransportType.HTML_FILE, TransportType.fromValue("htmlfile"));
|
||||
|
|
|
|||
|
|
@ -63,10 +63,6 @@ public class DefaultSockJsServiceTests extends AbstractHttpRequestTests {
|
|||
|
||||
@Mock private TransportHandler xhrSendHandler;
|
||||
|
||||
@Mock private SessionCreatingTransportHandler jsonpHandler;
|
||||
|
||||
@Mock private TransportHandler jsonpSendHandler;
|
||||
|
||||
@Mock private HandshakeTransportHandler wsTransportHandler;
|
||||
|
||||
@Mock private WebSocketHandler wsHandler;
|
||||
|
|
@ -89,9 +85,6 @@ public class DefaultSockJsServiceTests extends AbstractHttpRequestTests {
|
|||
given(this.xhrHandler.getTransportType()).willReturn(TransportType.XHR);
|
||||
given(this.xhrHandler.createSession(sessionId, this.wsHandler, attributes)).willReturn(this.session);
|
||||
given(this.xhrSendHandler.getTransportType()).willReturn(TransportType.XHR_SEND);
|
||||
given(this.jsonpHandler.getTransportType()).willReturn(TransportType.JSONP);
|
||||
given(this.jsonpHandler.createSession(sessionId, this.wsHandler, attributes)).willReturn(this.session);
|
||||
given(this.jsonpSendHandler.getTransportType()).willReturn(TransportType.JSONP_SEND);
|
||||
given(this.wsTransportHandler.getTransportType()).willReturn(TransportType.WEBSOCKET);
|
||||
|
||||
this.service = new TransportHandlingSockJsService(this.taskScheduler, this.xhrHandler, this.xhrSendHandler);
|
||||
|
|
@ -103,13 +96,11 @@ public class DefaultSockJsServiceTests extends AbstractHttpRequestTests {
|
|||
DefaultSockJsService service = new DefaultSockJsService(mock(TaskScheduler.class));
|
||||
Map<TransportType, TransportHandler> handlers = service.getTransportHandlers();
|
||||
|
||||
assertEquals(8, handlers.size());
|
||||
assertEquals(6, handlers.size());
|
||||
assertNotNull(handlers.get(TransportType.WEBSOCKET));
|
||||
assertNotNull(handlers.get(TransportType.XHR));
|
||||
assertNotNull(handlers.get(TransportType.XHR_SEND));
|
||||
assertNotNull(handlers.get(TransportType.XHR_STREAMING));
|
||||
assertNotNull(handlers.get(TransportType.JSONP));
|
||||
assertNotNull(handlers.get(TransportType.JSONP_SEND));
|
||||
assertNotNull(handlers.get(TransportType.HTML_FILE));
|
||||
assertNotNull(handlers.get(TransportType.EVENT_SOURCE));
|
||||
}
|
||||
|
|
@ -121,7 +112,7 @@ public class DefaultSockJsServiceTests extends AbstractHttpRequestTests {
|
|||
DefaultSockJsService service = new DefaultSockJsService(mock(TaskScheduler.class), xhrHandler);
|
||||
Map<TransportType, TransportHandler> handlers = service.getTransportHandlers();
|
||||
|
||||
assertEquals(8, handlers.size());
|
||||
assertEquals(6, handlers.size());
|
||||
assertSame(xhrHandler, handlers.get(xhrHandler.getTransportType()));
|
||||
}
|
||||
|
||||
|
|
@ -269,28 +260,6 @@ public class DefaultSockJsServiceTests extends AbstractHttpRequestTests {
|
|||
verifyNoMoreInteractions(this.xhrSendHandler);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleTransportRequestJsonp() throws Exception {
|
||||
TransportHandlingSockJsService jsonpService = new TransportHandlingSockJsService(
|
||||
this.taskScheduler, this.jsonpHandler, this.jsonpSendHandler);
|
||||
String sockJsPath = sessionUrlPrefix+ "jsonp";
|
||||
setRequest("GET", sockJsPrefix + sockJsPath);
|
||||
jsonpService.handleRequest(this.request, this.response, sockJsPath, this.wsHandler);
|
||||
assertEquals(404, this.servletResponse.getStatus());
|
||||
|
||||
resetRequestAndResponse();
|
||||
jsonpService.setAllowedOrigins(Collections.singletonList("http://mydomain1.com"));
|
||||
setRequest("GET", sockJsPrefix + sockJsPath);
|
||||
jsonpService.handleRequest(this.request, this.response, sockJsPath, this.wsHandler);
|
||||
assertEquals(404, this.servletResponse.getStatus());
|
||||
|
||||
resetRequestAndResponse();
|
||||
jsonpService.setAllowedOrigins(Collections.singletonList("*"));
|
||||
setRequest("GET", sockJsPrefix + sockJsPath);
|
||||
jsonpService.handleRequest(this.request, this.response, sockJsPath, this.wsHandler);
|
||||
assertNotEquals(404, this.servletResponse.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleTransportRequestWebsocket() throws Exception {
|
||||
TransportHandlingSockJsService wsService = new TransportHandlingSockJsService(
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* Copyright 2002-2018 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.
|
||||
|
|
@ -32,7 +32,7 @@ import static org.mockito.BDDMockito.*;
|
|||
|
||||
/**
|
||||
* Test fixture for {@link AbstractHttpReceivingTransportHandler} and sub-classes
|
||||
* {@link XhrReceivingTransportHandler} and {@link JsonpReceivingTransportHandler}.
|
||||
* {@link XhrReceivingTransportHandler}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
|
|
@ -46,35 +46,6 @@ public class HttpReceivingTransportHandlerTests extends AbstractHttpRequestTests
|
|||
assertEquals(204, this.servletResponse.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readMessagesJsonp() throws Exception {
|
||||
this.servletRequest.setContent("[\"x\"]".getBytes("UTF-8"));
|
||||
handleRequest(new JsonpReceivingTransportHandler());
|
||||
|
||||
assertEquals(200, this.servletResponse.getStatus());
|
||||
assertEquals("ok", this.servletResponse.getContentAsString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readMessagesJsonpFormEncoded() throws Exception {
|
||||
this.servletRequest.setContent("d=[\"x\"]".getBytes("UTF-8"));
|
||||
this.servletRequest.setContentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE);
|
||||
handleRequest(new JsonpReceivingTransportHandler());
|
||||
|
||||
assertEquals(200, this.servletResponse.getStatus());
|
||||
assertEquals("ok", this.servletResponse.getContentAsString());
|
||||
}
|
||||
|
||||
@Test // SPR-10621
|
||||
public void readMessagesJsonpFormEncodedWithEncoding() throws Exception {
|
||||
this.servletRequest.setContent("d=[\"x\"]".getBytes("UTF-8"));
|
||||
this.servletRequest.setContentType("application/x-www-form-urlencoded;charset=UTF-8");
|
||||
handleRequest(new JsonpReceivingTransportHandler());
|
||||
|
||||
assertEquals(200, this.servletResponse.getStatus());
|
||||
assertEquals("ok", this.servletResponse.getContentAsString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readMessagesBadContent() throws Exception {
|
||||
this.servletRequest.setContent("".getBytes("UTF-8"));
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2018 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.
|
||||
|
|
@ -24,14 +24,11 @@ import org.junit.Test;
|
|||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.web.socket.AbstractHttpRequestTests;
|
||||
import org.springframework.web.socket.WebSocketHandler;
|
||||
import org.springframework.web.socket.sockjs.SockJsTransportFailureException;
|
||||
import org.springframework.web.socket.sockjs.frame.SockJsFrame;
|
||||
import org.springframework.web.socket.sockjs.frame.SockJsFrameFormat;
|
||||
import org.springframework.web.socket.sockjs.transport.session.AbstractSockJsSession;
|
||||
import org.springframework.web.socket.sockjs.transport.session.PollingSockJsSession;
|
||||
import org.springframework.web.socket.sockjs.transport.session.StreamingSockJsSession;
|
||||
import org.springframework.web.socket.sockjs.transport.session.StubSockJsServiceConfig;
|
||||
import org.springframework.web.util.UriUtils;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
|
@ -91,50 +88,6 @@ public class HttpSendingTransportHandlerTests extends AbstractHttpRequestTests
|
|||
assertEquals("c[2010,\"Another connection still open\"]\n", this.servletResponse.getContentAsString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void jsonpTransport() throws Exception {
|
||||
testJsonpTransport(null, false);
|
||||
testJsonpTransport("_jp123xYz", true);
|
||||
testJsonpTransport("A..B__3..4", true);
|
||||
testJsonpTransport("!jp!abc", false);
|
||||
testJsonpTransport("<script>", false);
|
||||
testJsonpTransport("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_.", true);
|
||||
}
|
||||
|
||||
private void testJsonpTransport(String callbackValue, boolean expectSuccess) throws Exception {
|
||||
JsonpPollingTransportHandler transportHandler = new JsonpPollingTransportHandler();
|
||||
transportHandler.initialize(this.sockJsConfig);
|
||||
PollingSockJsSession session = transportHandler.createSession("1", this.webSocketHandler, null);
|
||||
|
||||
resetRequestAndResponse();
|
||||
setRequest("POST", "/");
|
||||
|
||||
if (callbackValue != null) {
|
||||
// need to encode the query parameter
|
||||
this.servletRequest.setQueryString("c=" + UriUtils.encodeQueryParam(callbackValue, "UTF-8"));
|
||||
this.servletRequest.addParameter("c", callbackValue);
|
||||
}
|
||||
|
||||
try {
|
||||
transportHandler.handleRequest(this.request, this.response, this.webSocketHandler, session);
|
||||
}
|
||||
catch (SockJsTransportFailureException ex) {
|
||||
if (expectSuccess) {
|
||||
throw new AssertionError("Unexpected transport failure", ex);
|
||||
}
|
||||
}
|
||||
|
||||
if (expectSuccess) {
|
||||
assertEquals(200, this.servletResponse.getStatus());
|
||||
assertEquals("application/javascript;charset=UTF-8", this.response.getHeaders().getContentType().toString());
|
||||
verify(this.webSocketHandler).afterConnectionEstablished(session);
|
||||
}
|
||||
else {
|
||||
assertEquals(500, this.servletResponse.getStatus());
|
||||
verifyNoMoreInteractions(this.webSocketHandler);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleRequestXhrStreaming() throws Exception {
|
||||
XhrStreamingTransportHandler transportHandler = new XhrStreamingTransportHandler();
|
||||
|
|
@ -205,10 +158,6 @@ public class HttpSendingTransportHandlerTests extends AbstractHttpRequestTests
|
|||
format = new EventSourceTransportHandler().getFrameFormat(this.request);
|
||||
formatted = format.format(frame);
|
||||
assertEquals("data: " + frame.getContent() + "\r\n\r\n", formatted);
|
||||
|
||||
format = new JsonpPollingTransportHandler().getFrameFormat(this.request);
|
||||
formatted = format.format(frame);
|
||||
assertEquals("/**/callback(\"" + frame.getContent() + "\");\r\n", formatted);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2030,11 +2030,6 @@ annotations. When further control is needed, a custom `ObjectMapper` can be inje
|
|||
through the `ObjectMapper` property for cases where custom JSON
|
||||
serializers/deserializers need to be provided for specific types.
|
||||
|
||||
http://en.wikipedia.org/wiki/JSONP[JSONP] is supported and automatically enabled when
|
||||
the request has a query parameter named `jsonp` or `callback`. The JSONP query parameter
|
||||
name(s) could be customized through the `jsonpParameterNames` property.
|
||||
|
||||
|
||||
|
||||
[[mvc-view-xml-mapping]]
|
||||
=== XML
|
||||
|
|
|
|||
|
|
@ -2645,30 +2645,6 @@ to the model:
|
|||
}
|
||||
----
|
||||
|
||||
[[mvc-ann-jsonp]]
|
||||
===== Jackson JSONP
|
||||
|
||||
In order to enable http://en.wikipedia.org/wiki/JSONP[JSONP] support for `@ResponseBody`
|
||||
and `ResponseEntity` methods, declare an `@ControllerAdvice` bean that extends
|
||||
`AbstractJsonpResponseBodyAdvice` as shown below where the constructor argument indicates
|
||||
the JSONP query parameter name(s):
|
||||
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
@ControllerAdvice
|
||||
public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice {
|
||||
|
||||
public JsonpAdvice() {
|
||||
super("callback");
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
For controllers relying on view resolution, JSONP is automatically enabled when the
|
||||
request has a query parameter named `jsonp` or `callback`. Those names can be
|
||||
customized through `jsonpParameterNames` property.
|
||||
|
||||
|
||||
|
||||
[[mvc-ann-modelattrib-methods]]
|
||||
|
|
|
|||
|
|
@ -401,8 +401,8 @@ The 3 possible behaviors are:
|
|||
transport is disabled since it does not allow to check the origin of a request.
|
||||
As a consequence, IE6 and IE7 are not supported when this mode is enabled.
|
||||
* Allow a specified list of origins: each provided _allowed origin_ must start with `http://`
|
||||
or `https://`. In this mode, when SockJS is enabled, both IFrame and JSONP based
|
||||
transports are disabled. As a consequence, IE6 through IE9 are not supported when this
|
||||
or `https://`. In this mode, when SockJS is enabled, IFrame transport is disabled.
|
||||
As a consequence, IE6 through IE9 are not supported when this
|
||||
mode is enabled.
|
||||
* Allow all origins: to enable this mode, you should provide `{asterisk}` as the allowed origin
|
||||
value. In this mode, all transports are available.
|
||||
|
|
|
|||
Loading…
Reference in New Issue