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