Я работаю над приложением Spring, которое обслуживает конечные точки REST.Одна из конечных точек, по сути, действует как прокси-сервер между клиентом HTML и сторонним поставщиком облачного хранилища.Эта конечная точка извлекает файлы из провайдера хранилища и передает их обратно клиенту.Что-то вроде следующего (обратите внимание, что существует синхронная и асинхронная версия одной и той же конечной точки):
@Controller
public class CloudStorageController {
...
@RequestMapping(value = "/fetch-image/{id}", method = RequestMethod.GET, produces = MediaType.IMAGE_JPEG_VALUE)
public ResponseEntity<byte[]> fetchImageSynchronous(@PathVariable final Long id) {
final byte[] imageFileContents = this.fetchImage(id);
return ResponseEntity.ok().body(imageFileContents);
}
@RequestMapping(value = "/fetch-image-async/{id}", method = RequestMethod.GET, produces = MediaType.IMAGE_JPEG_VALUE)
public Callable<ResponseEntity<byte[]>> fetchImageAsynchronous(@PathVariable final Long id) {
return () -> {
final byte[] imageFileContents = this.fetchImage(id);
return ResponseEntity.ok().body(imageFileContents);
};
}
private byte[] fetchImage(final long id) {
// fetch the file from cloud storage and return as byte array
...
}
...
}
Из-за характера клиентского приложения (HTML5 + ajax) и способа использования этой конечной точки, аутентификация пользователяподается на эту конечную точку иначе, чем на другие конечные точки.Для этого был разработан HandlerInterceptor для проверки подлинности для этой конечной точки:
@Component("cloudStorageAuthenticationInterceptor")
public class CloudStorageAuthenticationInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) {
// examine the request for the authentication information and verify it
final Authentication authenticated = ...
if (authenticated == null) {
try {
pResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
} catch (IOException e) {
throw new RuntimeException(e);
}
return false;
}
else {
try {
request.login(authenticated.getName(), (String) authenticated.getCredentials());
} catch (final ServletException e) {
throw new BadCredentialsException("Bad credentials");
}
}
return true;
}
}
Перехватчик регистрируется следующим образом:
@Configuration
@EnableWebMvc
public class ApiConfig extends WebMvcConfigurerAdapter {
@Autowired
@Qualifier("cloudStorageAuthenticationInterceptor")
private HandlerInterceptor cloudStorageAuthenticationInterceptor;
@Override
public void addInterceptors(final InterceptorRegistry registry) {
registry.addInterceptor(this.cloudStorageAuthenticationInterceptor)
.addPathPatterns(
"/fetch-image/**",
"/fetch-image-async/**"
);
}
@Override
public void configureAsyncSupport(final AsyncSupportConfigurer configurer) {
final ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(this.asyncThreadPoolCoreSize);
executor.setMaxPoolSize(this.asyncThreadPoolMaxSize);
executor.setQueueCapacity(this.asyncThreadPoolQueueCapacity);
executor.setThreadNamePrefix(this.asyncThreadPoolPrefix);
executor.initialize();
configurer.setTaskExecutor(executor);
super.configureAsyncSupport(configurer);
}
}
В идеале выборка изображения должна выполняться асинхронно(используя конечную точку / fetch-image-asyc / {id}), поскольку она должна вызывать стороннюю веб-службу, которая может иметь некоторую задержку.
Синхронная конечная точка (/ fetch-image / {id}) работает правильно для всех браузеров.Однако, если используется асинхронная конечная точка (/ fetch-image-async / {id}), Chrome и Firefox работают как положено.
Однако, если клиент является Microsoft IE или Microsoft Edge, нам кажется странным поведение,Конечная точка вызывается правильно, и ответ успешно отправлен (по крайней мере, с точки зрения сервера).Однако, похоже, что браузер ждет чего-то дополнительного.В окне IE / Edge DevTools сетевой запрос изображения отображается как ожидающий в течение 30 секунд, затем кажется, что время ожидания истекло, обновления успешно выполнены, и изображение успешно отображается.Также кажется, что соединение с сервером все еще открыто, поскольку ресурсы на стороне сервера, такие как соединения с базой данных, не освобождаются.В других браузерах асинхронный ответ принимается и обрабатывается в секунду или меньше.
Если я удаляю HandlerInterceptor и просто подключаю некоторые учетные данные для отладки, поведение исчезает.Так что, похоже, это как-то связано с взаимодействием между HandlerInterceptor и методом асинхронного контроллера и демонстрируется только для некоторых клиентов.
У кого-нибудь есть предложение о том, почему семантика IE / Edge вызывает такое поведение?