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