记由HttpUrlConnection的不同实现的底层差异引起的访问错误

2 篇文章 0 订阅

编写的类SourceCode用于发送网络请求,主要用HttpUrlConnection实现。同一段代码,在电脑上能运行,移植到安卓应用却运行失败。在电脑上运行响应200正常,在手机应用运行时通过打印得知响应码为405,但没有任何错误。
开始时我以为是应用网络权限问题、安卓网络安全机制问题,甚至是url写错了,后来都排除了。
405错误一般是请求方法错误,服务端只接收post请求而我们发送get请求,或相反。我发送的是get请求,打开的网址是网站首页,这个页面是不可能用post请求才能打开的。分析代码,又没发现什么问题,发送的确实是get请求,con.setRequestMethod(“GET”)。真让人摸不着头脑。我之前也是做过安卓的联网应用的,没出现这种问题。
这时,网络上的一句话引起了我的注意,“setDoOutput(true)可能导致请求变为POST请求”。我对于打开的url连接先进行了统一设置,其中正包含“setDoOutput(true)”。在发送get请求的函数中设置setDoOutput(false),果然成功了。
通过打印,得知在手机上url.openConnection()得到的连接的实现类是com.android.okhttp.internal.huc.HttpURLConnectionImpl/HttpsURLConnectionImpl,而电脑上的实现类是原装的sun.net.www.protocol.http.HttpURLConnection/HttpsURLConnectionImpl

结论:对于HttpUrlConnection,

即使设置了setRequestMethod(“GET”),在setDoOutput(true)的情况下也可能会被认为是POST请求

,并且该情况与两个set方法的先后顺序无关。所以 在用HttpURLConnection发送GET请求时,一定要设置setDoOutput(false)



import com.report.auto.multi.AutoRetry;
import com.report.auto.multi.WatchedThread;

import javax.net.ssl.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class SourceCode {
	StringBuffer sourceCode = null;

	public SourceCode() {
	    init();
	}
	private void getHeaders(HttpURLConnection coon){
		Map<String, List<String>> headerFields = coon.getHeaderFields();
		Set<String> strings = headerFields.keySet();
	    for(String key:strings){
			System.out.println(key+"="+headerFields.get(key));
		}
	}
	public StringBuffer getSourceCodeInGet(String url,boolean canJump) {
		try {
			HttpURLConnection httpUrlCon;
			httpUrlCon = getConnection(url);
			httpUrlCon.setRequestMethod("GET");
			httpUrlCon.setDoOutput(false);
			handleHeader(httpUrlCon);
			//Headers(httpUrlCon);
			int responseCode=httpUrlCon.getResponseCode();
			switch (responseCode){
				case 302: case 301:
					if(!canJump)return null;
					System.out.println("跳转");
					//跳转
					String newUrl=httpUrlCon.getHeaderField("Location");
			        if(newUrl==null)newUrl=httpUrlCon.getHeaderField("location");
					newUrl=getFullUrl(url,newUrl);
					if(newUrl==null)return null;
					return getSourceCodeInGet(newUrl,canJump);
				case 404: case 403:return null;
				case 200:
					return getContent(httpUrlCon);
			}
		} catch (IOException e) {
			System.err.println(url);
			e.printStackTrace();
			//userAgent=(userAgent+1)%userAgents.length;
		    //出现其他异常则返回
		}
		return null;
	}
	public String getFullUrl(String sourceUrl,String path){
		if(path.startsWith("http://")||path.startsWith("https://"))return path;
		try {
		int protocolIndex = sourceUrl.indexOf("//")+2;
	    int siteIndex=sourceUrl.indexOf("/",protocolIndex);
		String site;
			if(siteIndex!=-1)site=sourceUrl.substring(0,siteIndex);
			else site=sourceUrl+"/";
		return site+path;
		}catch (Exception e){}
		return null;
	}
	public StringBuffer getSourceCodeInPost(String url,String data){
		StringBuffer sourceCode = new StringBuffer();
		HttpURLConnection httpUrlCon;
		httpUrlCon = getConnection(url);
		PrintWriter printWriter = null;
		//关键代码 application/x-www-form-urlencoded
		try {
			httpUrlCon.setRequestMethod("POST");
			httpUrlCon.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
			//输出表单数据
			printWriter = new PrintWriter(httpUrlCon.getOutputStream());
			printWriter.write(data);
			printWriter.flush();
		    printWriter.close();
		    //处理响应头
            handleHeader(httpUrlCon);
			int responseCode=httpUrlCon.getResponseCode();
			switch (responseCode){
				case 302: case 301:
					//跳转
					String newUrl=httpUrlCon.getHeaderField("location");
					if(newUrl==null)newUrl=httpUrlCon.getHeaderField("Location");
					getSourceCodeInGet(newUrl,true);
					break;
				case 404: case 403:return null;
				case 200: return getContent(httpUrlCon);
			}
		    //获取响应内容
			//return getContent(httpUrlCon);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}
	Charset charset=StandardCharsets.UTF_8;
    public StringBuffer getContent(HttpURLConnection connection) throws IOException {
		BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), charset));
		String line;
		StringBuffer sourceCode=new StringBuffer();
		while ((line = reader.readLine()) != null) {
			sourceCode.append(line).append("\n");
		}
		reader.close();
		//if(tryTimes!=0) System.out.println("重试"+(tryTimes+1)+"次后找到文件");
		this.sourceCode=sourceCode;
		return sourceCode;
	}
	private void handleHeader(HttpURLConnection connection){
		addCookie(connection.getHeaderField("Set-Cookie"));

	}
	 int userAgent=2;
	static String[] userAgents={"Chrome/71.0.3578.98 Safari/537.36",
			"Mozilla/4.0 compatible; MSIE 5.0;Windows NT;",
			"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36"};
	public HttpURLConnection getConnection(String urlStr) {
		URL url;
		HttpURLConnection conn = null;
		try {
			url = new URL(urlStr);
			conn = (HttpURLConnection) url.openConnection();
			conn.setRequestProperty("user-agent",userAgents[userAgent]);
			conn.setRequestProperty("accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8");
			conn.setRequestProperty("accept-language", "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3");
			//conn.setRequestProperty("accept-encoding", "gzip, deflate, br");
			conn.setRequestProperty("cache-control","max-age=0");
			conn.setRequestProperty("connection", "keep-alive");
			conn.setRequestProperty("cookie",getCookieString());
			conn.setRequestProperty("upgrade-insecure-requests", "1");
			conn.setRequestMethod("GET");
		//	conn.setSSLSocketFactory(getSSLFactory());
			conn.setDoOutput(true);
			conn.setDoInput(true);
			conn.setInstanceFollowRedirects(false);
			conn.setReadTimeout(60*1000);
			conn.setConnectTimeout(60*1000);
			if(conn instanceof HttpsURLConnection){
				((HttpsURLConnection)conn).setSSLSocketFactory(getSSLFactory());
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		setRequestHeader(conn,initHeader);
		System.out.println(conn.getClass());
		return conn;
	}
	String initHeader=null;
	public void setRequestHeader(HttpURLConnection connection,String text){
		if(text==null)return;
		String[] lines = text.split("\n");
	    for(String line:lines){
			if(line.startsWith(":"))line=line.substring(1);
			String[] split = line.split(":");
		    if(split.length!=2)continue;
			connection.setRequestProperty(split[0].trim(),split[1].trim());
		}
	}

	public void setInitHeader(String initHeader) {
		this.initHeader = initHeader;
	}

	Map<String,String>cookieMap=new HashMap<>();
	public void addCookie(String set_cookie){
		if(set_cookie==null)return;
		 String[] cookies=set_cookie.split(";");
	     for(String c:cookies){
	     	if(c.contains("=")) {
				String[] split = c.split("=");
				if(!split[0].trim().equals("path"))
			    cookieMap.put(split[0].trim(),split[1].trim());
	     	}
		 }
	}
	public SSLSocketFactory getSSLFactory(){
		try {
		SSLContext sslContext=SSLContext.getInstance("SSL");
		TrustManager[] tm={new X509TrustManager(){

			@Override
			public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {

			}

			@Override
			public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {

			}

			@Override
			public X509Certificate[] getAcceptedIssuers() {
				return new X509Certificate[0];
			}
		}};
		//初始化
		sslContext.init(null, tm, new java.security.SecureRandom());
		//获取SSLSocketFactory对象
		SSLSocketFactory ssf=sslContext.getSocketFactory();
		return ssf;
		}catch ( NoSuchAlgorithmException | KeyManagementException e){

		}
		return null;
	}
	public String getCookieString(){
		StringBuilder cookie= new StringBuilder();
		for(String key: cookieMap.keySet()){
			cookie.append(key).append("=").append(cookieMap.get(key)).append(";");
		}
		//System.out.println(cookie.toString());
		return cookie.toString();
	}
	private void init() {

	}
	public void clearCookies(){
		cookieMap.clear();
	}
    public String getHtmlWithAutoRetry(String url,int time){
		AutoRetry<String> autoRetry=new AutoRetry<String>() {
			@Override
			public WatchedThread<String> getNewThread() {
				return new WatchedThread<String>() {
					@Override
					public void callBack(String result) {
					}
					@Override
					public String runTask() {
						StringBuffer sourceCodeInGet = getSourceCodeInGet(url,true);
						if(sourceCodeInGet==null)return null;
						return sourceCodeInGet.toString();
					}
				};
			}
		};
		autoRetry.setRetryTimes(time);
		return autoRetry.runForUsableResult();
	}
	int retryTime=3;
	public String getHtmlWithAutoRetry(String url){
		return getHtmlWithAutoRetry(url,retryTime);
	}

	public void setRetryTime(int retryTime) {
		this.retryTime = retryTime;
	}

	public static void main(String[] args) {
		System.out.println(new SourceCode().getSourceCodeInGet("http://www.baidu.com", true));
	}
package com.report.auto.multi;


public abstract class AutoRetry<T>{
    int retryTimes=3;//尝试的最大次数
    boolean retryOnNullResult=true;
    int retrySpace=5000;//重试的时间间隔
    protected WatchedThread<T> thread;
    public T runForUsableResult(){
        boolean shouldRetry=false;
        int time=0;
        do {
            thread=getNewThread();
            if(thread==null)return null;
            thread.start();
            boolean b=thread.waitUntilOK();//是否不超时
            time++;
            shouldRetry=(!b||(retryOnNullResult&&thread.getResult()==null))&&time<retryTimes;
            if(shouldRetry)sleep(retrySpace);
        }while (shouldRetry);
        return thread.getResult();
    }
    /**产生可运行的线程*/
    public abstract WatchedThread<T> getNewThread();
    private void sleep(int time){
        try {
            Thread.sleep(time);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public int getRetryTimes() {
        return retryTimes;
    }

    public void setRetryTimes(int retryTimes) {
        this.retryTimes = retryTimes;
    }

    public boolean isRetryOnNullResult() {
        return retryOnNullResult;
    }

    public void setRetryOnNullResult(boolean retryOnNullResult) {
        this.retryOnNullResult = retryOnNullResult;
    }

    public int getRetrySpace() {
        return retrySpace;
    }

    public void setRetrySpace(int retrySpace) {
        this.retrySpace = retrySpace;
    }
}

package com.report.auto.multi;

public abstract class WatchedThread<T> extends Thread{
    volatile boolean isOK=false;
    int timeOut=60000;
    long startTime;
    volatile boolean isGivenUp=false;
    T result;
    public WatchedThread(){
    }
    public boolean isOK() {
        return isOK;
    }
    @Override
    public synchronized void start() {
        super.start();
        startTime=System.currentTimeMillis();
    }
    public boolean isTimeOut(){
        return System.currentTimeMillis()-startTime>=timeOut;
    }
    public abstract T runTask();
    public abstract void callBack(T result);
    @Override
    public  void run(){
        T t = runTask();
        result=t;
        if(!isGivenUp) {
            callBack(t);
        }
        isOK=true;
    }
    private void  sleep(int time){
        try {
            Thread.sleep(time);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    /**由其他类调用,造成其所在运行线程阻塞
     * @return 不超时返回true,超时返回false*/
    public boolean waitUntilOK(){
        while(!isOK){
            sleep(10);
            if(isTimeOut()){
                isGivenUp=true;
                this.interrupt();
                return false;
            }
        }
        return true;
    }
    public T getResult() {
        return result;
    }
}

.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值