package org.codehaus.mojo.mockrepo.server; /* * 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. */ import org.apache.maven.plugin.logging.Log; import java.io.BufferedInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; import java.net.InetSocketAddress; import java.net.Socket; import java.net.URLDecoder; import java.util.Date; class BasicHttpWorker implements Runnable { private final static int BUF_SIZE = 2048; private static final byte[] EOL = {(byte) '\r', (byte) '\n'}; private final byte[] buf = new byte[BUF_SIZE]; private final Socket s; private final HttpSite site; private final Log log; BasicHttpWorker( Socket s, HttpSite site, Log log ) { this.s = s; this.site = site; this.log = log; } public void run() { try { handleClient(); } catch ( Exception e ) { e.printStackTrace(); } } void handleClient() throws IOException { InputStream is = new BufferedInputStream( s.getInputStream() ); PrintStream ps = new PrintStream( s.getOutputStream() ); s.setTcpNoDelay( true ); final InetSocketAddress socketAddress = (InetSocketAddress) s.getRemoteSocketAddress(); String clientSource = socketAddress.getAddress() + ":" + socketAddress.getPort(); try { int nread = 0, r = 0; outerloop: while ( nread < BUF_SIZE ) { r = is.read( buf, nread, BUF_SIZE - nread ); if ( r == -1 ) { /* EOF */ return; } int i = nread; nread += r; for ( ; i < nread; i++ ) { if ( buf[i] == (byte) '\n' || buf[i] == (byte) '\r' ) { /* read one line */ break outerloop; } } } boolean doingGet; int index; if ( buf[0] == (byte) 'G' && buf[1] == (byte) 'E' && buf[2] == (byte) 'T' && buf[3] == (byte) ' ' ) { doingGet = true; index = 4; } else if ( buf[0] == (byte) 'H' && buf[1] == (byte) 'E' && buf[2] == (byte) 'A' && buf[3] == (byte) 'D' && buf[4] == (byte) ' ' ) { doingGet = false; index = 5; } else { index = nread; for ( int i = 0; i < nread; i++ ) { if ( ( buf[i] == (byte) '\n' ) || ( buf[i] == (byte) '\r' ) ) { index = i; break; } } HttpContent content = new ErrorPageContent( 405, "Method Not Allowed" ); log.error( clientSource + " \"" + new String( buf, 0, index ) + "\" 405 " + content.getSize() ); sendHeaders( ps, 405, "Method Not Allowed", content ); ps.print( "Allow: HEAD GET" ); ps.write( EOL ); sendContent( ps, content ); ps.flush(); s.close(); return; } int i = index; while ( i < nread ) { if ( buf[i] == (byte) ' ' ) { break; } i++; } int lineEnd = nread; for ( int j = i; j < nread; j++ ) { if ( ( buf[j] == (byte) '\n' ) || ( buf[j] == (byte) '\r' ) ) { lineEnd = i; break; } } // request paths are always in US-ASCII according to RFC2396 String requestPath = ( new String( buf, index, i - index, "US-ASCII" ) ); // space can be escaped with the + symbol all other characters are escaped with %nnn // there is no defined charset for the high-byte characters, so we will use UTF-8 // sure why not! requestPath = URLDecoder.decode( requestPath.replace( '+', ' ' ), "UTF-8" ); if ( requestPath.startsWith( File.separator ) ) { requestPath = requestPath.substring( 1 ); } HttpContent content; log.info( requestPath ); if ( !site.isValidPath( requestPath ) ) { content = new ErrorPageContent( 400, "Bad Request" ); log.warn( clientSource + " \"" + new String( buf, 0, lineEnd ) + "\" 400 " + content.getSize() ); sendHeaders( ps, 400, "Bad Request", content ); if ( doingGet ) { sendContent( ps, content ); } } else { content = site.getContent( requestPath ); if ( content == null ) { content = new ErrorPageContent( 404, "Not Found", "The request path: " + requestPath + " was not found" ); log.info( clientSource + " \"" + new String( buf, 0, lineEnd ) + "\" 404 " + content.getSize() ); sendHeaders( ps, 404, "Not Found", content ); if ( doingGet ) { sendContent( ps, content ); } } else { log.info( clientSource + " \"" + new String( buf, 0, lineEnd ) + "\" 200 " + content.getSize() ); sendHeaders( ps, 200, "OK", content ); if ( doingGet ) { sendContent( ps, content ); } } } } finally { ps.flush(); s.close(); } } void sendHeaders( PrintStream ps, int code, String title, HttpContent content ) throws IOException { ps.print( "HTTP/1.0 " ); ps.print( code ); ps.print( ' ' ); ps.print( title ); ps.write( EOL ); ps.print( "Server: Mock Maven Remote Repository Server" ); ps.write( EOL ); ps.print( "Date: " + new Date() ); ps.write( EOL ); if ( content.getSize() > 0 ) { ps.print( "Content-length: " + content.getSize() ); ps.write( EOL ); if ( content.getLastModified() != null ) { ps.print( "Last Modified: " + content.getLastModified() ); ps.write( EOL ); } } if ( content.getContentType() != null ) { ps.print( "Content-type: " ); ps.print( content.getContentType() ); ps.write( EOL ); } } void sendContent( PrintStream ps, HttpContent content ) throws IOException { ps.write( EOL ); InputStream is = null; try { is = content.getInputStream(); int n; while ( ( n = is.read( buf ) ) > 0 ) { ps.write( buf, 0, n ); } } finally { if ( is != null ) { is.close(); } } } }