/***********************************************************
Copyright (C) 2024 VeriSign, Inc.

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

http://www.verisign.com/nds/naming/namestore/techdocs.html
***********************************************************/
package com.verisign.epp.interfaces;

import java.io.IOException;
import java.net.CookieManager;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpRequest.BodyPublishers;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
import java.time.Duration;

import javax.net.ssl.SSLParameters;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;

import com.verisign.epp.codec.gen.EPPCodec;
import com.verisign.epp.codec.gen.EPPCommand;
import com.verisign.epp.codec.gen.EPPGreeting;
import com.verisign.epp.codec.gen.EPPHello;
import com.verisign.epp.codec.gen.EPPLoginCmd;
import com.verisign.epp.codec.gen.EPPLogoutCmd;
import com.verisign.epp.codec.gen.EPPMessage;
import com.verisign.epp.codec.gen.EPPPollCmd;
import com.verisign.epp.codec.gen.EPPResponse;
import com.verisign.epp.exception.EPPException;
import com.verisign.epp.pool.parser.EPPSchemaCachingParserPool;
import com.verisign.epp.transport.client.EPPSSLContext;
import com.verisign.epp.transport.client.EPPSSLImpl;
import com.verisign.epp.util.EPPEnv;
import com.verisign.epp.util.EPPEnvException;
import com.verisign.epp.util.EPPSendReceiveLogger;
import com.verisign.epp.util.EPPXMLByteArray;
import com.verisign.epp.util.EPPXMLStream;

/**
 * EPPHttpSession provides behavior for communicating with an EPP Server using
 * the HTTP protocol as the transport.
 */
public class EPPHttpSession extends EPPSession {

	/** Category for logging */
	private static Logger cat = LoggerFactory.getLogger(EPPHttpSession.class);

	/**
	 * EPPCodec to use to encode and decode the EPP messages
	 */
	private EPPCodec code = EPPCodec.getInstance();

	/**
	 * HttpClient instance that we delegate to
	 */
	private HttpClient httpClient = null;

	/**
	 * Flag to see if a session is established
	 */
	private boolean sessionEstablished = false;

	/**
	 * The URL with port to connect to.
	 */
	private String url = null;

	/**
	 * EPP response packet associated with the last EPP command
	 */
	private byte[] responsePacket = null;

	/**
	 * Used to read and write XML packets from/to streams.
	 */
	private EPPXMLStream xmlStream = null;

	/**
	 * Used to encode and decode the packet byte arrays
	 */
	private EPPXMLByteArray byteArray = new EPPXMLByteArray();

	/**
	 * EPP Greeting returned when creating connection
	 */
	private EPPGreeting greeting = null;

	/**
	 * {@code EPPSSLContext} to see for the {@code EPPHttpSession}.
	 */
	private EPPSSLContext sslContext;

	/**
	 * Log the packet logging and receiver.
	 */
	private static EPPSendReceiveLogger sendReceiveLogger = EPPEnv.getSendReceiveLogger();

	/**
	 * Construct an {@code EPPHttpSession} instance that points to the given URL.
	 * An example is {@code https://test.vgrs.com:8080/}.
	 *
	 * @param aUrl
	 *           URL to connect to
	 * @throws EPPCommandException
	 *            Error initializing the session
	 */
	public EPPHttpSession(String aUrl) throws EPPCommandException {
		this.url = aUrl;
		this.init();
	}

	/**
	 * Construct an {@code EPPHttpSession} instance that points to the given URL
	 * and an {@code EPPSSLContext}. An example is
	 * {@code https://test.vgrs.com:8080/}.
	 *
	 * @param aUrl
	 *           URL to connect to
	 * @param aSSLContext
	 *           Optional specific SSL context to use. Set to {@code null} if
	 *           undefined.
	 * @throws EPPCommandException
	 *            Error initializing the session
	 */
	public EPPHttpSession(String aUrl, EPPSSLContext aSSLContext) throws EPPCommandException {
		this.url = aUrl;
		if (aSSLContext != null) {
			this.sslContext = aSSLContext;
		}
		this.init();
	}

	/**
	 * Contact an {@code EPPHttpSession} instance using the URL defined in the
	 * "EPP.ServerURL" configuration property.
	 * 
	 * @exception EPPCommandException
	 *               Error initializing the {@code EPPHttpSession}
	 */
	public EPPHttpSession() throws EPPCommandException {
		this.init();
	}

	/**
	 * Initializes SSL if required when the URL includes "https".
	 *
	 * @throws EPPCommandException
	 *            Error initializing SSL
	 */
	private void initSSL() throws EPPCommandException {

		try {
			// Ensure EPPSSLImpl and HttpsURLConnection is initialized for https
			if (this.url.startsWith("https") && (this.sslContext == null)) {
				if (this.sslContext != null && this.sslContext.getSslConfig() != null) {
					cat.debug("EPPHttpSession: Initializing SSL with provided EPPSSLContext");
					this.sslContext = EPPSSLImpl.initialize(this.sslContext.getSslConfig());
				}
				else {
					if (!EPPSSLImpl.isInitialized()) {
						cat.debug("EPPHttpSession: Initializing SSL with default EPPSSLImpl");
						EPPSSLImpl.initialize();
					}
					this.sslContext = EPPSSLImpl.getEPPSSLContext();
				}
			}
		}
		catch (Exception ex) {
			cat.error("EPPHttpSession.initSSL: Error initializing EPPSSLImpl", ex);
			throw new EPPCommandException("EPPHttpSession.initSSL: Error initializing EPPSSLImpl: " + ex.getMessage());
		}

	}

	/**
	 * Helper method called by constructor to perform any initialization required
	 * for the {@code EPPHttpSession} class.
	 *
	 * @throws EPPCommandException
	 *            if something goes wrong
	 */
	@Override
	public void init() throws EPPCommandException {

		try {
			this.url = EPPEnv.getServerName();
		}
		catch (EPPEnvException e) {
			cat.error("Error when trying to get EPP Server Name (URL): ", e);
			throw new EPPCommandException("Error when trying to get EPP Server Name (URL): " + e.getMessage());
		}

		// Initialize SSL if required
		this.initSSL();

		// Initialize the Java HTTP Client
		this.initHttpClient();

		// Initialize the XML stream
		this.xmlStream = new EPPXMLStream(EPPSchemaCachingParserPool.getInstance().getPool());

		HttpRequest.Builder theRequestBuilder = HttpRequest.newBuilder();
		try {
			theRequestBuilder.uri(new URI(this.url));
			theRequestBuilder.timeout(Duration.ofMillis(EPPEnv.getReadTimeOut()));
			theRequestBuilder.header("Accept", "application/epp+xml");
			theRequestBuilder.GET();

			HttpRequest theRequest = theRequestBuilder.build();

			cat.info("Connecting to server " + this.url + " using HTTP GET");

			HttpResponse<byte[]> theResponse = this.httpClient.send(theRequest, BodyHandlers.ofByteArray());

			// get the response body
			this.responsePacket = theResponse.body();
		}
		catch (URISyntaxException ex) {
			cat.error("URL is not syntactically correct: " + this.url);
			throw new EPPCommandException("URL is not syntactically correct: " + this.url);
		}
		catch (EPPEnvException ex) {
			cat.error("EPP.ReadTimeOut property not set");
			throw new EPPCommandException("EPP.ReadTimeOut property not set");
		}
		catch (IOException ex) {
			cat.error("Error connecting to server with URL: " + this.url);
			throw new EPPCommandException("Error connecting to server with URL: " + this.url);
		}
		catch (InterruptedException ex) {
			cat.error("InterruptedException connecting to server with URL: " + this.url);
			throw new EPPCommandException("InterruptedException connecting to server with URL: " + this.url);

		}

		// throw exception here if no response could be found
		if (this.responsePacket == null) {
			cat.error("Response body was null after executing HTTP GET.  Check the URL setting.");
			throw new EPPCommandException("Didn't get response body in response from server when sending initial GET");
		}

		// Decode greeting
		try {
			if (this.byteArray == null) {
				this.byteArray = new EPPXMLByteArray();
			}
			Document theDoc = this.byteArray.decode(this.responsePacket);

			this.greeting = this.codec.decodeGreeting(theDoc);
			sendReceiveLogger.logReceive(this.responsePacket, this.greeting);
		}
		catch (Exception e) {
			e.printStackTrace();
			cat.error("Caught Exception while trying to decode EPPGreeting " + e);
			throw new EPPCommandException("EPPHttpSession.init(): decode Exception: " + e);
		}

		// Initialize the login adapter
		super.initLoginAdapter();
	}

	/**
	 * Initialize the Java {@code HttpClient) attribute.
	 */
	private void initHttpClient() {

		HttpClient.Builder theBuilder = HttpClient.newBuilder();
		if (EPPSSLImpl.getSSLContext() != null) {
			theBuilder.sslContext(EPPSSLImpl.getSSLContext());
		}
		try {
			theBuilder.connectTimeout(Duration.ofMillis(EPPEnv.getConTimeOut()));
		}
		catch (EPPEnvException e) {
			cat.debug("EPP.ConTimeOut property not set");
		}

		try {
			theBuilder.version(getHttpVersion());
			cat.debug("HTTP Version = " + getHttpVersion());
		}
		catch (EPPEnvException e) {
			cat.error("Exception getting the HTTP version: " + e);
		}
		// Add Cookie Manager
		CookieManager theCookieManager = new CookieManager();
		theBuilder.cookieHandler(theCookieManager);

		this.httpClient = theBuilder.build();
	}

	/**
	 * Gets the {@code HttpClient} instance.
	 *
	 * @return the {@code HttpClient} instance if set; {@code null} otherwise.
	 */
	public HttpClient getHttpClient() {
		return this.httpClient;
	}

	/**
	 * Sets the {@code HttpClient} instance.
	 *
	 * @param aHttpClient
	 *           The {@code HttpClient} instance. Setting to {@code null} will
	 *           not apply.
	 */
	public void setHttpClient(HttpClient aHttpClient) {
		if (aHttpClient != null) {
			this.httpClient = aHttpClient;
		}
	}

	/**
	 * Send the message in an HTTP POST method.
	 *
	 * @param aMessage
	 *           Message to send
	 *
	 * @return The EPP message returned, which could be an {@code EPPGreeting} or
	 *         an {@code EPPResponse}.
	 *
	 * @throws EPPCommandException
	 *            Error posting the {@code aXml}
	 */
	public EPPMessage postMessage(EPPMessage aMessage) throws EPPCommandException {
		cat.debug("postMessage() enter");

		EPPMessage theRetMessage = null;

		// create the DOM form of the EPPLogin command
		Document theDoc = null;
		byte[] theMessageXml = null;

		try {
			theDoc = this.codec.encode(aMessage);
			theMessageXml = this.byteArray.encode(theDoc);
		}
		catch (EPPException e) {
			throw new EPPCommandException("Error with postMessage encode : " + e.getMessage());
		}

		HttpRequest.Builder theRequestBuilder = HttpRequest.newBuilder();
		try {

			theRequestBuilder.uri(new URI(this.url));
			theRequestBuilder.timeout(Duration.ofMillis(EPPEnv.getReadTimeOut()));
			theRequestBuilder.header("Content-Type", "application/epp+xml;charset=UTF-8");
			theRequestBuilder.header("Accept-Type", "application/epp+xml");
			theRequestBuilder.POST(BodyPublishers.ofByteArray(theMessageXml));
			HttpRequest theRequest = theRequestBuilder.build();

			HttpResponse<byte[]> theResponse = this.httpClient.send(theRequest, BodyHandlers.ofByteArray());

			sendReceiveLogger.logSend(theMessageXml, aMessage);

			// get the response body
			this.responsePacket = theResponse.body();

			// Decode response
			theDoc = this.byteArray.decode(this.responsePacket);

			theRetMessage = this.codec.decode(theDoc);
			if (theRetMessage instanceof EPPResponse) {
				this.response = (EPPResponse) theRetMessage;
			}
			sendReceiveLogger.logReceive(this.responsePacket, theRetMessage);
		}
		catch (URISyntaxException e) {
			cat.error("URI is invalid:" + this.url);
			throw new EPPCommandException("URI is invalid:" + this.url);
		}
		catch (Exception e) {
			cat.error("Caught Exception while trying to decode response:" + e.getMessage());
			throw new EPPCommandException("Error decoding response: " + e.getMessage());
		}

		cat.debug("postMessage() exit");

		return theRetMessage;
	}

	/**
	 * Sends a Hello Command to the EPP Server. The EPP Greeting from the EPP
	 * Server will be returned.
	 *
	 * @return EPP Greeting
	 *
	 * @exception EPPCommandException
	 *               Thrown if any exception occurs.
	 */
	@Override
	public EPPGreeting hello() throws EPPCommandException {
		cat.debug("hello(): enter");

		// create EPPHello instance
		EPPHello helloCmd = new EPPHello();

		// send the command to the server
		EPPMessage theRespMsg = this.postMessage(helloCmd);

		if (!(theRespMsg instanceof EPPGreeting)) {
			throw new EPPCommandException(
			      "Expected EPPGreeting to Hello Command, but received class " + theRespMsg.getClass().getName());
		}

		this.greeting = (EPPGreeting) theRespMsg;
		cat.debug("hello(): exit");
		return this.greeting;
	}

	/**
	 * This method creates an instance of {@link EPPPollCmd} and sets the given
	 * attributes and invokes the send method associated with the command.
	 *
	 * @return the response from the poll command
	 *
	 * @exception EPPCommandException
	 *               Error sending the poll command
	 */
	@Override
	public EPPResponse sendPoll() throws EPPCommandException {
		cat.debug("sendPoll(): enter");

		EPPPollCmd myCommand = new EPPPollCmd(this.transId, this.pollOp);

		if (this.pollOp.equals(EPPPollCmd.OP_ACK)) {
			myCommand.setMsgID(this.msgID);
		}

		// send the command to the server
		EPPMessage theRespMsg = this.postMessage(myCommand);

		if (!(theRespMsg instanceof EPPResponse)) {
			throw new EPPCommandException(
			      "Expected EPPResponse to Poll Command, but received class " + theRespMsg.getClass().getName());
		}

		// Check if there was an EPP error
		if (!this.response.isSuccess()) {
			throw new EPPCommandException("sendPoll() : Error in response from Server ", this.response);
		}

		cat.debug("sendPoll(): exit");
		return this.response;
	}

	/**
	 * Ends a session by logging out from the server and closing the connection
	 * with the server.
	 *
	 * @exception EPPCommandException
	 *               Error ending session
	 */
	@Override
	public void endSession() throws EPPCommandException {
		try {
			logout();
		}
		finally {
			// Ensure that the physical connection is closed
			try {
				endConnection();
			}
			catch (Exception ex) {
				// Ignore
			}
		}
	}

	/**
	 * End the {@code EPPHttpSession}.
	 *
	 * @throws EPPCommandException
	 *            Error ending the {@code EPPHttpSession}.
	 *
	 *            todo Explicitly close the underlying HttpClient once using Java
	 *            21 and above.
	 */
	@Override
	public void endConnection() throws EPPCommandException {
		cat.debug("endConnection() enter");

		// Uncomment with Java 21
		// this.httpClient.close();

		cat.debug("endConnection() exit");
	}

	/**
	 * Sends the EPP login command over the HTTP connection to establish an EPP
	 * session.
	 *
	 * @exception EPPCommandException
	 *               Error sending the EPP login command
	 */
	@Override
	protected void login() throws EPPCommandException {
		cat.debug("login(): enter");

		// setup the login command
		EPPLoginCmd theLoginCommand = setupLoginCommand();

		/**
		 * Need to Create a Command Object and set Attributes.
		 */
		if (this.newPassword == null) {
			theLoginCommand = new EPPLoginCmd(this.transId, this.clientId, this.password);
		}
		else {
			theLoginCommand = new EPPLoginCmd(this.transId, this.clientId, this.password, this.newPassword);
		}

		if (this.version != null) {
			theLoginCommand.setVersion(this.version);
		}

		if (this.language != null) {
			theLoginCommand.setLang(this.language);
		}

		// Merge greeting services and extension services with the default
		// services configured in the EPP SDK.
		theLoginCommand.mergeServicesAndExtensionServices(this.greeting);

		// Set the client specified extensions (setExtensions)
		if (this.extensionServices != null) {
			theLoginCommand.setExtensionServices(this.extensionServices);
		}

		// Set the client specified services (setServices)
		if (this.services != null) {
			theLoginCommand.setServices(this.services);
		}

		// Greeting and Login services are not compatible?
		if (cat.isDebugEnabled() && !theLoginCommand.isValidServices(this.greeting)) {
			cat.debug("login(EPPGreeting): Login services does not match the greeting services, greeting = ["
			      + this.greeting + "], login = [" + theLoginCommand + "]");
		}

		// Execute the login adapter?
		if (super.loginAdapter != null) {
			cat.debug("login(EPPGreeting): Adapt login command with " + this.loginAdapter.getClass().getName());
			this.loginAdapter.adaptLogin(theLoginCommand, this.greeting);
		}
		else {
			cat.debug("No login adapter defined");
		}

		// send the command to the server
		EPPMessage theRespMsg = this.postMessage(theLoginCommand);

		if (!(theRespMsg instanceof EPPResponse)) {
			throw new EPPCommandException(
			      "Expected EPPResponse to Login Command, but received class " + theRespMsg.getClass().getName());
		}

		// Check if there was an EPP error
		if (!this.response.isSuccess()) {
			throw new EPPCommandException("login() : Error in response from Server ", this.response);
		}

		// verify the trans id
		validateClientTransId(theLoginCommand, this.response);

		this.sessionEstablished = true;
		this.transId = null;

		cat.debug("login(): exit");
	}

	/**
	 * Sends the EPP logout command over the HTTP connection to gracefully end
	 * EPP session.
	 *
	 * @exception EPPCommandException
	 *               Error sending the EPP login command
	 */
	@Override
	protected void logout() throws EPPCommandException {
		cat.debug("logout(): enter");

		EPPLogoutCmd theLogoutCommand = new EPPLogoutCmd();

		// send the command to the server
		EPPMessage theRespMsg = this.postMessage(theLogoutCommand);

		if (!(theRespMsg instanceof EPPResponse)) {
			throw new EPPCommandException(
			      "Expected EPPResponse to Logout Command, but received class " + theRespMsg.getClass().getName());
		}

		// Check if there was an EPP error
		if (!this.response.isSuccess()) {
			throw new EPPCommandException("logout() : Error in response from Server ", this.response);
		}

		this.sessionEstablished = false;
		this.transId = null;

		cat.debug("logout(): exit");
	}

	/**
	 * Process an {@code EPPCommand} instance posting the command and processing
	 * the response. The only mode supported is {@link #MODE_SYNC}.
	 *
	 * @param aCommand
	 *           Command to post to the server
	 * @param aExpectedResponse
	 *           Expected type of {@code EPPResponse}. If
	 *           {@code aExpectedResponse} is non-{@code null} and the response
	 *           is not of the specified type, than an
	 *           {@code EPPCommandException} will be thrown.
	 *
	 * @return Response associated with passed in command
	 *
	 * @exception EPPCommandException
	 *               error processing the command. This can include an error
	 *               specified from the server or encountered while attempting to
	 *               process the command. If the exception contains an
	 *               {@code EPPResponse} than it was a server specified error.
	 */
	@Override
	public EPPResponse processDocument(EPPCommand aCommand, Class aExpectedResponse) throws EPPCommandException {
		cat.debug("processDocument(): enter");
		Document theDoc = null;

		// Encode aCommand to DOM Document (theDoc)
		try {
			theDoc = this.codec.encode(aCommand);
		}
		catch (Exception myException) {
			throw new EPPCommandException(
			      "EPPHttpSession.processDocument(): Exception on Command " + myException.getMessage());
		}

		EPPMessage theMessage = this.postMessage(aCommand);

		EPPResponse theResponse = null;
		if ((theMessage == null) || !(theMessage instanceof EPPResponse)) {
			throw new EPPCommandException(
			      "EPPHttpSession.processDocument(): Unexpected post response not being an EPPResponse with "
			            + theMessage);
		}
		else {
			theResponse = (EPPResponse) theMessage;
		}

		// Error response?
		if (!theResponse.isSuccess()) {
			cat.error(
			      "processDocument(): Error in response from Server with error code " + theResponse.getResult().getCode());
			throw new EPPCommandException("EPPHttpSession.processDocument(): Error in response from Server",
			      this.response);
		}

		// Specific response expected and response does not match expected type?
		if ((aExpectedResponse != null) && !aExpectedResponse.isInstance(theResponse)) {
			cat.error("processDocument(): Unxpected response class " + theResponse.getClass().getName());
			throw new EPPCommandException("EPPHttpSession.processDocument(): .Unexpected response type of "
			      + theResponse.getClass().getName() + ", expecting " + aExpectedResponse);
		}

		// Client transaction's match?
		super.validateClientTransId(aCommand, theResponse);

		cat.debug("processDocument(): exit");
		return theResponse;
	}

	/**
	 * A no-op for the EPPHttpSession implementation.
	 *
	 * @param aDocument
	 *           Document to send
	 * @param aMessage
	 *           Message associated with {@code newDoc} that is used for packet
	 *           logging logic. Set to {@code null} if unavailable.
	 *
	 * @throws EPPCommandException
	 *            Error processing the command
	 */
	@Override
	public void sendDocument(Document aDocument, EPPMessage aMessage) throws EPPCommandException {
		throw new EPPCommandException("sendDocument() is not implemented for EPPHttpSession.");
	}

	/**
	 * A no-op for the EPPHttpSession implementation.
	 *
	 * @return Document received
	 *
	 * @throws EPPCommandException
	 *            Error receiving the document
	 */
	@Override
	public Document recDocument() throws EPPCommandException {
		throw new EPPCommandException("recDocument() is not implemented for EPPHttpSession.");
	}

	/**
	 * Helper method to setup a login command with the username, password,
	 * version and lang values.
	 *
	 * @return an EPPLoginCmd instance
	 */
	private EPPLoginCmd setupLoginCommand() {
		EPPLoginCmd theLoginCommand = null;

		if (this.newPassword == null) {
			theLoginCommand = new EPPLoginCmd(this.transId, this.clientId, this.password);
		}
		else {
			theLoginCommand = new EPPLoginCmd(this.transId, this.clientId, this.password, this.newPassword);
		}

		if (this.version != null) {
			theLoginCommand.setVersion(this.version);
		}

		if (this.language != null) {
			theLoginCommand.setLang(this.language);
		}

		// Set the client specified services (setServices)
		if (this.services != null) {
			theLoginCommand.setServices(this.services);
		}

		return theLoginCommand;
	}

	/**
	 * Returns the HTTP version to use, with the default being HTTP/2 with
	 * {@code HttpClient.Version.HTTP_2}.
	 *
	 * @return {@code HTTPClient.Version} value
	 * @exception EPPEnvException
	 *               The &quot;EPP.ServerName&quot; property does not exist.
	 */
	public static HttpClient.Version getHttpVersion() throws EPPEnvException {
		String thePropValue = EPPEnv.getHttpVersion();
		HttpClient.Version theVersion = HttpClient.Version.HTTP_2;

		if (thePropValue != null) {
			switch (thePropValue) {
				case "1.1":
					theVersion = HttpClient.Version.HTTP_1_1;
					break;
				case "2":
					theVersion = HttpClient.Version.HTTP_2;
					break;
				// Add once HttpClient supports HTTP/3
				// case "3":
				// theVersion = HttpClient.Version.HTTP_3;
				// break;
				default:
					throw new EPPEnvException("Invalid EPP.HttpVersion value of " + thePropValue);
			}
		}

		return theVersion;
	}

	/**
	 * Gets the server URL to connect to.
	 *
	 * @return Gets the server URL to connection to if set; {@code null}
	 *         otherwise.
	 */
	public String getUrl() {
		return this.url;
	}

	/**
	 * Sets the server URL to connect to.
	 *
	 * @param aUrl
	 *           Server URL to
	 */
	public void setUrl(String aUrl) {
		this.url = aUrl;
	}

	/**
	 * Does the session support the specified mode {@link #MODE_SYNC} or
	 * {@link #MODE_ASYNC}? {@code EPPHttpSession} only supports
	 * {@link #MODE_SYNC}.
	 *
	 * @param aMode
	 *           {@link #MODE_SYNC} or {@link #MODE_ASYNC}
	 * @return {@code true} if supported; {@code false} otherwise.
	 */
	@Override
	public boolean isModeSupported(int aMode) {
		if (aMode == MODE_SYNC) {
			return true;
		}
		else {
			return false;
		}
	}

	/**
	 * Gets the EPP Greeting received when making the connection.
	 *
	 * @return The EPP Greeting if set; {@code null} otherwise.
	 */
	public EPPGreeting getGreeting() {
		return this.greeting;
	}

}
