text:
#ifndef SWAPCHAIN_H
#define SWAPCHAIN_H

#include "vulkan/vk_layer.h"
#include "vk_layer_config.h"
#include "vk_layer_logging.h"
#include <vector>
#include <unordered_map>

using namespace std;

// Swapchain ERROR codes
typedef enum _SWAPCHAIN_ERROR {
    SWAPCHAIN_INVALID_HANDLE,             // Handle used that isn't currently valid
    SWAPCHAIN_NULL_POINTER,               // Pointer set to NULL, instead of being a valid pointer
    SWAPCHAIN_EXT_NOT_ENABLED_BUT_USED,   // Did not enable WSI extension, but called WSI function
    SWAPCHAIN_DEL_OBJECT_BEFORE_CHILDREN, // Called vkDestroyDevice() before vkDestroySwapchainKHR()
    SWAPCHAIN_CREATE_UNSUPPORTED_SURFACE, // Called vkCreateSwapchainKHR() with a pCreateInfo->surface that wasn't seen as supported
                                          // by vkGetPhysicalDeviceSurfaceSupportKHR for the device
    SWAPCHAIN_CREATE_SWAP_WITHOUT_QUERY,  // Called vkCreateSwapchainKHR() without calling a query (e.g.
                                          // vkGetPhysicalDeviceSurfaceCapabilitiesKHR())
    SWAPCHAIN_CREATE_SWAP_BAD_MIN_IMG_COUNT,     // Called vkCreateSwapchainKHR() with out-of-bounds minImageCount
    SWAPCHAIN_CREATE_SWAP_OUT_OF_BOUNDS_EXTENTS, // Called vkCreateSwapchainKHR() with out-of-bounds imageExtent
    SWAPCHAIN_CREATE_SWAP_EXTENTS_NO_MATCH_WIN, // Called vkCreateSwapchainKHR() with imageExtent that doesn't match window's extent
    SWAPCHAIN_CREATE_SWAP_BAD_PRE_TRANSFORM,    // Called vkCreateSwapchainKHR() with a non-supported preTransform
    SWAPCHAIN_CREATE_SWAP_BAD_COMPOSITE_ALPHA,  // Called vkCreateSwapchainKHR() with a non-supported compositeAlpha
    SWAPCHAIN_CREATE_SWAP_BAD_IMG_ARRAY_SIZE,   // Called vkCreateSwapchainKHR() with a non-supported imageArraySize
    SWAPCHAIN_CREATE_SWAP_BAD_IMG_USAGE_FLAGS,  // Called vkCreateSwapchainKHR() with a non-supported imageUsageFlags
    SWAPCHAIN_CREATE_SWAP_BAD_IMG_COLOR_SPACE,  // Called vkCreateSwapchainKHR() with a non-supported imageColorSpace
    SWAPCHAIN_CREATE_SWAP_BAD_IMG_FORMAT,       // Called vkCreateSwapchainKHR() with a non-supported imageFormat
    SWAPCHAIN_CREATE_SWAP_BAD_IMG_FMT_CLR_SP,   // Called vkCreateSwapchainKHR() with a non-supported imageColorSpace
    SWAPCHAIN_CREATE_SWAP_BAD_PRESENT_MODE,     // Called vkCreateSwapchainKHR() with a non-supported presentMode
    SWAPCHAIN_CREATE_SWAP_BAD_SHARING_MODE,     // Called vkCreateSwapchainKHR() with a non-supported imageSharingMode
    SWAPCHAIN_CREATE_SWAP_BAD_SHARING_VALUES,   // Called vkCreateSwapchainKHR() with bad values when imageSharingMode is
                                                // VK_SHARING_MODE_CONCURRENT
    SWAPCHAIN_CREATE_SWAP_DIFF_SURFACE, // Called vkCreateSwapchainKHR() with pCreateInfo->oldSwapchain that has a different surface
                                        // than pCreateInfo->surface
    SWAPCHAIN_DESTROY_SWAP_DIFF_DEVICE, // Called vkDestroySwapchainKHR() with a different VkDevice than vkCreateSwapchainKHR()
    SWAPCHAIN_APP_OWNS_TOO_MANY_IMAGES, // vkAcquireNextImageKHR() asked for more images than are available
    SWAPCHAIN_INDEX_TOO_LARGE,          // Index is too large for swapchain
    SWAPCHAIN_INDEX_NOT_IN_USE,         // vkQueuePresentKHR() given index that is not owned by app
    SWAPCHAIN_BAD_BOOL,                 // VkBool32 that doesn't have value of VK_TRUE or VK_FALSE (e.g. is a non-zero form of true)
    SWAPCHAIN_INVALID_COUNT,            // Second time a query called, the pCount value didn't match first time
    SWAPCHAIN_WRONG_STYPE,              // The sType for a struct has the wrong value
    SWAPCHAIN_WRONG_NEXT,               // The pNext for a struct is not NULL
    SWAPCHAIN_ZERO_VALUE,               // A value should be non-zero
    SWAPCHAIN_INCOMPATIBLE_ALLOCATOR,   // pAllocator must be compatible (i.e. NULL or not) when object is created and destroyed
    SWAPCHAIN_DID_NOT_QUERY_QUEUE_FAMILIES,     // A function using a queueFamilyIndex was called before
                                                // vkGetPhysicalDeviceQueueFamilyProperties() was called
    SWAPCHAIN_QUEUE_FAMILY_INDEX_TOO_LARGE,     // A queueFamilyIndex value is not less than pQueueFamilyPropertyCount returned by
                                                // vkGetPhysicalDeviceQueueFamilyProperties()
    SWAPCHAIN_SURFACE_NOT_SUPPORTED_WITH_QUEUE, // A surface is not supported by a given queueFamilyIndex, as seen by
                                                // vkGetPhysicalDeviceSurfaceSupportKHR()
} SWAPCHAIN_ERROR;

// The following is for logging error messages:
#define LAYER_NAME (char *) "Swapchain"
#define LOG_ERROR_NON_VALID_OBJ(objType, type, obj)                                                                                \
    (my_data) ? log_msg(my_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, (objType), (uint64_t)(obj), __LINE__,                 \
                        SWAPCHAIN_INVALID_HANDLE, LAYER_NAME, "%s() called with a non-valid %s.", __FUNCTION__, (obj))             \
              : VK_FALSE
#define LOG_ERROR_NULL_POINTER(objType, type, obj)                                                                                \
    (my_data) ? log_msg(my_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, (objType), (uint64_t)(obj), 0,                        \
                        SWAPCHAIN_NULL_POINTER, LAYER_NAME, "%s() called with NULL pointer %s.", __FUNCTION__, (obj))              \
              : VK_FALSE
#define LOG_ERROR_INVALID_COUNT(objType, type, obj, obj2, val, val2)                                                               \
    (my_data) ? log_msg(my_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, (objType), (uint64_t)(obj), 0,                        \
                        SWAPCHAIN_INVALID_COUNT, LAYER_NAME, "%s() called with non-NULL %s, and with %s set to a "                 \
                                                             "value (%d) that is greater than the value (%d) that "                \
                                                             "was returned when %s was NULL.",                                     \
                        __FUNCTION__, (obj2), (obj), (val), (val2), (obj2))                                                        \
              : VK_FALSE
#define LOG_ERROR_WRONG_STYPE(objType, type, obj, val)                                                                             \
    (my_data) ? log_msg(my_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, (objType), (uint64_t)(obj), 0, SWAPCHAIN_WRONG_STYPE, \
                        LAYER_NAME, "%s() called with the wrong value for %s->sType "                                              \
                                    "(expected %s).",                                                                              \
                        __FUNCTION__, (obj), (val))                                                                                \
              : VK_FALSE
#define LOG_ERROR_ZERO_VALUE(objType, type, obj)                                                                                \
    (my_data) ? log_msg(my_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, (objType), (uint64_t)(obj), 0, SWAPCHAIN_ZERO_VALUE,  \
                        LAYER_NAME, "%s() called with a zero value for %s.", __FUNCTION__, (obj))                                  \
              : VK_FALSE
#define LOG_ERROR(objType, type, obj, enm, fmt, ...)                                                                               \
    (my_data) ? log_msg(my_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, (objType), (uint64_t)(obj), __LINE__, (enm),          \
                        LAYER_NAME, (fmt), __VA_ARGS__)                                                                            \
              : VK_FALSE
#define LOG_ERROR_QUEUE_FAMILY_INDEX_TOO_LARGE(objType, type, obj, val1, val2)                                                     \
    (my_data) ? log_msg(my_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, (objType), (uint64_t)(obj), 0,                        \
                        SWAPCHAIN_QUEUE_FAMILY_INDEX_TOO_LARGE, LAYER_NAME, "%s() called with a queueFamilyIndex that is too "     \
                                                                            "large (i.e. %d).  The maximum value (returned "       \
                                                                            "by vkGetPhysicalDeviceQueueFamilyProperties) is "     \
                                                                            "only %d.\n",                                          \
                        __FUNCTION__, (val1), (val2))                                                                              \
              : VK_FALSE
#define LOG_PERF_WARNING(objType, type, obj, enm, fmt, ...)                                                                        \
    (my_data) ? log_msg(my_data->report_data, VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT, (objType), (uint64_t)(obj), __LINE__,   \
                        (enm), LAYER_NAME, (fmt), __VA_ARGS__)                                                                     \
              : VK_FALSE
#define LOG_INFO_WRONG_NEXT(objType, type, obj)                                                                                \
    (my_data) ? log_msg(my_data->report_data, VK_DEBUG_REPORT_INFORMATION_BIT_EXT, (objType), (uint64_t)(obj), 0,                  \
                        SWAPCHAIN_WRONG_NEXT, LAYER_NAME, "%s() called with non-NULL value for %s->pNext.", __FUNCTION__, (obj))   \
              : VK_FALSE

// NOTE: The following struct's/typedef's are for keeping track of
// info that is used for validating the WSI extensions.

// Forward declarations:
struct _SwpInstance;
struct _SwpSurface;
struct _SwpPhysicalDevice;
struct _SwpDevice;
struct _SwpSwapchain;
struct _SwpImage;
struct _SwpQueue;

typedef _SwpInstance SwpInstance;
typedef _SwpSurface SwpSurface;
;
typedef _SwpPhysicalDevice SwpPhysicalDevice;
typedef _SwpDevice SwpDevice;
typedef _SwpSwapchain SwpSwapchain;
typedef _SwpImage SwpImage;
typedef _SwpQueue SwpQueue;

// Create one of these for each VkInstance:
struct _SwpInstance {
    // The actual handle for this VkInstance:
    VkInstance instance;

    // Remember the VkSurfaceKHR's that are created for this VkInstance:
    unordered_map<VkSurfaceKHR, SwpSurface *> surfaces;

    // When vkEnumeratePhysicalDevices is called, the VkPhysicalDevice's are
    // remembered:
    unordered_map<const void *, SwpPhysicalDevice *> physicalDevices;

    // Set to true if VK_KHR_SURFACE_EXTENSION_NAME was enabled for this VkInstance:
    bool surfaceExtensionEnabled;

// TODO: Add additional booleans for platform-specific extensions:
#ifdef VK_USE_PLATFORM_ANDROID_KHR
    // Set to true if VK_KHR_ANDROID_SURFACE_EXTENSION_NAME was enabled for this VkInstance:
    bool androidSurfaceExtensionEnabled;
#endif // VK_USE_PLATFORM_ANDROID_KHR
#ifdef VK_USE_PLATFORM_MIR_KHR
    // Set to true if VK_KHR_MIR_SURFACE_EXTENSION_NAME was enabled for this VkInstance:
    bool mirSurfaceExtensionEnabled;
#endif // VK_USE_PLATFORM_MIR_KHR
#ifdef VK_USE_PLATFORM_WAYLAND_KHR
    // Set to true if VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME was enabled for this VkInstance:
    bool waylandSurfaceExtensionEnabled;
#endif // VK_USE_PLATFORM_WAYLAND_KHR
#ifdef VK_USE_PLATFORM_WIN32_KHR
    // Set to true if VK_KHR_WIN32_SURFACE_EXTENSION_NAME was enabled for this VkInstance:
    bool win32SurfaceExtensionEnabled;
#endif // VK_USE_PLATFORM_WIN32_KHR
#ifdef VK_USE_PLATFORM_XCB_KHR
    // Set to true if VK_KHR_XCB_SURFACE_EXTENSION_NAME was enabled for this VkInstance:
    bool xcbSurfaceExtensionEnabled;
#endif // VK_USE_PLATFORM_XCB_KHR
#ifdef VK_USE_PLATFORM_XLIB_KHR
    // Set to true if VK_KHR_XLIB_SURFACE_EXTENSION_NAME was enabled for this VkInstance:
    bool xlibSurfaceExtensionEnabled;
#endif // VK_USE_PLATFORM_XLIB_KHR
};

// Create one of these for each VkSurfaceKHR:
struct _SwpSurface {
    // The actual handle for this VkSurfaceKHR:
    VkSurfaceKHR surface;

    // VkInstance that this VkSurfaceKHR is associated with:
    SwpInstance *pInstance;

    // When vkCreateSwapchainKHR is called, the VkSwapchainKHR's are
    // remembered:
    unordered_map<VkSwapchainKHR, SwpSwapchain *> swapchains;

    // 'true' if pAllocator was non-NULL when vkCreate*SurfaceKHR was called:
    bool usedAllocatorToCreate;

    // Value of pQueueFamilyPropertyCount that was returned by the
    // vkGetPhysicalDeviceQueueFamilyProperties() function:
    uint32_t numQueueFamilyIndexSupport;
    // Array of VkBool32's that is intialized by the
    // vkGetPhysicalDeviceSurfaceSupportKHR() function.  First call for a given
    // surface allocates and initializes this array to false for all
    // queueFamilyIndex's (and sets numQueueFamilyIndexSupport to non-zero).
    // All calls set the entry for a given queueFamilyIndex:
    VkBool32 *pQueueFamilyIndexSupport;
};

// Create one of these for each VkPhysicalDevice within a VkInstance:
struct _SwpPhysicalDevice {
    // The actual handle for this VkPhysicalDevice:
    VkPhysicalDevice physicalDevice;

    // Corresponding VkDevice (and info) to this VkPhysicalDevice:
    SwpDevice *pDevice;

    // VkInstance that this VkPhysicalDevice is associated with:
    SwpInstance *pInstance;

    // Records results of vkGetPhysicalDeviceQueueFamilyProperties()'s
    // numOfQueueFamilies parameter when pQueueFamilyProperties is NULL:
    bool gotQueueFamilyPropertyCount;
    uint32_t numOfQueueFamilies;

    // Record all surfaces that vkGetPhysicalDeviceSurfaceSupportKHR() was
    // called for:
    unordered_map<VkSurfaceKHR, SwpSurface *> supportedSurfaces;

    // TODO: Record/use this info per-surface, not per-device, once a
    // non-dispatchable surface object is added to WSI:
    // Results of vkGetPhysicalDeviceSurfaceCapabilitiesKHR():
    bool gotSurfaceCapabilities;
    VkSurfaceCapabilitiesKHR surfaceCapabilities;

    // TODO: Record/use this info per-surface, not per-device, once a
    // non-dispatchable surface object is added to WSI:
    // Count and VkSurfaceFormatKHR's returned by vkGetPhysicalDeviceSurfaceFormatsKHR():
    uint32_t surfaceFormatCount;
    VkSurfaceFormatKHR *pSurfaceFormats;

    // TODO: Record/use this info per-surface, not per-device, once a
    // non-dispatchable surface object is added to WSI:
    // Count and VkPresentModeKHR's returned by vkGetPhysicalDeviceSurfacePresentModesKHR():
    uint32_t presentModeCount;
    VkPresentModeKHR *pPresentModes;
};

// Create one of these for each VkDevice within a VkInstance:
struct _SwpDevice {
    // The actual handle for this VkDevice:
    VkDevice device;

    // Corresponding VkPhysicalDevice (and info) to this VkDevice:
    SwpPhysicalDevice *pPhysicalDevice;

    // Set to true if VK_KHR_SWAPCHAIN_EXTENSION_NAME was enabled:
    bool swapchainExtensionEnabled;

    // When vkCreateSwapchainKHR is called, the VkSwapchainKHR's are
    // remembered:
    unordered_map<VkSwapchainKHR, SwpSwapchain *> swapchains;

    // When vkGetDeviceQueue is called, the VkQueue's are remembered:
    unordered_map<VkQueue, SwpQueue *> queues;
};

// Create one of these for each VkImage within a VkSwapchainKHR:
struct _SwpImage {
    // The actual handle for this VkImage:
    VkImage image;

    // Corresponding VkSwapchainKHR (and info) to this VkImage:
    SwpSwapchain *pSwapchain;

    // true if application got this image from vkAcquireNextImageKHR(), and
    // hasn't yet called vkQueuePresentKHR() for it; otherwise false:
    bool ownedByApp;
};

// Create one of these for each VkSwapchainKHR within a VkDevice:
struct _SwpSwapchain {
    // The actual handle for this VkSwapchainKHR:
    VkSwapchainKHR swapchain;

    // Corresponding VkDevice (and info) to this VkSwapchainKHR:
    SwpDevice *pDevice;

    // Corresponding VkSurfaceKHR to this VkSwapchainKHR:
    SwpSurface *pSurface;

    // When vkGetSwapchainImagesKHR is called, the VkImage's are
    // remembered:
    uint32_t imageCount;
    unordered_map<int, SwpImage> images;

    // 'true' if pAllocator was non-NULL when vkCreateSwapchainKHR was called:
    bool usedAllocatorToCreate;
};

// Create one of these for each VkQueue within a VkDevice:
struct _SwpQueue {
    // The actual handle for this VkQueue:
    VkQueue queue;

    // Corresponding VkDevice (and info) to this VkSwapchainKHR:
    SwpDevice *pDevice;

    // Which queueFamilyIndex this VkQueue is associated with:
    uint32_t queueFamilyIndex;
};

struct layer_data {
    debug_report_data *report_data;
    std::vector<VkDebugReportCallbackEXT> logging_callback;
    VkLayerDispatchTable *device_dispatch_table;
    VkLayerInstanceDispatchTable *instance_dispatch_table;
    // NOTE: The following are for keeping track of info that is used for
    // validating the WSI extensions.
    std::unordered_map<void *, SwpInstance> instanceMap;
    std::unordered_map<VkSurfaceKHR, SwpSurface> surfaceMap;
    std::unordered_map<void *, SwpPhysicalDevice> physicalDeviceMap;
    std::unordered_map<void *, SwpDevice> deviceMap;
    std::unordered_map<VkSwapchainKHR, SwpSwapchain> swapchainMap;
    std::unordered_map<void *, SwpQueue> queueMap;

    layer_data() : report_data(nullptr), device_dispatch_table(nullptr), instance_dispatch_table(nullptr){};
};

#endif // SWAPCHAIN_H

redpajama_set_name: RedPajamaGithub


text:
package org.eclipse.jetty.proxy;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.charset.StandardCharsets;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.toolchain.test.http.SimpleHttpResponse;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

public class ConnectHandlerSSLTest extends AbstractConnectHandlerTest
{
    private SslContextFactory sslContextFactory;

    @Before
    public void prepare() throws Exception
    {
        sslContextFactory = new SslContextFactory();
        String keyStorePath = MavenTestingUtils.getTestResourceFile("keystore").getAbsolutePath();
        sslContextFactory.setKeyStorePath(keyStorePath);
        sslContextFactory.setKeyStorePassword("storepwd");
        sslContextFactory.setKeyManagerPassword("keypwd");
        server = new Server();
        serverConnector = new ServerConnector(server, sslContextFactory);
        server.addConnector(serverConnector);
        server.setHandler(new ServerHandler());
        server.start();
        prepareProxy();
    }

    @Test
    public void testGETRequest() throws Exception
    {
        String hostPort = "localhost:" + serverConnector.getLocalPort();
        String request = "" +
                "CONNECT " + hostPort + " HTTP/1.1\r\n" +
                "Host: " + hostPort + "\r\n" +
                "\r\n";
        try (Socket socket = newSocket())
        {
            OutputStream output = socket.getOutputStream();
            BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));

            output.write(request.getBytes(StandardCharsets.UTF_8));
            output.flush();

            // Expect 200 OK from the CONNECT request
            SimpleHttpResponse response = readResponse(input);
            Assert.assertEquals("200", response.getCode());

            // Be sure the buffered input does not have anything buffered
            Assert.assertFalse(input.ready());

            // Upgrade the socket to SSL
            try (SSLSocket sslSocket = wrapSocket(socket))
            {
                output = sslSocket.getOutputStream();
                input = new BufferedReader(new InputStreamReader(sslSocket.getInputStream()));

                request =
                        "GET /echo HTTP/1.1\r\n" +
                                "Host: " + hostPort + "\r\n" +
                                "\r\n";
                output.write(request.getBytes(StandardCharsets.UTF_8));
                output.flush();

                response = readResponse(input);
                Assert.assertEquals("200", response.getCode());
                Assert.assertEquals("GET /echo", response.getBody());
            }
        }
    }

    @Test
    public void testPOSTRequests() throws Exception
    {
        String hostPort = "localhost:" + serverConnector.getLocalPort();
        String request = "" +
                "CONNECT " + hostPort + " HTTP/1.1\r\n" +
                "Host: " + hostPort + "\r\n" +
                "\r\n";
        try (Socket socket = newSocket())
        {
            OutputStream output = socket.getOutputStream();
            BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));

            output.write(request.getBytes(StandardCharsets.UTF_8));
            output.flush();

            // Expect 200 OK from the CONNECT request
            SimpleHttpResponse response = readResponse(input);
            Assert.assertEquals("200", response.getCode());

            // Be sure the buffered input does not have anything buffered
            Assert.assertFalse(input.ready());

            // Upgrade the socket to SSL
            try (SSLSocket sslSocket = wrapSocket(socket))
            {
                output = sslSocket.getOutputStream();
                input = new BufferedReader(new InputStreamReader(sslSocket.getInputStream()));

                for (int i = 0; i < 10; ++i)
                {
                    request = "" +
                            "POST /echo?param=" + i + " HTTP/1.1\r\n" +
                            "Host: " + hostPort + "\r\n" +
                            "Content-Length: 5\r\n" +
                            "\r\n" +
                            "HELLO";
                    output.write(request.getBytes(StandardCharsets.UTF_8));
                    output.flush();

                    response = readResponse(input);
                    Assert.assertEquals("200", response.getCode());
                    Assert.assertEquals("POST /echo?param=" + i + "\r\nHELLO", response.getBody());
                }
            }
        }
    }

    private SSLSocket wrapSocket(Socket socket) throws Exception
    {
        SSLContext sslContext = sslContextFactory.getSslContext();
        SSLSocketFactory socketFactory = sslContext.getSocketFactory();
        SSLSocket sslSocket = (SSLSocket)socketFactory.createSocket(socket, socket.getInetAddress().getHostAddress(), socket.getPort(), true);
        sslSocket.setUseClientMode(true);
        sslSocket.startHandshake();
        return sslSocket;
    }

    private static class ServerHandler extends AbstractHandler
    {
        public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException
        {
            request.setHandled(true);

            String uri = httpRequest.getRequestURI();
            if ("/echo".equals(uri))
            {
                StringBuilder builder = new StringBuilder();
                builder.append(httpRequest.getMethod()).append(" ").append(uri);
                if (httpRequest.getQueryString() != null)
                    builder.append("?").append(httpRequest.getQueryString());

                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                InputStream input = httpRequest.getInputStream();
                int read;
                while ((read = input.read()) >= 0)
                    baos.write(read);
                baos.close();

                ServletOutputStream output = httpResponse.getOutputStream();
                output.println(builder.toString());
                output.write(baos.toByteArray());
            }
            else
            {
                throw new ServletException();
            }
        }
    }
}


text:
package com.borqs.server.market.controllers;

import com.borqs.server.market.Errors;
import com.borqs.server.market.ServiceException;
import com.borqs.server.market.context.ServiceContext;
import com.borqs.server.market.service.OperService;
import com.borqs.server.market.service.ServiceConsts;
import com.borqs.server.market.service.impl.PublishImpl;
import com.borqs.server.market.utils.*;
import com.borqs.server.market.utils.mybatis.record.RecordsWithTotal;
import com.borqs.server.market.utils.record.CheckResult;
import com.borqs.server.market.utils.record.Record;
import com.borqs.server.market.utils.record.Records;
import org.apache.commons.lang.StringUtils;
import org.codehaus.jackson.JsonNode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.List;

@Controller
@RequestMapping("/")
public class OperController extends AbstractController {

    public static final int PARTITION_COUNT_PER_PAGE = OperService.DEFAULT_PARTITION_COUNT_PER_PAGE;

    private OperService operService;

    public OperController() {
    }

    public OperService getOperService() {
        return operService;
    }

    @Autowired
    @Qualifier("service.defaultOperService")
    public void setOperService(OperService operService) {
        this.operService = operService;
    }

    private void setupNavigationData(ServiceContext ctx, HttpServletRequest req, HttpServletResponse resp, String currentAppId) {
        saveCurrentModule(req, resp, ServiceConsts.MODULE_OPER);
        Attributes.setCurrentAppId(req, currentAppId);

        Records apps = operService.listApps(ctx);
        localeSelector.selectLocale(apps, ctx);
        Attributes.setAllApps(req, apps);

//        if (StringUtils.isNotEmpty(currentAppId)) {
//            Records products = publishService.listProducts(ctx, currentAppId, null, false, false);
//            localeSelector.selectLocale(products, ctx);
//            Attributes.setAllProducts(req, products);
//        }
    }

    @RequestMapping(value = "/oper", method = RequestMethod.GET)
    public ModelAndView indexPage(ServiceContext ctx, HttpServletRequest req, HttpServletResponse resp) {
        if (!ctx.hasAccountId())
            return redirect("/signin");

        saveCurrentModule(req, resp, ServiceConsts.MODULE_OPER);
        return redirect("/oper/welcome");
    }

    @RequestMapping(value = "/oper/welcome", method = RequestMethod.GET)
    public ModelAndView welcomePage(ServiceContext ctx, HttpServletRequest req, HttpServletResponse resp) {
        setupNavigationData(ctx, req, resp, null);
        return new ModelAndView("oper_welcome");
    }

    @RequestMapping(value = "/oper/apps/{appId:.+}", method = RequestMethod.GET)
    public ModelAndView appPage(ServiceContext ctx, @PathVariable("appId") String appId) {
        return redirect("/oper/apps/" + appId + "/partitions");
    }

    @RequestMapping(value = "/oper/apps/{appId:.+}/partitions", method = {RequestMethod.GET, RequestMethod.POST})
    public ModelAndView listPartitions(ServiceContext ctx, HttpServletRequest req, HttpServletResponse resp,
                                       @PathVariable("appId") String appId,
                                       Params params) {
        if (!ctx.hasAccountId())
            return redirect("/signin");

        final String categoryId = params.param("category").asString();
        Records categories = operService.listCategories(ctx, appId);
        if (categories.isEmpty())
            throw new ServiceException(Errors.E_ILLEGAL_CATEGORY, "Illegal category");

        Record firstCategory = categories.get(0);
        if (StringUtils.isBlank(categoryId))
            return redirect("/oper/apps/" + appId + "/partitions?category=" + firstCategory.asString("category_id"));

        boolean hasCategory = categories.hasWithPredicate(new Records.Predicate() {
            @Override
            public boolean predicate(Record rec) {
                return StringUtils.equals(categoryId, rec.asString("category_id"));
            }
        });
        if (!hasCategory)
            throw new ServiceException(Errors.E_ILLEGAL_CATEGORY, "Illegal category_id or app_id");

        int page = params.param("page").asInt(0);

        RecordsWithTotal partitions = operService.listPartitions(ctx, appId, categoryId, Params.of(
                "page=>", page,
                "count=>", PARTITION_COUNT_PER_PAGE)
        );

        localeSelector.selectLocale(categories, ctx);
        localeSelector.selectLocale(partitions, ctx);

        setupNavigationData(ctx, req, resp, appId);
        return new ModelAndView("oper_partitions", CC.map(
                "partitions=>", partitions.getRecords(),
                "pagination=>", Pagination.create(page, PARTITION_COUNT_PER_PAGE, partitions.getTotal()),
                "categories=>", categories,
                "currentCategory=>", categoryId,
                "currentAppId=>", appId
        ));
    }

    @RequestMapping(value = "/oper/apps/{appId:.+}/promotions", method = {RequestMethod.GET, RequestMethod.POST})
    public ModelAndView listPromotions(ServiceContext ctx, HttpServletRequest req, HttpServletResponse resp,
                                       @PathVariable("appId") String appId,
                                       Params params) {
        if (!ctx.hasAccountId())
            return redirect("/signin");

        final String categoryId = params.param("category").asString();
        Records categories = operService.listCategories(ctx, appId);
        if (categories.isEmpty())
            throw new ServiceException(Errors.E_ILLEGAL_CATEGORY, "Illegal category");

        if (WebUtils2.isPostBack(req)) {
            JsonNode promotionsNode = params.param("promotions").asJson();
            promotionsNode = JsonUtils.asArrayNode(promotionsNode);
            Records promotions = new Records();
            for (int i = 0; i < promotionsNode.size(); i++) {
                JsonNode promotionNode = promotionsNode.get(i);
                Record pr = new Record();
                pr.set("type", promotionNode.path("type").asInt());
                pr.set("target", promotionNode.path("target").asText());
                pr.set("name", promotionNode.get("name"));
                pr.set("description", promotionNode.get("description"));
                pr.set("logo_image", promotionNode.get("logo_image"));
                promotions.add(pr);
            }
            operService.setPromotions(ctx, appId, categoryId, promotions);
            return redirect("/oper/apps/" + appId + "/promotions?category=" + categoryId);
        }

        Record firstCategory = categories.get(0);
        if (StringUtils.isBlank(categoryId))
            return redirect("/oper/apps/" + appId + "/promotions?category=" + firstCategory.asString("category_id"));

        boolean hasCategory = categories.hasWithPredicate(new Records.Predicate() {
            @Override
            public boolean predicate(Record rec) {
                return StringUtils.equals(categoryId, rec.asString("category_id"));
            }
        });
        if (!hasCategory)
            throw new ServiceException(Errors.E_ILLEGAL_CATEGORY, "Illegal category_id or app_id");

        Records promotions = operService.listPromotions(ctx, appId, categoryId);
        Records products = operService.listAllProducts(ctx, appId, categoryId);
        Records partitions = operService.listPartitions(ctx, appId, categoryId, Params.of("page=>", 0, "count=>", 100000)).getRecords();

        products.retainsFields("id", "name");
        partitions.retainsFields("id", "name");

        localeSelector.selectLocale(categories, ctx);
        localeSelector.selectLocale(products, ctx);
        localeSelector.selectLocale(partitions, ctx);

        setupNavigationData(ctx, req, resp, appId);
        return new ModelAndView("oper_promotions", CC.map(
                "categories=>", categories,
                "currentCategory=>", categoryId,
                "currentAppId=>", appId,
                "promotions=>", promotions,
                "availablePartitions=>", partitions,
                "availableProducts=>", products
        ));
    }

    @RequestMapping(value = "/oper/apps/{appId:.+}/new_partition", method = {RequestMethod.GET, RequestMethod.POST})
    public ModelAndView newPartitionPage(ServiceContext ctx, HttpServletRequest req, HttpServletResponse resp,
                                         @PathVariable("appId") String appId,
                                         Params params) {
        if (!ctx.hasAccountId())
            return redirect("/signin");

        String categoryId = params.param("category").asString("");

        setupNavigationData(ctx, req, resp, appId);
        if (WebUtils2.isPostBack(req)) {
            params.aggregateMultipleLocale("name", "description");
            CheckResult cr = operService.checkPartition(ctx, appId, categoryId, params);
            if (cr.ok()) {
                Record newPartition = operService.addPartition(ctx, appId, categoryId, params.put("list", ""));
                return redirect("/oper/partitions/" + newPartition.asString("id"));
            } else {
                return new ModelAndView("oper_partition", CC.map(
                        "mode=>", "create",
                        "partition=>", cr,
                        "currentAppId=>", appId
                ));
            }
        } else {
            return new ModelAndView("oper_partition", CC.map(
                    "mode=>", "create",
                    "partition=>", new Record(),
                    "currentAppId=>", appId
            ));
        }
    }

    @RequestMapping(value = "/oper/partitions/{partitionId:.+}", method = {RequestMethod.GET, RequestMethod.POST})
    public ModelAndView showPartitionPage(ServiceContext ctx, HttpServletRequest req, HttpServletResponse resp,
                                         @PathVariable("partitionId") String partitionId,
                                         Params params) {
        final String DISPLAY_INFO_COOKIE = "oper.partition.displayInfo";

        if (!ctx.hasAccountId())
            return redirect("/signin");

        Record partition = operService.getPartition(ctx, partitionId);
        if (partition == null)
            throw new ServiceException(Errors.E_ILLEGAL_PARTITION, "Illegal partition");

        String appId = partition.asString("app_id");
        String categoryId = partition.asString("category_id");

        setupNavigationData(ctx, req, resp, appId);
        if (WebUtils2.isPostBack(req)) {
            params.aggregateMultipleLocale("name", "description");
            CheckResult cr = operService.checkPartition(ctx, appId, categoryId, params);
            if (cr.ok()) {
                operService.updatePartition(ctx, partitionId, params);
                if (params.hasParam("products")) {
                    String[] productIds = StringUtils2.splitArray(params.param("products").asString(""), '\n', true);
                    operService.setPartitionList(ctx, partitionId, Arrays.asList(productIds));
                }
                return redirect("/oper/apps/" + appId + "/partitions");
            } else {
                WebUtils2.setCookie(resp, DISPLAY_INFO_COOKIE, "1");
                return new ModelAndView("oper_partition", CC.map(
                        "mode=>", "update",
                        "partition=>", cr,
                        "currentAppId=>", appId,
                        "partitionDisplayName=>", "",
                        "displayInfo=>", true
                ));
            }
        } else {
            String displayInfo = WebUtils2.getCookieWithDelete(req, resp, DISPLAY_INFO_COOKIE, "0");
            partition.disperseMultipleLocale("name", "description");
            String partitionDisplayName = partition.asString("name_" + ctx.getClientLocale("en_US"));
            return new ModelAndView("oper_partition", CC.map(
                    "mode=>", "update",
                    "partition=>", partition,
                    "currentAppId=>", appId,
                    "partitionDisplayName=>", partitionDisplayName,
                    "displayInfo=>", PrimitiveTypeConverter.toBoolean(displayInfo, false)
            ));
        }
    }

    @RequestMapping(value = "/oper/delete_partition", method = {RequestMethod.GET, RequestMethod.POST})
    public ModelAndView deletePartition(ServiceContext ctx, @RequestParam("id") String id) {
        if (!ctx.hasAccountId())
            return redirect("/signin");

        Record partition = operService.getPartition(ctx, id);
        operService.deletePartition(ctx, id);
        if (partition == null) {
            return redirect("/oper/");
        } else {
            return redirect("/oper/apps/" + partition.asString("app_id") + "/partitions");
        }
    }
}