smiley-http-proxy-servlet做代理application/x-www-form-urlencoded请求类型,报错failed to respond

这篇具有很好参考价值的文章主要介绍了smiley-http-proxy-servlet做代理application/x-www-form-urlencoded请求类型,报错failed to respond。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

背景

关于springboot项目用来做类型nginx的反向代理,利用smiley-http-proxy-servlet既可以做出,

但是如果请求application/x-www-form-urlencoded 会报错failed to respond,对此我前后梳理原因并做出解决

一、smiley-http-proxy-servlet的使用

引入依赖

        <!--http 反向代理-->
        <dependency>
            <groupId>org.mitre.dsmiley.httpproxy</groupId>
            <artifactId>smiley-http-proxy-servlet</artifactId>
            <version>1.12.1</version>
        </dependency>

编写动态生成prpxyServlet,通过yml配置,动态生成

yml配置
proxy:
  url_mapping:
    {
      #请求前置代理地址
      "[/proxy/front/*]": "http://10.203.40.3:8080/yyds",
      ##请求饿了么地址的
      "[/proxy/elm/]": "https://ppe-api-be.ele.me/"

    }
获取配置信息
package cn.hsa.gyjg.config.httpproxy;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

import java.util.Map;

/**
 * <h3>net_agent</h3>
 * <p>代理地址映射配置</p>
 *
 * @author : AbstractBin
 * @date : 2022/3/10 0:11
 **/
@Configuration
@ConfigurationProperties(prefix = "proxy")
public class ProxyMappingConfig {
    //将yml中的proxy.url_mapping自动转为map
    private Map<String, String> urlMapping;

    public void setUrlMapping(Map<String, String> urlMapping) {
        this.urlMapping = urlMapping;
    }
    public Map<String, String> getUrlMapping() {
        return urlMapping;
    }
}


编写动态生成prpxyServlet,
package cn.hsa.gyjg.config.httpproxy;
import org.mitre.dsmiley.httpproxy.ProxyServlet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.stereotype.Component;

import javax.servlet.Servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletRegistration;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * <h3>net_agent</h3>
 * <p></p>
 *
 * @author : AbstractBin
 * @date : 2022/3/10 9:35
 **/
@Component
public class ServletContextInitListener implements ServletContextInitializer {
    Logger log = LoggerFactory.getLogger(ServletContextInitializer.class);
    @Autowired
    private ProxyMappingConfig proxyMappingConfig;

    //项目启动时,会读取yml配置里所有的路径映射,每个映射注册一个ProxyServlet
    @Override
    public void onStartup(ServletContext servletContext) {
        Map<String, String> urlMapping = proxyMappingConfig.getUrlMapping();
        if (null == urlMapping || urlMapping.size() < 1) {
            return;
        }
        //计数
        AtomicInteger count = new AtomicInteger(0);
        urlMapping.forEach((serverUrl, targetUrl) -> {

            log.info("【请求代理注册,serverUrl={},targetUrl】", serverUrl, targetUrl);
            ProxyServlet servlet = new ProxyServlet();
            //每个servlet名字不能重复
            String servletName = "servlet" + count.get();
            ServletRegistration.Dynamic dynamic = servletContext.addServlet(servletName, servlet);
            Map<String, String> paramMap = new HashMap<>();
            paramMap.put(ProxyServlet.P_TARGET_URI, targetUrl);
            paramMap.put(ProxyServlet.P_LOG, "true");
            dynamic.setInitParameters(paramMap);
            dynamic.addMapping(serverUrl);
            count.incrementAndGet();

        });
    }
}
启动项目,访问对应yml配置接口即可代理到对应地址

二、smiley-http-proxy-servlet做代理时遇到的问题,报错failed to respond

源码分析


smiley-http-proxy-servlet的ProxyServlet就是一个servlet;
当前端请求代理地址时,首先经过各种FIlter,然后再调用该代理ProxyServlet的 service() 方法;
下面是service()方法重点部分

Object proxyRequest;
        if (servletRequest.getHeader("Content-Length") == null && servletRequest.getHeader("Transfer-Encoding") == null) {
            proxyRequest = new BasicHttpRequest(method, proxyRequestUri);
        } else {
            proxyRequest = this.newProxyRequestWithEntity(method, proxyRequestUri, servletRequest);
        }



当然最终走的是newProxyRequestWithEntity方法,下面看看该方法源码

protected HttpRequest newProxyRequestWithEntity(String method, String proxyRequestUri, HttpServletRequest servletRequest) throws IOException {
        HttpEntityEnclosingRequest eProxyRequest = new BasicHttpEntityEnclosingRequest(method, proxyRequestUri);
        eProxyRequest.setEntity(new InputStreamEntity(servletRequest.getInputStream(), this.getContentLength(servletRequest)));
        return eProxyRequest;
    }



这里看到了最终是通过getInputStream将请求的Stream流传给新的代理请求Request
再调试的时候getInputStream一直拿不到数据,所以请求代理的时候报错failed to respond

问题原因 由于application/x-www-form-urlencoded该类型,在项目中通过servlet时候项目已经将其流获取转成ParameterMap存储,所以获取不到流数据

request.getParameter()、request.getInputStream()和request.getReader()的区别
如果Post请求体是application/x- www-form-urlencoded,或是form表单提交。可以通过request.getParameter()方法来获取请求参数值。但当请求体不是该类型时,需要通过request.getInputStream()或request.getReader()方法来获取请求参数值。
当然请求体类型是application/x- www-form-urlencoded时也可以直接调用request.getInputStream()或request.getReader()方法来解析并获取请求参数,前提是还没调用request.getParameter()方法;
此时当request.getInputStream()或request.getReader()获取到请求数据后,无法再调request.getParameter()获取请求数据;
即对该类型的请求,三个方法互斥,只能调其中一个。

如何解决?

通过源码我们知道smiley-http-proxy-servlet代理底层是以httpcilent 作为转发请求工具,参考httpcilent 请求application/x- www-form-urlencoded需要如何入参可以知道

import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import java.util.ArrayList;
import java.util.List;

public class HttpClientExample {
    public static void main(String[] args) {
        // 1. 创建HttpClient对象
        HttpClient httpClient = new DefaultHttpClient();

        // 2. 创建HttpPost对象,并设置请求的URL
        HttpPost httpPost = new HttpPost("

        try {
            // 3. 创建参数列表,并进行参数编码
            List<NameValuePair> params = new ArrayList<>();
            params.add(new BasicNameValuePair("param1", "value1"));
            params.add(new BasicNameValuePair("param2", "value2"));
            UrlEncodedFormEntity entity = new UrlEncodedFormEntity(params, "UTF-8");

            // 4. 将参数设置到HttpPost对象的Entity中
            httpPost.setEntity(entity);

            // 5. 发送请求,并获取响应结果
            HttpResponse response = httpClient.execute(httpPost);
            String responseBody = EntityUtils.toString(response.getEntity());
            System.out.println(responseBody);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

所以自定义ProxyServlet,在对应参数转换里,加入改代码,判断请求类型,转换参数

if (MediaType.APPLICATION_FORM_URLENCODED_VALUE.equals(servletRequest.getContentType())
        || servletRequest.getContentType().contains(MediaType.APPLICATION_FORM_URLENCODED_VALUE)) {
    // 添加参数
    Map<String, String[]> mapdata = servletRequest.getParameterMap();
    List<NameValuePair> nameValuePairs = new ArrayList<>();
    if (mapdata.size() != 0) {
        // 将mapdata中的key存在set集合中,通过迭代器取出所有的key,再获取每一个键对应的值
        Set keySet = mapdata.keySet();
        Iterator it = keySet.iterator();
        while (it.hasNext()) {
            String k = it.next().toString();
            String[] v = mapdata.get(k);
            nameValuePairs.add(new BasicNameValuePair(k, v[0]));
        }
    }
    eProxyRequest.setEntity(new UrlEncodedFormEntity(nameValuePairs, servletRequest.getCharacterEncoding()));
} else {
    eProxyRequest.setEntity(new InputStreamEntity(servletRequest.getInputStream(), this.getContentLength(servletRequest)));
}
package cn.hsa.gyjg.config.httpproxy;

import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.message.BasicHttpEntityEnclosingRequest;
import org.apache.http.message.BasicHttpRequest;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.mitre.dsmiley.httpproxy.ProxyServlet;
import org.springframework.http.MediaType;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.*;

@Slf4j
public class MyProxyServlet extends ProxyServlet {
    @Override
    public void service(HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws ServletException, IOException {
        if (servletRequest.getAttribute(ATTR_TARGET_URI) == null) {
            servletRequest.setAttribute(ATTR_TARGET_URI, this.targetUri);
        }

        if (servletRequest.getAttribute(ATTR_TARGET_HOST) == null) {
            servletRequest.setAttribute(ATTR_TARGET_HOST, this.targetHost);
        }

        String method = servletRequest.getMethod();
        String proxyRequestUri = this.rewriteUrlFromRequest(servletRequest);
        Object proxyRequest;
        if (servletRequest.getHeader("Content-Length") == null && servletRequest.getHeader("Transfer-Encoding") == null) {
            proxyRequest = new BasicHttpRequest(method, proxyRequestUri);
        } else {
            proxyRequest = this.newProxyRequestWithEntity(method, proxyRequestUri, servletRequest);
        }

        this.copyRequestHeaders(servletRequest, (HttpRequest) proxyRequest);
        this.setXForwardedForHeader(servletRequest, (HttpRequest) proxyRequest);
        HttpResponse proxyResponse = null;

        try {
            if (this.doLog) {
                log.info("proxyRequestUri:"+ proxyRequestUri);
                log.info("Content-Type:"+ servletRequest.getContentType());
//                log.info("params:"+getRequestBodyByInputStream(((HttpEntityEnclosingRequest)proxyRequest).getEntity().getContent()));
            }
            proxyResponse = this.doExecute(servletRequest, servletResponse, (HttpRequest) proxyRequest);
            int statusCode = proxyResponse.getStatusLine().getStatusCode();
            servletResponse.setStatus(statusCode, proxyResponse.getStatusLine().getReasonPhrase());
            this.copyResponseHeaders(proxyResponse, servletRequest, servletResponse);
            if (statusCode == 304) {
                servletResponse.setIntHeader("Content-Length", 0);
            } else {
                this.copyResponseEntity(proxyResponse, servletResponse, (HttpRequest) proxyRequest, servletRequest);
            }
            if (this.doLog) {
                log.info("respStatusCode:"+ statusCode);
            }
        } catch (Exception var11) {
            this.handleRequestException((HttpRequest) proxyRequest, proxyResponse, var11);
        } finally {
            if (proxyResponse != null) {
                EntityUtils.consumeQuietly(proxyResponse.getEntity());
            }

        }

    }

    /**
     * 获取http中的请求报文
     *
     * @return
     */
    private String getRequestBodyByInputStream(InputStream inputStream) {
        try {
            // 获取ServletInputStream
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
            StringBuilder requestBody = new StringBuilder();
            char[] buffer = new char[1024];
            int read;
            while ((read = reader.read(buffer)) != -1) {
                requestBody.append(buffer, 0, read);
            }
            // 现在requestBody包含了请求体的字符串内容
            return requestBody.toString();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "";
    }

    @Override
    protected HttpRequest newProxyRequestWithEntity(String method, String proxyRequestUri, HttpServletRequest servletRequest) throws IOException {
        HttpEntityEnclosingRequest eProxyRequest = new BasicHttpEntityEnclosingRequest(method, proxyRequestUri);
        if (MediaType.APPLICATION_FORM_URLENCODED_VALUE.equals(servletRequest.getContentType())
                || servletRequest.getContentType().contains(MediaType.APPLICATION_FORM_URLENCODED_VALUE)) {
            // 添加参数
            Map<String, String[]> mapdata = servletRequest.getParameterMap();
            List<NameValuePair> nameValuePairs = new ArrayList<>();
            if (mapdata.size() != 0) {
                // 将mapdata中的key存在set集合中,通过迭代器取出所有的key,再获取每一个键对应的值
                Set keySet = mapdata.keySet();
                Iterator it = keySet.iterator();
                while (it.hasNext()) {
                    String k = it.next().toString();
                    String[] v = mapdata.get(k);
                    nameValuePairs.add(new BasicNameValuePair(k, v[0]));
                }
            }
            eProxyRequest.setEntity(new UrlEncodedFormEntity(nameValuePairs, servletRequest.getCharacterEncoding()));
        } else {
            eProxyRequest.setEntity(new InputStreamEntity(servletRequest.getInputStream(), this.getContentLength(servletRequest)));
        }
        return eProxyRequest;
    }

    private long getContentLength(HttpServletRequest request) {
        String contentLengthHeader = request.getHeader("Content-Length");
        return contentLengthHeader != null ? Long.parseLong(contentLengthHeader) : -1L;
    }


    private void setXForwardedForHeader(HttpServletRequest servletRequest, HttpRequest proxyRequest) {
        if (this.doForwardIP) {
            String forHeaderName = "X-Forwarded-For";
            String forHeader = servletRequest.getRemoteAddr();
            String existingForHeader = servletRequest.getHeader(forHeaderName);
            if (existingForHeader != null) {
                forHeader = existingForHeader + ", " + forHeader;
            }

            proxyRequest.setHeader(forHeaderName, forHeader);
            String protoHeaderName = "X-Forwarded-Proto";
            String protoHeader = servletRequest.getScheme();
            proxyRequest.setHeader(protoHeaderName, protoHeader);
        }

    }
}

最后在动态生成代理时候转成自己的代理类文章来源地址https://www.toymoban.com/news/detail-844634.html

package cn.hsa.gyjg.config.httpproxy;
import org.mitre.dsmiley.httpproxy.ProxyServlet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.stereotype.Component;

import javax.servlet.Servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletRegistration;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * <h3>net_agent</h3>
 * <p></p>
 *
 * @author : AbstractBin
 * @date : 2022/3/10 9:35
 **/
@Component
public class ServletContextInitListener implements ServletContextInitializer {
    Logger log = LoggerFactory.getLogger(ServletContextInitializer.class);
    @Autowired
    private ProxyMappingConfig proxyMappingConfig;

    //项目启动时,会读取yml配置里所有的路径映射,每个映射注册一个ProxyServlet
    @Override
    public void onStartup(ServletContext servletContext) {
        Map<String, String> urlMapping = proxyMappingConfig.getUrlMapping();
        if (null == urlMapping || urlMapping.size() < 1) {
            return;
        }
        //计数
        AtomicInteger count = new AtomicInteger(0);
        urlMapping.forEach((serverUrl, targetUrl) -> {

            log.info("【请求代理注册,serverUrl={},targetUrl】", serverUrl, targetUrl);
            ProxyServlet servlet = new MyProxyServlet();
            //每个servlet名字不能重复
            String servletName = "servlet" + count.get();
            ServletRegistration.Dynamic dynamic = servletContext.addServlet(servletName, servlet);
            Map<String, String> paramMap = new HashMap<>();
            paramMap.put(ProxyServlet.P_TARGET_URI, targetUrl);
            paramMap.put(ProxyServlet.P_LOG, "true");
            dynamic.setInitParameters(paramMap);
            dynamic.addMapping(serverUrl);
            count.incrementAndGet();

        });
    }
}

到了这里,关于smiley-http-proxy-servlet做代理application/x-www-form-urlencoded请求类型,报错failed to respond的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • docker如何设置http proxy代理

    如果您使用Docker构建镜像或拉取镜像时需要使用代理,可以按照以下步骤设置HTTP代理: 创建或编辑Docker服务配置文件 如果您使用systemd管理Docker服务,可以编辑该服务的配置文件 /etc/systemd/system/docker.service.d/http-proxy.conf 。如果文件不存在,可以创建该文件。或者是 docker.ser

    2024年02月11日
    浏览(39)
  • Jmeter-使用http proxy代理录制脚本

    Jmeter-使用http proxy代理录制脚本 第1步:打卡jmeter工具新增1个线程组 第2步:给线程组添加1个HTTP请求默认值   第3步:设置下HTTP请求默认值 第4步:在工作台中新增1个----HTTP代理服务器   第5步:设置HTTP代理服务器 设置完后记得点击启动 第6步:浏览器设置(这里选择ie浏览器

    2024年02月16日
    浏览(36)
  • docker使用http_proxy配置代理

    钢铁知识库,一个学习python爬虫、数据分析的知识库。人生苦短,快用python。 在内网服务器中,docker经常需要下载拉取镜像,但由于没有网络要么只能手动导入镜像包,又或者通过http_proxy代理到其它服务器下载。 增加 http-proxy.conf 配置文件,正确配置好代理服务器后重启d

    2024年01月22日
    浏览(52)
  • 记录hutool http通过代理模式proxy访问外面的链接

    效果: 代码:  如何获取代理端口:  

    2024年02月10日
    浏览(46)
  • Docker HTTP(S) Proxy代理方式连接互联网

    Docker HTTP(S) Proxy 是一种在 Docker 容器内部设置 HTTP(S) 代理的方法,以便于容器内的应用程序可以方便地通过代理访问互联网。设置 HTTP(S) 代理的方法主要有两种:使用 Dockerfile 配置和在使用 docker run 时添加参数。 以下是使用 Docker HTTP(S) Proxy 的具体步骤: 1. 使用 Dockerfile 配置

    2024年02月08日
    浏览(29)
  • vue3配置代理--[vite] http proxy error

    跨域请求数据, 浏览器 同源策略的保护机制, 通过 proxy 实现跨域请求数据; 如果直接 postman 请求是不会报错的, vue3 报错是因为经过浏览器了, 数据其实返回了, 但是别浏览器的同源策略屏蔽了。 本地调试, 后端使用** http://localhost:8081 作为接口地址, 报错 [vite] http proxy error ** 可

    2024年02月08日
    浏览(43)
  • vue cli 打包、生产环境http-proxy-middleware代理

    结构树 版本 1、创建vue.config.js 如果采用了本地cdn则index.html 2、创建ecosystem.config.js 3、创建himdcs.js 4、修改package.json 5、部署到docker 6、http-proxy-middleware参数说明 option.target:url字符串将与url模块解析 option.forward:url字符串将与url模块解析 option.target:传递给http(s)请求的对象(参

    2024年02月09日
    浏览(37)
  • NGINX编译ngx_http_proxy_connect_module及做正向代理

    1、下载NGINX(网址:http://nginx.org/download/ 当前文档使用版本为1.22.1)及ngx_http_proxy_connect_module模块 [root@localhost work]# tar xzf nginx-1.22.1.tar.gz [root@localhost work]# cd nginx-1.22.1 [root@localhost nginx-1.22.1]# git clone https://gitee.com/web_design_of_web_frontend/ngx_http_proxy_connect_module.git 2、下载该模块的补

    2024年02月13日
    浏览(36)
  • .NET Core(C#)使用Titanium.Web.Proxy实现Http(s)代理服务器监控HTTP请求

    关于Titanium.Web.Proxy详细信息可以去这里仔细看看,这里只记录简单用法 NuGet直接获取Titanium.Web.Proxy 配置 与其说是配置,不如就说这一部分就是未来你需要使用的部分,想知道具体每个部分是干什么的就去看原文链接 全放过来太占地方 最后的 Console.Read(); 是一个等待函数,你

    2024年02月09日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包