1d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru/* 2d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Copyright (C) 2011 The Android Open Source Project 3d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * 4d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Licensed under the Apache License, Version 2.0 (the "License"); 5d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * you may not use this file except in compliance with the License. 6d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * You may obtain a copy of the License at 7d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * 8d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * http://www.apache.org/licenses/LICENSE-2.0 9d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * 10d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Unless required by applicable law or agreed to in writing, software 11d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * distributed under the License is distributed on an "AS IS" BASIS, 12d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * See the License for the specific language governing permissions and 14d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * limitations under the License. 15d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru */ 16d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 17d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Querupackage com.android.volley.toolbox; 18d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 19d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport com.android.volley.AuthFailureError; 20d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport com.android.volley.Request; 21e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queruimport com.android.volley.Request.Method; 22d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 23d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport org.apache.http.HttpEntity; 24d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport org.apache.http.HttpResponse; 25d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport org.apache.http.NameValuePair; 26d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport org.apache.http.client.HttpClient; 27e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queruimport org.apache.http.client.methods.HttpDelete; 28e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queruimport org.apache.http.client.methods.HttpEntityEnclosingRequestBase; 29d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport org.apache.http.client.methods.HttpGet; 30364614be46d20fba35c3b6bc1c2d43fe8e861e9cMaurice Chuimport org.apache.http.client.methods.HttpHead; 31364614be46d20fba35c3b6bc1c2d43fe8e861e9cMaurice Chuimport org.apache.http.client.methods.HttpOptions; 32d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport org.apache.http.client.methods.HttpPost; 33e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queruimport org.apache.http.client.methods.HttpPut; 34364614be46d20fba35c3b6bc1c2d43fe8e861e9cMaurice Chuimport org.apache.http.client.methods.HttpTrace; 35d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport org.apache.http.client.methods.HttpUriRequest; 36d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport org.apache.http.entity.ByteArrayEntity; 37d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport org.apache.http.message.BasicNameValuePair; 38d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport org.apache.http.params.HttpConnectionParams; 39d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport org.apache.http.params.HttpParams; 40d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 41d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport java.io.IOException; 42364614be46d20fba35c3b6bc1c2d43fe8e861e9cMaurice Chuimport java.net.URI; 43d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport java.util.ArrayList; 44d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport java.util.List; 45d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport java.util.Map; 46d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 47d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru/** 48d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * An HttpStack that performs request over an {@link HttpClient}. 49d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru */ 50d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Querupublic class HttpClientStack implements HttpStack { 51d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru protected final HttpClient mClient; 52d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 53e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru private final static String HEADER_CONTENT_TYPE = "Content-Type"; 54e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru 55d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru public HttpClientStack(HttpClient client) { 56d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru mClient = client; 57d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 58d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 59d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru private static void addHeaders(HttpUriRequest httpRequest, Map<String, String> headers) { 60d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru for (String key : headers.keySet()) { 61d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru httpRequest.setHeader(key, headers.get(key)); 62d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 63d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 64d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 65d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru @SuppressWarnings("unused") 66d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru private static List<NameValuePair> getPostParameterPairs(Map<String, String> postParams) { 67d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru List<NameValuePair> result = new ArrayList<NameValuePair>(postParams.size()); 68d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru for (String key : postParams.keySet()) { 69d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru result.add(new BasicNameValuePair(key, postParams.get(key))); 70d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 71d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru return result; 72d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 73d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 74d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru @Override 75d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders) 76d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru throws IOException, AuthFailureError { 77e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders); 78d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru addHeaders(httpRequest, additionalHeaders); 79d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru addHeaders(httpRequest, request.getHeaders()); 80d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru onPrepareRequest(httpRequest); 81d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru HttpParams httpParams = httpRequest.getParams(); 82d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru int timeoutMs = request.getTimeoutMs(); 83d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru // TODO: Reevaluate this connection timeout based on more wide-scale 84d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru // data collection and possibly different for wifi vs. 3G. 85d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru HttpConnectionParams.setConnectionTimeout(httpParams, 5000); 86d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru HttpConnectionParams.setSoTimeout(httpParams, timeoutMs); 87d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru return mClient.execute(httpRequest); 88d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 89d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 90d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru /** 91e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru * Creates the appropriate subclass of HttpUriRequest for passed in request. 92e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru */ 93e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru @SuppressWarnings("deprecation") 94e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru /* protected */ static HttpUriRequest createHttpRequest(Request<?> request, 95e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru Map<String, String> additionalHeaders) throws AuthFailureError { 96e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru switch (request.getMethod()) { 97e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru case Method.DEPRECATED_GET_OR_POST: { 98e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru // This is the deprecated way that needs to be handled for backwards compatibility. 99e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru // If the request's post body is null, then the assumption is that the request is 100e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru // GET. Otherwise, it is assumed that the request is a POST. 101e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru byte[] postBody = request.getPostBody(); 102e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru if (postBody != null) { 103e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru HttpPost postRequest = new HttpPost(request.getUrl()); 104e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru postRequest.addHeader(HEADER_CONTENT_TYPE, request.getPostBodyContentType()); 105e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru HttpEntity entity; 106e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru entity = new ByteArrayEntity(postBody); 107e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru postRequest.setEntity(entity); 108e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru return postRequest; 109e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru } else { 110e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru return new HttpGet(request.getUrl()); 111e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru } 112e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru } 113e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru case Method.GET: 114e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru return new HttpGet(request.getUrl()); 115e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru case Method.DELETE: 116e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru return new HttpDelete(request.getUrl()); 117e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru case Method.POST: { 118e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru HttpPost postRequest = new HttpPost(request.getUrl()); 119e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru postRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType()); 120e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru setEntityIfNonEmptyBody(postRequest, request); 121e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru return postRequest; 122e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru } 123e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru case Method.PUT: { 124e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru HttpPut putRequest = new HttpPut(request.getUrl()); 125e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru putRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType()); 126e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru setEntityIfNonEmptyBody(putRequest, request); 127e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru return putRequest; 128e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru } 129364614be46d20fba35c3b6bc1c2d43fe8e861e9cMaurice Chu case Method.HEAD: 130364614be46d20fba35c3b6bc1c2d43fe8e861e9cMaurice Chu return new HttpHead(request.getUrl()); 131364614be46d20fba35c3b6bc1c2d43fe8e861e9cMaurice Chu case Method.OPTIONS: 132364614be46d20fba35c3b6bc1c2d43fe8e861e9cMaurice Chu return new HttpOptions(request.getUrl()); 133364614be46d20fba35c3b6bc1c2d43fe8e861e9cMaurice Chu case Method.TRACE: 134364614be46d20fba35c3b6bc1c2d43fe8e861e9cMaurice Chu return new HttpTrace(request.getUrl()); 135364614be46d20fba35c3b6bc1c2d43fe8e861e9cMaurice Chu case Method.PATCH: { 136364614be46d20fba35c3b6bc1c2d43fe8e861e9cMaurice Chu HttpPatch patchRequest = new HttpPatch(request.getUrl()); 137364614be46d20fba35c3b6bc1c2d43fe8e861e9cMaurice Chu patchRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType()); 138364614be46d20fba35c3b6bc1c2d43fe8e861e9cMaurice Chu setEntityIfNonEmptyBody(patchRequest, request); 139364614be46d20fba35c3b6bc1c2d43fe8e861e9cMaurice Chu return patchRequest; 140364614be46d20fba35c3b6bc1c2d43fe8e861e9cMaurice Chu } 141e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru default: 142e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru throw new IllegalStateException("Unknown request method."); 143e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru } 144e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru } 145e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru 146e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru private static void setEntityIfNonEmptyBody(HttpEntityEnclosingRequestBase httpRequest, 147e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru Request<?> request) throws AuthFailureError { 148e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru byte[] body = request.getBody(); 149e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru if (body != null) { 150e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru HttpEntity entity = new ByteArrayEntity(body); 151e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru httpRequest.setEntity(entity); 152e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru } 153e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru } 154e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru 155e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru /** 156d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Called before the request is executed using the underlying HttpClient. 157d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * 158d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * <p>Overwrite in subclasses to augment the request.</p> 159d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru */ 160d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru protected void onPrepareRequest(HttpUriRequest request) throws IOException { 161d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru // Nothing. 162d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 163364614be46d20fba35c3b6bc1c2d43fe8e861e9cMaurice Chu 164364614be46d20fba35c3b6bc1c2d43fe8e861e9cMaurice Chu /** 165364614be46d20fba35c3b6bc1c2d43fe8e861e9cMaurice Chu * The HttpPatch class does not exist in the Android framework, so this has been defined here. 166364614be46d20fba35c3b6bc1c2d43fe8e861e9cMaurice Chu */ 167364614be46d20fba35c3b6bc1c2d43fe8e861e9cMaurice Chu public static final class HttpPatch extends HttpEntityEnclosingRequestBase { 168364614be46d20fba35c3b6bc1c2d43fe8e861e9cMaurice Chu 169364614be46d20fba35c3b6bc1c2d43fe8e861e9cMaurice Chu public final static String METHOD_NAME = "PATCH"; 170364614be46d20fba35c3b6bc1c2d43fe8e861e9cMaurice Chu 171364614be46d20fba35c3b6bc1c2d43fe8e861e9cMaurice Chu public HttpPatch() { 172364614be46d20fba35c3b6bc1c2d43fe8e861e9cMaurice Chu super(); 173364614be46d20fba35c3b6bc1c2d43fe8e861e9cMaurice Chu } 174364614be46d20fba35c3b6bc1c2d43fe8e861e9cMaurice Chu 175364614be46d20fba35c3b6bc1c2d43fe8e861e9cMaurice Chu public HttpPatch(final URI uri) { 176364614be46d20fba35c3b6bc1c2d43fe8e861e9cMaurice Chu super(); 177364614be46d20fba35c3b6bc1c2d43fe8e861e9cMaurice Chu setURI(uri); 178364614be46d20fba35c3b6bc1c2d43fe8e861e9cMaurice Chu } 179364614be46d20fba35c3b6bc1c2d43fe8e861e9cMaurice Chu 180364614be46d20fba35c3b6bc1c2d43fe8e861e9cMaurice Chu /** 181364614be46d20fba35c3b6bc1c2d43fe8e861e9cMaurice Chu * @throws IllegalArgumentException if the uri is invalid. 182364614be46d20fba35c3b6bc1c2d43fe8e861e9cMaurice Chu */ 183364614be46d20fba35c3b6bc1c2d43fe8e861e9cMaurice Chu public HttpPatch(final String uri) { 184364614be46d20fba35c3b6bc1c2d43fe8e861e9cMaurice Chu super(); 185364614be46d20fba35c3b6bc1c2d43fe8e861e9cMaurice Chu setURI(URI.create(uri)); 186364614be46d20fba35c3b6bc1c2d43fe8e861e9cMaurice Chu } 187364614be46d20fba35c3b6bc1c2d43fe8e861e9cMaurice Chu 188364614be46d20fba35c3b6bc1c2d43fe8e861e9cMaurice Chu @Override 189364614be46d20fba35c3b6bc1c2d43fe8e861e9cMaurice Chu public String getMethod() { 190364614be46d20fba35c3b6bc1c2d43fe8e861e9cMaurice Chu return METHOD_NAME; 191364614be46d20fba35c3b6bc1c2d43fe8e861e9cMaurice Chu } 192364614be46d20fba35c3b6bc1c2d43fe8e861e9cMaurice Chu 193364614be46d20fba35c3b6bc1c2d43fe8e861e9cMaurice Chu } 194d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru} 195