001package ca.uhn.fhir.okhttp.client; 002 003import static ca.uhn.fhir.okhttp.utils.UrlStringUtils.*; 004 005import java.util.List; 006import java.util.Map; 007 008import org.hl7.fhir.instance.model.api.IBaseBinary; 009 010/* 011 * #%L 012 * HAPI FHIR OkHttp Client 013 * %% 014 * Copyright (C) 2014 - 2018 University Health Network 015 * %% 016 * Licensed under the Apache License, Version 2.0 (the "License"); 017 * you may not use this file except in compliance with the License. 018 * You may obtain a copy of the License at 019 * 020 * http://www.apache.org/licenses/LICENSE-2.0 021 * 022 * Unless required by applicable law or agreed to in writing, software 023 * distributed under the License is distributed on an "AS IS" BASIS, 024 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 025 * See the License for the specific language governing permissions and 026 * limitations under the License. 027 * #L% 028 */ 029import ca.uhn.fhir.context.FhirContext; 030import ca.uhn.fhir.rest.api.*; 031import ca.uhn.fhir.rest.client.api.*; 032import ca.uhn.fhir.rest.client.impl.BaseHttpClientInvocation; 033import ca.uhn.fhir.rest.client.method.MethodUtil; 034import okhttp3.*; 035import okhttp3.internal.Version; 036 037/** 038 * A Http Request based on OkHttp. This is an adapter around the class 039 * {@link OkHttpClient} 040 * 041 * @author Matthew Clarke | matthew.clarke@orionhealth.com | Orion Health 042 */ 043public class OkHttpRestfulClient implements IHttpClient { 044 045 private Call.Factory myClient; 046 private StringBuilder myUrl; 047 private Map<String, List<String>> myIfNoneExistParams; 048 private String myIfNoneExistString; 049 private RequestTypeEnum myRequestType; 050 private List<Header> myHeaders; 051 private OkHttpRestfulRequest myRequest; 052 053 public OkHttpRestfulClient(Call.Factory theClient, 054 StringBuilder theUrl, 055 Map<String, List<String>> theIfNoneExistParams, 056 String theIfNoneExistString, 057 RequestTypeEnum theRequestType, 058 List<Header> theHeaders) { 059 myClient = theClient; 060 myUrl = theUrl; 061 myIfNoneExistParams = theIfNoneExistParams; 062 myIfNoneExistString = theIfNoneExistString; 063 myRequestType = theRequestType; 064 myHeaders = theHeaders; 065 } 066 067 @Override 068 public IHttpRequest createByteRequest(FhirContext theContext, String theContents, String theContentType, EncodingEnum theEncoding) { 069 initBaseRequest(theContext, theEncoding, createPostBody(theContents, theContentType)); 070 return myRequest; 071 } 072 073 private void initBaseRequest(FhirContext theContext, EncodingEnum theEncoding, RequestBody body) { 074 String sanitisedUrl = withTrailingQuestionMarkRemoved(myUrl.toString()); 075 myRequest = new OkHttpRestfulRequest(myClient, sanitisedUrl, myRequestType, body); 076 addHeadersToRequest(myRequest, theEncoding, theContext); 077 } 078 079 private RequestBody createPostBody(String theContents, String theContentType) { 080 return RequestBody.create(MediaType.parse(theContentType), theContents); 081 } 082 083 @Override 084 public IHttpRequest createParamRequest(FhirContext theContext, Map<String, List<String>> theParams, EncodingEnum theEncoding) { 085 initBaseRequest(theContext, theEncoding, getFormBodyFromParams(theParams)); 086 return myRequest; 087 } 088 089 private RequestBody getFormBodyFromParams(Map<String, List<String>> queryParams) { 090 FormBody.Builder formBuilder = new FormBody.Builder(); 091 for (Map.Entry<String, List<String>> paramEntry : queryParams.entrySet()) { 092 for (String value : paramEntry.getValue()) { 093 formBuilder.add(paramEntry.getKey(), value); 094 } 095 } 096 097 return formBuilder.build(); 098 } 099 100 @Override 101 public IHttpRequest createBinaryRequest(FhirContext theContext, IBaseBinary theBinary) { 102 initBaseRequest(theContext, null, createPostBody(theBinary.getContent(), theBinary.getContentType())); 103 return myRequest; 104 } 105 106 private RequestBody createPostBody(byte[] theContents, String theContentType) { 107 return RequestBody.create(MediaType.parse(theContentType), theContents); 108 } 109 110 @Override 111 public IHttpRequest createGetRequest(FhirContext theContext, EncodingEnum theEncoding) { 112 initBaseRequest(theContext, theEncoding, null); 113 return myRequest; 114 } 115 116 private void addHeadersToRequest(OkHttpRestfulRequest theHttpRequest, EncodingEnum theEncoding, FhirContext theContext) { 117 if (myHeaders != null) { 118 for (Header next : myHeaders) { 119 theHttpRequest.addHeader(next.getName(), next.getValue()); 120 } 121 } 122 123 addUserAgentHeader(theHttpRequest, theContext); 124 addAcceptCharsetHeader(theHttpRequest); 125 MethodUtil.addAcceptHeaderToRequest(theEncoding, theHttpRequest, theContext); 126 addIfNoneExistHeader(theHttpRequest); 127 } 128 129 private void addUserAgentHeader(OkHttpRestfulRequest theHttpRequest, FhirContext theContext) { 130 theHttpRequest.addHeader("User-Agent", HttpClientUtil.createUserAgentString(theContext, Version.userAgent())); 131 } 132 133 private void addAcceptCharsetHeader(OkHttpRestfulRequest theHttpRequest) { 134 theHttpRequest.addHeader("Accept-Charset", "utf-8"); 135 } 136 137 private void addIfNoneExistHeader(IHttpRequest result) { 138 if (myIfNoneExistParams != null) { 139 addIfNoneExistHeaderFromParams(result, myIfNoneExistParams); 140 } else if (myIfNoneExistString != null) { 141 addIfNoneExistHeaderFromString(result, myIfNoneExistString); 142 } 143 } 144 145 private void addIfNoneExistHeaderFromString(IHttpRequest result, String ifNoneExistString) { 146 StringBuilder sb = newHeaderBuilder(myUrl); 147 boolean shouldAddQuestionMark = !hasQuestionMark(sb); 148 sb.append(shouldAddQuestionMark ? '?' : '&'); 149 sb.append(everythingAfterFirstQuestionMark(ifNoneExistString)); 150 result.addHeader(Constants.HEADER_IF_NONE_EXIST, sb.toString()); 151 } 152 153 private void addIfNoneExistHeaderFromParams(IHttpRequest result, Map<String, List<String>> ifNoneExistParams) { 154 StringBuilder sb = newHeaderBuilder(myUrl); 155 boolean shouldAddInitialQuestionMark = !hasQuestionMark(sb); 156 BaseHttpClientInvocation.appendExtraParamsWithQuestionMark(ifNoneExistParams, sb, shouldAddInitialQuestionMark); 157 result.addHeader(Constants.HEADER_IF_NONE_EXIST, sb.toString()); 158 } 159 160 public static StringBuilder newHeaderBuilder(StringBuilder baseUrl) { 161 StringBuilder sb = new StringBuilder(baseUrl); 162 if (endsWith(baseUrl, '/')) { 163 deleteLastCharacter(sb); 164 } 165 return sb; 166 } 167 168}