ADDITIONAL SYSTEM INFORMATION :
Java 17 Windows 10
A DESCRIPTION OF THE PROBLEM :
HttpClient will not send more than 64kb of data in an POST/PUT request from the 2nd request onwards regardless of how many window updates frames we send from the server side.
It hangs indefinitely after sending 64kb of data without sending any data frames afterwards
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Create an HttpClient which uses http2
1) Send an initial request of any type[GET/PUT/POST etc] all these requests work on the 1st try so that the server can send back a 101 upgrade response to fully transform the connection to http2
2) Upload a byte array or anything greater than 64 kb[65535 bytes]
3) Observe output on server . Server never receives a Data frame with THE END_STREAM flag hence it never processes the full request and hence cannot send a response which causes the client to hang forever
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Client should send Data frames till all 100 kb of data is uploaded with the final frame having the END_STREAM flag set
ACTUAL -
Client only sends data frames up to the 64kb mark after which it pauses forever
HEADER TABLE SIZE=16384
ENABLE PUSH=1
MAX CONCURRENT STREAMS=100
INITIAL WINDOW SIZE=16777216
MAX FRAME SIZE=16384
======================
Data Frame,Stream=3,Size=1000,Total Bytes Received=1000
Data Frame,Stream=3,Size=1000,Total Bytes Received=2000
Data Frame,Stream=3,Size=1000,Total Bytes Received=3000
Data Frame,Stream=3,Size=1000,Total Bytes Received=4000
Data Frame,Stream=3,Size=1000,Total Bytes Received=5000
Data Frame,Stream=3,Size=1000,Total Bytes Received=6000
Data Frame,Stream=3,Size=1000,Total Bytes Received=7000
Data Frame,Stream=3,Size=1000,Total Bytes Received=8000
Data Frame,Stream=3,Size=1000,Total Bytes Received=9000
Data Frame,Stream=3,Size=1000,Total Bytes Received=10000
Data Frame,Stream=3,Size=1000,Total Bytes Received=11000
Data Frame,Stream=3,Size=1000,Total Bytes Received=12000
Data Frame,Stream=3,Size=1000,Total Bytes Received=13000
Data Frame,Stream=3,Size=1000,Total Bytes Received=14000
Data Frame,Stream=3,Size=1000,Total Bytes Received=15000
Data Frame,Stream=3,Size=1000,Total Bytes Received=16000
Data Frame,Stream=3,Size=384,Total Bytes Received=16384
Data Frame,Stream=3,Size=616,Total Bytes Received=17000
Data Frame,Stream=3,Size=1000,Total Bytes Received=18000
Data Frame,Stream=3,Size=1000,Total Bytes Received=19000
Data Frame,Stream=3,Size=1000,Total Bytes Received=20000
Data Frame,Stream=3,Size=1000,Total Bytes Received=21000
Data Frame,Stream=3,Size=1000,Total Bytes Received=22000
Data Frame,Stream=3,Size=1000,Total Bytes Received=23000
Data Frame,Stream=3,Size=1000,Total Bytes Received=24000
Data Frame,Stream=3,Size=1000,Total Bytes Received=25000
Data Frame,Stream=3,Size=1000,Total Bytes Received=26000
Data Frame,Stream=3,Size=1000,Total Bytes Received=27000
Data Frame,Stream=3,Size=1000,Total Bytes Received=28000
Data Frame,Stream=3,Size=1000,Total Bytes Received=29000
Data Frame,Stream=3,Size=1000,Total Bytes Received=30000
Data Frame,Stream=3,Size=1000,Total Bytes Received=31000
Data Frame,Stream=3,Size=1000,Total Bytes Received=32000
Data Frame,Stream=3,Size=768,Total Bytes Received=32768
Data Frame,Stream=3,Size=232,Total Bytes Received=33000
Data Frame,Stream=3,Size=1000,Total Bytes Received=34000
Data Frame,Stream=3,Size=1000,Total Bytes Received=35000
Data Frame,Stream=3,Size=1000,Total Bytes Received=36000
Data Frame,Stream=3,Size=1000,Total Bytes Received=37000
Data Frame,Stream=3,Size=1000,Total Bytes Received=38000
Data Frame,Stream=3,Size=1000,Total Bytes Received=39000
Data Frame,Stream=3,Size=1000,Total Bytes Received=40000
Data Frame,Stream=3,Size=1000,Total Bytes Received=41000
Data Frame,Stream=3,Size=1000,Total Bytes Received=42000
Data Frame,Stream=3,Size=1000,Total Bytes Received=43000
Data Frame,Stream=3,Size=1000,Total Bytes Received=44000
Data Frame,Stream=3,Size=1000,Total Bytes Received=45000
Data Frame,Stream=3,Size=1000,Total Bytes Received=46000
Data Frame,Stream=3,Size=1000,Total Bytes Received=47000
Data Frame,Stream=3,Size=1000,Total Bytes Received=48000
Data Frame,Stream=3,Size=1000,Total Bytes Received=49000
Data Frame,Stream=3,Size=152,Total Bytes Received=49152
Data Frame,Stream=3,Size=848,Total Bytes Received=50000
Data Frame,Stream=3,Size=1000,Total Bytes Received=51000
Data Frame,Stream=3,Size=1000,Total Bytes Received=52000
Data Frame,Stream=3,Size=1000,Total Bytes Received=53000
Data Frame,Stream=3,Size=1000,Total Bytes Received=54000
Data Frame,Stream=3,Size=1000,Total Bytes Received=55000
Data Frame,Stream=3,Size=1000,Total Bytes Received=56000
Data Frame,Stream=3,Size=1000,Total Bytes Received=57000
Data Frame,Stream=3,Size=1000,Total Bytes Received=58000
Data Frame,Stream=3,Size=1000,Total Bytes Received=59000
Data Frame,Stream=3,Size=1000,Total Bytes Received=60000
Data Frame,Stream=3,Size=1000,Total Bytes Received=61000
Data Frame,Stream=3,Size=1000,Total Bytes Received=62000
Data Frame,Stream=3,Size=1000,Total Bytes Received=63000
Data Frame,Stream=3,Size=1000,Total Bytes Received=64000
Data Frame,Stream=3,Size=1000,Total Bytes Received=65000
Data Frame,Stream=3,Size=535,Total Bytes Received=65535
// HANGS HERE DOES NOT SEND ANY MORE DATA FRAMES EVEN THOUGH ACTUAL PAYLOAD IS 100 KB //
---------- BEGIN SOURCE ----------
Source code is divided into 2 applications which needs to be run on separate JVM's
Server Source Code
final class PUT_Server
{
private static final int
WINDOW_SIZE=1000, //initial window size sent as setting frame to server
WINDOW_INC=1000; //make it -1 to make server send window updates equal to data frame payload length
private static byte[] encodeFrame(byte type,byte flags,int streamID,byte[] data)throws Exception
{
try(ByteArrayOutputStream output=new ByteArrayOutputStream())
{
output.write((byte)((data.length>>16)&0xFF));
output.write((byte)((data.length>>8)&0xFF));
output.write((byte)(data.length&0xFF));
output.write(type);
output.write(flags);
output.write((byte)(((streamID>>24)&0xFF)&0b01111111));
output.write((byte)((streamID>>16)&0xFF));
output.write((byte)((streamID>>8)&0xFF));
output.write((byte)(streamID&0xFF));
output.write(data);
return output.toByteArray();
}
}
private static void writeOK(OutputStream output,int streamID)throws Exception
{
//we respond with a Header Frame[status=200] &
output.write
(
encodeFrame
(
(byte)0x1, //type headers 0x1
(byte)0x4, //flags = END_HEADERS
streamID, //stream
new byte[]{(byte)0b10001000} //indexed header for status 200
)
);
//we respond with a Data Frame with text OK
output.write
(
encodeFrame
(
(byte)0x0, //type Data 0x0
(byte)(0x1), //flags = END_STREAM
streamID, //stream
"OK".getBytes() //Payload data
)
);
}
public static void main(String[] args)throws Exception
{
try(ServerSocket server=new ServerSocket(1000))
{
try(Socket client=server.accept())
{
try(InputStream input=client.getInputStream();
OutputStream output=client.getOutputStream())
{
int length,bytesReceived=0,windowInc;
byte[] data=new byte[100*1024];
Frame frame=new Frame();
byte[] connection_preface="PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n".getBytes("UTF-8");
int stage=1;
while((length=input.read(data))>0)
{
if(stage==1)
{
/*
We dont care about the request client sends
but respond with an 101 switching protocol
*/
output.write
(
(
"HTTP/1.1 101 Switching Protocols\r\n"+
"Connection:Upgrade\r\n"+
"Upgrade:h2c\r\n\r\n"
).getBytes("UTF-8")
);
//write our custom settings frame with specific initial window size
output.write
(
encodeFrame
(
(byte)0x4, //type settings 0x4
(byte)0 , //no flags
0, //stream 0
new byte[]
{
0,
(byte)0x4, //setting type initial window
(byte)(((WINDOW_SIZE>>24)&0xFF)&0b01111111), //encoded window size
(byte)((WINDOW_SIZE>>16)&0xFF),
(byte)((WINDOW_SIZE>>8)&0xFF),
(byte)(WINDOW_SIZE&0xFF)
}
)
);
//write 200 ok response
writeOK(output,1);
//stage 2 is where we process the client preface and its setting frame
stage=2;
}
else if(stage==2)
{
//decode frames after connection preface
frame.decode(new ByteArrayInputStream(data,connection_preface.length,length-connection_preface.length));
//settings frame without the ACK flag
if(frame.type==0x4 && frame.flags==0)
{
for(int i=0;i<=frame.payload.length-6;i+=6)
{
short option=(short)((frame.payload[i] & 0xFF) << 8 |
(frame.payload[i+1] & 0xFF));
int value=((frame.payload[i+2] & 0xFF) << 24) |
((frame.payload[i+3] & 0xFF) << 16) |
((frame.payload[i+4] & 0xFF) << 8) |
(frame.payload[i+5] & 0xFF);
//Initial Window Size
switch(option)
{
case 0x1:System.out.println("HEADER TABLE SIZE="+value);
break;
case 0x2:System.out.println("ENABLE PUSH="+value);
break;
case 0x3:System.out.println("MAX CONCURRENT STREAMS="+value);
break;
case 0x4:System.out.println("INITIAL WINDOW SIZE="+value);
break;
case 0x5:System.out.println("MAX FRAME SIZE="+value);
break;
case 0x6:System.out.println("MAX HEADER LIST SIZE="+value);
}
}
}
System.out.println("======================");
//decode normally remaining data in the next stage without skipping
stage=3;
continue;
}
if(stage==3)
{
frame.decode(new ByteArrayInputStream(data,0,length));
if(frame.type==0x0) //Data frame
{
bytesReceived+=frame.payload.length;
System.out.println("Data Frame,Stream="+frame.streamID+",Size="+frame.payload.length+",Total Bytes Received="+bytesReceived);
//window increment use frame payload length if WINDOW_INC < 0
windowInc=WINDOW_INC<=0?frame.payload.length:WINDOW_INC;
//write window update frame
output.write
(
encodeFrame
(
(byte)0x8, //type Window Increment 0x8
(byte)0, //no flags
frame.streamID, //stream
new byte[]
{
(byte)(((windowInc>>24)&0xFF) & 0b01111111), //encoded size[first byte reserved]
(byte)((windowInc>>16)&0xFF),
(byte)((windowInc>>8)&0xFF),
(byte)(windowInc&0xFF)
}
)
);
//end of data received test successfull
if(frame.flags==0x1)
{
System.out.println("End Received Test Successfull");
//write 200 ok and end test
writeOK(output,frame.streamID);
}
}
}
}
}
}
}
}
private static class Frame
{
private int length,type,flags,streamID;
private byte[] payload;
private void decode(InputStream input)throws Exception
{
//9 bytes containing all header info about the frame
byte[] frameInfo=new byte[9];
input.read(frameInfo);
length=((frameInfo[0]&0xFF)<<16) |
((frameInfo[1]&0xFF)<<8) |
((frameInfo[2]&0xFF));
type=frameInfo[3];
flags=frameInfo[4];
streamID=(((frameInfo[5] & 0xFF) & 0b01111111)<<24) |
((frameInfo[6] & 0xFF)<<16) |
((frameInfo[7] & 0xFF)<<8) |
(frameInfo[8] & 0xFF);
//payload of frame
payload=new byte[length];
input.read(payload);
}
}
}
Client Source Code
final class PUT_Client
{
private static void debug(HttpResponse response)throws Exception
{
System.out.println("==========Status Code=============");
System.out.println(""+response.statusCode());
System.out.println("Thread="+Thread.currentThread().getName());
System.out.println("===========Headers=========");
response.headers().map().forEach((header,values)->System.out.println(header+"=>"+values));
System.out.println("===========Body=============");
System.out.println(response.body().toString());
}
public static void main(String[] args)throws Exception
{
HttpRequest.Builder builder=HttpRequest.newBuilder();
HttpClient client=HttpClient.newHttpClient();
//1st GET request to allow connection to upgrade o http 2
HttpResponse response=client.send
(
builder.uri(URI.create("http://localhost:1000/Test.txt"))
.GET()
.timeout(Duration.ofSeconds(5))
.version(HttpClient.Version.HTTP_2)
.build(),
HttpResponse.BodyHandlers.ofString()
);
debug(response);
System.out.println("**********************************************");
//2nd request to put data of size 100kb[it never uploads the full 100kb only upto the 64kb mark]
byte[] data=new byte[100*1024];
response=client.send
(
builder.uri(URI.create("http://localhost:1000/Test.txt"))
.PUT(HttpRequest.BodyPublishers.ofByteArray(data))
.timeout(Duration.ofSeconds(20))
.version(HttpClient.Version.HTTP_2)
.build(),
HttpResponse.BodyHandlers.ofString()
);
debug(response);
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Upload remaining data as separate requests
FREQUENCY : always
Java 17 Windows 10
A DESCRIPTION OF THE PROBLEM :
HttpClient will not send more than 64kb of data in an POST/PUT request from the 2nd request onwards regardless of how many window updates frames we send from the server side.
It hangs indefinitely after sending 64kb of data without sending any data frames afterwards
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Create an HttpClient which uses http2
1) Send an initial request of any type[GET/PUT/POST etc] all these requests work on the 1st try so that the server can send back a 101 upgrade response to fully transform the connection to http2
2) Upload a byte array or anything greater than 64 kb[65535 bytes]
3) Observe output on server . Server never receives a Data frame with THE END_STREAM flag hence it never processes the full request and hence cannot send a response which causes the client to hang forever
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Client should send Data frames till all 100 kb of data is uploaded with the final frame having the END_STREAM flag set
ACTUAL -
Client only sends data frames up to the 64kb mark after which it pauses forever
HEADER TABLE SIZE=16384
ENABLE PUSH=1
MAX CONCURRENT STREAMS=100
INITIAL WINDOW SIZE=16777216
MAX FRAME SIZE=16384
======================
Data Frame,Stream=3,Size=1000,Total Bytes Received=1000
Data Frame,Stream=3,Size=1000,Total Bytes Received=2000
Data Frame,Stream=3,Size=1000,Total Bytes Received=3000
Data Frame,Stream=3,Size=1000,Total Bytes Received=4000
Data Frame,Stream=3,Size=1000,Total Bytes Received=5000
Data Frame,Stream=3,Size=1000,Total Bytes Received=6000
Data Frame,Stream=3,Size=1000,Total Bytes Received=7000
Data Frame,Stream=3,Size=1000,Total Bytes Received=8000
Data Frame,Stream=3,Size=1000,Total Bytes Received=9000
Data Frame,Stream=3,Size=1000,Total Bytes Received=10000
Data Frame,Stream=3,Size=1000,Total Bytes Received=11000
Data Frame,Stream=3,Size=1000,Total Bytes Received=12000
Data Frame,Stream=3,Size=1000,Total Bytes Received=13000
Data Frame,Stream=3,Size=1000,Total Bytes Received=14000
Data Frame,Stream=3,Size=1000,Total Bytes Received=15000
Data Frame,Stream=3,Size=1000,Total Bytes Received=16000
Data Frame,Stream=3,Size=384,Total Bytes Received=16384
Data Frame,Stream=3,Size=616,Total Bytes Received=17000
Data Frame,Stream=3,Size=1000,Total Bytes Received=18000
Data Frame,Stream=3,Size=1000,Total Bytes Received=19000
Data Frame,Stream=3,Size=1000,Total Bytes Received=20000
Data Frame,Stream=3,Size=1000,Total Bytes Received=21000
Data Frame,Stream=3,Size=1000,Total Bytes Received=22000
Data Frame,Stream=3,Size=1000,Total Bytes Received=23000
Data Frame,Stream=3,Size=1000,Total Bytes Received=24000
Data Frame,Stream=3,Size=1000,Total Bytes Received=25000
Data Frame,Stream=3,Size=1000,Total Bytes Received=26000
Data Frame,Stream=3,Size=1000,Total Bytes Received=27000
Data Frame,Stream=3,Size=1000,Total Bytes Received=28000
Data Frame,Stream=3,Size=1000,Total Bytes Received=29000
Data Frame,Stream=3,Size=1000,Total Bytes Received=30000
Data Frame,Stream=3,Size=1000,Total Bytes Received=31000
Data Frame,Stream=3,Size=1000,Total Bytes Received=32000
Data Frame,Stream=3,Size=768,Total Bytes Received=32768
Data Frame,Stream=3,Size=232,Total Bytes Received=33000
Data Frame,Stream=3,Size=1000,Total Bytes Received=34000
Data Frame,Stream=3,Size=1000,Total Bytes Received=35000
Data Frame,Stream=3,Size=1000,Total Bytes Received=36000
Data Frame,Stream=3,Size=1000,Total Bytes Received=37000
Data Frame,Stream=3,Size=1000,Total Bytes Received=38000
Data Frame,Stream=3,Size=1000,Total Bytes Received=39000
Data Frame,Stream=3,Size=1000,Total Bytes Received=40000
Data Frame,Stream=3,Size=1000,Total Bytes Received=41000
Data Frame,Stream=3,Size=1000,Total Bytes Received=42000
Data Frame,Stream=3,Size=1000,Total Bytes Received=43000
Data Frame,Stream=3,Size=1000,Total Bytes Received=44000
Data Frame,Stream=3,Size=1000,Total Bytes Received=45000
Data Frame,Stream=3,Size=1000,Total Bytes Received=46000
Data Frame,Stream=3,Size=1000,Total Bytes Received=47000
Data Frame,Stream=3,Size=1000,Total Bytes Received=48000
Data Frame,Stream=3,Size=1000,Total Bytes Received=49000
Data Frame,Stream=3,Size=152,Total Bytes Received=49152
Data Frame,Stream=3,Size=848,Total Bytes Received=50000
Data Frame,Stream=3,Size=1000,Total Bytes Received=51000
Data Frame,Stream=3,Size=1000,Total Bytes Received=52000
Data Frame,Stream=3,Size=1000,Total Bytes Received=53000
Data Frame,Stream=3,Size=1000,Total Bytes Received=54000
Data Frame,Stream=3,Size=1000,Total Bytes Received=55000
Data Frame,Stream=3,Size=1000,Total Bytes Received=56000
Data Frame,Stream=3,Size=1000,Total Bytes Received=57000
Data Frame,Stream=3,Size=1000,Total Bytes Received=58000
Data Frame,Stream=3,Size=1000,Total Bytes Received=59000
Data Frame,Stream=3,Size=1000,Total Bytes Received=60000
Data Frame,Stream=3,Size=1000,Total Bytes Received=61000
Data Frame,Stream=3,Size=1000,Total Bytes Received=62000
Data Frame,Stream=3,Size=1000,Total Bytes Received=63000
Data Frame,Stream=3,Size=1000,Total Bytes Received=64000
Data Frame,Stream=3,Size=1000,Total Bytes Received=65000
Data Frame,Stream=3,Size=535,Total Bytes Received=65535
// HANGS HERE DOES NOT SEND ANY MORE DATA FRAMES EVEN THOUGH ACTUAL PAYLOAD IS 100 KB //
---------- BEGIN SOURCE ----------
Source code is divided into 2 applications which needs to be run on separate JVM's
Server Source Code
final class PUT_Server
{
private static final int
WINDOW_SIZE=1000, //initial window size sent as setting frame to server
WINDOW_INC=1000; //make it -1 to make server send window updates equal to data frame payload length
private static byte[] encodeFrame(byte type,byte flags,int streamID,byte[] data)throws Exception
{
try(ByteArrayOutputStream output=new ByteArrayOutputStream())
{
output.write((byte)((data.length>>16)&0xFF));
output.write((byte)((data.length>>8)&0xFF));
output.write((byte)(data.length&0xFF));
output.write(type);
output.write(flags);
output.write((byte)(((streamID>>24)&0xFF)&0b01111111));
output.write((byte)((streamID>>16)&0xFF));
output.write((byte)((streamID>>8)&0xFF));
output.write((byte)(streamID&0xFF));
output.write(data);
return output.toByteArray();
}
}
private static void writeOK(OutputStream output,int streamID)throws Exception
{
//we respond with a Header Frame[status=200] &
output.write
(
encodeFrame
(
(byte)0x1, //type headers 0x1
(byte)0x4, //flags = END_HEADERS
streamID, //stream
new byte[]{(byte)0b10001000} //indexed header for status 200
)
);
//we respond with a Data Frame with text OK
output.write
(
encodeFrame
(
(byte)0x0, //type Data 0x0
(byte)(0x1), //flags = END_STREAM
streamID, //stream
"OK".getBytes() //Payload data
)
);
}
public static void main(String[] args)throws Exception
{
try(ServerSocket server=new ServerSocket(1000))
{
try(Socket client=server.accept())
{
try(InputStream input=client.getInputStream();
OutputStream output=client.getOutputStream())
{
int length,bytesReceived=0,windowInc;
byte[] data=new byte[100*1024];
Frame frame=new Frame();
byte[] connection_preface="PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n".getBytes("UTF-8");
int stage=1;
while((length=input.read(data))>0)
{
if(stage==1)
{
/*
We dont care about the request client sends
but respond with an 101 switching protocol
*/
output.write
(
(
"HTTP/1.1 101 Switching Protocols\r\n"+
"Connection:Upgrade\r\n"+
"Upgrade:h2c\r\n\r\n"
).getBytes("UTF-8")
);
//write our custom settings frame with specific initial window size
output.write
(
encodeFrame
(
(byte)0x4, //type settings 0x4
(byte)0 , //no flags
0, //stream 0
new byte[]
{
0,
(byte)0x4, //setting type initial window
(byte)(((WINDOW_SIZE>>24)&0xFF)&0b01111111), //encoded window size
(byte)((WINDOW_SIZE>>16)&0xFF),
(byte)((WINDOW_SIZE>>8)&0xFF),
(byte)(WINDOW_SIZE&0xFF)
}
)
);
//write 200 ok response
writeOK(output,1);
//stage 2 is where we process the client preface and its setting frame
stage=2;
}
else if(stage==2)
{
//decode frames after connection preface
frame.decode(new ByteArrayInputStream(data,connection_preface.length,length-connection_preface.length));
//settings frame without the ACK flag
if(frame.type==0x4 && frame.flags==0)
{
for(int i=0;i<=frame.payload.length-6;i+=6)
{
short option=(short)((frame.payload[i] & 0xFF) << 8 |
(frame.payload[i+1] & 0xFF));
int value=((frame.payload[i+2] & 0xFF) << 24) |
((frame.payload[i+3] & 0xFF) << 16) |
((frame.payload[i+4] & 0xFF) << 8) |
(frame.payload[i+5] & 0xFF);
//Initial Window Size
switch(option)
{
case 0x1:System.out.println("HEADER TABLE SIZE="+value);
break;
case 0x2:System.out.println("ENABLE PUSH="+value);
break;
case 0x3:System.out.println("MAX CONCURRENT STREAMS="+value);
break;
case 0x4:System.out.println("INITIAL WINDOW SIZE="+value);
break;
case 0x5:System.out.println("MAX FRAME SIZE="+value);
break;
case 0x6:System.out.println("MAX HEADER LIST SIZE="+value);
}
}
}
System.out.println("======================");
//decode normally remaining data in the next stage without skipping
stage=3;
continue;
}
if(stage==3)
{
frame.decode(new ByteArrayInputStream(data,0,length));
if(frame.type==0x0) //Data frame
{
bytesReceived+=frame.payload.length;
System.out.println("Data Frame,Stream="+frame.streamID+",Size="+frame.payload.length+",Total Bytes Received="+bytesReceived);
//window increment use frame payload length if WINDOW_INC < 0
windowInc=WINDOW_INC<=0?frame.payload.length:WINDOW_INC;
//write window update frame
output.write
(
encodeFrame
(
(byte)0x8, //type Window Increment 0x8
(byte)0, //no flags
frame.streamID, //stream
new byte[]
{
(byte)(((windowInc>>24)&0xFF) & 0b01111111), //encoded size[first byte reserved]
(byte)((windowInc>>16)&0xFF),
(byte)((windowInc>>8)&0xFF),
(byte)(windowInc&0xFF)
}
)
);
//end of data received test successfull
if(frame.flags==0x1)
{
System.out.println("End Received Test Successfull");
//write 200 ok and end test
writeOK(output,frame.streamID);
}
}
}
}
}
}
}
}
private static class Frame
{
private int length,type,flags,streamID;
private byte[] payload;
private void decode(InputStream input)throws Exception
{
//9 bytes containing all header info about the frame
byte[] frameInfo=new byte[9];
input.read(frameInfo);
length=((frameInfo[0]&0xFF)<<16) |
((frameInfo[1]&0xFF)<<8) |
((frameInfo[2]&0xFF));
type=frameInfo[3];
flags=frameInfo[4];
streamID=(((frameInfo[5] & 0xFF) & 0b01111111)<<24) |
((frameInfo[6] & 0xFF)<<16) |
((frameInfo[7] & 0xFF)<<8) |
(frameInfo[8] & 0xFF);
//payload of frame
payload=new byte[length];
input.read(payload);
}
}
}
Client Source Code
final class PUT_Client
{
private static void debug(HttpResponse response)throws Exception
{
System.out.println("==========Status Code=============");
System.out.println(""+response.statusCode());
System.out.println("Thread="+Thread.currentThread().getName());
System.out.println("===========Headers=========");
response.headers().map().forEach((header,values)->System.out.println(header+"=>"+values));
System.out.println("===========Body=============");
System.out.println(response.body().toString());
}
public static void main(String[] args)throws Exception
{
HttpRequest.Builder builder=HttpRequest.newBuilder();
HttpClient client=HttpClient.newHttpClient();
//1st GET request to allow connection to upgrade o http 2
HttpResponse response=client.send
(
builder.uri(URI.create("http://localhost:1000/Test.txt"))
.GET()
.timeout(Duration.ofSeconds(5))
.version(HttpClient.Version.HTTP_2)
.build(),
HttpResponse.BodyHandlers.ofString()
);
debug(response);
System.out.println("**********************************************");
//2nd request to put data of size 100kb[it never uploads the full 100kb only upto the 64kb mark]
byte[] data=new byte[100*1024];
response=client.send
(
builder.uri(URI.create("http://localhost:1000/Test.txt"))
.PUT(HttpRequest.BodyPublishers.ofByteArray(data))
.timeout(Duration.ofSeconds(20))
.version(HttpClient.Version.HTTP_2)
.build(),
HttpResponse.BodyHandlers.ofString()
);
debug(response);
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Upload remaining data as separate requests
FREQUENCY : always