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.artifact.repository.metadata.Metadata;
import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Writer;
import org.apache.maven.plugin.logging.Log;
import org.codehaus.mojo.mockrepo.ByteArrayContent;
import org.codehaus.mojo.mockrepo.Content;
import org.codehaus.mojo.mockrepo.Repository;
import org.codehaus.plexus.util.StringUtils;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
class SimpleHttpWorker
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 Repository repository;
private final Date lastModified;
private final Log log;
SimpleHttpWorker( Socket s, Repository repository, Date lastModified, Log log )
{
this.s = s;
this.lastModified = lastModified;
this.repository = repository;
this.log = log;
}
private boolean isDir( String path )
{
return StringUtils.isEmpty( path ) || path.endsWith( "/" );
}
private boolean isMetadata( String path )
{
return path.endsWith( "metadata.xml" ) || path.endsWith( "metadata.xml.md5" ) || path.endsWith(
"metadata.xml.sha1" );
}
private boolean isFile( String path )
{
String artifactId;
String version;
int index1;
int index2;
int index3;
index3 = path.lastIndexOf( '/' );
if ( index3 == -1 || index3 + 1 >= path.length() )
{
// not a valid content path, so nothing at this path
return false;
}
index2 = path.lastIndexOf( '/', index3 - 1 );
if ( index2 == -1 )
{
// not a valid content path, so nothing at this path
return false;
}
version = path.substring( index2 + 1, index3 );
index1 = path.lastIndexOf( '/', index2 - 1 );
if ( index1 == -1 )
{
// not a valid content path, so nothing at this path
return false;
}
artifactId = path.substring( index1 + 1, index2 );
String name = path.substring( index3 + 1 );
return name.startsWith( artifactId + '-' + version );
}
private String[] listDir( String path )
{
final Set result = new TreeSet(repository.getChildPaths( path ));
if ( repository.getMetadata( path ) != null )
{
result.add( "metadata.xml" );
result.add( "metadata.xml.md5" );
result.add( "metadata.xml.sha1" );
}
return (String[]) result.toArray( new String[result.size()] );
}
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 );
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
{
ps.print( "HTTP/1.0 405 unsupported method type: " );
ps.write( buf, 0, 5 );
ps.write( EOL );
ps.flush();
s.close();
return;
}
int i = index;
while ( i < nread )
{
if ( buf[i] == (byte) ' ' )
{
break;
}
i++;
}
String requestPath = ( new String( buf, index, i - index, "UTF-8" ) ).replace( '/', File.separatorChar );
if ( requestPath.startsWith( File.separator ) )
{
requestPath = requestPath.substring( 1 );
}
final Content content = sendHeaders( ps, requestPath );
boolean OK = content != null || isDir( requestPath );
if ( doingGet )
{
if ( OK )
{
sendFile( ps, requestPath, content );
}
else
{
send404( ps, requestPath );
}
}
}
finally
{
s.close();
}
}
Content sendHeaders( PrintStream ps, String requestPath )
throws IOException
{
final Content content;
final boolean isDirectory;
final boolean isExists;
if ( isMetadata( requestPath ) )
{
log.debug("request path: " + requestPath + " is for metadata");
isDirectory = false;
Metadata metadata = repository.getMetadata( requestPath );
if ( metadata != null )
{
MetadataXpp3Writer writer = new MetadataXpp3Writer();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
OutputStreamWriter osw = new OutputStreamWriter( bos );
writer.write( osw, metadata );
osw.close();
bos.close();
content = new ByteArrayContent( bos.toByteArray() );
isExists = true;
}
else
{
isExists = false;
content = null;
}
}
else if ( isDir(requestPath ))
{
log.debug("request path: " + requestPath + " is for directory");
content = null;
isDirectory = true;
isExists = true;
}
else if (isFile( requestPath ))
{
log.debug("request path: " + requestPath + " is for file");
content = repository.getContent( requestPath );
isDirectory = false;
isExists = content != null;
} else {
log.debug("request path: " + requestPath + " does not exist");
isDirectory = false;
isExists = false;
content = null;
}
boolean result;
if ( !isExists )
{
ps.print( "HTTP/1.0 404 not found" );
ps.write( EOL );
}
else
{
ps.print( "HTTP/1.0 200 OK" );
ps.write( EOL );
}
ps.print( "Server: Mock Maven Remote Repository Server" );
ps.write( EOL );
ps.print( "Date: " + ( new Date() ) );
ps.write( EOL );
if ( isExists )
{
if ( !isDirectory )
{
ps.print( "Content-length: " + content.getSize() );
ps.write( EOL );
ps.print( "Last Modified: " + content.getLastModified() );
ps.write( EOL );
int ind = requestPath.lastIndexOf( '/' );
String name = ind == -1 ? requestPath : requestPath.substring( ind + 1 );
ind = name.lastIndexOf( '.' );
String ct = null;
if ( ind > 0 )
{
ct = (String) map.get( name.substring( ind ) );
}
if ( ct == null )
{
ct = "unknown/unknown";
}
ps.print( "Content-type: " + ct );
ps.write( EOL );
}
else
{
ps.print( "Content-type: text/html" );
ps.write( EOL );
}
}
else
{
ps.print( "Content-type: text/html" );
ps.write( EOL );
}
return content;
}
void send404( PrintStream ps, String requestPath )
throws IOException
{
ps.println( "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">" );
ps.println( "<html>" );
ps.println( " <head>" );
ps.println( " <title>404 Not Found</title>" );
ps.println( " </head>" );
ps.println( " <body style='font-family: \"Trebuchet MS\",verdana,lucida,arial,helvetica,sans-serif;'>" );
ps.println( " <h1>Not Found</h1>" );
ps.println( " <p>The requested URL " + requestPath + " was not found on this server.</p>" );
ps.println( " </body>" );
ps.println( "</html>" );
}
void sendFile( PrintStream ps, String requestPath, Content content )
throws IOException
{
InputStream is;
final boolean isDirectory = isDir( requestPath );
ps.write( EOL );
if ( isDirectory )
{
listDirectory( ps, requestPath );
return;
}
else
{
is = content.getInputStream();
}
try
{
int n;
while ( ( n = is.read( buf ) ) > 0 )
{
ps.write( buf, 0, n );
}
}
finally
{
is.close();
}
}
/* mapping of file extensions to repository-types */
private static final Map map = new HashMap();
static
{
map.put( ".jar", "application/octet-stream" );
map.put( ".war", "application/octet-stream" );
map.put( ".ear", "application/octet-stream" );
map.put( ".par", "application/octet-stream" );
map.put( ".rar", "application/octet-stream" );
map.put( ".sar", "application/octet-stream" );
map.put( ".exe", "application/octet-stream" );
map.put( ".zip", "application/zip" );
map.put( ".tar", "application/x-tar" );
map.put( ".htm", "text/html" );
map.put( ".html", "text/html" );
map.put( ".xml", "text/xml" );
map.put( ".pom", "text/xml" );
map.put( ".java", "text/plain" );
map.put( ".md5", "text/plain" );
map.put( ".sha1", "text/plain" );
}
void listDirectory( PrintStream ps, String path )
throws IOException
{
ps.println( "<html>" );
ps.println( " <head>" );
ps.println( " <title>Index of /" + path + "</title>" );
ps.println( " <meta http-equiv=\"Content-Type\" repository=\"text/html; charset=utf-8\"/>" );
ps.println( "</head>" );
ps.println( "<body style='font-family: \"Trebuchet MS\",verdana,lucida,arial,helvetica,sans-serif;'>" );
ps.println( "<h1>Index of /" + path + "</h1>" );
ps.println( " <table cellspacing='10'>" );
ps.println( " <tr>" );
ps.println( " <th align='left'>Name</th>" );
ps.println( " <th>Last Modified</th>" );
ps.println( " <th>Size</th>" );
ps.println( " <th>Description</th>" );
ps.println( " </tr>" );
if ( path.length() > 0 )
{
ps.println( " <tr>" );
ps.println( " <td>" );
ps.println( " <a href='..'>Parent Directory</a>" );
ps.println( " </td>" );
ps.println( " </tr>" );
}
String[] list = listDir( path );
for ( int i = 0; list != null && i < list.length; i++ )
{
ps.println( " <tr>" );
ps.println( " <td>" );
final String childPath = path + list[i];
Content childContent = childPath.endsWith( "/" ) ? null : repository.getContent( childPath );
if ( childContent != null )
{
ps.println( " <a href=\"" + list[i] + "\">" + list[i] + "</a>" );
}
else
{
ps.println( " <a href=\"" + list[i] + "\">" + list[i] + "</a>" );
}
ps.println( " </td>" );
ps.println( " <td>" );
ps.println( " " + (childContent != null ? childContent.getLastModified() : lastModified) );
ps.println( " </td>" );
ps.println( " <td align='right'>" );
if ( childContent != null )
{
ps.println( " " + childContent.getSize() );
}
else
{
ps.println( " " );
}
ps.println( " </td>" );
ps.println( " <td> </td>" );
ps.println( " </tr>" );
}
ps.println( " </table>" );
ps.println( "</body>" );
ps.println( "</html>" );
}
}