I using httpcomponenets nio server to handle post request file upload.
Below is the sample code. I have the complete data in data byte array including params, uploaded file etc. separated by boundary. Is there a parser utility to parse data and get the parameters? Something like request.getParameter("param1"), request.getFile() etc.
public static void main(String[] args) throws Exception {
int port = 8280;
// Create HTTP protocol processing chain
HttpProcessor httpproc = HttpProcessorBuilder.create()
.add(new ResponseDate())
.add(new ResponseServer("Test/1.1"))
.add(new ResponseContent())
.add(new ResponseConnControl()).build();
// Create request handler registry
UriHttpAsyncRequestHandlerMapper reqistry = new UriHttpAsyncRequestHandlerMapper();
// Register the default handler for all URIs
reqistry.register("/test*", new RequestHandler());
// Create server-side HTTP protocol handler
HttpAsyncService protocolHandler = new HttpAsyncService(httpproc, reqistry) {
#Override
public void connected(final NHttpServerConnection conn) {
System.out.println(conn + ": connection open");
super.connected(conn);
}
#Override
public void closed(final NHttpServerConnection conn) {
System.out.println(conn + ": connection closed");
super.closed(conn);
}
};
// Create HTTP connection factory
NHttpConnectionFactory<DefaultNHttpServerConnection> connFactory;
connFactory = new DefaultNHttpServerConnectionFactory(
ConnectionConfig.DEFAULT);
// Create server-side I/O event dispatch
IOEventDispatch ioEventDispatch = new DefaultHttpServerIODispatch(protocolHandler, connFactory);
// Set I/O reactor defaults
IOReactorConfig config = IOReactorConfig.custom()
.setIoThreadCount(1)
.setSoTimeout(3000)
.setConnectTimeout(3000)
.build();
// Create server-side I/O reactor
ListeningIOReactor ioReactor = new DefaultListeningIOReactor(config);
try {
// Listen of the given port
ioReactor.listen(new InetSocketAddress(port));
// Ready to go!
ioReactor.execute(ioEventDispatch);
} catch (InterruptedIOException ex) {
System.err.println("Interrupted");
} catch (IOException e) {
System.err.println("I/O error: " + e.getMessage());
}
System.out.println("Shutdown");
}
public static class RequestHandler implements HttpAsyncRequestHandler<HttpRequest> {
public void handleInternal(HttpRequest httpRequest, HttpResponse httpResponse, HttpContext httpContext) throws HttpException, IOException {
HttpEntity entity = null;
if (httpRequest instanceof HttpEntityEnclosingRequest)
entity = ((HttpEntityEnclosingRequest)httpRequest).getEntity();
byte[] data;
if (entity == null) {
data = new byte [0];
} else {
data = EntityUtils.toByteArray(entity);
}
System.out.println(new String(data));
httpResponse.setEntity(new StringEntity("success response"));
}
#Override public HttpAsyncRequestConsumer<HttpRequest> processRequest(HttpRequest request, HttpContext context) throws HttpException, IOException {
return new BasicAsyncRequestConsumer();
}
#Override
public void handle(HttpRequest request, HttpAsyncExchange httpExchange, HttpContext context) throws HttpException, IOException {
HttpResponse response = httpExchange.getResponse();
handleInternal(request, response, context);
httpExchange.submitResponse(new BasicAsyncResponseProducer(response));
}
}
MIME content parsing (as well handling of content of any type) is out of scope for Apache HttpComponents. Please consider using Apache Mime4J.
Related
Two Springboot microservices need to interact with each other to achieve mass emailing feature. Microservice 1: CollectorService collects information about clients and
Microservice 2: NotificationService to send documents via SMTP service. NotificationService is unable to handle the load that CollectorService produces. Now I want to understand how NotificationService can signal CollectorService to slow down. My code looks like below currently.
CollectorSerivce calling NotificationService
final WebClient client = WebClient.builder().baseUrl(BASE_URL.get()).defaultHeaders(httpHeaders -> {
httpHeaders.set("requestId", requestId);
}).build();
Mono<List> responseFlux = client.post().uri(EXTERNAL_API_PATH.get() + "/withattFluxBackPressure")
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(message)).retrieve().bodyToMono(List.class);
NotificationService processing requests
#PostMapping(value = "/externalNotification/withattFluxBackPressure")
public List<NotificationResponse> sendMailMessageWAttFlux(#RequestBody List<EmailTemplate> mailMessages) throws Exception{
log.info("Processing email sending request async...");
// byte[] byteArrayResource = get file content from S3
List<NotificationResponse> response = new ArrayList<>();
Flux<NotificationResponse> responseFlux = Flux.fromIterable(mailMessages).flatMap(messages -> {
try {
return Flux.just(notificationService.sendMailWAtt(messages, byteArrayResource).map(NotificationResponse::error).orElse(NotificationResponse.success()));
} catch (IOException e) {
log.error("Exception", e);
return Flux.just(new NotificationResponse().error(e.getMessage()));
}
});
responseFlux.subscribe(new BaseSubscriber<NotificationResponse>() {
#Override
protected void hookOnSubscribe(Subscription subscription) {
log.info("called hookOnSubscribe......");
subscription.request(1);
}
#Override
protected void hookOnNext(NotificationResponse value) {
log.info("called hookOnNext.......{} ", value);
response.add(value);
request(1);
}
#Override
protected void hookOnComplete() {
log.info("called hookOnComplete.......");
}
#Override
protected void hookOnError(Throwable throwable) {
log.info("called hookOnError.......");
if(throwable instanceof ConnectException) {
log.error("called ConnectException.......");
}
if(throwable instanceof ResourceAccessException) {
log.error("called ResourceAccessException.......");
}
if(throwable instanceof ConnectTimeoutException) {
log.error("called ConnectTimeoutException.......");
}
if(throwable instanceof io.netty.channel.ConnectTimeoutException) {
log.error("called netty ConnectTimeoutException.......");
}
}
});
return response;
}```
1. When NotificationService overloads, how can it signal(backpressure) CollectorService to slow down? (Ideal scenario)
2. Alternatively, NotificationService processes 5 emails then signal/request CollectorService for the next 5.
Thanks for your time!
I am required to develop Web Service using the Spring Framework.
Scenario is like this:
My program will send a SOAP request (header + payload) to a service and expects a response (header + payload) back from the service.
Both the payload and header are important and needed by the program.
Problem is that I am unable to find any example in Spring WS where both header and payload are sent as part of request and header and payload extracted from response.
I am using WebServiceGatewaySupport and WebServiceTemplate for sending of request and fetching of response.
WebServiceTemplate provides 2 methods for sending request:
marshalSendAndReceive
sendAndReceive
Problem with marshalSendAndReceive is that I will not get back the response header though I can send the request header.
Problem with sendAndReceive is that I will not be able to send the request header though I will be able to extract the response header.
The only solution currently available right now is to use Interceptors but it seems that this is not the proper way of handling the headers as header is intercepted before it reaches the calling function. To have the calling function access to response header, we will need to make the Interceptor as stateful which is not desirable.
I will really appreciate guidance and help from anyone who can provide me with an example of how to properly achieve this.
Please find my code below when using sendAndReceive:
public class ClientAccountInformation extends WebServiceGatewaySupport {
public ClientAccountInformation() {
}
public FIGetAcctsInfoCallBackRs sendRequest(GetAcctInfoRq request, HeaderRq headerRq) {
WebServiceTemplate webServiceTemplate = getWebServiceTemplate();
try {
ResponseAndHeader responseAndHeader = webServiceTemplate.sendAndReceive(Constants.WEBSERVICE_URL,
new WebServiceMessageCallback() {
public void doWithMessage(WebServiceMessage message) throws IOException {
try {
Jaxb2Marshaller marshallerRq = new Jaxb2Marshaller();
marshallerRq.setContextPath("com.abc.domain..getacctinfo");
marshallerRq.afterPropertiesSet();
MarshallingUtils.marshal(marshallerRq, request, message);
SoapHeader soapHeader = ((SoapMessage)message).getSoapHeader();
JAXBContext context = JAXBContext.newInstance(HeaderRq.class);
Marshaller marshaller = context.createMarshaller();
marshaller.marshal(HeaderRq, soapHeader.getResult());
} catch (Exception e) {
e.printStackTrace();
}
}
},
new WebServiceMessageExtractor<ResponseAndHeader>() {
public ResponseAndHeader extractData(WebServiceMessage message) throws IOException {
SoapHeader header = ((SoapMessage)message).getSoapHeader();
Iterator<SoapHeaderElement> it = header.examineHeaderElements(new QName("urn:test", "HeaderRs"));
return new ResponseAndHeader(
it.hasNext() ? (HeaderRs)jaxb2Marshaller().unmarshal(it.next().getSource())
: null,
(GetAcctInfoRs) MarshallingUtils.unmarshal(jaxb2Marshaller(), message));
}
});
return null;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
Jaxb2Marshaller jaxb2Marshaller() {
Jaxb2Marshaller jaxb2Marshaller = new Jaxb2Marshaller();
jaxb2Marshaller.setContextPath("com.abc.domain.getacctinfo");
return jaxb2Marshaller;
}
}
The above code always returns null for the response header.
I have solved the issue. I can send the SOAP header to a service and extract the response header from response also. No need for interceptors.
Main help was from blog post from Andreas Veithen. Thanks.
public class ClientSamaAccountInformation extends WebServiceGatewaySupport {
public ClientSamaAccountInformation() {
}
public FIGetAcctsInfoCallBackRs sendRequest(FIGetAcctsInfoCallBackRq request, MsgHdrRq msgHdrRq) {
WebServiceTemplate webServiceTemplate = getWebServiceTemplate();
try {
ResponseAndHeader responseAndHeader = webServiceTemplate.sendAndReceive(Constants.WEBSERVICE_URL,
new WebServiceMessageCallback() {
public void doWithMessage(WebServiceMessage message) throws IOException {
try {
Jaxb2Marshaller marshallerRq = new Jaxb2Marshaller();
marshallerRq.setContextPath("com.abc.domain..getacctinfo");
marshallerRq.afterPropertiesSet();
MarshallingUtils.marshal(marshallerRq, request, message);
SoapHeader soapHeader = ((SoapMessage)message).getSoapHeader();
JAXBContext context = JAXBContext.newInstance(MsgHdrRq.class);
Marshaller marshaller = context.createMarshaller();
marshaller.marshal(msgHdrRq, soapHeader.getResult());
} catch (Exception e) {
e.printStackTrace();
}
}
},
new WebServiceMessageExtractor<ResponseAndHeader>() {
public ResponseAndHeader extractData(WebServiceMessage message) throws IOException {
//Extract response payload
FIGetAcctsInfoCallBackRs response = null;
try {
Jaxb2Marshaller marshallerRs = new Jaxb2Marshaller();
marshallerRs.setContextPath("com.abc.domain..getacctinfo");
marshallerRs.afterPropertiesSet();
response = (FIGetAcctsInfoCallBackRs) MarshallingUtils.unmarshal(marshallerRs, message);
} catch (Exception e) {
e.printStackTrace();
}
//Extract response header
MsgHdrRs msgHdrRs = null;
try {
JAXBContext context = JAXBContext.newInstance(MsgHdrRs.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
SoapHeader header = ((SoapMessage)message).getSoapHeader();
Iterator<SoapHeaderElement> it = header.examineHeaderElements(new QName("http://www.abc.def.com/common/Header", "MsgHdrRs"));
while(it.hasNext()) {
msgHdrRs = (MsgHdrRs) unmarshaller.unmarshal(it.next().getSource());
System.out.println(msgHdrRs);
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
});
return null;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
I have the following problem: I try to get body of a POST request before it is handled by a spring controller. For that I am using the HandlerInterceptorAdapter's preHandle() method.
As stated in this discussion Spring REST service: retrieving JSON from Request I also use the HttpServletRequestWrapper. With this wrapper I managed to print the body of the first POST request, but the second POST throws an IOException: StreamClosed.
Do you have any ideas on how I can get the body of all POST requests?
Here is the preHandle() method from the interceptor:
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
System.out.println(request.getMethod());
MyRequestWrapper w = new MyRequestWrapper(request);
BufferedReader r = w.getReader();
System.out.println(r.readLine());
return super.preHandle(request, response, handler);
}
The HttpServletRequestWrapper:
public class MyRequestWrapper extends HttpServletRequestWrapper {
private ByteArrayOutputStream cachedBytes;
private HttpServletRequest request;
public MyRequestWrapper(HttpServletRequest request) {
super(request);
this.request = request;
}
#Override
public ServletInputStream getInputStream() throws IOException {
cachedBytes = new ByteArrayOutputStream();
if (request.getMethod().equals("POST"))
cacheInputStream();
return new CachedServletInputStream();
}
#Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
private void cacheInputStream() throws IOException {
/*
* Cache the inputstream in order to read it multiple times. For
* convenience, I use apache.commons IOUtils
*/
ServletInputStream inputStream = super.getInputStream();
if (inputStream == null) {
return;
}
IOUtils.copy(inputStream, cachedBytes);
}
/* An inputstream which reads the cached request body */
public class CachedServletInputStream extends ServletInputStream {
private ByteArrayInputStream input;
public CachedServletInputStream() {
/* create a new input stream from the cached request body */
input = new ByteArrayInputStream(cachedBytes.toByteArray());
}
#Override
public int read() throws IOException {
return input.read();
}
}
}
The console output:
2014-10-15 12:13:00 INFO [http-nio-8080-exec-1] org.springframework.web.servlet.DispatcherServlet - FrameworkServlet 'dispatcherServlet': initialization completed in 9 ms
GET
null
GET
null
POST
{"long":null,"owner":{"__type":"Owner","id":20,"version":1,"md5Password":""},"string":"ws","tool":{"__type":"Tool","id":33,"version":1}}
POST
2014-10-15 12:13:00 ERROR [http-nio-8080-exec-3] org.apache.catalina.core.ContainerBase.[Tomcat].[localhost].[/].[dispatcherServlet] - Servlet.service() for servlet dispatcherServlet threw exception
java.io.IOException: Stream closed
You're attempting to read from the original request in your Wrapper, but after this, the original request is still being read - hence the request input stream has been consumed and cannot be read from again.
Instead of using an Interceptor, consider using a javax.servlet.Filter. In the doFilter method, you can pass the wrapped request on down the chain.
I've used filter that implements Filter & interceptor that extends HandlerInterceptorAdapter (because in the filter all fields are nullable and I can't save anything to DB. see Autowired Null Pointer Exception) to retreive request and response body and save them to DB. If your filter works fine then use only filter.
filter. Here I wrap a request and a response to read from them not only once. You can use ContentCachingRequestWrapper and ContentCachingResponseWrapper for that.
#Component
public class RequestLogFilter implements Filter {
private final Logger logger = LoggerFactory.getLogger(RequestLogFilter.class);
#Override
public void init(FilterConfig filterConfig) throws ServletException {
// TODO Auto-generated method stub
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
logger.info("======================> FILTER <======================");
HttpServletRequest requestToCache = new ContentCachingRequestWrapper((HttpServletRequest) request);
HttpServletResponse responseToCache = new ContentCachingResponseWrapper((HttpServletResponse) response);
// before method
chain.doFilter(requestToCache, responseToCache);
// after method
// your logic(save to DB, logging...)
getRequestData(request);
getResponseData(response);
}
#Override
public void destroy() {
}
}
-
#Component
public class RequestLogInterceptor extends HandlerInterceptorAdapter {
private final Logger logger = LoggerFactory.getLogger(RequestLogInterceptor.class);
#Autowired
private InboundRequestLogStore inboundRequestLogStore;
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
logger.info("====================> INTERCEPTOR <========================");
try {
if (request.getAttribute(InboundRequestAspect.INBOUND_LOG_MARKER) != null) {
InboundRequestLogRecord logRecord = new InboundRequestLogRecord();
logRecord.setIpAddress(request.getRemoteAddr());
// getting request and response body
logRecord.setRequestBody(getRequestData(request));
logRecord.setResponseBody(getResponseData(response));
logRecord.setResponseCode(((HttpServletResponse) response).getStatus());
String uri = request.getScheme() + "://" + request.getServerName()
+ ("http".equals(request.getScheme()) && request.getServerPort() == 80
|| "https".equals(request.getScheme()) && request.getServerPort() == 443 ? ""
: ":" + request.getServerPort())
+ request.getRequestURI()
+ (request.getQueryString() != null ? "?" + request.getQueryString() : "");
logRecord.setUrl(uri);
inboundRequestLogStore.add(logRecord); // save to DB
} else {
((ContentCachingResponseWrapper) response).copyBodyToResponse(); // in other case you send null to the response
}
} catch (Exception e) {
logger.error("error ", e);
try {
((ContentCachingResponseWrapper) response).copyBodyToResponse(); // in other case you send null to the response
} catch (Exception e2) {
// TODO Auto-generated catch block
logger.error("error ", e2);
}
}
}
public static String getRequestData(final HttpServletRequest request) throws UnsupportedEncodingException {
String payload = null;
ContentCachingRequestWrapper wrapper = WebUtils.getNativeRequest(request, ContentCachingRequestWrapper.class);
if (wrapper != null) {
byte[] buf = wrapper.getContentAsByteArray();
if (buf.length > 0) {
payload = new String(buf, 0, buf.length, wrapper.getCharacterEncoding());
}
}
return payload;
}
public static String getResponseData(final HttpServletResponse response) throws UnsupportedEncodingException, IOException {
String payload = null;
ContentCachingResponseWrapper wrapper = WebUtils.getNativeResponse(response, ContentCachingResponseWrapper.class);
if (wrapper != null) {
byte[] buf = wrapper.getContentAsByteArray();
if (buf.length > 0) {
payload = new String(buf, 0, buf.length, wrapper.getCharacterEncoding());
}
wrapper.copyBodyToResponse(); // in other case you send null to the response
}
return payload;
}
}
add to servlet-context.xml
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**" />
<beans:bean class="path.to.RequestLogInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
namespaces:
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
ContentCachingRequestWrapper - http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/util/ContentCachingRequestWrapper.html
ContentCachingResponseWrapper - http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/util/ContentCachingResponseWrapper.html
when you use netty (4.0.23, java 1.7u67, win8x64) as a client ChannelInboundHandlerAdapter.channelReadComplete(...) should be called 1 time once netty completes reading response, right?
Trying different sites, it's always called twice:
#Test
public void testDoubleReadComplete() throws Exception {
final String host = "www.google.de";
final CountDownLatch count = new CountDownLatch(20);
bootstrap = new Bootstrap()
.group(new NioEventLoopGroup())
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
#Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
#Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
String request = String.format(
"GET / HTTP/1.1\n" +
"Host: " + host +"\n" +
"\n\n"
);
System.out.println("sending...");
System.out.println(request);
ByteBuf req = Unpooled.wrappedBuffer(request.getBytes(Charset.defaultCharset()));
ctx.writeAndFlush(req);
}
#Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
System.err.println("777 read complete");
}
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf resp = (ByteBuf) msg;
count.countDown();
System.out.printf("****************************************************>>>>> %s%n", Thread.currentThread().getName());
System.out.println(resp.toString(Charset.defaultCharset()));
System.out.println("<<<<<****************************************************");
resp.release();
}
});
}
});
ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, 80));
future.awaitUninterruptibly();
count.await(5, TimeUnit.SECONDS);
}
outputs 777 read complete 2 times, why?
I'm not sure that the channelReadComplete is intended for how you are trying to use it.
Read the javadoc for the method. The data is not guaranteed to arrive all at once and so netty reads the data as it arrives in a non-blocking manner. This methods notifies you when it has finished the current read operation which is not necessarily the last read operation.
I'm not exactly sure about your use case but here is some non-production code that may be closer to what you are trying to accomplish?
#Test
public void testDoubleReadComplete() throws Exception {
final String host = "www.google.de";
final CountDownLatch count = new CountDownLatch(20);
Bootstrap bootstrap = new Bootstrap()
.group(new NioEventLoopGroup())
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
#Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
#Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
String request = String.format(
"GET / HTTP/1.1\r\n" +
"Host: " + host +"\r\n" +
"Connection: close\r\n" +
"\r\n"
);
System.out.println("sending...");
System.out.println(request);
ByteBuf req = Unpooled.wrappedBuffer(request.getBytes(Charset.defaultCharset()));
ctx.writeAndFlush(req);
}
#Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.err.println("777 read complete");
}
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf resp = (ByteBuf) msg;
count.countDown();
System.out.printf("****************************************************>>>>> %s%n", Thread.currentThread().getName());
System.out.println(resp.toString(Charset.defaultCharset()));
System.out.println("<<<<<****************************************************");
resp.release();
}
});
}
});
found the cause and solution: ChannelOption.MAX_MESSAGES_PER_READ
So now I'm testing with .option(ChannelOption.MAX_MESSAGES_PER_READ, Integer.MAX_VALUE) and so far so good - channelReadComplete is called once at the end of response as I need
I have a problem with Paypal IPN verification in my Spring boot server. I'm not sure where is the problem, if at the server's side or in the other hand, it's Paypal's fault. I already selected UTF-8 as enconding in my profile page.
The main problem it's IPN with UTF-8 characters, which are making the verification fail I guess.
If I have no CharacterEncodingFilter in my Spring and Spring security server, IPN verification works fine. BUT makes other things (forms, for example) not showing with UTF-8 encoding, so this is an unacceptable solution.
I find strange when I'm printing the IPN (with no CharacterEnconding, so payment gets Verified) the response I get (among other things):
charset=UTF-8
address_name=Adrián
payment_status=Completed
So Paypal says that IPN it's UTF-8 but that's what I'm not receiving.
The server's encoding it's working fine adding CharacterEncodingFilter before Spring Security filter chain:
#Order(1)
public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
#Override
protected void beforeSpringSecurityFilterChain(ServletContext servletContext) {
FilterRegistration.Dynamic characterEncodingFilter = servletContext
.addFilter("characterEncodingFilter", new CharacterEncodingFilter());
characterEncodingFilter.setInitParameter("encoding", "UTF-8");
characterEncodingFilter.setInitParameter("forceEncoding", "false");
characterEncodingFilter.addMappingForUrlPatterns(null, false, "/*");
insertFilters(servletContext, new MultipartFilter());
}
}
And now, Paypal's IPN printing show params well encoded:
charset=UTF-8
first_name=Adrián
payment_status=Completed
but Paypal's response is INVALID.
This is my Controller that handles Paypal IPN's post:
#RequestMapping(value = "paypalok", method = RequestMethod.POST)
public void processIPN(HttpServletRequest request) {
String PAY_PAL_DEBUG = "https://www.sandbox.paypal.com/cgi-bin/webscr";
String CONTENT_TYPE = "Content-Type";
String MIME_APP_URLENC = "application/x-www-form-urlencoded";
String PARAM_NAME_CMD = "cmd";
String PARAM_VAL_CMD = "_notify-validate";
String PAYMENT_COMPLETED = "Completed";
String paymentStatus = "";
// Create client for Http communication
HttpClient httpClient = HttpClientBuilder.create().build();
// Request configuration can be overridden at the request level.
// They will take precedence over the one set at the client level.
RequestConfig requestConfig = RequestConfig.custom()
.setSocketTimeout(40000).setConnectTimeout(40000)
.setConnectionRequestTimeout(40000).build();
HttpPost httppost = new HttpPost(PAY_PAL_DEBUG);
httppost.setHeader(CONTENT_TYPE, MIME_APP_URLENC);
try {
Map<String, String> params = new HashMap<String, String>();
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);
nameValuePairs.add(new BasicNameValuePair(PARAM_NAME_CMD, PARAM_VAL_CMD));
// Process the parameters
Enumeration<String> names = request.getParameterNames();
while (names.hasMoreElements()) {
// String param = names.nextElement();
// String value = request.getParameter(param);
String param = new String (names.nextElement().getBytes ("iso-8859-1"), "UTF-8");
String value = new String (request.getParameter(param).getBytes ("iso-8859-1"), "UTF-8");
nameValuePairs.add(new BasicNameValuePair(param, value));
params.put(param, value);
System.out.println(param + "=" + value);
// Get the payment status
if (param.equalsIgnoreCase("payment_status")) paymentStatus = value;
}
httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
if (verifyResponse(httpClient.execute(httppost))) {
// if (paymentStatus.equalsIgnoreCase(PAYMENT_COMPLETED)) do...
return "elovendo/pricing/paymentOk";
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return "redirect:/error";
} catch (ClientProtocolException e) {
e.printStackTrace();
return "redirect:/error";
} catch (IOException e) {
e.printStackTrace();
return "redirect:/error";
}
}
private boolean verifyResponse(HttpResponse response) throws IllegalStateException, IOException {
String RESP_VERIFIED = "VERIFIED";
InputStream is = response.getEntity().getContent();
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
String responseText = reader.readLine();
is.close();
System.out.println("RESPONSE : " + responseText);
return responseText.equals(RESP_VERIFIED);
}
I have uri encoding with:
#Configuration
public class WebAppConfiguration {
/** HTTPS and Paging error **/
#Bean
public EmbeddedServletContainerFactory servletContainer() {
TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory();
factory.setUriEncoding("UTF-8");
}
}
Resuming, if I send the characters UTF-8 encoded Paypal verification fails, even when it shouldn't come bad-encoded. If I send them bad-encoded, Paypal's response it's ok.
I can't send the IPN's response bad-encoded using CharacterEncodingFilter, can't I?
I'm don't really know what's going on.
Thank you!
Well, I actually don't know why Paypal is sending data wrong encoded, but a simply workaround manages that.
Just overriding CharacterEncodingFilter like this:
public class CharacterEncodingFilter extends org.springframework.web.filter.CharacterEncodingFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
if (request != null && !request.getRequestURI().contains("paypalcheck")) {
super.doFilterInternal(request, response, filterChain);
}
else {
filterChain.doFilter(request, response);
}
}
}
making reference to the controller URL that listens the Paypal's IPN and telling the Filter that don't encode the data.
And also, making sure that the filter is before Spring Security chain:
public class SecurityConf extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
filter.setForceEncoding(true);
http.addFilterBefore(filter, WebAsyncManagerIntegrationFilter.class);
}
}