forked from adamfisk/LittleProxy
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
LP-5: Added general framework for adding basic authentication handlers
- Loading branch information
afisk
committed
Nov 6, 2009
1 parent
c5c1286
commit 1423e6e
Showing
10 changed files
with
336 additions
and
137 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
52 changes: 52 additions & 0 deletions
52
src/main/java/org/littleshoot/proxy/DefaultHttpProxyServer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package org.littleshoot.proxy; | ||
|
||
import java.net.InetSocketAddress; | ||
import java.util.concurrent.Executors; | ||
|
||
import org.jboss.netty.bootstrap.ServerBootstrap; | ||
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
/** | ||
* HTTP proxy server. | ||
*/ | ||
public class DefaultHttpProxyServer implements HttpProxyServer { | ||
|
||
private final Logger m_log = | ||
LoggerFactory.getLogger(DefaultHttpProxyServer.class); | ||
private final int m_port; | ||
|
||
private final ProxyAuthorizationManager m_authenticationManager = | ||
new DefaultProxyAuthorizationManager(); | ||
|
||
public DefaultHttpProxyServer(final int port) { | ||
this.m_port = port; | ||
} | ||
|
||
public void start() { | ||
m_log.info("Starting proxy on port: "+this.m_port); | ||
final ServerBootstrap bootstrap = new ServerBootstrap( | ||
new NioServerSocketChannelFactory( | ||
Executors.newCachedThreadPool(), | ||
Executors.newCachedThreadPool())); | ||
|
||
bootstrap.setPipelineFactory( | ||
new HttpServerPipelineFactory(m_authenticationManager)); | ||
bootstrap.bind(new InetSocketAddress(m_port)); | ||
|
||
/* | ||
final ServerBootstrap sslBootstrap = new ServerBootstrap( | ||
new NioServerSocketChannelFactory( | ||
Executors.newCachedThreadPool(), | ||
Executors.newCachedThreadPool())); | ||
sslBootstrap.setPipelineFactory(new HttpServerPipelineFactory()); | ||
sslBootstrap.bind(new InetSocketAddress("127.0.0.1", 8443)); | ||
*/ | ||
} | ||
|
||
public void addProxyAuthenticationHandler( | ||
final ProxyAuthorizationHandler pah) { | ||
this.m_authenticationManager.addHandler(pah); | ||
} | ||
} |
95 changes: 95 additions & 0 deletions
95
src/main/java/org/littleshoot/proxy/DefaultProxyAuthorizationManager.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
package org.littleshoot.proxy; | ||
|
||
import java.io.UnsupportedEncodingException; | ||
import java.util.Collection; | ||
import java.util.LinkedList; | ||
import java.util.List; | ||
|
||
import org.apache.commons.lang.StringUtils; | ||
import org.jboss.netty.channel.ChannelHandlerContext; | ||
import org.jboss.netty.handler.codec.http.HttpRequest; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
/** | ||
* Default authentication manager that simply processes each authentication | ||
* handler in the order it was added. | ||
* | ||
* See: http://tools.ietf.org/html/rfc2617 | ||
*/ | ||
public class DefaultProxyAuthorizationManager implements | ||
ProxyAuthorizationManager { | ||
|
||
private final Logger m_log = LoggerFactory.getLogger(getClass()); | ||
private final Collection<ProxyAuthorizationHandler> m_handlers = | ||
new LinkedList<ProxyAuthorizationHandler>(); | ||
|
||
public void addHandler(final ProxyAuthorizationHandler pah) { | ||
this.m_handlers.add(pah); | ||
} | ||
|
||
public boolean handleProxyAuthorization(final HttpRequest request, | ||
final ChannelHandlerContext ctx) { | ||
if (!request.containsHeader("Proxy-Authorization")) { | ||
if (!m_handlers.isEmpty()) { | ||
rejectRequest(ctx); | ||
return false; | ||
} | ||
return true; | ||
} | ||
|
||
final List<String> values = request.getHeaders("Proxy-Authorization"); | ||
final String fullValue = values.iterator().next(); | ||
final String value = | ||
StringUtils.substringAfter(fullValue, "Basic ").trim(); | ||
final byte[] decodedValue = Base64.decode(value); | ||
try { | ||
final String decodedString = new String(decodedValue, "UTF-8"); | ||
final String userName = StringUtils.substringBefore(decodedString, ":"); | ||
final String password = StringUtils.substringAfter(decodedString, ":"); | ||
for (final ProxyAuthorizationHandler handler : this.m_handlers) { | ||
if (!handler.authenticate(userName, password)) { | ||
rejectRequest(ctx); | ||
return false; | ||
} | ||
} | ||
} | ||
catch (final UnsupportedEncodingException e) { | ||
m_log.error("Could not decode?", e); | ||
} | ||
|
||
m_log.info("Got proxy authorization!"); | ||
// We need to remove the header before sending the request on. | ||
final String authentication = | ||
request.getHeader("Proxy-Authorization"); | ||
m_log.info(authentication); | ||
request.removeHeader("Proxy-Authorization"); | ||
return true; | ||
} | ||
|
||
private void rejectRequest(final ChannelHandlerContext ctx) { | ||
final String statusLine = "HTTP/1.1 407 Proxy Authentication Required\r\n"; | ||
final String headers = | ||
"Date: "+ProxyUtils.httpDate()+"\r\n"+ | ||
"Proxy-Authenticate: Basic realm=\"Restricted Files\"\r\n"+ | ||
"Content-Length: 415\r\n"+ | ||
"Content-Type: text/html; charset=iso-8859-1\r\n" + | ||
"\r\n"; | ||
|
||
final String responseBody = | ||
"<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n"+ | ||
"<html><head>\n"+ | ||
"<title>407 Proxy Authentication Required</title>\n"+ | ||
"</head><body>\n"+ | ||
"<h1>Proxy Authentication Required</h1>\n"+ | ||
"<p>This server could not verify that you\n"+ | ||
"are authorized to access the document\n"+ | ||
"requested. Either you supplied the wrong\n"+ | ||
"credentials (e.g., bad password), or your\n"+ | ||
"browser doesn't understand how to supply\n"+ | ||
"the credentials required.</p>\n"+ | ||
"</body></html>\n"; | ||
m_log.info("Content-Length is really: "+responseBody.length()); | ||
ProxyUtils.writeResponse(ctx.getChannel(), statusLine, headers, responseBody); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,57 +1,21 @@ | ||
package org.littleshoot.proxy; | ||
|
||
import java.net.InetSocketAddress; | ||
import java.util.concurrent.Executors; | ||
|
||
import org.jboss.netty.bootstrap.ServerBootstrap; | ||
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
/** | ||
* HTTP proxy server. | ||
*/ | ||
public class HttpProxyServer { | ||
|
||
private static final Logger LOG = | ||
LoggerFactory.getLogger(HttpProxyServer.class); | ||
|
||
/** | ||
* Starts the proxy from the command line. | ||
* | ||
* @param args Any command line arguments. | ||
*/ | ||
public static void main(final String[] args) { | ||
final ServerBootstrap bootstrap = new ServerBootstrap( | ||
new NioServerSocketChannelFactory( | ||
Executors.newCachedThreadPool(), | ||
Executors.newCachedThreadPool())); | ||
|
||
bootstrap.setPipelineFactory(new HttpServerPipelineFactory()); | ||
|
||
final int defaultPort = 8080; | ||
int port; | ||
if (args.length > 0) { | ||
final String arg = args[0]; | ||
try { | ||
port = Integer.parseInt(arg); | ||
} catch (final NumberFormatException e) { | ||
port = defaultPort; | ||
} | ||
} else { | ||
port = defaultPort; | ||
} | ||
LOG.info("Starting proxy on port: "+port); | ||
//bootstrap.bind(new InetSocketAddress("127.0.0.1", port)); | ||
bootstrap.bind(new InetSocketAddress(port)); | ||
|
||
/* | ||
final ServerBootstrap sslBootstrap = new ServerBootstrap( | ||
new NioServerSocketChannelFactory( | ||
Executors.newCachedThreadPool(), | ||
Executors.newCachedThreadPool())); | ||
sslBootstrap.setPipelineFactory(new HttpServerPipelineFactory()); | ||
sslBootstrap.bind(new InetSocketAddress("127.0.0.1", 8443)); | ||
*/ | ||
} | ||
} | ||
package org.littleshoot.proxy; | ||
|
||
/** | ||
* Interface for the top-level proxy server class. | ||
*/ | ||
public interface HttpProxyServer { | ||
|
||
/** | ||
* Starts the server. | ||
*/ | ||
void start(); | ||
|
||
/** | ||
* Adds a new handler for proxy authentication. Handlers are called in the | ||
* order they're added. If one handler accepts the user's credentials, it | ||
* passes them on to the next handler. | ||
* | ||
* @param pah The new authentication handler. | ||
*/ | ||
void addProxyAuthenticationHandler(ProxyAuthorizationHandler pah); | ||
} |
Oops, something went wrong.