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