Update DCAE Startup Info
[aaf/authz.git] / cadi / client / src / main / java / org / onap / aaf / cadi / http / HClient.java
1 /**
2  * ============LICENSE_START====================================================
3  * org.onap.aaf
4  * ===========================================================================
5  * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
6  * ===========================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END====================================================
19  *
20  */
21
22 package org.onap.aaf.cadi.http;
23
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.io.InputStreamReader;
27 import java.io.OutputStream;
28 import java.io.Reader;
29 import java.net.HttpURLConnection;
30 import java.net.URI;
31 import java.net.URISyntaxException;
32 import java.net.URL;
33 import java.util.ArrayList;
34
35 import javax.net.ssl.SSLException;
36 import javax.net.ssl.SSLHandshakeException;
37 import javax.servlet.http.HttpServletResponse;
38
39 import org.onap.aaf.cadi.CadiException;
40 import org.onap.aaf.cadi.LocatorException;
41 import org.onap.aaf.cadi.SecuritySetter;
42 import org.onap.aaf.cadi.client.EClient;
43 import org.onap.aaf.cadi.client.Future;
44 import org.onap.aaf.cadi.client.Rcli;
45 import org.onap.aaf.misc.env.APIException;
46 import org.onap.aaf.misc.env.Data;
47 import org.onap.aaf.misc.env.Data.TYPE;
48 import org.onap.aaf.misc.env.util.Pool.Pooled;
49 import org.onap.aaf.misc.rosetta.env.RosettaDF;
50
51 /**
52  * Low Level Http Client Mechanism. Chances are, you want the high level "HRcli"
53  * for Rosetta Object Translation
54  *
55  * @author Jonathan
56  *
57  */
58 public class HClient implements EClient<HttpURLConnection> {
59     private URI uri;
60     private ArrayList<Header> headers;
61     private String meth;
62     private String pathinfo;
63     private String query;
64     private String fragment;
65     private Transfer transfer;
66     private SecuritySetter<HttpURLConnection> ss;
67     private HttpURLConnection huc;
68     private int connectTimeout;
69
70     public HClient(SecuritySetter<HttpURLConnection> ss, URI uri,int connectTimeout) throws LocatorException {
71         if (uri == null) {
72             throw new LocatorException("No Service available to call");
73         }
74         this.uri = uri;
75         this.ss = ss;
76         this.connectTimeout = connectTimeout;
77         pathinfo = query = fragment = null;
78     }
79
80     @Override
81     public void setMethod(String meth) {
82         this.meth = meth;
83     }
84
85     @Override
86     public void setPathInfo(String pathinfo) {
87         this.pathinfo = pathinfo;
88     }
89
90     @Override
91     public void setPayload(Transfer transfer) {
92         this.transfer = transfer;
93     }
94
95     @Override
96     public void addHeader(String tag, String value) {
97         if (headers == null)
98             headers = new ArrayList<>();
99         headers.add(new Header(tag, value));
100     }
101
102     @Override
103     public void setQueryParams(String q) {
104         query = q;
105     }
106
107     @Override
108     public void setFragment(String f) {
109         fragment = f;
110     }
111
112     @Override
113     public void send() throws APIException {
114         // Build URL from given URI plus current Settings
115         if (uri.getPath()==null) {
116             throw new APIException("Invalid URL entered for HClient");
117         }
118         StringBuilder pi=null;
119         if (pathinfo!=null) { // additional pathinfo
120             pi = new StringBuilder(uri.getPath());
121             if (!pathinfo.startsWith("/")) {
122                 pi.append('/');
123             }
124             pi.append(pathinfo);
125         }
126            URI sendURI = null;
127         try {
128             sendURI = new URI(
129                     uri.getScheme(),
130                     uri.getAuthority(),
131                     pi==null?uri.getPath():pi.toString(),
132                     query==null?uri.getQuery():query,
133                     fragment==null?uri.getFragment():fragment
134                     );
135             huc = getConnection(sendURI, pi);
136             huc.setRequestMethod(meth);
137             if (ss!=null) {
138                 ss.setSecurity(huc);
139             }
140             if (headers != null)
141                 for (Header d : headers) {
142                     huc.addRequestProperty(d.tag, d.value);
143                 }
144             huc.setDoInput(true);
145             huc.setDoOutput(true);
146             huc.setUseCaches(false);
147             huc.setConnectTimeout(connectTimeout);
148             huc.connect();
149             if (transfer != null) {
150                 transfer.transfer(huc.getOutputStream());
151             }
152             // TODO other settings? There's a bunch here.
153         } catch (APIException e) {
154             throw e;
155         } catch (Exception e) {
156             if(sendURI==null) {
157                 throw new APIException("Cannot connect to Root URI: '" + uri.toString() + '\'',e);
158             } else {
159                 throw new APIException("Cannot connect to '" + sendURI.toString() + "' (Root URI: '" + uri.toString() + "')",e);
160             }
161         } finally { // ensure all these are reset after sends
162             meth=pathinfo=null;
163             if (headers!=null) {
164                 headers.clear();
165             }
166             pathinfo = query = fragment = "";
167         }
168     }
169
170     public URI getURI() {
171         return uri;
172     }
173
174     public void setURI(URI uri) {
175         this.uri = uri;
176     }
177
178     public int timeout() {
179         return connectTimeout;
180     }
181
182     protected HttpURLConnection getConnection(URI uri, StringBuilder pi) throws IOException, URISyntaxException {
183         URL url = new URI(
184                 uri.getScheme(),
185                 uri.getAuthority(),
186                 pi==null?uri.getPath():pi.toString(),
187                 query,
188                 fragment).toURL();
189         return (HttpURLConnection) url.openConnection();
190     }
191
192      public abstract class HFuture<T> extends Future<T> {
193         protected HttpURLConnection huc;
194         protected int respCode;
195         protected IOException exception;
196         protected StringBuilder errContent;
197
198         public HFuture(final HttpURLConnection huc) {
199             this.huc = huc;
200         }
201
202         protected boolean evalInfo(HttpURLConnection huc) throws APIException, IOException{
203             return respCode == 200;
204         };
205
206         @Override
207         public final boolean get(int timeout) throws CadiException {
208             try {
209                 huc.setReadTimeout(timeout);
210                 respCode = huc.getResponseCode();
211                 ss.setLastResponse(respCode);
212                 if (evalInfo(huc)) {
213                     return true;
214                 } else {
215                     extractError();
216                     return false;
217                 }
218             } catch (IOException | APIException e) {
219                 throw new CadiException(e);
220             } finally {
221                 close();
222             }
223         }
224
225         private void extractError() {
226             InputStream is = huc.getErrorStream();
227             try {
228                 if (is==null) {
229                     is = huc.getInputStream();
230                 }
231                 if (is!=null) {
232                 errContent = new StringBuilder();
233                 int c;
234                     while ((c=is.read())>=0) {
235                         errContent.append((char)c);
236                     }
237                 }
238             } catch (IOException e) {
239                 exception = e;
240             }
241         }
242
243         // Typically only used by Read
244         public StringBuilder inputStreamToString(InputStream is) {
245             // Avoids Carriage returns, and is reasonably efficient, given
246             // the buffer reads.
247             try {
248                 StringBuilder sb = new StringBuilder();
249                 Reader rdr = new InputStreamReader(is);
250                 try {
251                     char[] buf = new char[256];
252                     int read;
253                     while ((read = rdr.read(buf)) >= 0) {
254                         sb.append(buf, 0, read);
255                     }
256                 } finally {
257                     rdr.close();
258                 }
259                 return sb;
260             } catch (IOException e) {
261                 exception = e;
262                 return null;
263             }
264         }
265
266
267         @Override
268         public int code() {
269             return respCode;
270         }
271
272         public HttpURLConnection huc() {
273             return huc;
274         }
275
276         public IOException exception() {
277             return exception;
278         }
279
280         @Override
281         public String header(String tag) {
282             return huc.getHeaderField(tag);
283         }
284
285         public void close() {
286             if (huc!=null) {
287                 huc.disconnect();
288             }
289         }
290     }
291
292     @Override
293     public <T> Future<T> futureCreate(Class<T> t) {
294         return new HFuture<T>(huc) {
295             public boolean evalInfo(HttpURLConnection huc) {
296                 return respCode==201;
297             }
298
299             @Override
300             public String body() {
301                 if (errContent != null) {
302                     return errContent.toString();
303                 }
304                 return "";
305             }
306         };
307     }
308
309     @Override
310     public Future<String> futureReadString() {
311         return new HFuture<String>(huc) {
312             public boolean evalInfo(HttpURLConnection huc) throws IOException {
313                 if (respCode == 200) {
314                     StringBuilder sb = inputStreamToString(huc.getInputStream());
315                     if (sb != null) {
316                         value = sb.toString();
317                     }
318                     return true;
319                 }
320                 return false;
321             }
322
323             @Override
324             public String body() {
325                 if (value != null) {
326                     return value;
327                 } else if (errContent != null) {
328                     return errContent.toString();
329                 }
330                 return "";
331             }
332
333         };
334     }
335
336     @Override
337     public <T> Future<T> futureRead(final RosettaDF<T> df, final TYPE type) {
338         return new HFuture<T>(huc) {
339             private Data<T> data;
340
341             public boolean evalInfo(HttpURLConnection huc) throws APIException, IOException {
342                 if (respCode == 200) {
343                     data = df.newData().in(type).load(huc.getInputStream());
344                     value = data.asObject();
345                     return true;
346                 }
347                 return false;
348             }
349
350             @Override
351             public String body() {
352                 if (data != null) {
353                     try {
354                         return data.asString();
355                     } catch (APIException e) {
356                     }
357                 } else if (errContent != null) {
358                     return errContent.toString();
359                 }
360                 return "";
361             }
362         };
363     }
364
365     @Override
366     public <T> Future<T> future(final T t) {
367         return new HFuture<T>(huc) {
368             public boolean evalInfo(HttpURLConnection huc) {
369                 if (respCode == 200) {
370                     value = t;
371                     return true;
372                 }
373                 return false;
374             }
375
376             @Override
377             public String body() {
378                 if (errContent != null) {
379                     return errContent.toString();
380                 }
381                 return Integer.toString(respCode);
382             }
383         };
384     }
385
386     @Override
387     public Future<Void> future(final HttpServletResponse resp, final int expected) throws APIException {
388         return new HFuture<Void>(huc) {
389             public boolean evalInfo(HttpURLConnection huc) throws IOException, APIException {
390                 resp.setStatus(respCode);
391                 int read;
392                 InputStream is;
393                 OutputStream os = resp.getOutputStream();
394                 if (respCode==expected) {
395                     is = huc.getInputStream();
396                     // reuse Buffers
397                     Pooled<byte[]> pbuff = Rcli.buffPool.get();
398                     try {
399                         while ((read=is.read(pbuff.content))>=0) {
400                             os.write(pbuff.content,0,read);
401                         }
402                     } finally {
403                         pbuff.done();
404                     }
405                     return true;
406                 } else {
407                     is = huc.getErrorStream();
408                     if (is==null) {
409                         is = huc.getInputStream();
410                     }
411                     if (is!=null) {
412                         errContent = new StringBuilder();
413                         Pooled<byte[]> pbuff = Rcli.buffPool.get();
414                         try {
415                             while ((read=is.read(pbuff.content))>=0) {
416                                 os.write(pbuff.content,0,read);
417                             }
418                         } finally {
419                             pbuff.done();
420                         }
421                     }
422                 }
423                 return false;
424             }
425
426             @Override
427             public String body() {
428                 return errContent==null?null:errContent.toString();
429             }
430         };
431     }
432
433     private static class Header {
434         public final String tag;
435         public final String value;
436
437         public Header(String t, String v) {
438             this.tag = t;
439             this.value = v;
440         }
441
442         public String toString() {
443             return tag + '=' + value;
444         }
445     }
446
447     public String toString() {
448         return "HttpURLConnection Client configured to " + uri.toString();
449     }
450 }