1
+ /*
2
+ * Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
3
+ *
4
+ * This program and the accompanying materials are made available under the
5
+ * terms of the Eclipse Public License v. 2.0, which is available at
6
+ * http://www.eclipse.org/legal/epl-2.0.
7
+ *
8
+ * This Source Code may also be made available under the following Secondary
9
+ * Licenses when the conditions for such availability set forth in the
10
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
11
+ * version 2 with the GNU Classpath Exception, which is available at
12
+ * https://www.gnu.org/software/classpath/license.html.
13
+ *
14
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
+ */
16
+
17
+ package org .glassfish .jersey .tests .e2e .client ;
18
+
19
+ import com .sun .net .httpserver .HttpExchange ;
20
+ import com .sun .net .httpserver .HttpHandler ;
21
+ import com .sun .net .httpserver .HttpServer ;
22
+
23
+ import java .io .BufferedReader ;
24
+ import java .io .IOException ;
25
+ import java .io .InputStream ;
26
+ import java .io .InputStreamReader ;
27
+ import java .io .OutputStream ;
28
+ import java .net .InetSocketAddress ;
29
+ import java .nio .charset .StandardCharsets ;
30
+ import java .nio .file .Files ;
31
+ import java .nio .file .Path ;
32
+ import java .nio .file .Paths ;
33
+ import java .nio .file .StandardCopyOption ;
34
+ import java .util .UUID ;
35
+ import java .util .concurrent .Executors ;
36
+
37
+
38
+ /**
39
+ * Server for the file upload test that redirects from /submit to /upload.
40
+ */
41
+ class RedirectFileUploadServerTest {
42
+ private static final String UPLOAD_DIRECTORY = "target/uploads" ;
43
+ private static final String BOUNDARY_PREFIX = "boundary=" ;
44
+ private static final Path uploadDir = Paths .get (UPLOAD_DIRECTORY );
45
+
46
+ private static HttpServer server ;
47
+
48
+
49
+ public static void start (int port ) throws IOException {
50
+ // Create upload directory if it doesn't exist
51
+ if (!Files .exists (uploadDir )) {
52
+ Files .createDirectory (uploadDir );
53
+ }
54
+
55
+ // Create HTTP server
56
+ server = HttpServer .create (new InetSocketAddress (port ), 0 );
57
+
58
+ // Create contexts for different endpoints
59
+ server .createContext ("/submit" , new SubmitHandler ());
60
+ server .createContext ("/upload" , new UploadHandler ());
61
+
62
+ // Set executor and start server
63
+ server .setExecutor (Executors .newFixedThreadPool (10 ));
64
+ server .start ();
65
+ }
66
+
67
+ public static void stop () {
68
+ server .stop (0 );
69
+ }
70
+
71
+
72
+ // Handler for /submit endpoint that redirects to /upload
73
+ static class SubmitHandler implements HttpHandler {
74
+ @ Override
75
+ public void handle (HttpExchange exchange ) throws IOException {
76
+ try {
77
+ if (!"POST" .equals (exchange .getRequestMethod ())) {
78
+ sendResponse (exchange , 405 , "Method Not Allowed. Only POST is supported." );
79
+ return ;
80
+ }
81
+
82
+ // Send a 307 Temporary Redirect to /upload
83
+ // This preserves the POST method and body in the redirect
84
+ exchange .getResponseHeaders ().add ("Location" , "/upload" );
85
+ exchange .sendResponseHeaders (307 , -1 );
86
+ } finally {
87
+ exchange .close ();
88
+ }
89
+ }
90
+ }
91
+
92
+ // Handler for /upload endpoint that processes file uploads
93
+ static class UploadHandler implements HttpHandler {
94
+ @ Override
95
+ public void handle (HttpExchange exchange ) throws IOException {
96
+ try {
97
+ if (!"POST" .equals (exchange .getRequestMethod ())) {
98
+ sendResponse (exchange , 405 , "Method Not Allowed. Only POST is supported." );
99
+ return ;
100
+ }
101
+
102
+ // Check if the request contains multipart form data
103
+ String contentType = exchange .getRequestHeaders ().getFirst ("Content-Type" );
104
+ if (contentType == null || !contentType .startsWith ("multipart/form-data" )) {
105
+ sendResponse (exchange , 400 , "Bad Request. Content type must be multipart/form-data." );
106
+ return ;
107
+ }
108
+
109
+ // Extract boundary from content type
110
+ String boundary = extractBoundary (contentType );
111
+ if (boundary == null ) {
112
+ sendResponse (exchange , 400 , "Bad Request. Could not determine boundary." );
113
+ return ;
114
+ }
115
+
116
+ // Process the multipart request and save the file
117
+ String fileName = processMultipartRequest (exchange , boundary );
118
+
119
+ if (fileName != null ) {
120
+ sendResponse (exchange , 200 , "File uploaded successfully: " + fileName );
121
+ } else {
122
+ sendResponse (exchange , 400 , "Bad Request. No file found in request." );
123
+ }
124
+ } catch (Exception e ) {
125
+ e .printStackTrace ();
126
+ sendResponse (exchange , 500 , "Internal Server Error: " + e .getMessage ());
127
+ } finally {
128
+ exchange .close ();
129
+ Files .deleteIfExists (uploadDir );
130
+ }
131
+ }
132
+
133
+ private String extractBoundary (String contentType ) {
134
+ int boundaryIndex = contentType .indexOf (BOUNDARY_PREFIX );
135
+ if (boundaryIndex != -1 ) {
136
+ return "--" + contentType .substring (boundaryIndex + BOUNDARY_PREFIX .length ());
137
+ }
138
+ return null ;
139
+ }
140
+
141
+ private String processMultipartRequest (HttpExchange exchange , String boundary ) throws IOException {
142
+ InputStream requestBody = exchange .getRequestBody ();
143
+ BufferedReader reader = new BufferedReader (new InputStreamReader (requestBody , StandardCharsets .UTF_8 ));
144
+
145
+ String line ;
146
+ String fileName = null ;
147
+ Path tempFile = null ;
148
+ boolean isFileContent = false ;
149
+
150
+ // Generate a random filename for the temporary file
151
+ String tempFileName = UUID .randomUUID ().toString ();
152
+ tempFile = Files .createTempFile (tempFileName , ".tmp" );
153
+
154
+ try (OutputStream fileOut = Files .newOutputStream (tempFile )) {
155
+ while ((line = reader .readLine ()) != null ) {
156
+ // Check for the boundary
157
+ if (line .startsWith (boundary )) {
158
+ if (isFileContent ) {
159
+ // We've reached the end of the file content
160
+ break ;
161
+ }
162
+
163
+ // Read the next line (Content-Disposition)
164
+ line = reader .readLine ();
165
+ if (line != null && line .startsWith ("Content-Type" )) {
166
+ line = reader .readLine ();
167
+ }
168
+ if (line != null && line .contains ("filename=" )) {
169
+ // Extract filename
170
+ int filenameStart = line .indexOf ("filename=\" " ) + 10 ;
171
+ int filenameEnd = line .indexOf ("\" " , filenameStart );
172
+ fileName = line .substring (filenameStart , filenameEnd );
173
+
174
+ // Skip Content-Type line and empty line
175
+ reader .readLine (); // Content-Type
176
+ // System.out.println(reader.readLine()); // Empty line
177
+ isFileContent = true ;
178
+ }
179
+ } else if (isFileContent ) {
180
+ // If we're reading file content and this line is not a boundary,
181
+ // write it to the file (append a newline unless it's the first line)
182
+ fileOut .write (line .getBytes (StandardCharsets .UTF_8 ));
183
+ fileOut .write ('\n' );
184
+ }
185
+ }
186
+ }
187
+
188
+ // If we found a file, move it from the temp location to the uploads directory
189
+ if (fileName != null && !fileName .isEmpty ()) {
190
+ Path targetPath = Paths .get (UPLOAD_DIRECTORY , fileName );
191
+ Files .move (tempFile , targetPath , StandardCopyOption .REPLACE_EXISTING );
192
+ return fileName ;
193
+ } else {
194
+ // If no file was found, delete the temp file
195
+ Files .deleteIfExists (tempFile );
196
+ return null ;
197
+ }
198
+ }
199
+ }
200
+
201
+ // Helper method to send HTTP responses
202
+ private static void sendResponse (HttpExchange exchange , int statusCode , String response ) throws IOException {
203
+ exchange .getResponseHeaders ().set ("Content-Type" , "text/plain; charset=UTF-8" );
204
+ exchange .sendResponseHeaders (statusCode , response .length ());
205
+ try (OutputStream os = exchange .getResponseBody ()) {
206
+ os .write (response .getBytes (StandardCharsets .UTF_8 ));
207
+ }
208
+ }
209
+ }
0 commit comments