/*
* httpServer.java
*
* Created on October 23, 2007, 2:22 AM
*
* CodaServer and related original technologies are copyright 2008, 18th Street Software, LLC.
*
* Permission to use them is granted under the terms of the GNU GPLv2.
*/
package org.codalang.codaserver.httpServer;
import com.caucho.hessian.io.AbstractHessianOutput;
import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.HessianOutput;
import com.caucho.hessian.io.SerializerFactory;
import com.caucho.hessian.server.HessianSkeleton;
import org.apache.http.*;
import org.apache.http.entity.ContentProducer;
import org.apache.http.entity.EntityTemplate;
import org.apache.http.impl.DefaultConnectionReuseStrategy;
import org.apache.http.impl.DefaultHttpResponseFactory;
import org.apache.http.impl.nio.DefaultServerIOEventDispatch;
import org.apache.http.impl.nio.reactor.DefaultListeningIOReactor;
import org.apache.http.nio.NHttpConnection;
import org.apache.http.nio.protocol.BufferingHttpServiceHandler;
import org.apache.http.nio.protocol.EventListener;
import org.apache.http.nio.reactor.IOEventDispatch;
import org.apache.http.nio.reactor.ListeningIOReactor;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.protocol.*;
import org.apache.http.util.EntityUtils;
import org.codalang.codaserver.CodaServer;
import java.io.*;
import java.net.InetSocketAddress;
import java.util.logging.Level;
import java.util.logging.Logger;
public class httpServer {
BasicHttpParams params = new BasicHttpParams();
int port;
ListeningIOReactor ioReactor;
Logger logger;
public httpServer(Logger logger, int port, int soTimeout, int socketBufferSize, boolean staleConnectorCheck, boolean tcpNoDelay, String listenerString) {
this.params
.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, soTimeout) //5000
.setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, socketBufferSize) // 8 * 1024
.setBooleanParameter(CoreConnectionPNames.STALE_CONNECTION_CHECK, staleConnectorCheck) // false
.setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, tcpNoDelay) // true
.setParameter(CoreProtocolPNames.ORIGIN_SERVER, listenerString); // anything you want
this.port = port;
this.logger = logger;
}
public void start(CodaServer server) {
BasicHttpProcessor httpproc = new BasicHttpProcessor();
httpproc.addInterceptor(new ResponseDate());
httpproc.addInterceptor(new ResponseServer());
httpproc.addInterceptor(new ResponseContent());
httpproc.addInterceptor(new ResponseConnControl());
BufferingHttpServiceHandler handler = new BufferingHttpServiceHandler(
httpproc,
new DefaultHttpResponseFactory(),
new DefaultConnectionReuseStrategy(),
params);
// Set up request handlers
HttpRequestHandlerRegistry reqistry = new HttpRequestHandlerRegistry();
reqistry.register("*", new HttpFileHandler(server) );
handler.setHandlerResolver(reqistry);
// Provide an event logger
handler.setEventListener(new EventLogger(logger));
IOEventDispatch ioEventDispatch = new DefaultServerIOEventDispatch(handler, params);
try {
ioReactor = new DefaultListeningIOReactor(2, params);
ioReactor.listen(new InetSocketAddress(port));
ioReactor.execute(ioEventDispatch);
} catch (InterruptedIOException ex) {
logger.log(Level.WARNING, "Listener interrupted");
} catch (IOException e) {
logger.log(Level.WARNING, "Listener I/O error: " + e.getMessage());
}
}
public void stop() {
try {
ioReactor.shutdown();
} catch(Exception e) {
logger.log(Level.WARNING, "Listener could not be shut down");
}
}
static class HttpFileHandler implements HttpRequestHandler {
private static CodaServer server;
public HttpFileHandler (CodaServer server) {
if (this.server == null) {
this.server = server;
}
_objectSkeleton = new HessianSkeleton( new CodaAPI(server), ICodaAPI.class );
}
private SerializerFactory _serializerFactory;
public SerializerFactory getSerializerFactory()
{
if (_serializerFactory == null)
_serializerFactory = new SerializerFactory();
return _serializerFactory;
}
private Hessian2Input in;
private HessianSkeleton _objectSkeleton = null;
public void handle(
final HttpRequest request,
final HttpResponse response,
final HttpContext context) throws HttpException, IOException {
String method = request.getRequestLine().getMethod().toUpperCase();
// if (!method.equals("GET") && !method.equals("HEAD") && !method.equals("POST")) {
if ( ! method.equals("POST") ) {
response.setStatusCode( 500 );
response.setReasonPhrase( "CodaServer Requires POST." );
EntityTemplate body = new EntityTemplate( new ContentProducer() {
public void writeTo( final OutputStream outstream ) throws IOException {
OutputStreamWriter writer = new OutputStreamWriter( outstream, "UTF-8" );
writer.write("<html><body><h1>CodaServer Requires POST</h1></body></html>");
writer.flush();
}
});
body.setContentType( "text/html; charset=UTF-8" );
response.setEntity( body );
//throw new MethodNotSupportedException( "Only POST is supported" );
}
if (request instanceof HttpEntityEnclosingRequest) {
HttpEntity entity = ((HttpEntityEnclosingRequest) request).getEntity();
byte[] entityContent = EntityUtils.toByteArray(entity);
try {
InputStream is = new ByteArrayInputStream( entityContent );
in = new Hessian2Input(is);
getSerializerFactory();
in.setSerializerFactory( _serializerFactory);
int code = in.read();
if (code != 'c') {
// XXX: deflate
throw new IOException("expected 'c' in hessian input at " + code);
}
EntityTemplate body = new EntityTemplate( new ContentProducer() {
@SuppressWarnings("unused")
public void writeTo( final OutputStream outstream ) throws IOException {
int major = in.read();
int minor = in.read();
AbstractHessianOutput out;
out = new HessianOutput( outstream );
out.setSerializerFactory(_serializerFactory);
try {
_objectSkeleton.invoke(in, out);
} catch ( Exception e ) {
e.printStackTrace();
} catch ( Throwable t ) {
t.printStackTrace();
}
out.close();
}
});
body.setContentType( "text/html; charset=UTF-8" );
response.setEntity( body );
} catch (RuntimeException e) {
throw e;
}
}
}
}
static class EventLogger implements EventListener {
Logger logger;
public EventLogger(Logger logger) {
this.logger = logger;
}
public void connectionOpen(final NHttpConnection conn) {
logger.log(Level.INFO, "Connection open: " + conn);
}
public void connectionTimeout(final NHttpConnection conn) {
logger.log(Level.INFO, "Connection timed out: " + conn);
}
public void connectionClosed(final NHttpConnection conn) {
logger.log(Level.INFO, "Connection closed: " + conn);
}
public void fatalIOException(final IOException ex, final NHttpConnection conn) {
logger.log(Level.WARNING, "I/O error: " + ex.getMessage());
}
public void fatalProtocolException(final HttpException ex, final NHttpConnection conn) {
logger.log(Level.WARNING, "HTTP error: " + ex.getMessage());
}
}
}