001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018 package org.apache.commons.net.ftp;
019
020 import java.io.BufferedReader;
021 import java.io.IOException;
022 import java.io.InputStream;
023 import java.io.InputStreamReader;
024 import java.io.OutputStream;
025 import java.io.UnsupportedEncodingException;
026 import java.net.Socket;
027 import java.net.SocketException;
028 import java.util.ArrayList;
029 import java.util.List;
030
031 import org.apache.commons.net.util.Base64;
032
033 /**
034 * Experimental attempt at FTP client that tunnels over an HTTP proxy connection.
035 *
036 * @author rory
037 * @since 2.2
038 */
039 public class FTPHTTPClient extends FTPClient {
040 private final String proxyHost;
041 private final int proxyPort;
042 private final String proxyUsername;
043 private final String proxyPassword;
044 private String host;
045 private int port;
046
047 private final byte[] CRLF;
048 private final Base64 base64 = new Base64();
049
050 public FTPHTTPClient(String proxyHost, int proxyPort, String proxyUser, String proxyPass) {
051 this.proxyHost = proxyHost;
052 this.proxyPort = proxyPort;
053 this.proxyUsername = proxyUser;
054 this.proxyPassword = proxyPass;
055
056 try {
057 CRLF = "\r\n".getBytes(getControlEncoding());
058 } catch (UnsupportedEncodingException e) {
059 throw new RuntimeException(e);
060 }
061 }
062
063 public FTPHTTPClient(String proxyHost, int proxyPort) {
064 this(proxyHost, proxyPort, null, null);
065 }
066
067
068 @Override
069 protected Socket _openDataConnection_(int command, String arg)
070 throws IOException {
071 Socket socket = new Socket(host, port);
072 InputStream is = socket.getInputStream();
073 OutputStream os = socket.getOutputStream();
074
075 tunnelHandshake(host, port, is, os);
076
077 return socket;
078 }
079
080 @Override
081 public void connect(String host, int port) throws SocketException,
082 IOException {
083 this.host = host;
084 this.port = port;
085
086 _socket_ = new Socket(proxyHost, proxyPort);
087 _input_ = _socket_.getInputStream();
088 _output_ = _socket_.getOutputStream();
089 try {
090 tunnelHandshake(host, port, _input_, _output_);
091 }
092 catch (Exception e) {
093 IOException ioe = new IOException("Could not connect to " + host);
094 ioe.initCause(e);
095 throw ioe;
096 }
097 }
098
099 private void tunnelHandshake(String host, int port, InputStream input, OutputStream output) throws IOException,
100 UnsupportedEncodingException {
101 final String connectString = "CONNECT " + host + ":" + port + " HTTP/1.1";
102
103 _output_.write(connectString.getBytes(getControlEncoding()));
104 _output_.write(CRLF);
105
106 if (proxyUsername != null && proxyPassword != null) {
107 final String header = "Proxy-Authorization: Basic "
108 + base64.encode(proxyUsername + ":" + proxyPassword) + "\r\n";
109 _output_.write(header.getBytes("UTF-8"));
110 _output_.write(CRLF);
111
112 List<String> response = new ArrayList<String>();
113 BufferedReader reader = new BufferedReader(
114 new InputStreamReader(_input_));
115
116 for (String line = reader.readLine(); line != null
117 && line.length() > 0; line = reader.readLine()) {
118 response.add(line);
119 }
120
121 int size = response.size();
122 if (size == 0) {
123 throw new IOException("No response from proxy");
124 }
125
126 String code = null;
127 String resp = response.get(0);
128 if (resp.startsWith("HTTP/") && resp.length() >= 12) {
129 code = resp.substring(9, 12);
130 } else {
131 throw new IOException("Invalid response from proxy: " + resp);
132 }
133
134 if (!"200".equals(code)) {
135 StringBuilder msg = new StringBuilder();
136 msg.append("HTTPTunnelConnector: connection failed\r\n");
137 msg.append("Response received from the proxy:\r\n");
138 for (String line : response) {
139 msg.append(line);
140 msg.append("\r\n");
141 }
142 throw new IOException(msg.toString());
143 }
144 }
145 }
146 }
147
148