| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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 org.apache.servicecomb.it.schema; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.PipedInputStream; |
| import java.io.PipedOutputStream; |
| import java.lang.Thread.State; |
| import java.net.HttpURLConnection; |
| import java.net.URI; |
| import java.net.URL; |
| import java.net.URLDecoder; |
| import java.net.URLEncoder; |
| import java.nio.charset.StandardCharsets; |
| import java.util.UUID; |
| import java.util.concurrent.TimeUnit; |
| |
| import javax.servlet.http.Part; |
| |
| import org.apache.commons.io.FileUtils; |
| import org.apache.http.entity.StringEntity; |
| import org.apache.http.impl.bootstrap.HttpServer; |
| import org.apache.http.impl.bootstrap.ServerBootstrap; |
| import org.apache.servicecomb.core.BootListener; |
| import org.apache.servicecomb.foundation.common.part.FilePart; |
| import org.apache.servicecomb.it.ITUtils; |
| import org.apache.servicecomb.provider.rest.common.RestSchema; |
| import org.springframework.core.io.ByteArrayResource; |
| import org.springframework.core.io.Resource; |
| import org.springframework.http.HttpHeaders; |
| import org.springframework.http.MediaType; |
| import org.springframework.http.ResponseEntity; |
| import org.springframework.web.bind.annotation.GetMapping; |
| import org.springframework.web.bind.annotation.RequestMapping; |
| |
| import io.swagger.annotations.ApiResponse; |
| import io.swagger.annotations.ApiResponses; |
| |
| @RestSchema(schemaId = "download") |
| @RequestMapping(path = "/v1/download") |
| public class DownloadSchema implements BootListener { |
| File tempDir = new File("target/downloadTemp"); |
| |
| HttpServer server; |
| |
| public DownloadSchema() throws IOException { |
| FileUtils.deleteQuietly(tempDir); |
| FileUtils.forceMkdir(tempDir); |
| |
| // for download from net stream case |
| server = ServerBootstrap |
| .bootstrap() |
| .setListenerPort(0) |
| .registerHandler("/download/netInputStream", (req, resp, context) -> { |
| String uri = req.getRequestLine().getUri(); |
| String query = URI.create(uri).getQuery(); |
| int idx = query.indexOf('='); |
| String content = query.substring(idx + 1); |
| content = URLDecoder.decode(content, StandardCharsets.UTF_8.name()); |
| resp.setEntity(new StringEntity(content, StandardCharsets.UTF_8.name())); |
| }).create(); |
| server.start(); |
| } |
| |
| @Override |
| public void onBootEvent(BootEvent event) { |
| if (EventType.AFTER_CLOSE.equals(event.getEventType())) { |
| server.stop(); |
| } |
| } |
| |
| protected File createTempFile(String content) throws IOException { |
| return createTempFile(null, content); |
| } |
| |
| protected File createTempFile(String name, String content) throws IOException { |
| if (name == null) { |
| name = "download-" + UUID.randomUUID() + ".txt"; |
| } |
| File file = new File(tempDir, name); |
| FileUtils.write(file, content, StandardCharsets.UTF_8, false); |
| return file; |
| } |
| |
| // customize HttpHeaders.CONTENT_DISPOSITION to be "attachment;filename=tempFileEntity.txt" |
| @GetMapping(path = "/tempFileEntity") |
| public ResponseEntity<Part> tempFileEntity(String content) throws IOException { |
| File file = createTempFile(content); |
| |
| return ResponseEntity |
| .ok() |
| .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=tempFileEntity.txt") |
| .body(new FilePart(null, file) |
| .setDeleteAfterFinished(true)); |
| } |
| |
| // generate HttpHeaders.CONTENT_DISPOSITION to be "attachment;filename=tempFilePart.txt" automatically |
| @GetMapping(path = "/tempFilePart") |
| public Part tempFilePart(String content) throws IOException { |
| File file = createTempFile(content); |
| |
| return new FilePart(null, file) |
| .setDeleteAfterFinished(true) |
| .setSubmittedFileName("tempFilePart.txt"); |
| } |
| |
| @GetMapping(path = "/file") |
| public File file(String content) throws IOException { |
| return createTempFile("file.txt", content); |
| } |
| |
| @GetMapping(path = "/chineseAndSpaceFile") |
| public Part chineseAndSpaceFile(String content) throws IOException { |
| File file = createTempFile(content); |
| return new FilePart(null, file) |
| .setDeleteAfterFinished(true) |
| .setSubmittedFileName("测 试.test.txt"); |
| } |
| |
| @GetMapping(path = "/resource") |
| @ApiResponses({ |
| @ApiResponse(code = 200, response = File.class, message = ""), |
| }) |
| public Resource resource(String content) { |
| return new ByteArrayResource(content.getBytes(StandardCharsets.UTF_8)) { |
| @Override |
| public String getFilename() { |
| return "resource.txt"; |
| } |
| }; |
| } |
| |
| @GetMapping(path = "/entityResource") |
| @ApiResponses({ |
| @ApiResponse(code = 200, response = File.class, message = ""), |
| }) |
| public ResponseEntity<Resource> entityResource(String content) { |
| return ResponseEntity |
| .ok() |
| .header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN_VALUE) |
| .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=entityResource.txt") |
| .body(new ByteArrayResource(content.getBytes(StandardCharsets.UTF_8))); |
| } |
| |
| @GetMapping(path = "/entityInputStream") |
| @ApiResponses({ |
| @ApiResponse(code = 200, response = File.class, message = ""), |
| }) |
| public ResponseEntity<InputStream> entityInputStream(String content) { |
| return ResponseEntity |
| .ok() |
| .header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN_VALUE) |
| .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=entityInputStream.txt") |
| .body(new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8))); |
| } |
| |
| @GetMapping(path = "/bytes") |
| @ApiResponses({ |
| @ApiResponse(code = 200, response = File.class, message = ""), |
| }) |
| public ResponseEntity<byte[]> bytes(String content) { |
| return ResponseEntity |
| .ok() |
| .header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN_VALUE) |
| .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=bytes.txt") |
| .body(content.getBytes(StandardCharsets.UTF_8)); |
| } |
| |
| @GetMapping(path = "/netInputStream") |
| @ApiResponses({ |
| @ApiResponse(code = 200, response = File.class, message = ""), |
| }) |
| public ResponseEntity<InputStream> netInputStream(String content) throws IOException { |
| URL url = new URL("http://localhost:" + server.getLocalPort() + "/download/netInputStream?content=" |
| + URLEncoder.encode(content, StandardCharsets.UTF_8.name())); |
| HttpURLConnection conn = (HttpURLConnection) url.openConnection(); |
| return ResponseEntity |
| .ok() |
| .header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN_VALUE) |
| .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=netInputStream.txt") |
| .body(conn.getInputStream()); |
| } |
| |
| private Thread slowInputStreamThread; |
| |
| @GetMapping(path = "/waitSlowInputStreamClosed") |
| public void waitSlowInputStreamClosed() { |
| while (!slowInputStreamThread.getState().equals(State.TERMINATED)) { |
| ITUtils.forceWait(TimeUnit.MILLISECONDS, 500); |
| } |
| } |
| |
| @ApiResponses({@ApiResponse(code = 200, response = File.class, message = "")}) |
| @GetMapping(path = "/slowInputStream") |
| public ResponseEntity<InputStream> slowInputStream() throws IOException { |
| PipedInputStream in = new PipedInputStream(); |
| PipedOutputStream out = new PipedOutputStream(); |
| in.connect(out); |
| |
| slowInputStreamThread = new Thread(() -> { |
| Thread.currentThread().setName("download thread"); |
| byte[] bytes = "1".getBytes(StandardCharsets.UTF_8); |
| while (true) { |
| try { |
| out.write(bytes); |
| out.flush(); |
| Thread.sleep(1000); |
| } catch (Throwable e) { |
| break; |
| } |
| } |
| try { |
| out.close(); |
| } catch (final IOException ioe) { |
| // ignore |
| } |
| }); |
| slowInputStreamThread.start(); |
| |
| ResponseEntity<InputStream> responseEntity = ResponseEntity |
| .ok() |
| .header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN_VALUE) |
| .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=slowInputStream.txt") |
| .body(in); |
| return responseEntity; |
| } |
| } |