001package org.hl7.fhir.utilities; 002 003import java.io.DataOutputStream; 004import java.io.IOException; 005import java.net.HttpURLConnection; 006import java.net.URL; 007import java.net.URLDecoder; 008import java.nio.charset.StandardCharsets; 009import java.util.ArrayList; 010import java.util.Base64; 011import java.util.HashMap; 012import java.util.List; 013import java.util.Map; 014 015import org.hl7.fhir.utilities.SimpleHTTPClient.HTTPResult; 016import org.hl7.fhir.utilities.SimpleHTTPClient.Header; 017import org.hl7.fhir.utilities.npm.SSLCertTruster; 018 019public class SimpleHTTPClient { 020 021 public class Header { 022 private String name; 023 private String value; 024 public Header(String name, String value) { 025 super(); 026 this.name = name; 027 this.value = value; 028 } 029 public String getName() { 030 return name; 031 } 032 public String getValue() { 033 return value; 034 } 035 } 036 037 private static final int MAX_REDIRECTS = 5; 038 private static int counter = 1; 039 040 public class HTTPResult { 041 private int code; 042 private String contentType; 043 private byte[] content; 044 private String source; 045 private String message; 046 047 048 public HTTPResult(String source, int code, String message, String contentType, byte[] content) { 049 super(); 050 this.source = source; 051 this.code = code; 052 this.contentType = contentType; 053 this.content = content; 054 this.message = message; 055 } 056 057 public int getCode() { 058 return code; 059 } 060 public String getContentType() { 061 return contentType; 062 } 063 public byte[] getContent() { 064 return content; 065 } 066 067 public String getSource() { 068 return source; 069 } 070 071 public void checkThrowException() throws IOException { 072 if (code >= 300) { 073 String filename = Utilities.path("[tmp]", "fhir-http-"+(++counter)+".log"); 074 if (content == null || content.length == 0) { 075 throw new IOException("Invalid HTTP response "+code+" from "+source+" ("+message+") (no content)"); 076 } else { 077 TextFile.bytesToFile(content, filename); 078 throw new IOException("Invalid HTTP response "+code+" from "+source+" ("+message+") (content in "+filename+")"); 079 } 080 } 081 } 082 083 public String getMessage() { 084 return message; 085 } 086 } 087 088 private List<Header> headers = new ArrayList<>(); 089 private String username; 090 private String password; 091 092 public void addHeader(String name, String value) { 093 headers.add(new Header(name, value)); 094 } 095 096 public String getUsername() { 097 return username; 098 } 099 100 public void setUsername(String username) { 101 this.username = username; 102 } 103 104 public String getPassword() { 105 return password; 106 } 107 108 public void setPassword(String password) { 109 this.password = password; 110 } 111 112 113 private boolean trustAll = false; 114 115 public void trustAllhosts() { 116 trustAll = true; 117 SSLCertTruster.trustAllHosts(); 118 } 119 120 public HTTPResult get(String url) throws IOException { 121 return get(url, null); 122 } 123 124 public HTTPResult get(String url, String accept) throws IOException { 125 URL u = new URL(url); 126// boolean isSSL = url.startsWith("https://"); 127 128 // handling redirects - setInstanceFollowRedirects(true) doesn't handle crossing http to https 129 130 Map<String, Integer> visited = new HashMap<>(); 131 HttpURLConnection c = null; 132 boolean done = false; 133 134 while (!done) { 135 int times = visited.compute(url, (key, count) -> count == null ? 1 : count + 1); 136 if (times > MAX_REDIRECTS) 137 throw new IOException("Stuck in redirect loop"); 138 139 u = new URL(url); 140 c = (HttpURLConnection) u.openConnection(); 141 c.setRequestMethod("GET"); 142 if (accept != null) { 143 c.setRequestProperty("Accept", accept); 144 } 145 setHeaders(c); 146 c.setInstanceFollowRedirects(false); 147 if (trustAll && url.startsWith("https://")) { 148 ((javax.net.ssl.HttpsURLConnection) c).setHostnameVerifier(SSLCertTruster.DO_NOT_VERIFY); 149 } 150 151 switch (c.getResponseCode()) { 152 case HttpURLConnection.HTTP_MOVED_PERM: 153 case HttpURLConnection.HTTP_MOVED_TEMP: 154 String location = c.getHeaderField("Location"); 155 location = URLDecoder.decode(location, "UTF-8"); 156 URL base = new URL(url); 157 URL next = new URL(base, location); // Deal with relative URLs 158 url = next.toExternalForm(); 159 continue; 160 default: 161 done = true; 162 } 163 } 164 165 return new HTTPResult(url, c.getResponseCode(), c.getResponseMessage(), c.getRequestProperty("Content-Type"), TextFile.streamToBytes(c.getResponseCode() >= 400 ? c.getErrorStream() : c.getInputStream())); 166 } 167 168 private void setHeaders(HttpURLConnection c) { 169 for (Header h : headers) { 170 c.setRequestProperty(h.getName(), h.getValue()); 171 } 172 c.setConnectTimeout(15000); 173 c.setReadTimeout(15000); 174 if (username != null) { 175 String auth = username+":"+password; 176 byte[] encodedAuth = Base64.getEncoder().encode(auth.getBytes(StandardCharsets.UTF_8)); 177 String authHeaderValue = "Basic " + new String(encodedAuth); 178 c.setRequestProperty("Authorization", authHeaderValue); 179 } 180 } 181 182 public HTTPResult post(String url, String contentType, byte[] content, String accept) throws IOException { 183 URL u = new URL(url); 184 HttpURLConnection c = (HttpURLConnection) u.openConnection(); 185 c.setDoOutput(true); 186 c.setDoInput(true); 187 c.setRequestMethod("POST"); 188 c.setRequestProperty("Content-type", contentType); 189 if (accept != null) { 190 c.setRequestProperty("Accept", accept); 191 } 192 setHeaders(c); 193 c.getOutputStream().write(content); 194 c.getOutputStream().close(); 195 return new HTTPResult(url, c.getResponseCode(), c.getResponseMessage(), c.getRequestProperty("Content-Type"), TextFile.streamToBytes(c.getResponseCode() >= 400 ? c.getErrorStream() : c.getInputStream())); 196 } 197 198 199 public HTTPResult put(String url, String contentType, byte[] content, String accept) throws IOException { 200 URL u = new URL(url); 201 HttpURLConnection c = (HttpURLConnection) u.openConnection(); 202 c.setDoOutput(true); 203 c.setDoInput(true); 204 c.setRequestMethod("PUT"); 205 c.setRequestProperty("Content-type", contentType); 206 if (accept != null) { 207 c.setRequestProperty("Accept", accept); 208 } 209 setHeaders(c); 210 c.getOutputStream().write(content); 211 c.getOutputStream().close(); 212 return new HTTPResult(url, c.getResponseCode(), c.getResponseMessage(), c.getRequestProperty("Content-Type"), TextFile.streamToBytes(c.getResponseCode() >= 400 ? c.getErrorStream() : c.getInputStream())); 213 } 214 215 216}