zoukankan      html  css  js  c++  java
  • Volley和OkHttp并用时导致http header数据被覆盖的bug

    问题出在BasicNetwork的performRequest()方法中,出问题的代码如下:

    public NetworkResponse performRequest(Request<?> request) throws VolleyError {
            long requestStart = SystemClock.elapsedRealtime();
            while (true) {
                HttpResponse httpResponse = null;
                byte[] responseContents = null;
                Map<String, String> responseHeaders = Collections.emptyMap();
                try {
                    // Gather headers.
                    Map<String, String> headers = new HashMap<String, String>();
                    addCacheHeaders(headers, request.getCacheEntry());
                    httpResponse = mHttpStack.performRequest(request, headers);
                    StatusLine statusLine = httpResponse.getStatusLine();
                    int statusCode = statusLine.getStatusCode();
    
                    responseHeaders = convertHeaders(httpResponse.getAllHeaders());
                    // Handle cache validation.
                    if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
    
                        Entry entry = request.getCacheEntry();
                        if (entry == null) {
                            return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null,
                                    responseHeaders, true,
                                    SystemClock.elapsedRealtime() - requestStart);
                        }
    
                        // A HTTP 304 response does not have all header fields. We
                        // have to use the header fields from the cache entry plus
                        // the new ones from the response.
                        // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5
                        entry.responseHeaders.putAll(responseHeaders);
                        return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data,
                                entry.responseHeaders, true,
                                SystemClock.elapsedRealtime() - requestStart);
                    }
    
                    // Some responses such as 204s do not have content.  We must check.
                    if (httpResponse.getEntity() != null) {
                      responseContents = entityToBytes(httpResponse.getEntity());
                    } else {
                      // Add 0 byte response as a way of honestly representing a
                      // no-content request.
                      responseContents = new byte[0];
                    }
    
                    // if the request is slow, log it.
                    long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
                    logSlowRequests(requestLifetime, request, responseContents, statusLine);
    
                    if (statusCode < 200 || statusCode > 299) {
                        throw new IOException();
                    }
                    return new NetworkResponse(statusCode, responseContents, responseHeaders, false,
                            SystemClock.elapsedRealtime() - requestStart);
                } catch (SocketTimeoutException e) {
                    attemptRetryOnException("socket", request, new TimeoutError());
                } catch (ConnectTimeoutException e) {
                    attemptRetryOnException("connection", request, new TimeoutError());
                } catch (MalformedURLException e) {
                    throw new RuntimeException("Bad URL " + request.getUrl(), e);
                } catch (IOException e) {
                    int statusCode = 0;
                    NetworkResponse networkResponse = null;
                    if (httpResponse != null) {
                        statusCode = httpResponse.getStatusLine().getStatusCode();
                    } else {
                        throw new NoConnectionError(e);
                    }
                    VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());
                    if (responseContents != null) {
                        networkResponse = new NetworkResponse(statusCode, responseContents,
                                responseHeaders, false, SystemClock.elapsedRealtime() - requestStart);
                        if (statusCode == HttpStatus.SC_UNAUTHORIZED ||
                                statusCode == HttpStatus.SC_FORBIDDEN) {
                            attemptRetryOnException("auth",
                                    request, new AuthFailureError(networkResponse));
                        } else {
                            // TODO: Only throw ServerError for 5xx status codes.
                            throw new ServerError(networkResponse);
                        }
                    } else {
                        throw new NetworkError(networkResponse);
                    }
                }
            }
        }
    convertHeaders()方法如下:
    protected static Map<String, String> convertHeaders(Header[] headers) {
            Map<String, String> result = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
            for (int i = 0; i < headers.length; i++) {
                result.put(headers[i].getName(), headers[i].getValue());
            }
            return result;
        }
    convertHeaders()方法中对HttpResponse的headers进行处理,转换成了TreeMap<String,String>,TreeMap中不能出现重复的键,那么如果有相同的键,数据就会被覆盖,如下图:

    解决办法:

    1、修改OkHttp库中对http header的解析;将相同的键放一起(未验证);

    2、修改convertHeaders()方法,对相同的键做判断处理,如下:

    protected static Map<String,String> convertHeadersFixed(Header[] headers){
            Map<String, String> result = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
            for (int i = 0; i < headers.length; i++) {
                String name = headers[i].getName() ;
                String value = headers[i].getValue() ;
                if(result.containsKey(name)){
                    String existValue = result.get(name);
                    existValue+=";"+value;
                    result.put(name, existValue);
                }else {
                    result.put(name, value);
                }
            }
            return result;
        }

    至于为什么是添加分号处理,参照于http header中的“Content-Type: text/html;charset=UTF-8”此种格式。

  • 相关阅读:
    Zephyr网络协议
    Zephyr启动过程与中断响应
    CortexM处理器的一些特性记录
    qemu 使用记录
    Request与Response详解
    http请求头(Header)参数详解
    win10java环境变量配置
    逻辑运算符:与、或、非、异或
    SQL注入相关知识整理
    网页是否存在SQL注入的简单判断
  • 原文地址:https://www.cnblogs.com/alexthecoder/p/4660027.html
Copyright © 2011-2022 走看看