import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;


public class HTTTP_Continue 
{
 private static final boolean DO_CONTINUE=false;
 
 private static final class OverSimplifiedHPack
 {
  private static byte[] encode_literal_non_indexed_headers(String[] index_values)throws Exception
  {
   try(ByteArrayOutputStream encoding=new ByteArrayOutputStream();
       ByteArrayOutputStream buffer=new ByteArrayOutputStream())
   {
    for(int i=0;i<=index_values.length-2;i+=2)
    {
     String value=index_values[i+1]; 

     byte[] 
     header_index=encodePrefix(encoding,Integer.parseInt(index_values[i]),(byte)4),
     valueBytes=value.getBytes(),
     valueLength=encodePrefix(encoding,valueBytes.length,(byte)7); 
    
     encoding.reset();
     
     encoding.write(header_index[0]&0b00001111);
     if(header_index.length>1){encoding.write(header_index,1,header_index.length-1);}
     
     encoding.write(valueLength[0]&0b01111111);
     if(valueLength.length>1){encoding.write(valueLength,1,valueLength.length-1);}
     encoding.write(valueBytes);
     
     buffer.write(encoding.toByteArray());
    }
    return buffer.toByteArray();
   } 
  } 
  
  private static byte[] encodePrefix(ByteArrayOutputStream encoding,int value,byte prefix)throws Exception
  {
   encoding.reset();
   
   int max=(int)Math.pow(2,prefix)-1;
   if(value<max){encoding.write(value);}
   else
   {
    encoding.write(max);

    value-=max;
    while(value>128)
    {
     encoding.write(((value%128)+128));

     value/=128;
    }

    encoding.write(value);
   } 

   return encoding.toByteArray();    
  }
 } 
 
 
 public static void server(String[] args)throws Exception
 {
  try(ServerSocket socket=new ServerSocket(5000))
  {
   try(Socket client=socket.accept())
   {
    try(InputStream input=client.getInputStream();
        OutputStream output=client.getOutputStream())
    {
     byte[] array=new byte[8196];
     
     int 
     stage=1,
     requestID=0;   
     while(input.read(array)>0)
     {
      if(stage==1)
      {
       try(ByteArrayOutputStream serverPreface=new ByteArrayOutputStream())
       {
        serverPreface.write
        (
         (
          "HTTP/1.1 101 Switching Protocols\r\n"+
          "Connection:Upgrade\r\n"+
          "Upgrade:h2c\r\n\r\n"
         ).getBytes()
        );
        
        byte[] settings={};
        serverPreface.write
        (
         new byte[]
         {
          (byte)((settings.length>>16) & 0xFF),
          (byte)((settings.length>>8) & 0xFF),
          (byte)(settings.length & 0xFF),
          (byte)0x4,
          (byte)0x1,
          0,0,0,0
         } 
        );
        serverPreface.write(settings);
        
        output.write(serverPreface.toByteArray());
        output.flush();
       } 
      
       if(requestID==0){stage=3;}
      }
      else if(stage==2)
      {    
       if(requestID==1 && array[3]!=0x1){continue;}
      
       try(ByteArrayOutputStream frameBytes=new ByteArrayOutputStream())
       {
        byte[] headerBytes=OverSimplifiedHPack.encode_literal_non_indexed_headers
        (
         new String[]
         {
          "8",DO_CONTINUE?"100":"407",
          "28","0"
         }
        );       
        frameBytes.write
        (
         new byte[]
         {
          (byte)((headerBytes.length>>16) & 0xFF),
          (byte)((headerBytes.length>>8) & 0xFF),
          (byte)(headerBytes.length & 0xFF),
          (byte)0x1,
          (byte)0x4,
          0,0,0,3
         } 
        );
        frameBytes.write(headerBytes);
        
        output.write(frameBytes.toByteArray());  
        output.flush();
       }
      
       if(DO_CONTINUE){stage=3;}
       else{stage=-1;}
      } 
      else if(stage==3)
      {
       if(requestID==1 && !(array[3]==0x1 || array[3]==0x0)){continue;}
   
       try(ByteArrayOutputStream frameBytes=new ByteArrayOutputStream())
       {
        byte[] content=(requestID==0?"Request 1 Done":"Test Complete").getBytes();
        
        byte[] headerBytes=OverSimplifiedHPack.encode_literal_non_indexed_headers
        (
         new String[]
         {
          "8",(requestID==0?"200":"202"),
          "28",""+content.length
         } 
        );     
        frameBytes.write
        (
         new byte[]
         {
          (byte)((headerBytes.length>>16) & 0xFF),
          (byte)((headerBytes.length>>8) & 0xFF),
          (byte)(headerBytes.length & 0xFF),
          (byte)0x1,
          (byte)0x4,
          0,0,0,(byte)(requestID==0?1:3)
         } 
        );
        frameBytes.write(headerBytes);
        
      
        frameBytes.write
        (
         new byte[]
         {
          (byte)((content.length>>16) & 0xFF),
          (byte)((content.length>>8) & 0xFF),
          (byte)(content.length & 0xFF),
          (byte)0x0,
          (byte)0x1,
          0,0,0,(byte)(requestID==0?1:3)
         } 
        );
        frameBytes.write(content);
        
        output.write(frameBytes.toByteArray());
        output.flush();
       }
       
       if(requestID==0)
       {
        requestID=1;
        stage=2;
       }
       else{stage=-1;}
      } 
     } 
    } 
   } 
  } 
 }
 
 public static void main(String[] args)throws Exception
 {
  HttpClient client=HttpClient.newBuilder()
                              .version(HttpClient.Version.HTTP_2)
                              .build();
  
  HttpRequest request=HttpRequest.newBuilder(URI.create("http://localhost:5000/Test.txt"))
                                  .GET()
                                  .build();
  
  HttpResponse response=client.send(request,HttpResponse.BodyHandlers.ofString());
  System.out.println("Status code: " + response.statusCode());                            
  System.out.println("Headers: " + response.headers().map());
  System.out.println("Body: " + response.body());
  
  System.out.println("=======================");
  
  request=HttpRequest.newBuilder(URI.create("http://localhost:5000/Test.txt"))
                                 .PUT(HttpRequest.BodyPublishers.ofString("Test")) 
                                 .expectContinue(true)
                                 .build();
  
  response=client.send(request,HttpResponse.BodyHandlers.ofString());
  System.out.println("Status code: " + response.statusCode());                            
  System.out.println("Headers: " + response.headers().map());
  System.out.println("Body: " + response.body());
 } 
}
