| /* |
| * Copyright 2011 Google Inc. |
| * |
| * Licensed 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. |
| */ |
| |
| package com.google.ipc.invalidation.ticl.android; |
| |
| import com.google.common.base.Preconditions; |
| import com.google.ipc.invalidation.external.client.SystemResources.Logger; |
| import com.google.protos.ipc.invalidation.AndroidChannel.AddressedAndroidMessage; |
| |
| import org.apache.http.client.ClientProtocolException; |
| import org.apache.http.client.HttpClient; |
| import org.apache.http.client.methods.HttpPost; |
| import org.apache.http.entity.ByteArrayEntity; |
| import org.apache.http.impl.client.BasicResponseHandler; |
| |
| import java.io.IOException; |
| |
| |
| /** |
| * Implementation of the HTTP communication used by {@code AndroidChannel}. Factored into |
| * a separate class that can be run outside the Android environment to improve testing. |
| * |
| */ |
| |
| public abstract class AndroidChannelBase { |
| /** Http client to use when making requests to . */ |
| HttpClient httpClient; |
| |
| /** Authentication type for frontends. */ |
| private final String authType; |
| |
| /** URL of the frontends. */ |
| private final String channelUrl; |
| |
| /** The token that will be echoed to the data center in the headers of all HTTP requests. */ |
| private String echoToken = null; |
| |
| /** |
| * Creates an instance that uses {@code httpClient} to send requests to {@code channelUrl} |
| * using an auth type of {@code authType}. |
| */ |
| protected AndroidChannelBase(HttpClient httpClient, String authType, String channelUrl) { |
| this.httpClient = httpClient; |
| this.authType = authType; |
| this.channelUrl = channelUrl; |
| } |
| |
| /** Sends {@code outgoingMessage} to . */ |
| void deliverOutboundMessage(final byte[] outgoingMessage) { |
| getLogger().fine("Delivering outbound message: %s bytes", outgoingMessage.length); |
| StringBuilder target = new StringBuilder(); |
| |
| // Build base URL that targets the inbound request service with the encoded network endpoint id |
| target.append(channelUrl); |
| target.append(AndroidHttpConstants.REQUEST_URL); |
| target.append(getWebEncodedEndpointId()); |
| |
| // Add query parameter indicating the service to authenticate against |
| target.append('?'); |
| target.append(AndroidHttpConstants.SERVICE_PARAMETER); |
| target.append('='); |
| target.append(authType); |
| |
| // Construct entity containing the outbound protobuf msg |
| ByteArrayEntity contentEntity = new ByteArrayEntity(outgoingMessage); |
| contentEntity.setContentType(AndroidHttpConstants.PROTO_CONTENT_TYPE); |
| |
| // Construct POST request with the entity content and appropriate authorization |
| HttpPost httpPost = new HttpPost(target.toString()); |
| httpPost.setEntity(contentEntity); |
| setPostHeaders(httpPost); |
| try { |
| String response = httpClient.execute(httpPost, new BasicResponseHandler()); |
| } catch (ClientProtocolException exception) { |
| // TODO: Distinguish between key HTTP error codes and handle more specifically |
| // where appropriate. |
| getLogger().warning("Error from server on request: %s", exception); |
| } catch (IOException exception) { |
| getLogger().warning("Error writing request: %s", exception); |
| } catch (RuntimeException exception) { |
| getLogger().warning("Runtime exception writing request: %s", exception); |
| } |
| } |
| |
| /** Sets the Authorization and echo headers on {@code httpPost}. */ |
| private void setPostHeaders(HttpPost httpPost) { |
| httpPost.setHeader("Authorization", "GoogleLogin auth=" + getAuthToken()); |
| if (echoToken != null) { |
| // If we have a token to echo to the server, echo it. |
| httpPost.setHeader(AndroidHttpConstants.ECHO_HEADER, echoToken); |
| } |
| } |
| |
| /** |
| * If {@code echoToken} is not {@code null}, updates the token that will be sent in the header |
| * of all HTTP requests. |
| */ |
| void updateEchoToken(String echoToken) { |
| if (echoToken != null) { |
| this.echoToken = echoToken; |
| } |
| } |
| |
| /** Returns the token that will be sent in the header of all HTTP requests. */ |
| String getEchoTokenForTest() { |
| return this.echoToken; |
| } |
| |
| /** Sets the HTTP client to {@code client}. */ |
| void setHttpClientForTest(HttpClient client) { |
| this.httpClient = Preconditions.checkNotNull(client); |
| } |
| |
| /** Returns the base-64-encoded network endpoint id for the client. */ |
| protected abstract String getWebEncodedEndpointId(); |
| |
| /** Returns the current authentication token for the client for web requests to . */ |
| protected abstract String getAuthToken(); |
| |
| /** Returns the logger to use. */ |
| protected abstract Logger getLogger(); |
| |
| /** Attempts to deliver a {@code message} from to the local client. */ |
| protected abstract void tryDeliverMessage(AddressedAndroidMessage message); |
| } |