Path: blob/master/test/jdk/sun/net/ftp/MarkResetTest.java
41149 views
/*1* Copyright (c) 2002, 2019, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223/*24* @test25* @bug 467310326* @library /test/lib27* @run main/othervm/timeout=140 MarkResetTest28* @summary URLConnection.getContent() hangs over FTP for DOC, PPT, XLS files29*/3031import java.io.BufferedReader;32import java.io.File;33import java.io.FileInputStream;34import java.io.IOException;35import java.io.InputStream;36import java.io.InputStreamReader;37import java.io.OutputStream;38import java.io.PrintWriter;39import java.io.UncheckedIOException;40import java.net.InetAddress;41import java.net.InetSocketAddress;42import java.net.Proxy;43import java.net.ServerSocket;44import java.net.Socket;45import java.net.URL;46import java.net.URLConnection;47import java.nio.file.Files;48import java.nio.file.Paths;4950import jdk.test.lib.net.URIBuilder;5152public class MarkResetTest {53private static final String FILE_NAME = "EncDec.doc";5455/**56* A class that simulates, on a separate, an FTP server.57*/58private class FtpServer extends Thread {59private final ServerSocket server;60private volatile boolean done = false;61private boolean pasvEnabled = true;62private boolean portEnabled = true;63private boolean extendedEnabled = true;6465/**66* This Inner class will handle ONE client at a time.67* That's where 99% of the protocol handling is done.68*/6970private class FtpServerHandler extends Thread {71BufferedReader in;72PrintWriter out;73Socket client;74private final int ERROR = 0;75private final int USER = 1;76private final int PASS = 2;77private final int CWD = 3;78private final int TYPE = 4;79private final int RETR = 5;80private final int PASV = 6;81private final int PORT = 7;82private final int QUIT = 8;83private final int EPSV = 9;84String[] cmds = { "USER", "PASS", "CWD",85"TYPE", "RETR", "PASV",86"PORT", "QUIT", "EPSV"};87private String arg = null;88private ServerSocket pasv = null;89private int data_port = 0;90private InetAddress data_addr = null;9192/**93* Parses a line to match it with one of the supported FTP commands.94* Returns the command number.95*/9697private int parseCmd(String cmd) {98if (cmd == null || cmd.length() < 3)99return ERROR;100int blank = cmd.indexOf(' ');101if (blank < 0)102blank = cmd.length();103if (blank < 3)104return ERROR;105String s = cmd.substring(0, blank);106if (cmd.length() > blank+1)107arg = cmd.substring(blank+1, cmd.length());108else109arg = null;110for (int i = 0; i < cmds.length; i++) {111if (s.equalsIgnoreCase(cmds[i]))112return i+1;113}114return ERROR;115}116117public FtpServerHandler(Socket cl) {118client = cl;119}120121protected boolean isPasvSet() {122if (pasv != null && !pasvEnabled) {123try {124pasv.close();125} catch (IOException ex) {126}127pasv = null;128}129if (pasvEnabled && pasv != null)130return true;131return false;132}133134/**135* Open the data socket with the client. This can be the136* result of a "PASV" or "PORT" command.137*/138139protected OutputStream getOutDataStream() {140try {141if (isPasvSet()) {142Socket s = pasv.accept();143return s.getOutputStream();144}145if (data_addr != null) {146Socket s = new Socket(data_addr, data_port);147data_addr = null;148data_port = 0;149return s.getOutputStream();150}151} catch (Exception e) {152e.printStackTrace();153}154return null;155}156157protected InputStream getInDataStream() {158try {159if (isPasvSet()) {160Socket s = pasv.accept();161return s.getInputStream();162}163if (data_addr != null) {164Socket s = new Socket(data_addr, data_port);165data_addr = null;166data_port = 0;167return s.getInputStream();168}169} catch (Exception e) {170e.printStackTrace();171}172return null;173}174175/**176* Handles the protocol exchange with the client.177*/178179public void run() {180done = false;181String str;182int res;183boolean logged = false;184boolean waitpass = false;185186try {187in = new BufferedReader(new InputStreamReader(188client.getInputStream()));189out = new PrintWriter(client.getOutputStream(), true);190out.println("220 tatooine FTP server (SunOS 5.8) ready.");191} catch (Exception ex) {192return;193}194while (!done) {195try {196str = in.readLine();197res = parseCmd(str);198if ((res > PASS && res != QUIT) && !logged) {199out.println("530 Not logged in.");200continue;201}202switch (res) {203case ERROR:204out.println("500 '" + str +205"': command not understood.");206break;207case USER:208if (!logged && !waitpass) {209out.println("331 Password required for " + arg);210waitpass = true;211} else {212out.println("503 Bad sequence of commands.");213}214break;215case PASS:216if (!logged && waitpass) {217out.println("230-Welcome to the FTP server!");218out.println("ab");219out.println("230 Guest login ok, " +220"access restrictions apply.");221logged = true;222waitpass = false;223} else224out.println("503 Bad sequence of commands.");225break;226case QUIT:227out.println("221 Goodbye.");228out.flush();229out.close();230if (pasv != null)231pasv.close();232done = true;233break;234case TYPE:235out.println("200 Type set to " + arg + ".");236break;237case CWD:238out.println("250 CWD command successful.");239break;240case EPSV:241if (!extendedEnabled || !pasvEnabled) {242out.println("500 EPSV is disabled, " +243"use PORT instead.");244continue;245}246if ("all".equalsIgnoreCase(arg)) {247out.println("200 EPSV ALL command successful.");248continue;249}250try {251if (pasv == null) {252pasv = new ServerSocket();253pasv.bind(new InetSocketAddress(server.getInetAddress(), 0));254}255int port = pasv.getLocalPort();256out.println("229 Entering Extended" +257" Passive Mode (|||" + port + "|)");258} catch (IOException ssex) {259out.println("425 Can't build data connection:" +260" Connection refused.");261}262break;263264case PASV:265if (!pasvEnabled) {266out.println("500 PASV is disabled, " +267"use PORT instead.");268continue;269}270try {271if (pasv == null) {272pasv = new ServerSocket();273pasv.bind(new InetSocketAddress("127.0.0.1", 0));274}275int port = pasv.getLocalPort();276277// Parenthesis are optional, so let's be278// nasty and don't put them279out.println("227 Entering Passive Mode" +280" 127,0,0,1," +281(port >> 8) + "," + (port & 0xff));282} catch (IOException ssex) {283out.println("425 Can't build data connection:" +284"Connection refused.");285}286break;287case PORT:288if (!portEnabled) {289out.println("500 PORT is disabled, " +290"use PASV instead");291continue;292}293StringBuffer host;294int i = 0, j = 4;295while (j > 0) {296i = arg.indexOf(',', i + 1);297if (i < 0)298break;299j--;300}301if (j != 0) {302out.println("500 '" + arg + "':" +303" command not understood.");304continue;305}306try {307host = new StringBuffer(arg.substring(0, i));308for (j = 0; j < host.length(); j++)309if (host.charAt(j) == ',')310host.setCharAt(j, '.');311String ports = arg.substring(i+1);312i = ports.indexOf(',');313data_port = Integer.parseInt(314ports.substring(0, i)) << 8;315data_port += (Integer.parseInt(316ports.substring(i+1)));317data_addr = InetAddress.getByName(318host.toString());319out.println("200 Command okay.");320} catch (Exception ex3) {321data_port = 0;322data_addr = null;323out.println("500 '" + arg + "':" +324" command not understood.");325}326break;327case RETR:328{329File file = new File(arg);330if (!file.exists()) {331System.out.println("File not found");332out.println("200 Command okay.");333out.println("550 '" + arg +334"' No such file or directory.");335break;336}337FileInputStream fin = new FileInputStream(file);338OutputStream dout = getOutDataStream();339if (dout != null) {340out.println("150 Binary data connection" +341" for " + arg +342" (" + client.getInetAddress().343getHostAddress() + ") (" +344file.length() + " bytes).");345int c;346int len = 0;347while ((c = fin.read()) != -1) {348dout.write(c);349len++;350}351dout.flush();352dout.close();353fin.close();354out.println("226 Binary Transfer complete.");355} else {356out.println("425 Can't build data" +357" connection: Connection refused.");358}359}360break;361}362} catch (IOException ioe) {363ioe.printStackTrace();364try {365out.close();366} catch (Exception ex2) {367}368done = true;369}370}371}372}373374public FtpServer(int port) {375this(InetAddress.getLoopbackAddress(), port);376}377378public FtpServer(InetAddress address, int port) {379try {380if (address == null) {381server = new ServerSocket(port);382} else {383server = new ServerSocket();384server.bind(new InetSocketAddress(address, port));385}386} catch (IOException e) {387throw new UncheckedIOException(e);388}389}390391public FtpServer() {392this(null, 21);393}394395public int getPort() {396return server.getLocalPort();397}398399/**400* A way to tell the server that it can stop.401*/402synchronized public void terminate() {403done = true;404}405406407/*408* All we got to do here is create a ServerSocket and wait for a409* connection. When a connection happens, we just have to create410* a thread that will handle it.411*/412public void run() {413try {414System.out.println("FTP server waiting for connections at: "415+ server.getLocalSocketAddress());416Socket client;417client = server.accept();418(new FtpServerHandler(client)).start();419server.close();420} catch (Exception e) {421}422}423}424425public static void main(String[] args) throws Exception {426Files.copy(Paths.get(System.getProperty("test.src"), FILE_NAME),427Paths.get(".", FILE_NAME));428new MarkResetTest();429}430431public MarkResetTest() {432FtpServer server = null;433try {434server = new FtpServer(0);435server.start();436int port = 0;437while (port == 0) {438Thread.sleep(500);439port = server.getPort();440}441442URL url = URIBuilder.newBuilder()443.scheme("ftp")444.loopback()445.port(port)446.path("/" + FILE_NAME)447.toURL();448449URLConnection con = url.openConnection(Proxy.NO_PROXY);450System.out.println("getContent: " + con.getContent());451System.out.println("getContent-length: " + con.getContentLength());452453InputStream is = con.getInputStream();454455/**456* guessContentTypeFromStream method calls mark and reset methods457* on the given stream. Make sure that calling458* guessContentTypeFromStream repeatedly does not affect459* reading from the stream afterwards460*/461System.out.println("Call GuessContentTypeFromStream()" +462" several times..");463for (int i = 0; i < 5; i++) {464System.out.println((i + 1) + " mime-type: " +465con.guessContentTypeFromStream(is));466}467468int len = 0;469int c;470while ((c = is.read()) != -1) {471len++;472}473is.close();474System.out.println("read: " + len + " bytes of the file");475476// We're done!477server.terminate();478server.interrupt();479480// Did we pass ?481if (len != (new File(FILE_NAME)).length()) {482throw new Exception("Failed to read the file correctly");483}484System.out.println("PASSED: File read correctly");485} catch (Exception e) {486e.printStackTrace();487try {488server.terminate();489server.interrupt();490} catch (Exception ex) {491}492throw new RuntimeException("FTP support error: " + e.getMessage());493}494}495}496497498