public void handle(final AbstractActiveNetModule aOwner, @Nonnull final Socket aSocket) { final String sClientInfo = getClientInfo(aSocket); s_aLogger.info("Incoming connection " + sClientInfo); final AS2Message aMsg = createMessage(aSocket); final IAS2HttpResponseHandler aResponseHandler = new AS2HttpResponseHandlerSocket(aSocket); // Time the transmission final StopWatch aSW = StopWatch.createdStarted(); byte[] aMsgData = null; try { // Read in the message request, headers, and data aMsgData = readAndDecodeHttpRequest( new AS2InputStreamProviderSocket(aSocket), aResponseHandler, aMsg); } catch (final Exception ex) { final NetException ne = new NetException(aSocket.getInetAddress(), aSocket.getPort(), ex); ne.terminate(); } aSW.stop(); if (aMsgData != null) { s_aLogger.info( "received " + IOHelper.getTransferRate(aMsgData.length, aSW) + " from " + sClientInfo + aMsg.getLoggingText()); handleIncomingMessage(sClientInfo, aMsgData, aMsg, aResponseHandler); } }
/** * Create a new message and record the source ip and port * * @param aSocket The socket through which the message will be read. * @return The {@link AS2Message} to use and never <code>null</code>. */ @Nonnull protected AS2Message createMessage(@Nonnull final Socket aSocket) { final AS2Message aMsg = new AS2Message(); aMsg.setAttribute(CNetAttribute.MA_SOURCE_IP, aSocket.getInetAddress().toString()); aMsg.setAttribute(CNetAttribute.MA_SOURCE_PORT, Integer.toString(aSocket.getPort())); aMsg.setAttribute(CNetAttribute.MA_DESTINATION_IP, aSocket.getLocalAddress().toString()); aMsg.setAttribute(CNetAttribute.MA_DESTINATION_PORT, Integer.toString(aSocket.getLocalPort())); aMsg.setAttribute(AS2Message.ATTRIBUTE_RECEIVED, Boolean.TRUE.toString()); return aMsg; }
private void _sendAsyncMDN(@Nonnull final AS2Message aMsg) throws OpenAS2Exception { s_aLogger.info("Async MDN submitted" + aMsg.getLoggingText()); final DispositionType aDisposition = DispositionType.createSuccess(); try { final IMessageMDN aMdn = aMsg.getMDN(); // Create a HTTP connection final String sUrl = aMsg.getAsyncMDNurl(); final boolean bOutput = true; final boolean bInput = true; final boolean bUseCaches = false; final HttpURLConnection aConn = getConnection(sUrl, bOutput, bInput, bUseCaches, "POST", getSession().getHttpProxy()); try { s_aLogger.info("connected to " + sUrl + aMsg.getLoggingText()); aConn.setRequestProperty(CAS2Header.HEADER_CONNECTION, CAS2Header.DEFAULT_CONNECTION); aConn.setRequestProperty(CAS2Header.HEADER_USER_AGENT, CAS2Header.DEFAULT_USER_AGENT); // Copy all the header from mdn to the RequestProperties of conn final Enumeration<?> aHeaders = aMdn.getHeaders().getAllHeaders(); while (aHeaders.hasMoreElements()) { final Header aHeader = (Header) aHeaders.nextElement(); final String sHeaderValue = aHeader.getValue().replace('\t', ' ').replace('\n', ' ').replace('\r', ' '); aConn.setRequestProperty(aHeader.getName(), sHeaderValue); } // Note: closing this stream causes connection abort errors on some AS2 // servers final OutputStream aMessageOS = aConn.getOutputStream(); // Transfer the data final InputStream aMessageIS = aMdn.getData().getInputStream(); final StopWatch aSW = StopWatch.createdStarted(); final long nBytes = IOHelper.copy(aMessageIS, aMessageOS); aSW.stop(); s_aLogger.info( "transferred " + IOHelper.getTransferRate(nBytes, aSW) + aMsg.getLoggingText()); // Check the HTTP Response code final int nResponseCode = aConn.getResponseCode(); if (nResponseCode != HttpURLConnection.HTTP_OK && nResponseCode != HttpURLConnection.HTTP_CREATED && nResponseCode != HttpURLConnection.HTTP_ACCEPTED && nResponseCode != HttpURLConnection.HTTP_PARTIAL && nResponseCode != HttpURLConnection.HTTP_NO_CONTENT) { s_aLogger.error( "sent AsyncMDN [" + aDisposition.getAsString() + "] Fail " + aMsg.getLoggingText()); throw new HttpResponseException(sUrl, nResponseCode, aConn.getResponseMessage()); } s_aLogger.info( "sent AsyncMDN [" + aDisposition.getAsString() + "] OK " + aMsg.getLoggingText()); // log & store mdn into backup folder. try { getSession() .getMessageProcessor() .handle(IProcessorStorageModule.DO_STOREMDN, aMsg, null); } catch (final ComponentNotFoundException ex) { // May be } } finally { aConn.disconnect(); } } catch (final HttpResponseException ex) { // Resend if the HTTP Response has an error code ex.terminate(); _resend(aMsg, ex); } catch (final IOException ex) { // Resend if a network error occurs during transmission final OpenAS2Exception wioe = WrappedOpenAS2Exception.wrap(ex); wioe.addSource(OpenAS2Exception.SOURCE_MESSAGE, aMsg); wioe.terminate(); _resend(aMsg, wioe); } catch (final Exception ex) { // Propagate error if it can't be handled by a resend throw WrappedOpenAS2Exception.wrap(ex); } }
/** * This method can be used to handle an incoming HTTP message AFTER the headers where extracted. * * @param sClientInfo Client connection info * @param aMsgData The message body * @param aMsg The AS2 message that will be filled by this method * @param aResponseHandler The response handler which handles HTTP error messages as well as * synchronous MDN. */ public void handleIncomingMessage( @Nonnull final String sClientInfo, @Nonnull final byte[] aMsgData, @Nonnull final AS2Message aMsg, @Nonnull final IAS2HttpResponseHandler aResponseHandler) { // TODO store HTTP request, headers, and data to file in Received folder // -> use message-id for filename? try { final IAS2Session aSession = m_aReceiverModule.getSession(); try { // Put received data in a MIME body part final ContentType aReceivedContentType = new ContentType(aMsg.getHeader(CAS2Header.HEADER_CONTENT_TYPE)); final String sReceivedContentType = aReceivedContentType.toString(); final MimeBodyPart aReceivedPart = new MimeBodyPart(); aReceivedPart.setDataHandler( new DataHandler(new ByteArrayDataSource(aMsgData, sReceivedContentType, null))); // Header must be set AFTER the DataHandler! aReceivedPart.setHeader(CAS2Header.HEADER_CONTENT_TYPE, sReceivedContentType); aMsg.setData(aReceivedPart); } catch (final Exception ex) { throw new DispositionException( DispositionType.createError("unexpected-processing-error"), AbstractActiveNetModule.DISP_PARSING_MIME_FAILED, ex); } // Extract AS2 ID's from header, find the message's partnership and // update the message try { final String sAS2From = aMsg.getAS2From(); aMsg.getPartnership().setSenderAS2ID(sAS2From); final String sAS2To = aMsg.getAS2To(); aMsg.getPartnership().setReceiverAS2ID(sAS2To); // Fill all partnership attributes etc. aSession.getPartnershipFactory().updatePartnership(aMsg, false); } catch (final OpenAS2Exception ex) { throw new DispositionException( DispositionType.createError("authentication-failed"), AbstractActiveNetModule.DISP_PARTNERSHIP_NOT_FOUND, ex); } // Per RFC5402 compression is always before encryption but can be before // or after signing of message but only in one place final ICryptoHelper aCryptoHelper = AS2Helper.getCryptoHelper(); boolean bIsDecompressed = false; // Decrypt and verify signature of the data, and attach data to the // message decrypt(aMsg); if (aCryptoHelper.isCompressed(aMsg.getContentType())) { if (s_aLogger.isTraceEnabled()) s_aLogger.trace("Decompressing received message before checking signature..."); decompress(aMsg); bIsDecompressed = true; } verify(aMsg); if (aCryptoHelper.isCompressed(aMsg.getContentType())) { // Per RFC5402 compression is always before encryption but can be before // or after signing of message but only in one place if (bIsDecompressed) { throw new DispositionException( DispositionType.createError("decompression-failed"), AbstractActiveNetModule.DISP_DECOMPRESSION_ERROR, new Exception( "Message has already been decompressed. Per RFC5402 it cannot occur twice.")); } if (s_aLogger.isTraceEnabled()) if (aMsg.containsAttribute(AS2Message.ATTRIBUTE_RECEIVED_SIGNED)) s_aLogger.trace("Decompressing received message after verifying signature..."); else s_aLogger.trace("Decompressing received message after decryption..."); decompress(aMsg); bIsDecompressed = true; } if (s_aLogger.isTraceEnabled()) try { s_aLogger.trace( "SMIME Decrypted Content-Disposition: " + aMsg.getContentDisposition() + "\n Content-Type received: " + aMsg.getContentType() + "\n HEADERS after decryption: " + aMsg.getData().getAllHeaders() + "\n Content-Disposition in MSG detData() MIMEPART after decryption: " + aMsg.getData().getContentType()); } catch (final MessagingException ex) { s_aLogger.error("Failed to trace message: " + aMsg, ex); } // Validate the received message before storing try { aSession .getMessageProcessor() .handle(IProcessorStorageModule.DO_VALIDATE_BEFORE_STORE, aMsg, null); } catch (final NoModuleException ex) { // No module installed - ignore } catch (final OpenAS2Exception ex) { throw new DispositionException( DispositionType.createError("unexpected-processing-error"), AbstractActiveNetModule.DISP_VALIDATION_FAILED + "\n" + StackTraceHelper.getStackAsString(ex), ex); } // Store the received message try { aSession.getMessageProcessor().handle(IProcessorStorageModule.DO_STORE, aMsg, null); } catch (final NoModuleException ex) { // No module installed - ignore } catch (final OpenAS2Exception ex) { throw new DispositionException( DispositionType.createError("unexpected-processing-error"), AbstractActiveNetModule.DISP_STORAGE_FAILED + "\n" + ex.getMessage(), ex); } // Validate the received message after storing try { aSession .getMessageProcessor() .handle(IProcessorStorageModule.DO_VALIDATE_AFTER_STORE, aMsg, null); } catch (final NoModuleException ex) { // No module installed - ignore } catch (final OpenAS2Exception ex) { throw new DispositionException( DispositionType.createError("unexpected-processing-error"), AbstractActiveNetModule.DISP_VALIDATION_FAILED + "\n" + StackTraceHelper.getStackAsString(ex), ex); } try { if (aMsg.isRequestingMDN()) { // Transmit a success MDN if requested sendSyncMDN( sClientInfo, aResponseHandler, aMsg, DispositionType.createSuccess(), AbstractActiveNetModule.DISP_SUCCESS); } else { // Just send a HTTP OK HTTPHelper.sendSimpleHTTPResponse(aResponseHandler, HttpURLConnection.HTTP_OK); s_aLogger.info("sent HTTP OK " + sClientInfo + aMsg.getLoggingText()); } } catch (final Exception ex) { throw new WrappedOpenAS2Exception( "Error creating and returning MDN, message was stilled processed", ex); } } catch (final DispositionException ex) { sendSyncMDN(sClientInfo, aResponseHandler, aMsg, ex.getDisposition(), ex.getText()); m_aReceiverModule.handleError(aMsg, ex); } catch (final OpenAS2Exception ex) { m_aReceiverModule.handleError(aMsg, ex); } }
protected void sendSyncMDN( @Nonnull final String sClientInfo, @Nonnull final IAS2HttpResponseHandler aResponseHandler, @Nonnull final AS2Message aMsg, @Nonnull final DispositionType aDisposition, @Nonnull final String sText) { final boolean bMDNBlocked = aMsg.getPartnership().isBlockErrorMDN(); if (!bMDNBlocked) { try { final IAS2Session aSession = m_aReceiverModule.getSession(); final IMessageMDN aMdn = AS2Helper.createMDN(aSession, aMsg, aDisposition, sText); if (aMsg.isRequestingAsynchMDN()) { // if asyncMDN requested, close connection and initiate separate MDN // send final InternetHeaders aHeaders = new InternetHeaders(); aHeaders.setHeader(CAS2Header.HEADER_CONTENT_LENGTH, Integer.toString(0)); // Empty data final NonBlockingByteArrayOutputStream aData = new NonBlockingByteArrayOutputStream(); aResponseHandler.sendHttpResponse(HttpURLConnection.HTTP_OK, aHeaders, aData); s_aLogger.info( "Setup to send asynch MDN [" + aDisposition.getAsString() + "] " + sClientInfo + aMsg.getLoggingText()); // trigger explicit sending aSession.getMessageProcessor().handle(IProcessorSenderModule.DO_SENDMDN, aMsg, null); } else { // otherwise, send sync MDN back on same connection s_aLogger.info( "Sending back sync MDN [" + aDisposition.getAsString() + "] " + sClientInfo + aMsg.getLoggingText()); // Get data and therefore content length for sync MDN final NonBlockingByteArrayOutputStream aData = new NonBlockingByteArrayOutputStream(); final MimeBodyPart aPart = aMdn.getData(); StreamHelper.copyInputStreamToOutputStream(aPart.getInputStream(), aData); aMdn.setHeader(CAS2Header.HEADER_CONTENT_LENGTH, Integer.toString(aData.getSize())); // start HTTP response aResponseHandler.sendHttpResponse(HttpURLConnection.HTTP_OK, aMdn.getHeaders(), aData); // Save sent MDN for later examination try { aSession.getMessageProcessor().handle(IProcessorStorageModule.DO_STOREMDN, aMsg, null); } catch (final ComponentNotFoundException ex) { // No message processor found } catch (final NoModuleException ex) { // No module found in message processor } s_aLogger.info( "sent MDN [" + aDisposition.getAsString() + "] " + sClientInfo + aMsg.getLoggingText()); } } catch (final Exception ex) { final OpenAS2Exception we = WrappedOpenAS2Exception.wrap(ex); we.addSource(OpenAS2Exception.SOURCE_MESSAGE, aMsg); we.terminate(); } } }