Decodes responses using SAX, which is supported both in normal JVM environments, as well Android. Basic example with with Feign.Builder api = Feign.builder() .decoder(SAXDecoder.builder() .registerContentHandler(ContentHandlerForFoo.class) .registerCon
| 54 | * </pre> |
| 55 | */ |
| 56 | public class SAXDecoder implements Decoder { |
| 57 | |
| 58 | private final Map<Type, ContentHandlerWithResult.Factory<?>> handlerFactories; |
| 59 | |
| 60 | private SAXDecoder(Map<Type, ContentHandlerWithResult.Factory<?>> handlerFactories) { |
| 61 | this.handlerFactories = handlerFactories; |
| 62 | } |
| 63 | |
| 64 | public static Builder builder() { |
| 65 | return new Builder(); |
| 66 | } |
| 67 | |
| 68 | @Override |
| 69 | public Object decode(Response response, Type type) throws IOException, DecodeException { |
| 70 | if (response.status() == 404 || response.status() == 204) return Util.emptyValueOf(type); |
| 71 | if (response.body() == null) return null; |
| 72 | ContentHandlerWithResult.Factory<?> handlerFactory = handlerFactories.get(type); |
| 73 | checkState( |
| 74 | handlerFactory != null, |
| 75 | "type %s not in configured handlers %s", |
| 76 | type, |
| 77 | handlerFactories.keySet()); |
| 78 | ContentHandlerWithResult<?> handler = handlerFactory.create(); |
| 79 | try { |
| 80 | XMLReader xmlReader = XMLReaderFactory.createXMLReader(); |
| 81 | xmlReader.setFeature("http://xml.org/sax/features/namespaces", false); |
| 82 | xmlReader.setFeature("http://xml.org/sax/features/validation", false); |
| 83 | /* Explicitly control sax configuration to prevent XXE attacks */ |
| 84 | xmlReader.setFeature("http://xml.org/sax/features/external-general-entities", false); |
| 85 | xmlReader.setFeature("http://xml.org/sax/features/external-parameter-entities", false); |
| 86 | xmlReader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", false); |
| 87 | xmlReader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); |
| 88 | xmlReader.setContentHandler(handler); |
| 89 | InputStream inputStream = response.body().asInputStream(); |
| 90 | try { |
| 91 | xmlReader.parse(new InputSource(inputStream)); |
| 92 | } finally { |
| 93 | ensureClosed(inputStream); |
| 94 | } |
| 95 | return handler.result(); |
| 96 | } catch (SAXException e) { |
| 97 | throw new DecodeException(response.status(), e.getMessage(), response.request(), e); |
| 98 | } |
| 99 | } |
| 100 | |
| 101 | /** Implementations are not intended to be shared across requests. */ |
| 102 | public interface ContentHandlerWithResult<T> extends ContentHandler { |
| 103 | |
| 104 | /** expected to be set following a call to {@link XMLReader#parse(InputSource)} */ |
| 105 | T result(); |
| 106 | |
| 107 | public interface Factory<T> { |
| 108 | |
| 109 | ContentHandlerWithResult<T> create(); |
| 110 | } |
| 111 | } |
| 112 | |
| 113 | public static class Builder { |
nothing calls this directly
no outgoing calls
no test coverage detected