Allow to specify the mode the encoder uses for form params. This allows it to be used with OAUTH
The OAuth 1 spec has small deviations from UrlEncoder.encode's output. + Percent encodes the parameters + Added tests to verify + See relevant OAuth section http://oauth.net/core/1.0/#encoding_parameters + Detailed explanation http://hueniverse.com/oauth/guide/authentication/
This commit is contained in:
parent
b46760f93f
commit
2d9cc9f63b
@ -31,9 +31,12 @@ import java.io.UnsupportedEncodingException;
|
|||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.ListIterator;
|
import java.util.ListIterator;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import static io.netty.buffer.Unpooled.*;
|
import static io.netty.buffer.Unpooled.*;
|
||||||
|
|
||||||
@ -41,6 +44,31 @@ import static io.netty.buffer.Unpooled.*;
|
|||||||
* This encoder will help to encode Request for a FORM as POST.
|
* This encoder will help to encode Request for a FORM as POST.
|
||||||
*/
|
*/
|
||||||
public class HttpPostRequestEncoder implements ChunkedMessageInput<HttpContent> {
|
public class HttpPostRequestEncoder implements ChunkedMessageInput<HttpContent> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Different modes to use to encode form data.
|
||||||
|
*/
|
||||||
|
public enum EncoderMode {
|
||||||
|
/**
|
||||||
|
* Legacy mode which should work for most. It is known to not work with OAUTH. For OAUTH use
|
||||||
|
* {@link EncoderMode#RFC3986}. The W3C form recommentations this for submitting post form data.
|
||||||
|
*/
|
||||||
|
RFC1738,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mode which is more new and is used for OAUTH
|
||||||
|
*/
|
||||||
|
RFC3986
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Map<Pattern, String> percentEncodings = new HashMap<Pattern, String>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
percentEncodings.put(Pattern.compile("\\*"), "%2A");
|
||||||
|
percentEncodings.put(Pattern.compile("\\+"), "%20");
|
||||||
|
percentEncodings.put(Pattern.compile("%7E"), "~");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Factory used to create InterfaceHttpData
|
* Factory used to create InterfaceHttpData
|
||||||
*/
|
*/
|
||||||
@ -89,6 +117,8 @@ public class HttpPostRequestEncoder implements ChunkedMessageInput<HttpContent>
|
|||||||
*/
|
*/
|
||||||
private boolean headerFinalized;
|
private boolean headerFinalized;
|
||||||
|
|
||||||
|
private final EncoderMode encoderMode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param request
|
* @param request
|
||||||
@ -102,7 +132,7 @@ public class HttpPostRequestEncoder implements ChunkedMessageInput<HttpContent>
|
|||||||
*/
|
*/
|
||||||
public HttpPostRequestEncoder(FullHttpRequest request, boolean multipart) throws ErrorDataEncoderException {
|
public HttpPostRequestEncoder(FullHttpRequest request, boolean multipart) throws ErrorDataEncoderException {
|
||||||
this(new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE), request, multipart,
|
this(new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE), request, multipart,
|
||||||
HttpConstants.DEFAULT_CHARSET);
|
HttpConstants.DEFAULT_CHARSET, EncoderMode.RFC1738);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -120,7 +150,7 @@ public class HttpPostRequestEncoder implements ChunkedMessageInput<HttpContent>
|
|||||||
*/
|
*/
|
||||||
public HttpPostRequestEncoder(HttpDataFactory factory, FullHttpRequest request, boolean multipart)
|
public HttpPostRequestEncoder(HttpDataFactory factory, FullHttpRequest request, boolean multipart)
|
||||||
throws ErrorDataEncoderException {
|
throws ErrorDataEncoderException {
|
||||||
this(factory, request, multipart, HttpConstants.DEFAULT_CHARSET);
|
this(factory, request, multipart, HttpConstants.DEFAULT_CHARSET, EncoderMode.RFC1738);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -133,13 +163,16 @@ public class HttpPostRequestEncoder implements ChunkedMessageInput<HttpContent>
|
|||||||
* True if the FORM is a ENCTYPE="multipart/form-data"
|
* True if the FORM is a ENCTYPE="multipart/form-data"
|
||||||
* @param charset
|
* @param charset
|
||||||
* the charset to use as default
|
* the charset to use as default
|
||||||
|
* @param encoderMode
|
||||||
|
* the mode for the encoder to use. See {@link EncoderMode} for the details.
|
||||||
* @throws NullPointerException
|
* @throws NullPointerException
|
||||||
* for request or charset or factory
|
* for request or charset or factory
|
||||||
* @throws ErrorDataEncoderException
|
* @throws ErrorDataEncoderException
|
||||||
* if the request is not a POST
|
* if the request is not a POST
|
||||||
*/
|
*/
|
||||||
public HttpPostRequestEncoder(
|
public HttpPostRequestEncoder(
|
||||||
HttpDataFactory factory, FullHttpRequest request, boolean multipart, Charset charset)
|
HttpDataFactory factory, FullHttpRequest request, boolean multipart, Charset charset,
|
||||||
|
EncoderMode encoderMode)
|
||||||
throws ErrorDataEncoderException {
|
throws ErrorDataEncoderException {
|
||||||
if (factory == null) {
|
if (factory == null) {
|
||||||
throw new NullPointerException("factory");
|
throw new NullPointerException("factory");
|
||||||
@ -163,6 +196,7 @@ public class HttpPostRequestEncoder implements ChunkedMessageInput<HttpContent>
|
|||||||
isLastChunkSent = false;
|
isLastChunkSent = false;
|
||||||
isMultipart = multipart;
|
isMultipart = multipart;
|
||||||
multipartHttpDatas = new ArrayList<InterfaceHttpData>();
|
multipartHttpDatas = new ArrayList<InterfaceHttpData>();
|
||||||
|
this.encoderMode = encoderMode;
|
||||||
if (isMultipart) {
|
if (isMultipart) {
|
||||||
initDataMultipart();
|
initDataMultipart();
|
||||||
}
|
}
|
||||||
@ -688,12 +722,19 @@ public class HttpPostRequestEncoder implements ChunkedMessageInput<HttpContent>
|
|||||||
* @throws ErrorDataEncoderException
|
* @throws ErrorDataEncoderException
|
||||||
* if the encoding is in error
|
* if the encoding is in error
|
||||||
*/
|
*/
|
||||||
private static String encodeAttribute(String s, Charset charset) throws ErrorDataEncoderException {
|
private String encodeAttribute(String s, Charset charset) throws ErrorDataEncoderException {
|
||||||
if (s == null) {
|
if (s == null) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
return URLEncoder.encode(s, charset.name());
|
String encoded = URLEncoder.encode(s, charset.name());
|
||||||
|
if (encoderMode == EncoderMode.RFC3986) {
|
||||||
|
for (Map.Entry<Pattern, String> entry : percentEncodings.entrySet()) {
|
||||||
|
String replacement = entry.getValue();
|
||||||
|
encoded = entry.getKey().matcher(encoded).replaceAll(replacement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return encoded;
|
||||||
} catch (UnsupportedEncodingException e) {
|
} catch (UnsupportedEncodingException e) {
|
||||||
throw new ErrorDataEncoderException(charset.name(), e);
|
throw new ErrorDataEncoderException(charset.name(), e);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user