HTTP-сервер Java отправляет фрагментированный ответ

Я работаю над приложением Java, которое имеет встроенный HTTP-сервер, в данный момент сервер реализован с использованием ServerSocketChannel, он прослушивает запросы на порту 1694:

        msvrCh = ServerSocketChannel.open();
        msvrCh.socket().bind(new InetSocketAddress(mintPort));
        msvrCh.configureBlocking(false);

Поток установлен для управления запросами и ответами:

        Thread thrd = new Thread(msgReceiver);
        thrd.setUncaughtExceptionHandler(exceptionHandler);
        thrd.start();

Тема довольно проста:

        Runnable msgReceiver = new Runnable() {
            @Override
            public void run() {
                try{
                    while( !Thread.interrupted() ) {
    //Sleep a short period between checks for new requests                          
                        try{
                            Thread.sleep(DELAY_BETWEEN_ACCEPTS);
                        } catch(Exception ex) {
                            ex.printStackTrace();
                        }                           
                        SocketChannel cliCh = msvrCh.accept();

                        if ( blnExit() == true ) {
                            break;
                        }                           
                        if ( cliCh == null ) {
                            continue;
                        }
                        processRequest(cliCh.socket());
                    }                       
                } catch (IOException ex) {
                    ex.printStackTrace();
                } finally {                     
                    logMsg(TERMINATING_THREAD + 
                            "for accepting cluster connections", true);

                    if ( msvrCh != null ) {
                        try {
                            msvrCh.close();
                        } catch (IOException ex) {
                            ex.printStackTrace();
                        }
                        msvrCh = null;
                    }
                }               
            }
        };

Основная часть кода для работы с ответом находится в функции processRequest:

private void processRequest(Socket sck) {
    try {
    //AJAX Parameters
        final String AJAX_ID            = "ajmid";
    //The 'Handler Key' used to decode response         
        final String HANDLER_KEY        = "hkey";
    //Message payload           
        final String PAYLOAD            = "payload";
    //Post input buffer size            
        final int REQUEST_BUFFER_SIZE   = 4096;
    //Double carriage return marks the end of the headers           
        final String CRLF               = "\r\n";

        BufferedReader in = new BufferedReader(new InputStreamReader(sck.getInputStream()));
        String strAMID = null, strHKey = null, strRequest;
        char[] chrBuffer = new char[REQUEST_BUFFER_SIZE];
        StringBuffer sbRequest = new StringBuffer();
        eMsgTypes eType = eMsgTypes.UNKNOWN;
        clsHTTPparameters objParams = null;
        int intPos, intCount;               
    //Extract the entire request, including headers         
        if ( (intCount = in.read(chrBuffer)) == 0 ) {
            throw new Exception("Cannot read request!");
        }
        sbRequest.append(chrBuffer, 0, intCount);           
        strRequest = sbRequest.toString();
    //What method is being used by this request?
        if ( strRequest.startsWith(HTTP_GET) ) {
    //The request should end with a HTTP marker, remove this before trying to interpret the data
            if ( strRequest.indexOf(HTTP_MARKER) != -1 ) {
                strRequest = strRequest.substring(0, strRequest.indexOf(HTTP_MARKER)).trim();
            }            
    //Look for a data marker
            if ( (intPos = strRequest.indexOf(HTTP_DATA_START)) >= 0 ) {
    //Data is present in the query, skip to the start of the data
                strRequest = strRequest.substring(intPos + 1);
            } else {
    //Remove the method indicator
                strRequest = strRequest.substring(HTTP_GET.length());                   
            }
        } else if ( strRequest.startsWith(HTTP_POST) ) {
    //Discard the headers and jump to the data
            if ( (intPos = strRequest.lastIndexOf(CRLF)) >= 0 ) {
                strRequest = strRequest.substring(intPos + CRLF.length());  
            }
        }
        if ( strRequest.length() > 1 ) {
    //Extract the parameters                    
            objParams = new clsHTTPparameters(strRequest);
        }            
        if ( strRequest.startsWith("/") == true ) {
    //Look for the document reference
            strRequest = strRequest.substring(1);               
            eType = eMsgTypes.SEND_DOC;             
        }
        if ( objParams != null ) {
    //Transfer the payload to the request
            String strPayload = objParams.getValue(PAYLOAD);

            if ( strPayload != null ) {
                byte[] arybytPayload = Base64.decodeBase64(strPayload.getBytes()); 
                strRequest = new String(arybytPayload);
                strAMID = objParams.getValue(AJAX_ID);
                strHKey = objParams.getValue(HANDLER_KEY);
            }
        } 
        if ( eType == eMsgTypes.UNKNOWN 
          && strRequest.startsWith("{") && strRequest.endsWith("}") ) {
    //The payload is JSON, is there a type parameter?
            String strType = strGetJSONItem(strRequest, JSON_LBL_TYPE);

            if ( strType != null && strType.length() > 0 ) {
    //Decode the type                   
                eType = eMsgTypes.valueOf(strType.toUpperCase().trim());
    //What system is the message from?
                String strIP = strGetJSONItem(strRequest, JSON_LBL_IP)
                      ,strMAC = strGetJSONItem(strRequest, JSON_LBL_MAC);                   
                if ( strIP != null && strIP.length() > 0
                 && strMAC != null && strMAC.length() > 0 ) {
    //Is this system known in the cluster?
                    clsIPmon objSystem = objAddSysToCluster(strIP, strMAC);

                    if ( objSystem != null ) {
    //Update the date/time stamp of the remote system                           
                        objSystem.touch();                          
                    }
    //This is an internal cluster message, no response required
                    return;
                }                   
            }
        }            
        String strContentType = null, strRespPayload = null;
        OutputStream out = sck.getOutputStream();
        byte[] arybytResponse = null;
        boolean blnShutdown = false;
        out.write("HTTP/1.0 200\n".getBytes());

        switch( eType ) {
        case SEND_DOC:
            if ( strRequest.length() <= 1 ) {
                strRequest = HTML_ROOT + DEFAULT_DOC;
            } else {
                strRequest = HTML_ROOT + strRequest;
            }
            logMsg("HTTP Request for: " + strRequest, true);

            if ( strRequest.toLowerCase().endsWith(".css") == true ) {
                strContentType = MIME_CSS;
            } else if ( strRequest.toLowerCase().endsWith(".gif") == true ) {
                strContentType = MIME_GIF;
            } else if ( strRequest.toLowerCase().endsWith(".jpg") == true ) {
                strContentType = MIME_JPG;
            } else if ( strRequest.toLowerCase().endsWith(".js") == true ) {
                strContentType = MIME_JS;
            } else if ( strRequest.toLowerCase().endsWith(".png") == true ) {
                strContentType = MIME_PNG;
            } else if ( strRequest.toLowerCase().endsWith(".html") == true 
                     || strRequest.toLowerCase().endsWith(".htm") == true ) {
                strContentType = MIME_HTML;
            }
            File objFile = new File(strRequest);

            if ( objFile.exists() == true ) {
                FileInputStream objFIS = new FileInputStream(objFile);

                if ( objFIS != null ) {
                    arybytResponse = new byte[(int)objFile.length()];

                    if ( objFIS.read(arybytResponse) == 0 ) {
                        arybytResponse = null;
                    }
                    objFIS.close();
                }
            }
            break;
        case CHANNEL_STS:
            strRespPayload = strChannelStatus(strRequest);
            strContentType = MIME_JSON;
            break;
        case CLUSTER_STS:
            strRespPayload = strClusterStatus();
            strContentType = MIME_JSON; 
            break;
        case MODULE_STS:
            strRespPayload = strModuleStatus(strRequest);
            strContentType = MIME_JSON;
            break;
        case NETWORK_INF:
            strRespPayload = strNetworkInfo(strRequest);
            strContentType = MIME_JSON;
            break;
        case NODE_STS:
            strRespPayload = strNodeStatus(strRequest);
            strContentType = MIME_JSON;
            break;
        case POLL_STS:
            strRespPayload = strPollStatus(strRequest);
            strContentType = MIME_JSON;
            break;
        case SYS_STS:
    //Issue system status               
            strRespPayload = strAppStatus();
            strContentType = MIME_JSON;
            break;          
        case SHUTDOWN:
    //Issue instruction to restart system
            strRespPayload = "Shutdown in progress!";
            strContentType = MIME_PLAIN;
    //Flag that shutdown has been requested             
            blnShutdown = true;
            break;
        default:
        }
        if ( strRespPayload != null ) {
    //Convert response string to byte array             
            arybytResponse = strRespPayload.getBytes();
    System.out.println("[ " + strRespPayload.length() + " ]: " + strRespPayload);           //HACK          
        }           
        if ( arybytResponse != null && arybytResponse.length > 0 ) {
            if ( strContentType == MIME_JSON ) {
                String strResponse = "{";

                if ( strAMID != null ) {
    //Include the request AJAX Message ID in the response
                    if ( strResponse.length() > 1 ) {
                        strResponse += ",";
                    }   
                    strResponse += "\"" + AJAX_ID + "\":" + strAMID;
                }
                if ( strHKey != null ) {
                    if ( strResponse.length() > 1 ) {
                        strResponse += ",";
                    }
                    strResponse += "\"" + HANDLER_KEY + "\":\"" + strHKey + "\"";
                }
                if ( strResponse.length() > 1 ) {
                    strResponse += ",";
                }
                strResponse += "\"payload\":" + new String(arybytResponse) 
                             + "}";                 
                arybytResponse = strResponse.getBytes();
            }
            String strHeaders = "";

            if ( strContentType != null ) {
                strHeaders += "Content-type: " + strContentType + "\n";                 
            }
            strHeaders += "Content-length: " + arybytResponse.length + "\n" 
                        + "Access-Control-Allow-Origin: *\n"
                        + "Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE, PUT\n"
                        + "Access-Control-Allow-Credentials: true\n"
                        + "Keep-Alive: timeout=2, max=100\n"
                        + "Cache-Control: no-cache\n" 
                        + "Pragma: no-cache\n\n";
            out.write(strHeaders.getBytes());
            out.write(arybytResponse);
            out.flush();                
        }
        out.close();
        sck.close();

        if ( blnShutdown == true ) {
            String strSystem =  mobjLocalIP.strGetIP();

            if ( strSystem.compareTo(mobjLocalIP.strGetIP()) != 0 ) {
    //Specified system is not the local system, issue message to remote system.
                broadcastMessage("{\"" + JSON_LBL_TYPE  + "\":\"" + 
                                                   eMsgTypes.SHUTDOWN + "\""
                               + ",\"" + JSON_LBL_TIME  + "\":\"" + 
                                           clsTimeMan.lngTimeNow() + "\"}");                            
            } else {
    //Shutdown addressed to local system                    
                if ( getOS().indexOf("linux") >= 0 ) {
    //TO DO!!!                  
                } else if ( getOS().indexOf("win") >= 0 ) {
                    Runtime runtime = Runtime.getRuntime();
                    runtime.exec("shutdown /r /c \"Shutdown request\" /t 0 /f");
                    System.exit(EXITCODE_REQUESTED_SHUTDOWN);
                }               
            }
        }
    } catch (Exception ex) {            
    } finally {
        if (sck != null) {
            try {
                sck.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }
}

Я хотел бы реализовать чанкованный ответ, в настоящее время чанкованные ответы не поддерживаются приведенным выше кодом.

[Edit] Я попытался реализовать фрагментарный ответ, добавив метод:

    /**
     * @param strData - The data to split into chunks
     * @return A string array containing the chunks
     */
 public static String[] arystrChunkData(String strData) {
    int intChunks = (strData.length() / CHUNK_THRESHOLD_BYTESIZE) + 1;
    String[] arystrChunks = new String[intChunks];
    int intLength = strData.length(), intPos = 0;

    for( int c=0; c<arystrChunks.length; c++ ) {            
        if ( intPos < intLength ) {
    //Extract a chunk from the data         
            int intEnd = Math.min(intLength - 1, intPos + CHUNK_THRESHOLD_BYTESIZE);
            arystrChunks[c] = strData.substring(intPos, intEnd);
        }
    //Advance data position to next chunk           
        intPos += CHUNK_THRESHOLD_BYTESIZE;
    }       
    return arystrChunks;
}

Модифицированный processRequest теперь выглядит так:

        private void processRequest(Socket sck) {
    try {
        //AJAX Parameters
        final String AJAX_ID            = "ajmid";
        //The 'Handler Key' used to decode response         
        final String HANDLER_KEY        = "hkey";
        //Message payload           
        final String PAYLOAD            = "payload";
        //Post input buffer size            
        final int REQUEST_BUFFER_SIZE   = 4096;
        //Double carriage return marks the end of the headers           
        final String CRLF               = "\r\n";

        BufferedReader in = new BufferedReader(new InputStreamReader(sck.getInputStream()));
        String strAMID = null, strHKey = null, strRequest;
        char[] chrBuffer = new char[REQUEST_BUFFER_SIZE];
        StringBuffer sbRequest = new StringBuffer();
        eMsgTypes eType = eMsgTypes.UNKNOWN;
        clsHTTPparameters objParams = null;
        int intPos, intCount;               
        //Extract the entire request, including headers         
        if ( (intCount = in.read(chrBuffer)) == 0 ) {
            throw new Exception("Cannot read request!");
        }
        sbRequest.append(chrBuffer, 0, intCount);           
        strRequest = sbRequest.toString();
        //What method is being used by this request?
        if ( strRequest.startsWith(HTTP_GET) ) {
        //The request should end with a HTTP marker, remove this before trying to interpret the data
            if ( strRequest.indexOf(HTTP_MARKER) != -1 ) {
                strRequest = strRequest.substring(0, strRequest.indexOf(HTTP_MARKER)).trim();
            }            
        //Look for a data marker
            if ( (intPos = strRequest.indexOf(HTTP_DATA_START)) >= 0 ) {
        //Data is present in the query, skip to the start of the data
                strRequest = strRequest.substring(intPos + 1);
            } else {
        //Remove the method indicator
                strRequest = strRequest.substring(HTTP_GET.length());                   
            }
        } else if ( strRequest.startsWith(HTTP_POST) ) {
        //Discard the headers and jump to the data
            if ( (intPos = strRequest.lastIndexOf(CRLF)) >= 0 ) {
                strRequest = strRequest.substring(intPos + CRLF.length());  
            }
        }
        if ( strRequest.length() > 1 ) {
        //Extract the parameters                    
            objParams = new clsHTTPparameters(strRequest);
        }            
        if ( strRequest.startsWith("/") == true ) {
        //Look for the document reference
            strRequest = strRequest.substring(1);               
            eType = eMsgTypes.SEND_DOC;             
        }
        if ( objParams != null ) {
        //Transfer the payload to the request
            String strPayload = objParams.getValue(PAYLOAD);

            if ( strPayload != null ) {
                byte[] arybytPayload = Base64.decodeBase64(strPayload.getBytes()); 
                strRequest = new String(arybytPayload);
                strAMID = objParams.getValue(AJAX_ID);
                strHKey = objParams.getValue(HANDLER_KEY);
            }
        } 
        if ( eType == eMsgTypes.UNKNOWN 
          && strRequest.startsWith("{") && strRequest.endsWith("}") ) {
        //The payload is JSON, is there a type parameter?
            String strType = strGetJSONItem(strRequest, JSON_LBL_TYPE);

              if ( strType != null && strType.length() > 0 ) {
        //Decode the type                   
                eType = eMsgTypes.valueOf(strType.toUpperCase().trim());
        //What system is the message from?
                String strIP = strGetJSONItem(strRequest, JSON_LBL_IP)
                      ,strMAC = strGetJSONItem(strRequest, JSON_LBL_MAC);                   
                if ( strIP != null && strIP.length() > 0
                 && strMAC != null && strMAC.length() > 0 ) {
        //Is this system known in the cluster?
                    clsIPmon objSystem = objAddSysToCluster(strIP, strMAC);

                    if ( objSystem != null ) {
        //Update the date/time stamp of the remote system                           
                        objSystem.touch();                          
                    }
        //This is an internal cluster message, no response required
                    return;
                }                   
            }
        }            
        String strContentType = null, strRespPayload = null;            
        OutputStream out = sck.getOutputStream();
        byte[] arybytResponse = null;
        boolean blnShutdown = false;
        //Start the writing the headers
        String strHeaders = "HTTP/1.0 200\n"
                          + "Date: " + (new Date()).toString() + "\n"
                          + "Access-Control-Allow-Origin: *\n"
                          + "Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE, PUT\n"
                          + "Access-Control-Allow-Credentials: true\n"
                          + "Keep-Alive: timeout=2, max=100\n"
                          + "Cache-Control: no-cache\n" 
                          + "Pragma: no-cache\n";            
        out.write(strHeaders.getBytes());
        strHeaders = "";

        switch( eType ) {
        case SEND_DOC:
            if ( strRequest.length() <= 1 ) {
                strRequest = HTML_ROOT + DEFAULT_DOC;
            } else {
                strRequest = HTML_ROOT + strRequest;
            }
            logMsg("HTTP Request for: " + strRequest, true);

            if ( strRequest.toLowerCase().endsWith(".css") == true ) {
                strContentType = MIME_CSS;
            } else if ( strRequest.toLowerCase().endsWith(".gif") == true ) {
                strContentType = MIME_GIF;
            } else if ( strRequest.toLowerCase().endsWith(".jpg") == true ) {
                strContentType = MIME_JPG;
            } else if ( strRequest.toLowerCase().endsWith(".js") == true ) {
                strContentType = MIME_JS;
            } else if ( strRequest.toLowerCase().endsWith(".png") == true ) {
                strContentType = MIME_PNG;
            } else if ( strRequest.toLowerCase().endsWith(".html") == true 
                     || strRequest.toLowerCase().endsWith(".htm") == true ) {
                strContentType = MIME_HTML;
            }
            File objFile = new File(strRequest);

            if ( objFile.exists() == true ) {
                FileInputStream objFIS = new FileInputStream(objFile);

                if ( objFIS != null ) {
                    arybytResponse = new byte[(int)objFile.length()];

                    if ( objFIS.read(arybytResponse) == 0 ) {
                        arybytResponse = null;
                    }
                    objFIS.close();
                }
            }
            break;
        case CHANNEL_STS:
            strRespPayload = strChannelStatus(strRequest);
            strContentType = MIME_JSON;
            break;
        case CLUSTER_STS:
            strRespPayload = strClusterStatus();
            strContentType = MIME_JSON; 
            break;
        case MODULE_STS:
            strRespPayload = strModuleStatus(strRequest);
            strContentType = MIME_JSON;
            break;
        case NETWORK_INF:
            strRespPayload = strNetworkInfo(strRequest);
            strContentType = MIME_JSON;
            break;
        case NODE_STS:
            strRespPayload = strNodeStatus(strRequest);
            strContentType = MIME_JSON;
            break;
        case POLL_STS:
            strRespPayload = strPollStatus(strRequest);
            strContentType = MIME_JSON;
            break;
        case SYS_STS:
        //Issue system status               
            strRespPayload = strAppStatus();
            strContentType = MIME_JSON;
            break;          
        case SHUTDOWN:
        //Issue instruction to restart system
            strRespPayload = "Shutdown in progress!";
            strContentType = MIME_PLAIN;
        //Flag that shutdown has been requested             
            blnShutdown = true;
            break;
        default:
        }
        if ( strRespPayload != null ) {
        //Convert response string to byte array             
            arybytResponse = strRespPayload.getBytes();
        }           
        if ( arybytResponse != null && arybytResponse.length > 0 ) {
            boolean blnChunked = false;

            if ( strContentType != null ) {
                strHeaders += "Content-type: " + strContentType + "\n";                 
            }               
            if ( strContentType == MIME_JSON ) {
                String strResponse = "{";

                if ( strAMID != null ) {
        //Include the request AJAX Message ID in the response
                    if ( strResponse.length() > 1 ) {
                        strResponse += ",";
                    }   
                    strResponse += "\"" + AJAX_ID + "\":" + strAMID;
                }
                if ( strHKey != null ) {
                    if ( strResponse.length() > 1 ) {
                        strResponse += ",";
                    }
                    strResponse += "\"" + HANDLER_KEY + "\":\"" + strHKey + "\"";
                }
                if ( strResponse.length() > 1 ) {
                    strResponse += ",";
                }
                strResponse += "\"payload\":" + new String(arybytResponse) 
                             + "}";
        //How big is the response?
    if ( strResponse.length() > CHUNK_THRESHOLD_BYTESIZE ) {
                    blnChunked = true;
                    strHeaders += "Transfer-Encoding: chunked\n\n";
                    out.write(strHeaders.getBytes());
        //Slice up the string into chunks
                            String[] arystrChunks = arystrChunkData(strResponse);

                    for( int c=0; c<arystrChunks.length; c++ ) {
                        String strChunk = arystrChunks[c];

                        if ( strChunk != null ) {
                            String strLength = Integer.toHexString(strChunk.length()) + "\r\n";
                            strChunk += "\r\n";
                            out.write(strLength.getBytes());
                            out.write(strChunk.getBytes());
                        }                           
                    }
        //Last chunk is always 0 bytes                      
                    out.write("0\r\n\r\n".getBytes());
                } else {
                    arybytResponse = strResponse.getBytes();
                }
            }
            if ( blnChunked == false ) {    
                strHeaders += "Content-length: " + arybytResponse.length + "\n\n";                          
                out.write(strHeaders.getBytes());
                out.write(arybytResponse);
            }
            out.flush();                
        }
        out.close();
        sck.close();

        if ( blnShutdown == true ) {
            String strSystem =  mobjLocalIP.strGetIP();

            if ( strSystem.compareTo(mobjLocalIP.strGetIP()) != 0 ) {
        //Specified system is not the local system, issue message to remote system.
                broadcastMessage("{\"" + JSON_LBL_TYPE  + "\":\"" + 
                                                   eMsgTypes.SHUTDOWN + "\""
                               + ",\"" + JSON_LBL_TIME  + "\":\"" + 
                                           clsTimeMan.lngTimeNow() + "\"}");                            
            } else {
    //Shutdown addressed to local system                    
                if ( getOS().indexOf("linux") >= 0 ) {
        //TO DO!!!                  
                } else if ( getOS().indexOf("win") >= 0 ) {
                    Runtime runtime = Runtime.getRuntime();
                    runtime.exec("shutdown /r /c \"Shutdown request\" /t 0 /f");
                    System.exit(EXITCODE_REQUESTED_SHUTDOWN);
                }               
            }
        }
    } catch (Exception ex) {            
    } finally {
        if (sck != null) {
            try {
                sck.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }
}

Я прочитал несколько спецификаций для ответов Chunked и, насколько я могу судить, я отправляю данные в правильном формате, однако я ничего не получаю в браузере.

Возможно, я ошибочно предположил, что браузер правильно собрал бы куски в один, но я могу ошибаться. Обработчик на стороне клиента выглядит так:

      this.responseHandler = function() {
try {    
  if ( mobjHTTP == null
  || !(mobjHTTP.readyState == 4 && mobjHTTP.status == 200)
  || !(mstrResponseText = mobjHTTP.responseText)
  || mstrResponseText.length == 0 ) {
    //Not ready or no response to decode      
    return;
  }
    //Do something with the response
    } catch( ex ) {
  T.error("responseHandler:", ex);
}

};

Этот обработчик устанавливается в другом месте объекта:

    mobjHTTP.onreadystatechange = this.responseHandler;

3 ответа

Решение

Решил, не уверен почему, но удалил заголовок:

  Transfer-Encoding: chunked

А также длины чанков в начале каждого чанка решили проблему, я все еще записываю данные в 768 байт. Это работает надежно и очень хорошо.

Не уверен, почему я должен был это сделать.

Последний метод для получения кусков из строки данных:

    public static String[] arystrChunkData(String strData) {
            int intChunks = (strData.length() / CHUNK_THRESHOLD_BYTESIZE) + 1;
            String[] arystrChunks = new String[intChunks];
            int intLength = strData.length(), intPos = 0;

            for( int c=0; c<arystrChunks.length; c++ ) {            
                if ( intPos < intLength ) {
    //Extract a chunk from the data         
                    int intEnd = Math.min(intLength, intPos + CHUNK_THRESHOLD_BYTESIZE);
                    arystrChunks[c] = strData.substring(intPos, intEnd);
                    intPos = intEnd;
                }
            }       
            return arystrChunks;
        }

Цикл для записи чанков, без длин в начале и без 0 байтов в конце чанков:

    String[] arystrChunks = arystrChunkData(strResponse);
    for( String strChunk : arystrChunks ) {
            if ( strChunk != null ) {
                    out.write(strChunk.getBytes());
            }                           
    }

Как я уже отмечал, нет официального ограничения на размер ответа HTTP. TCP делает эту работу за вас. Однако вы всегда можете настроить свой веб-сервер для реализации такой политики, установив Content-Length:: 32-bit Integer max size или 64-bit для современных браузеров (см. Здесь).

Технически, вы можете иметь неограниченное количество ответов, используя Chunked Transfer, как вы указали в своем сообщении. Теоретически это используется для обхода максимальной длины содержимого.

Чаще всего, и если есть такое требование для огромного файла JSON (размером не менее нескольких МБ), вы можете использовать какую-то логику разбиения на страницы через последовательные запросы AJAX. В вашем случае вы могли бы разделить ваши большие данные JSON на куски программным способом и отправить каждую из них с помощью другого запроса AJAX. Затем позвольте Javascript выполнить задачу объединения.

Как правило, JSON-ответ размером в несколько МБ успешно загружается в любом браузере. Я предлагаю вам взглянуть на эту статью; ему 3 года, но я думаю, что сейчас все еще лучше.

Короче говоря, в приведенном выше тесте указано, что JSON размером менее 35 МБ, вероятно, будет успешно загружен в любом современном настольном браузере. Это, однако, может не иметь место для мобильных браузеров. Например, есть некоторые отчеты об ограничениях мобильного сафари для файлов Json размером более 10 МБ.

Если вы передаете пользователя Transfer-Encoding=chunked, то каждому фрагменту данных должен предшествовать размер фрагмента.

См. здесь хорошее объяснение:https://en.wikipedia.org/wiki/Chunked_transfer_encoding

Другие вопросы по тегам