/*
 * ====================================================================
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 *
 */

package org.apache.http.localserver;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;

import org.apache.http.HttpHost;
import org.apache.http.HttpVersion;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.scheme.SchemeSocketFactory;
import org.apache.http.impl.DefaultHttpClientConnection;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.params.SyncBasicHttpParams;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.BasicHttpProcessor;
import org.apache.http.protocol.HttpRequestExecutor;
import org.apache.http.protocol.RequestConnControl;
import org.apache.http.protocol.RequestContent;
import org.junit.Before;

/**
 * Base class for tests using {@link LocalTestServer LocalTestServer}. Note that
 * the test server will be {@link #setUp set up} before each individual tests
 * and {@link #tearDown teared down} afterwards. Use this base class
 * <i>exclusively</i> for tests that require the server. If you have some tests
 * that require the server and others that don't, split them in two different
 * classes.
 */
public abstract class ServerTestBase extends BasicServerTestBase
{

	/** The available schemes. */
	protected SchemeRegistry supportedSchemes;

	/** The default parameters for the client side. */
	protected HttpParams defaultParams;

	/** The HTTP processor for the client side. */
	protected BasicHttpProcessor httpProcessor;

	/** The default context for the client side. */
	protected BasicHttpContext httpContext;

	/** The request executor for the client side. */
	protected HttpRequestExecutor httpExecutor;

	/**
	 * Prepares the local server for testing. Derived classes that override this
	 * method MUST call the implementation here. That SHOULD be done at the
	 * beginning of the overriding method. <br/>
	 * Derived methods can modify for example the default parameters being set
	 * up, or the interceptors.
	 * <p>
	 * This method will re-use the helper objects from a previous run if they are
	 * still available. For example, the local test server will be re-started
	 * rather than re-created. {@link #httpContext httpContext} will always be
	 * re-created. Tests that modify the other helper objects should afterwards
	 * set the respective attributes to <code>null</code> in a
	 * <code>finally{}</code> block to force re-creation for subsequent tests. Of
	 * course that shouldn't be done with the test server, or only after shutting
	 * that down.
	 * 
	 * @throws Exception
	 *            in case of a problem
	 */
	@Before
	public void setUp() throws Exception
	{

		if( defaultParams == null )
		{
			defaultParams = new SyncBasicHttpParams();
			HttpProtocolParams.setVersion( defaultParams, HttpVersion.HTTP_1_1 );
			HttpProtocolParams.setContentCharset( defaultParams, "UTF-8" );
			HttpProtocolParams.setUserAgent( defaultParams, "TestAgent/1.1" );
			HttpProtocolParams.setUseExpectContinue( defaultParams, false );
		}

		if( supportedSchemes == null )
		{
			supportedSchemes = new SchemeRegistry();
			SchemeSocketFactory sf = PlainSocketFactory.getSocketFactory();
			supportedSchemes.register( new Scheme( "http", 80, sf ) );
		}

		if( httpProcessor == null )
		{
			httpProcessor = new BasicHttpProcessor();
			httpProcessor.addInterceptor( new RequestContent() );
			httpProcessor.addInterceptor( new RequestConnControl() ); // optional
		}

		// the context is created each time, it may get modified by test cases
		httpContext = new BasicHttpContext( null );

		if( httpExecutor == null )
		{
			httpExecutor = new HttpRequestExecutor();
		}

		if( localServer == null )
		{
			localServer = new LocalTestServer( null, null );
			localServer.registerDefaultHandlers();
		}

		localServer.start();

	} // setUp

	/**
	 * Opens a connection to the given target using {@link #defaultParams default
	 * parameters}. Maps to {@link #connectTo(HttpHost,HttpParams)
	 * connectTo(target,defaultParams)}.
	 * 
	 * @param target
	 *           the target to connect to
	 * 
	 * @return a new connection opened to the target
	 * 
	 * @throws Exception
	 *            in case of a problem
	 */
	protected DefaultHttpClientConnection connectTo( HttpHost target ) throws Exception
	{

		return connectTo( target, defaultParams );
	}

	/**
	 * Opens a connection to the given target using the given parameters.
	 * 
	 * @param target
	 *           the target to connect to
	 * 
	 * @return a new connection opened to the target
	 * 
	 * @throws Exception
	 *            in case of a problem
	 */
	protected DefaultHttpClientConnection connectTo( HttpHost target, HttpParams params ) throws Exception
	{

		Scheme schm = supportedSchemes.get( target.getSchemeName() );
		int port = schm.resolvePort( target.getPort() );

		DefaultHttpClientConnection conn = new DefaultHttpClientConnection();
		InetSocketAddress address = new InetSocketAddress( InetAddress.getByName( target.getHostName() ), port );
		Socket sock = schm.getSchemeSocketFactory().connectSocket( null, address, null, params );
		conn.bind( sock, params );

		return conn;
	}

}
