001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.camel.component.gae.http;
018    
019    import java.io.ByteArrayInputStream;
020    import java.io.InputStream;
021    import java.net.URL;
022    import java.util.zip.GZIPInputStream;
023    
024    import javax.servlet.http.HttpServletRequest;
025    import javax.servlet.http.HttpServletResponse;
026    
027    import com.google.appengine.api.urlfetch.HTTPHeader;
028    import com.google.appengine.api.urlfetch.HTTPMethod;
029    import com.google.appengine.api.urlfetch.HTTPRequest;
030    import com.google.appengine.api.urlfetch.HTTPResponse;
031    
032    import org.apache.camel.Exchange;
033    import org.apache.camel.Message;
034    import org.apache.camel.component.gae.bind.InboundBinding;
035    import org.apache.camel.component.gae.bind.OutboundBinding;
036    import org.apache.camel.spi.HeaderFilterStrategy;
037    import org.apache.camel.util.UnsafeUriCharactersEncoder;
038    
039    import static org.apache.camel.component.gae.http.GHttpEndpoint.getEndpointUrl;
040    import static org.apache.camel.component.http.helper.GZIPHelper.isGzip;
041    
042    /**
043     * Binds the {@link HTTPRequest}/{@link HTTPResponse} pair of the URL fetch
044     * service to a Camel {@link Exchange}.
045     */
046    public class GHttpBinding implements 
047        OutboundBinding <GHttpEndpoint, HTTPRequest, HTTPResponse>, 
048        InboundBinding  <GHttpEndpoint, HttpServletRequest, HttpServletResponse> { 
049        
050        // ----------------------------------------------------------------
051        //  Outbound binding
052        // ----------------------------------------------------------------
053        
054        /**
055         * Reads data from <code>response</code> and writes it to the out-message of
056         * the <code>exchange</code>.
057         * 
058         * @param endpoint
059         * @param exchange
060         * @param response
061         * @return the original <code>exchange</code> instance populated with
062         *         response data.
063         * @throws GHttpException
064         *             if the response code is >= 400 and
065         *             {@link GHttpEndpoint#isThrowExceptionOnFailure()} returns
066         *             <code>true</code>.
067         */
068        public Exchange readResponse(GHttpEndpoint endpoint, Exchange exchange, HTTPResponse response) throws Exception {
069            int responseCode = response.getResponseCode();
070            readResponseHeaders(endpoint, exchange, response);
071            readResponseBody(endpoint, exchange, response);
072            if (responseCode >= 400 && endpoint.isThrowExceptionOnFailure()) {
073                throw new GHttpException(responseCode, 
074                    exchange.getOut().getBody(InputStream.class), 
075                    exchange.getOut().getHeaders());
076            }
077            return exchange;
078        }
079    
080        /**
081         * Reads data from <code>exchange</code> and writes it to a newly created
082         * {@link HTTPRequest} instance. The <code>request</code> parameter is
083         * ignored.
084         * 
085         * @param endpoint
086         * @param exchange
087         * @param request
088         *            ignored.
089         * @return a newly created {@link HTTPRequest} instance containing data from
090         *         <code>exchange</code>.
091         */
092        public HTTPRequest writeRequest(GHttpEndpoint endpoint, Exchange exchange, HTTPRequest request) throws Exception {
093            HTTPRequest answer = new HTTPRequest(
094                    getRequestUrl(endpoint, exchange), 
095                    getRequestMethod(endpoint, exchange));
096            writeRequestHeaders(endpoint, exchange, answer);
097            writeRequestBody(endpoint, exchange, answer);
098            return answer;
099        }
100        
101        // ----------------------------------------------------------------
102        //  Inbound binding
103        // ----------------------------------------------------------------
104        
105        public Exchange readRequest(GHttpEndpoint endpoint, Exchange exchange, HttpServletRequest request) {
106            readRequestHeaders(endpoint, exchange, request);
107            return exchange;
108        }
109    
110        public HttpServletResponse writeResponse(GHttpEndpoint endpoint, Exchange exchange, HttpServletResponse response) {
111            return response;
112        }
113    
114        // ----------------------------------------------------------------
115        //  Customization points
116        // ----------------------------------------------------------------
117        
118        protected void readResponseHeaders(GHttpEndpoint endpoint, Exchange exchange, HTTPResponse response) {
119            HeaderFilterStrategy strategy = endpoint.getHeaderFilterStrategy();
120            
121            Message in = exchange.getIn();
122            Message out = exchange.getOut();
123            
124            out.setHeaders(in.getHeaders());
125            out.setHeader(Exchange.HTTP_RESPONSE_CODE, response.getResponseCode());
126            
127            String contentType = getResponseHeader("Content-Type", response);
128            if (contentType != null) {
129                out.setHeader(Exchange.CONTENT_TYPE, contentType);
130            }
131            
132            for (HTTPHeader header : response.getHeaders()) {
133                String name = header.getName();
134                String value = header.getValue();
135                if (strategy != null && !strategy.applyFilterToExternalHeaders(name, value, exchange)) {
136                    out.setHeader(name, value);
137                }
138            }
139        }
140        
141        protected void readRequestHeaders(GHttpEndpoint endpoint, Exchange exchange, HttpServletRequest request) {
142            // EXPERIMENTAL // TODO: resolve gzip encoding issues
143            exchange.getIn().removeHeader("Accept-Encoding");
144            exchange.getIn().removeHeader("Content-Encoding");
145        }
146    
147        protected void writeRequestHeaders(GHttpEndpoint endpoint, Exchange exchange, HTTPRequest request) {
148            HeaderFilterStrategy strategy = endpoint.getHeaderFilterStrategy();
149            for (String headerName : exchange.getIn().getHeaders().keySet()) {
150                String headerValue = exchange.getIn().getHeader(headerName, String.class);
151                if (strategy != null && !strategy.applyFilterToCamelHeaders(headerName, headerValue, exchange)) {
152                    request.addHeader(new HTTPHeader(headerName, headerValue));
153                }
154            }
155        }
156        
157        protected void readResponseBody(GHttpEndpoint endpoint, Exchange exchange, HTTPResponse response) throws Exception {
158            byte[] content = response.getContent();
159            if (content != null) {
160                InputStream stream = new ByteArrayInputStream(content);
161                if (isGzip(getResponseHeader("Content-Encoding", response))) {
162                    stream = new GZIPInputStream(stream);
163                }
164                exchange.getOut().setBody(stream);
165            }
166        }
167        
168        protected void writeRequestBody(GHttpEndpoint endpoint, Exchange exchange, HTTPRequest request) {
169            request.setPayload(exchange.getIn().getBody(byte[].class));
170        }
171        
172        protected URL getRequestUrl(GHttpEndpoint endpoint, Exchange exchange) throws Exception {
173            String uri = exchange.getIn().getHeader(Exchange.HTTP_URI, String.class);
174            String query = exchange.getIn().getHeader(Exchange.HTTP_QUERY, String.class);
175            if (uri != null && !endpoint.isBridgeEndpoint()) {
176                return getEndpointUrl(UnsafeUriCharactersEncoder.encode(uri), query);
177            }
178            return getEndpointUrl(endpoint.getEndpointUri(), query);
179        }
180        
181        protected HTTPMethod getRequestMethod(GHttpEndpoint endpoint, Exchange exchange) {
182            String method = (String)exchange.getIn().getHeader(Exchange.HTTP_METHOD);
183            if (method != null) {
184                return HTTPMethod.valueOf(method);
185            } else if (exchange.getIn().getBody() != null) {
186                return HTTPMethod.POST;
187            } else {
188                return HTTPMethod.GET;
189            }
190        }
191        
192        protected String getResponseHeader(String name, HTTPResponse response) {
193            for (HTTPHeader header : response.getHeaders()) {
194                if (header.getName().equalsIgnoreCase(name)) {
195                    return header.getValue();
196                }
197            }
198            return null;
199        }
200    
201    }