/* * Copyright 2008-2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.nominanuda.web.http; import static com.nominanuda.zen.classwork.Reflect.REFL; import static com.nominanuda.zen.common.Ex.EX; import static com.nominanuda.zen.common.Str.STR; import static com.nominanuda.zen.obj.JsonPath.JPATH; import static com.nominanuda.zen.oio.OioUtils.IO; import static com.nominanuda.zen.seq.Seq.SEQ; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PushbackInputStream; import java.io.UnsupportedEncodingException; import java.net.ProxySelector; import java.net.URI; import java.net.URLDecoder; import java.nio.charset.Charset; import java.nio.charset.UnsupportedCharsetException; import java.sql.Struct; import java.util.Arrays; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.annotation.Nullable; import javax.annotation.concurrent.ThreadSafe; import javax.mail.BodyPart; import javax.mail.MessagingException; import javax.mail.internet.MimeMultipart; import javax.mail.util.ByteArrayDataSource; import org.apache.http.Header; import org.apache.http.HeaderElement; import org.apache.http.HeaderIterator; import org.apache.http.HttpEntity; import org.apache.http.HttpEntityEnclosingRequest; import org.apache.http.HttpException; import org.apache.http.HttpHost; import org.apache.http.HttpMessage; import org.apache.http.HttpRequest; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; import org.apache.http.ProtocolVersion; import org.apache.http.RequestLine; import org.apache.http.StatusLine; import org.apache.http.client.HttpClient; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPut; import org.apache.http.client.utils.URLEncodedUtils; import org.apache.http.config.Registry; import org.apache.http.config.RegistryBuilder; import org.apache.http.conn.socket.ConnectionSocketFactory; import org.apache.http.conn.socket.PlainConnectionSocketFactory; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.cookie.Cookie; import org.apache.http.cookie.CookieSpec; import org.apache.http.entity.AbstractHttpEntity; import org.apache.http.entity.ByteArrayEntity; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.entity.mime.FormBodyPart; import org.apache.http.entity.mime.HttpMultipart; import org.apache.http.entity.mime.MultipartEntity; import org.apache.http.entity.mime.content.ContentBody; import org.apache.http.impl.DefaultConnectionReuseStrategy; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.impl.conn.SystemDefaultRoutePlanner; import org.apache.http.impl.cookie.BasicClientCookie; import org.apache.http.impl.cookie.BrowserCompatSpec; import org.apache.http.impl.cookie.NetscapeDraftSpec; import org.apache.http.impl.cookie.RFC2109Spec; import org.apache.http.impl.cookie.RFC2965Spec; import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; import org.apache.http.impl.nio.client.HttpAsyncClients; import org.apache.http.message.BasicHeader; import org.apache.http.message.BasicHttpRequest; import org.apache.http.message.BasicHttpResponse; import org.apache.http.message.BasicLineFormatter; import org.apache.http.message.BasicLineParser; import org.apache.http.message.BasicNameValuePair; import org.apache.http.message.BasicStatusLine; import org.apache.http.message.LineFormatter; import org.apache.http.message.LineParser; import org.apache.http.message.ParserCursor; import org.apache.http.util.CharArrayBuffer; import org.apache.http.util.EntityUtils; import org.eclipse.jetty.util.IO; import org.springframework.expression.spel.support.ReflectionHelper; import com.nominanuda.zen.classwork.Reflect; import com.nominanuda.zen.codec.Base64Codec; import com.nominanuda.zen.common.Check; import com.nominanuda.zen.common.Ex; import com.nominanuda.zen.common.Str; import com.nominanuda.zen.common.Tuple2; import com.nominanuda.zen.obj.JsonPath; import com.nominanuda.zen.obj.Obj; import com.nominanuda.zen.obj.Stru; import com.nominanuda.zen.seq.Seq; @ThreadSafe public class HttpCoreHelper implements HttpProtocol { public static final HttpCoreHelper HTTP = new HttpCoreHelper(); private static final byte CR = 13;/*US-ASCII CR carriage return*/ private static final byte LF = 10;/*US-ASCII LF linefeed*/ private static final byte[] CRLF = new byte[] {CR, LF}; private static final LineFormatter LINE_FORMATTER = BasicLineFormatter.INSTANCE; private static final LineParser LINE_PARSER = BasicLineParser.INSTANCE; private static final Pattern MULTIPART_NAME_RE = Pattern.compile("^.*\\bname=\"([^\"]+)\";?.*$"); private final Base64Codec base64 = new Base64Codec(); public final ProtocolVersion ProtocolVersion_1_1 = new ProtocolVersion("HTTP", 1, 1); public HttpMessage deserialize(InputStream is) throws IOException, HttpException { PushbackInputStream pis = new PushbackInputStream(is, 4); byte[] bb = new byte[4]; Check.illegalargument.assertTrue(4 == pis.read(bb), "premature end of stream"); pis.unread(bb); return bb[0] == 'H' && bb[1] == 'T' && bb[2] == 'T' && bb[3] == 'P' ? deserializeResponse(pis) : deserializeRequest(pis); } private HttpRequest deserializeRequest(InputStream is) throws IOException, HttpException { CharArrayBuffer lineBuf = readLine(is); final ParserCursor cursor = new ParserCursor(0, lineBuf.length()); RequestLine requestline = LINE_PARSER.parseRequestLine(lineBuf, cursor); HttpRequest req = createRequest(requestline.getMethod(), requestline.getUri()); fillMessageHeadersAndContent(req, is); return req; } private HttpResponse deserializeResponse(InputStream is) throws IOException, HttpException { CharArrayBuffer lineBuf = readLine(is); final ParserCursor cursor = new ParserCursor(0, lineBuf.length()); StatusLine requestline = LINE_PARSER.parseStatusLine(lineBuf, cursor); HttpResponse resp = createBasicResponse(requestline.getStatusCode(), requestline.getReasonPhrase()); fillMessageHeadersAndContent(resp, is); return resp; } private void fillMessageHeadersAndContent(HttpMessage msg, InputStream is) throws IOException, HttpException { CharArrayBuffer lineBuf = null; long cl = 0; Header ct = null; Header ce = null; while((lineBuf = readLine(is)).length() > 0) { Header h = LINE_PARSER.parseHeader(lineBuf); String hn = h.getName(); if(HDR_CONTENT_LENGTH.equalsIgnoreCase(hn)) { cl = Long.valueOf(h.getValue()); } else if(HDR_CONTENT_TYPE.equalsIgnoreCase(hn)) { ct = h; } else if(HDR_CONTENT_ENCODING.equalsIgnoreCase(hn)) { ce = h; } else { msg.addHeader(h); } } if(cl > 0) { byte[] payload = IO.readAndClose(is); Check.runtime.assertTrue(cl == payload.length); ByteArrayEntity bae = new ByteArrayEntity(payload); if(ct != null) { bae.setContentType(ct); } if(ce != null) { bae.setContentEncoding(ce); } setEntity(msg, bae); } } private void setEntity(HttpMessage msg, HttpEntity entity) { if(Check.notNull(msg) instanceof HttpResponse) { ((HttpResponse)msg).setEntity(entity); } else { ((HttpEntityEnclosingRequest)msg).setEntity(entity); } } public HttpRequest createRequest(String method, String url) { if (GET.equals(method)) { return new HttpGet(url); } else if (POST.equals(method)) { return new HttpPost(url); } else if (PUT.equals(method)) { return new HttpPut(url); } else if (SEQ.find(method, RFC2616_SPECIAL_METHODS)) { return new BasicHttpRequest(method, url); } else { throw new IllegalArgumentException(method + " method not supported"); } } private CharArrayBuffer readLine(InputStream is) throws IOException { CharArrayBuffer cab = new CharArrayBuffer(128); int status = 0;//0 reading, 1 CR seen while(true) { char c = (char)is.read(); switch (c) { case (char)-1: throw new IOException("premature end of stream"); case CR: if(status == 1) { cab.append(c); } status = 1; break; case LF: if(status == 1) { return cab; } else { cab.append(LF); } break; default: cab.append(c); break; } } } public byte[] serialize(HttpMessage message) throws IOException, HttpException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); serializeTo(message, baos); return baos.toByteArray(); } public void serializeTo(HttpMessage message, OutputStream os) throws IOException, HttpException { String hl = null; HttpEntity entity = null; if (message instanceof HttpRequest) { HttpRequest req = (HttpRequest) message; hl = new String(LINE_FORMATTER.formatRequestLine(null, req.getRequestLine()).toCharArray()); if (req instanceof HttpEntityEnclosingRequest) { entity = ((HttpEntityEnclosingRequest) req).getEntity(); } } else { HttpResponse resp = (HttpResponse) message; hl = new String(LINE_FORMATTER.formatStatusLine(null, resp.getStatusLine()).toCharArray()); entity = resp.getEntity(); } serializeInternal(hl, message.headerIterator(), entity, os); } private void serializeInternal(String headLine, HeaderIterator hi, @Nullable HttpEntity entity, OutputStream os) throws IOException, HttpException { os.write(headLine.getBytes(CS_ASCII)); os.write(CRLF); for(final HeaderIterator it = hi; it.hasNext();) { final Header header = it.nextHeader(); String hn = header.getName(); if(HDR_CONTENT_LENGTH.equalsIgnoreCase(hn) || HDR_CONTENT_ENCODING.equalsIgnoreCase(hn) || HDR_CONTENT_TYPE.equalsIgnoreCase(hn) ) { continue; } writeHeaderTo(header, os); } if (entity != null) { Long len = entity.getContentLength(); writeHeaderTo(new BasicHeader(HDR_CONTENT_LENGTH, len.toString()), os); writeHeaderTo(entity.getContentEncoding(), os); writeHeaderTo(entity.getContentType(), os); } os.write(CRLF); if (entity != null) { os.write(IO.readAndClose(entity.getContent())); } } private void writeHeaderTo(@Nullable Header header, OutputStream os) throws IOException { if (header == null || header.getValue() == null) { return; } char[] carr = LINE_FORMATTER.formatHeader(null, header).toCharArray(); os.write(new String(carr).getBytes(CS_ASCII)); os.write(CRLF); } public StatusLine statusLine(int code) { String reason = Check.ifNull(HttpProtocol.statusToReason.get(code), "XXX"); return statusLine(code, reason); } public StatusLine statusLine(int code, String reason) { return statusLine(code, reason, ProtocolVersion_1_1); } public StatusLine statusLine(int code, String reason, ProtocolVersion httpVersion) { Check.illegalargument.assertTrue(code > 99 && code < 600); return new BasicStatusLine(Check.notNull(httpVersion), code, Check.notNull(reason)); } public void setContentType(HttpResponse resp, String contentType) { HttpEntity e = resp.getEntity(); if (e != null && e instanceof AbstractHttpEntity) { AbstractHttpEntity ae = (AbstractHttpEntity)e; ae.setContentType(contentType); } else { resp.setHeader(HDR_CONTENT_TYPE, contentType); } } public void setContentEncoding(HttpResponse resp, String contentEncoding) { HttpEntity e = resp.getEntity(); if (e != null && e instanceof AbstractHttpEntity) { AbstractHttpEntity ae = (AbstractHttpEntity)e; ae.setContentEncoding(contentEncoding); } else { resp.setHeader(HDR_CONTENT_ENCODING, contentEncoding); } } public void setContentLength(HttpResponse resp, int contentLength) { resp.setHeader(HDR_CONTENT_LENGTH, Integer.valueOf(contentLength).toString()); } public @Nullable String guessCharset(HttpEntity entity) { Header h = entity.getContentType(); return h == null ? null : guessCharset(h); } public @Nullable String guessCharset(String contentType) { Header h = new BasicHeader(HDR_CONTENT_TYPE, contentType); return guessCharset(h); } private @Nullable String guessCharset(Header h) { HeaderElement[] elems = h.getElements(); if (elems.length > 0) { HeaderElement elem = elems[0]; NameValuePair param = elem.getParameterByName("charset"); if (param != null) { return param.getValue(); } } return null; } public List<NameValuePair> parseEntityWithDefaultUtf8(final HttpEntity entity) throws IOException { List<NameValuePair> result;// = new LinkedList<NameValuePair>(); String contentType = null; String charset = UTF_8; Header h = entity.getContentType(); if (h != null) { HeaderElement[] elems = h.getElements(); if (elems.length > 0) { HeaderElement elem = elems[0]; contentType = elem.getName(); NameValuePair param = elem.getParameterByName("charset"); if (param != null) { charset = param.getValue(); } } } if (contentType != null && contentType.trim().toLowerCase().startsWith(CT_WWW_FORM_URLENCODED.toLowerCase())) { final String content = EntityUtils.toString(entity, charset); result = URLEncodedUtils.parse(content, Charset.forName(charset)); // parseUrlEncodedParamList(result, content, charset); } else { result = new LinkedList<NameValuePair>(); if (isMultipart(entity)) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); entity.writeTo(baos); try { MimeMultipart mm = new MimeMultipart(new ByteArrayDataSource(baos.toByteArray(), CT_APPLICATION_OCTET_STREAM)); for (int i=0; i<mm.getCount(); i++) { BodyPart bp = mm.getBodyPart(i); String name = getBodyPartName(bp); if (name != null) { String value = null; switch (bp.getContentType()) { case CT_TEXT_PLAIN: value = bp.getContent().toString(); break; case CT_APPLICATION_OCTET_STREAM: byte[] bytes = IO.readAndClose(bp.getInputStream()); value = base64.encodeClassic(bytes); break; } result.add(new BasicNameValuePair(name, value)); } } } catch (Exception e) { // Exception instead of MessagingException (causes failure when building com.nominanuda.springsoy.SoySourceTest... why?) throw new IOException(); } } } return result; } private void parseUrlEncodedParamList(List<NameValuePair> result, String content, String charset) { try { content = URLDecoder.decode(content, charset); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } if (content != null && content.length() > 0) { char[] carr = content.toCharArray(); int len = carr.length; StringBuilder name = new StringBuilder(); StringBuilder value = new StringBuilder(); int STATE_PARSING_KEY = 0; int STATE_PARSING_VAL = 1; int state = STATE_PARSING_KEY; for (int i = 0; i < len; i++) { char c = carr[i]; switch (c) { // case '%': // Check.illegalargument.assertTrue(len > i + 2); // String sss = new StringBuilder(2) // .append(carr[i+1]) // .append(carr[i+2]) // .toString(); // i += 2; // char rr = Character.forDigit(Integer.parseInt(sss, 16), 16); // if(state == STATE_PARSING_KEY) { // name.append(rr); // } else { // value.append(rr); // } // break; case '=': if (state == STATE_PARSING_KEY) { state = STATE_PARSING_VAL; Check.illegalargument.assertTrue(name.length() > 0); } else { value.append(c); } break; case '&': Check.illegalargument.assertTrue(state == STATE_PARSING_VAL); result.add(new BasicNameValuePair( name.toString(), value.toString())); name = new StringBuilder(); value = new StringBuilder(); state = STATE_PARSING_KEY; break; // case '+': // if(state == STATE_PARSING_KEY) { // name.append(' '); // } else { // value.append(' '); // } // break; default: if (state == STATE_PARSING_KEY) { name.append(c); } else { value.append(c); } break; } } if (name.length() > 0) { result.add(new BasicNameValuePair( name.toString(), value.toString())); } } } private String getBodyPartName(BodyPart bp) throws MessagingException { String[] d = bp.getHeader("Content-Disposition"); if (d.length == 1) { Matcher matcher = MULTIPART_NAME_RE.matcher(d[0]); if (matcher.matches()) { return matcher.group(1); } } return null; } public @Nullable String getQueryParamFirstOccurrence(HttpRequest request, String name) { List<NameValuePair> l = URLEncodedUtils.parse(URI.create(request.getRequestLine().getUri()), UTF_8); for (NameValuePair nvp : l) { if (name.equals(nvp.getName())) { return nvp.getValue(); } } return null; } public Stru getQueryParams(HttpRequest request) { List<NameValuePair> l = URLEncodedUtils.parse(URI.create(request.getRequestLine().getUri()), UTF_8); return toStru(l); } public Stru toStru(List<NameValuePair> l) { Obj res = Obj.make(); for (NameValuePair nvp : l) { JPATH.setOrPushPathProperty(res, nvp.getName(), nvp.getValue()); } return res; } public HttpResponse createBasicResponse(int status) { StatusLine statusline = statusLine(status); HttpResponse resp = new BasicHttpResponse(statusline); return resp; } public HttpResponse redirectTo(String url) { StatusLine statusline = statusLine(302); HttpResponse resp = new BasicHttpResponse(statusline); resp.addHeader(HDR_LOCATION, url); return resp; } public boolean hasEntity(HttpMessage msg) { return Check.notNull(msg) instanceof HttpResponse ? ((HttpResponse)msg).getEntity() != null : msg instanceof HttpEntityEnclosingRequest ? ((HttpEntityEnclosingRequest)msg).getEntity() != null : false; } public @Nullable HttpEntity getEntity(HttpMessage msg) { if (! hasEntity(msg)) { return null; } else if (msg instanceof HttpResponse) { return ((HttpResponse)msg).getEntity(); } else { return ((HttpEntityEnclosingRequest)msg).getEntity(); } } public HttpResponse resp404TextPlainUtf8(String msg) { BasicHttpResponse resp = new BasicHttpResponse(statusLine(404)); resp.setEntity(new StringEntity(msg, ContentType.create(CT_TEXT_PLAIN, CS_UTF_8))); return resp; } public HttpResponse resp500TextPlainUtf8(Exception e) { BasicHttpResponse resp = new BasicHttpResponse(statusLine(500)); resp.setEntity(new StringEntity(EX.toStackTrace(e), ContentType.create(CT_TEXT_PLAIN, CS_UTF_8))); return resp; } public HttpResponse respInternalServerError() { BasicHttpResponse resp = new BasicHttpResponse(statusLine(500)); resp.setEntity(new StringEntity("Internal Server Error", ContentType.create(CT_TEXT_PLAIN, CS_UTF_8))); return resp; } public HttpResponse createBasicResponse(int status, String message, String contentType) { BasicHttpResponse resp = new BasicHttpResponse(statusLine(status)); try { String declaredCharset = guessCharset(contentType); HttpEntity e = new StringEntity(message, declaredCharset == null ? ContentType.create(contentType, CS_UTF_8) : ContentType.create(contentType)); resp.setEntity(e); } catch (UnsupportedCharsetException ex) { throw new IllegalArgumentException(ex); } return resp; } public HttpResponse createBasicResponse(int status, String reason) { BasicHttpResponse resp = new BasicHttpResponse(statusLine(status, reason)); return resp; } public boolean isMultipart(HttpEntity entity) { return Check.notNull(entity) instanceof MultipartEntity; } public HttpMultipart extractHttpMultipart(MultipartEntity entity) { HttpMultipart mp = (HttpMultipart)REFL.getFieldValue("multipart", entity, true); return Check.notNull(mp); } public ContentBody extractFirstPartBody(MultipartEntity entity) { HttpMultipart mp = extractHttpMultipart(entity); FormBodyPart part = mp.getBodyParts().get(0); return part.getBody(); } /* clients */ public HttpClient createClient(int maxConnPerRoute, int connTimeoutMillis, int soTimeoutMillis) { return createClient(maxConnPerRoute, connTimeoutMillis, soTimeoutMillis, null); } //if proxyHostAnPort value is jvm the normal jvm settings apply public HttpClient createClient(int maxConnPerRoute, int connTimeoutMillis, int soTimeoutMillis, @Nullable String proxyHostAnPort) { Registry<ConnectionSocketFactory> defaultRegistry = RegistryBuilder .<ConnectionSocketFactory> create() .register("http", PlainConnectionSocketFactory.getSocketFactory()) .register("https", SSLConnectionSocketFactory.getSocketFactory()) .build(); PoolingHttpClientConnectionManager connMgr = new PoolingHttpClientConnectionManager(defaultRegistry); connMgr.setDefaultMaxPerRoute(maxConnPerRoute); RequestConfig rCfg = RequestConfig.custom() .setConnectTimeout(connTimeoutMillis) .setSocketTimeout(soTimeoutMillis) .build(); HttpClientBuilder hcb = HttpClientBuilder.create() .setConnectionManager(connMgr) .setDefaultRequestConfig(rCfg); if (proxyHostAnPort == null) { // do something? } else if("jvm".equalsIgnoreCase(proxyHostAnPort)) { SystemDefaultRoutePlanner rp = new SystemDefaultRoutePlanner(ProxySelector.getDefault()); hcb.setRoutePlanner(rp); } else { String[] hostAndPort = proxyHostAnPort.split(":"); Check.illegalargument.assertTrue(hostAndPort.length < 3, "wrong hostAndPort:"+proxyHostAnPort); String host = hostAndPort[0]; int port = 80; if (hostAndPort.length > 1) { port = Integer.valueOf(hostAndPort[1]); } HttpHost proxy = new HttpHost(host, port); hcb.setProxy(proxy); } HttpClient httpClient = hcb.build(); return httpClient; } public CloseableHttpAsyncClient createAsyncClient(int maxConnPerRoute) { CloseableHttpAsyncClient httpclient = HttpAsyncClients .custom() .setMaxConnPerRoute(maxConnPerRoute) .setMaxConnTotal(maxConnPerRoute) .setConnectionReuseStrategy(DefaultConnectionReuseStrategy.INSTANCE) .build(); httpclient.start(); return httpclient; } /* cookies */ public enum CookieSpecKind { rfc2965Spec(new RFC2965Spec()), rfc2109Spec(new RFC2109Spec()), browserCompat(new BrowserCompatSpec()), netscape(new NetscapeDraftSpec()); private CookieSpec cookieSpec; CookieSpecKind(CookieSpec spec) { cookieSpec = spec; } CookieSpec getSpec() { return cookieSpec; } } public List<Cookie> getRequestCookiesByName(HttpRequest req, String name) { final HeaderIterator iterator = req.headerIterator(); List<Cookie> cookies = new LinkedList<Cookie>(); while (iterator.hasNext()) { Header header = iterator.nextHeader(); if ("Cookie".equals(header.getName())) { String remaining = header.getValue(); List<String> pairs = STR.splitAndTrim(remaining, ";"); for (String nav : pairs) { int w = nav.indexOf('='); if (w > 0) { String cname = nav.substring(0, w); if (name.equals(cname)) { cookies.add(new NameValueCookie(cname, nav.substring(w))); } } } } } return cookies; } public void setResponseCookie(HttpResponse resp, Cookie cookie, CookieSpecKind cookieSpec) { Header h = cookieSpec.getSpec().formatCookies(Arrays.asList(cookie)).get(0); resp.addHeader("Set-Cookie", h.getValue()); } public void setResponseCookie(HttpResponse req, String name, String value, CookieSpecKind cookieSpec) { BasicClientCookie c = new BasicClientCookie(name, value); setResponseCookie(req, c, cookieSpec); } /* basic auth */ public Tuple2<String, String> extractBasicAuth(String value) { if (value != null) { String[] parts = value.split("\\s+"); if (parts.length == 2 && "basic".equals(parts[0].toLowerCase())) { String credentials = new String(base64.decodeNoGzip(parts[1])); int colon = credentials.indexOf(':'); // not using credentials.split() as password can contain ":" if (colon > -1) { return new Tuple2<String, String>(credentials.substring(0, colon), credentials.substring(colon + 1)); } } } return null; } public Tuple2<String, String> extractBasicAuth(Header header) { return (header != null ? extractBasicAuth(header.getValue()) : null); } public Tuple2<String, String> extractBasicAuth(HttpRequest request) { return extractBasicAuth(request.getFirstHeader("Authorization")); } public String basicAuthString(String name, String value) { return STR.joinArgs(" ", "Basic", base64.encodeClassic(STR.joinArgs(":", name, value).getBytes())); } public Header basicAuthHeader(String name, String value) { return new BasicHeader("Authorization", basicAuthString(name, value)); } }