| package org.bouncycastle.est.jcajce; |
| |
| |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.net.URL; |
| import java.nio.charset.Charset; |
| import java.util.Iterator; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.bouncycastle.est.ESTClient; |
| import org.bouncycastle.est.ESTClientSourceProvider; |
| import org.bouncycastle.est.ESTException; |
| import org.bouncycastle.est.ESTRequest; |
| import org.bouncycastle.est.ESTRequestBuilder; |
| import org.bouncycastle.est.ESTResponse; |
| import org.bouncycastle.est.Source; |
| import org.bouncycastle.util.Properties; |
| |
| class DefaultESTClient |
| implements ESTClient |
| { |
| private static final Charset utf8 = Charset.forName("UTF-8"); |
| private static byte[] CRLF = new byte[]{'\r', '\n'}; |
| private final ESTClientSourceProvider sslSocketProvider; |
| |
| public DefaultESTClient(ESTClientSourceProvider sslSocketProvider) |
| { |
| this.sslSocketProvider = sslSocketProvider; |
| } |
| |
| private static void writeLine(OutputStream os, String s) |
| throws IOException |
| { |
| os.write(s.getBytes()); |
| os.write(CRLF); |
| } |
| |
| public ESTResponse doRequest(ESTRequest req) |
| throws IOException |
| { |
| ESTResponse resp = null; |
| ESTRequest r = req; |
| int rcCount = 15; |
| do |
| { |
| resp = performRequest(r); |
| r = redirectURL(resp); |
| } |
| while (r != null && --rcCount > 0); // Follow redirects. |
| |
| if (rcCount == 0) |
| { |
| throw new ESTException("Too many redirects.."); |
| } |
| |
| return resp; |
| } |
| |
| protected ESTRequest redirectURL(ESTResponse response) |
| throws IOException |
| { |
| ESTRequest redirectingRequest = null; |
| |
| if (response.getStatusCode() >= 300 && response.getStatusCode() <= 399) |
| { |
| |
| switch (response.getStatusCode()) |
| { |
| case 301: |
| case 302: |
| case 303: |
| case 306: |
| case 307: |
| String loc = response.getHeader("Location"); |
| if ("".equals(loc)) |
| { |
| throw new ESTException("Redirect status type: " + response.getStatusCode() + " but no location header"); |
| } |
| |
| ESTRequestBuilder requestBuilder = new ESTRequestBuilder(response.getOriginalRequest()); |
| if (loc.startsWith("http")) |
| { |
| redirectingRequest = requestBuilder.withURL(new URL(loc)).build(); |
| } |
| else |
| { |
| URL u = response.getOriginalRequest().getURL(); |
| redirectingRequest = requestBuilder.withURL(new URL(u.getProtocol(), u.getHost(), u.getPort(), loc)).build(); |
| } |
| break; |
| default: |
| throw new ESTException("Client does not handle http status code: " + response.getStatusCode()); |
| } |
| } |
| |
| if (redirectingRequest != null) |
| { |
| response.close(); // Close original request. |
| } |
| |
| return redirectingRequest; |
| } |
| |
| public ESTResponse performRequest(ESTRequest c) |
| throws IOException |
| { |
| |
| |
| ESTResponse res = null; |
| Source socketSource = null; |
| try |
| { |
| socketSource = sslSocketProvider.makeSource(c.getURL().getHost(), c.getURL().getPort()); |
| if (c.getListener() != null) |
| { |
| c = c.getListener().onConnection(socketSource, c); |
| } |
| |
| // socketSource = new SSLSocketSource((SSLSocket)sock); |
| |
| OutputStream os = null; |
| |
| Set<String> opts = Properties.asKeySet("org.bouncycastle.debug.est"); |
| if (opts.contains("output") || |
| opts.contains("all")) |
| { |
| os = new PrintingOutputStream(socketSource.getOutputStream()); |
| } |
| else |
| { |
| os = socketSource.getOutputStream(); |
| } |
| |
| String req = c.getURL().getPath() + ((c.getURL().getQuery() != null) ? c.getURL().getQuery() : ""); |
| |
| ESTRequestBuilder rb = new ESTRequestBuilder(c); |
| |
| Map<String, String[]> headers = c.getHeaders(); |
| |
| if (!headers.containsKey("Connection")) |
| { |
| rb.addHeader("Connection", "close" ); |
| } |
| |
| // Replace host header. |
| URL u = c.getURL(); |
| if (u.getPort() > -1) |
| { |
| rb.setHeader("Host", String.format("%s:%d", u.getHost(), u.getPort())); |
| } |
| else |
| { |
| rb.setHeader("Host", u.getHost()); |
| } |
| |
| |
| ESTRequest rc = rb.build(); |
| |
| writeLine(os, rc.getMethod() + " " + req + " HTTP/1.1"); |
| |
| |
| for (Iterator it = rc.getHeaders().entrySet().iterator(); it.hasNext();) |
| { |
| Map.Entry<String, String[]> ent = (Map.Entry<String, String[]>)it.next(); |
| String[] vs = (String[])ent.getValue(); |
| |
| for (int i = 0; i != vs.length; i++) |
| { |
| writeLine(os, ent.getKey() + ": " + vs[i]); |
| } |
| } |
| |
| os.write(CRLF); |
| os.flush(); |
| |
| rc.writeData(os); |
| |
| os.flush(); |
| |
| if (rc.getHijacker() != null) |
| { |
| res = rc.getHijacker().hijack(rc, socketSource); |
| return res; |
| } |
| else |
| { |
| res = new ESTResponse(rc, socketSource); |
| } |
| |
| return res; |
| |
| } |
| finally |
| { |
| // Close only if response not generated. |
| if (socketSource != null && res == null) |
| { |
| socketSource.close(); |
| } |
| } |
| |
| } |
| |
| private class PrintingOutputStream |
| extends OutputStream |
| { |
| private final OutputStream tgt; |
| |
| public PrintingOutputStream(OutputStream tgt) |
| { |
| this.tgt = tgt; |
| } |
| |
| public void write(int b) |
| throws IOException |
| { |
| System.out.print(String.valueOf((char)b)); |
| tgt.write(b); |
| } |
| } |
| } |