001package com.gigya.android.sdk.network.adapter;
002
003import android.os.AsyncTask;
004
005import com.gigya.android.sdk.api.GigyaApiRequest;
006import com.gigya.android.sdk.api.GigyaApiHttpRequest;
007import com.gigya.android.sdk.api.IApiRequestFactory;
008import com.gigya.android.sdk.network.GigyaError;
009
010import java.io.BufferedReader;
011import java.io.IOException;
012import java.io.InputStream;
013import java.io.InputStreamReader;
014import java.io.OutputStreamWriter;
015import java.net.HttpURLConnection;
016import java.net.URL;
017import java.util.Iterator;
018import java.util.Queue;
019import java.util.concurrent.ConcurrentLinkedQueue;
020import java.util.zip.GZIPInputStream;
021
022public class HttpNetworkProvider extends NetworkProvider {
023
024    private Queue<HttpTask> _queue = new ConcurrentLinkedQueue<>();
025
026    public HttpNetworkProvider(IApiRequestFactory requestFactory) {
027        super(requestFactory);
028    }
029
030    @Override
031    public void addToQueue(GigyaApiRequest request, IRestAdapterCallback networkCallbacks) {
032        if (_blocked) {
033            _queue.add(new HttpTask(_requestFactory, new GigyaNetworkAsyncTask(networkCallbacks), request));
034            return;
035        }
036        // If not blocked send the request.
037        new GigyaNetworkAsyncTask(networkCallbacks).execute(_requestFactory.sign(request));
038    }
039
040    @Override
041    public void sendBlocking(GigyaApiRequest request, IRestAdapterCallback networkCallbacks) {
042        _requestFactory.sign(request);
043        new GigyaNetworkAsyncTask(networkCallbacks).execute(_requestFactory.sign(request));
044        _blocked = true;
045    }
046
047    @Override
048    public void release() {
049        super.release();
050        if (_queue.isEmpty()) {
051            return;
052        }
053
054        HttpTask queued = _queue.poll();
055        // Requests need to be re-signed when released from the blocking queue.
056        _requestFactory.sign(queued.getRequest());
057
058        while (queued != null) {
059            queued.run();
060            queued = _queue.poll();
061        }
062    }
063
064    @Override
065    public void cancel(String tag) {
066        if (tag == null) {
067            _queue.clear();
068            // Unable to cancel already sent requests.
069        }
070        if (!_queue.isEmpty()) {
071            Iterator it = _queue.iterator();
072            while (it.hasNext()) {
073                final HttpTask task = (HttpTask) it.next();
074                final String requestTag = task.request.getTag();
075                if (requestTag.equals(tag)) {
076                    it.remove();
077                }
078            }
079        }
080    }
081
082    // Async implementation.
083
084    private static class AsyncResult {
085        private int code;
086        private String result;
087        private String date;
088
089        AsyncResult(int code, String result, String date) {
090            this.code = code;
091            this.result = result;
092            this.date = date;
093        }
094
095        public int getCode() {
096            return code;
097        }
098    }
099
100    private static class HttpTask {
101
102        private GigyaNetworkAsyncTask asyncTask;
103        private GigyaApiRequest request;
104        private IApiRequestFactory requestFactory;
105
106        public GigyaApiRequest getRequest() {
107            return request;
108        }
109
110        public void setRequest(GigyaApiRequest request) {
111            this.request = request;
112        }
113
114        HttpTask(IApiRequestFactory requestFactory, GigyaNetworkAsyncTask asyncTask, GigyaApiRequest request) {
115            this.requestFactory = requestFactory;
116            this.asyncTask = asyncTask;
117            this.request = request;
118        }
119
120        void run() {
121            final GigyaApiHttpRequest signedRequest = this.requestFactory.sign(request);
122            this.asyncTask.execute(signedRequest);
123        }
124    }
125
126    private static class GigyaNetworkAsyncTask extends AsyncTask<GigyaApiHttpRequest, Void, AsyncResult> {
127
128        private IRestAdapterCallback networkCallbacks;
129
130        GigyaNetworkAsyncTask(IRestAdapterCallback networkCallbacks) {
131            this.networkCallbacks = networkCallbacks;
132        }
133
134        @Override
135        protected AsyncResult doInBackground(GigyaApiHttpRequest... gigyaApiRequests) {
136            GigyaApiHttpRequest request = gigyaApiRequests[0];
137            if (request != null) {
138                HttpURLConnection connection = null;
139                OutputStreamWriter outputStreamWriter = null;
140                BufferedReader bufferedReader = null;
141                StringBuilder response = new StringBuilder();
142                try {
143                    URL url = new URL(request.getUrl());
144                    connection = (HttpURLConnection) url.openConnection();
145                    connection.setUseCaches(false);
146                    connection.setConnectTimeout(10000);
147                    connection.setRequestProperty("Accept-Encoding", "gzip");
148                    connection.setRequestProperty("connection", "close");
149
150                    connection.setRequestMethod(request.getHttpMethod().intValue() == 0 ? "GET" : "POST");
151                    if (request.getHttpMethod().intValue() == 1) {
152                        connection.setDoOutput(true);
153                        outputStreamWriter = new OutputStreamWriter(connection.getOutputStream());
154                        outputStreamWriter.write(request.getEncodedParams());
155                        outputStreamWriter.flush();
156                    }
157                    int responseStatusCode = connection.getResponseCode();
158                    boolean badRequest = (responseStatusCode >= HttpURLConnection.HTTP_BAD_REQUEST);
159                    InputStream input;
160                    if (badRequest)
161                        input = connection.getErrorStream();
162                    else
163                        input = connection.getInputStream();
164                    if ("gzip".equals(connection.getContentEncoding())) {
165                        input = new GZIPInputStream(input);
166                    }
167                    bufferedReader = new BufferedReader(new InputStreamReader(input, "UTF-8"));
168                    String line;
169                    while ((line = bufferedReader.readLine()) != null) {
170                        response.append(line);
171                    }
172
173                    final String dateHeader = connection.getHeaderField("Date");
174
175                    return new AsyncResult(responseStatusCode, response.toString(), dateHeader);
176                } catch (Exception ex) {
177                    ex.printStackTrace();
178                } finally {
179                    if (outputStreamWriter != null) {
180                        try {
181                            outputStreamWriter.close();
182                        } catch (IOException ignored) {
183                        }
184                    }
185                    if (bufferedReader != null) {
186                        try {
187                            bufferedReader.close();
188                        } catch (IOException ignored) {
189                        }
190                    }
191                    if (connection != null) {
192                        connection.disconnect();
193                    }
194                }
195            }
196            return null;
197        }
198
199        @Override
200        protected void onPostExecute(AsyncResult asyncResult) {
201            if (networkCallbacks == null) {
202                return;
203            }
204            if (asyncResult == null) {
205                networkCallbacks.onError(GigyaError.generalError());
206                return;
207            }
208
209            boolean badRequest = asyncResult.getCode() >= HttpURLConnection.HTTP_BAD_REQUEST;
210            if (badRequest) {
211                // Generate gigya error.
212                final GigyaError gigyaError = new GigyaError(asyncResult.code, asyncResult.result, null);
213                networkCallbacks.onError(gigyaError);
214                return;
215            }
216
217            networkCallbacks.onResponse(asyncResult.result, asyncResult.date);
218        }
219    }
220
221    //endregion
222
223}