[Git][java-team/qpid-proton-j-extensions][master] 6 commits: New upstream version 1.2.5

Tony Mancill (@tmancill) gitlab at salsa.debian.org
Mon Nov 25 05:14:32 GMT 2024



Tony Mancill pushed to branch master at Debian Java Maintainers / qpid-proton-j-extensions


Commits:
ea27fddc by tony mancill at 2024-11-24T20:49:27-08:00
New upstream version 1.2.5
- - - - -
35fc4a2b by tony mancill at 2024-11-24T20:49:28-08:00
Update upstream source from tag 'upstream/1.2.5'

Update to upstream version '1.2.5'
with Debian dir 7f651997cc967e08486cfd29f9fa0dfe62f0bdcd
- - - - -
0f90ab40 by tony mancill at 2024-11-24T21:03:11-08:00
Add patch for deprecated Mockito method (Closes: #1086310)

- - - - -
df0cb178 by tony mancill at 2024-11-24T21:03:11-08:00
Freshen years in debian/copyright

- - - - -
0e833d3c by tony mancill at 2024-11-24T21:03:11-08:00
Bump Standards-Version to 4.7.0

- - - - -
842738d2 by tony mancill at 2024-11-24T21:08:33-08:00
Prepare changelog for upload

- - - - -


26 changed files:

- + SECURITY.md
- ci.yml
- debian/changelog
- debian/control
- debian/copyright
- + debian/patches/02-mockito-compat.patch
- debian/patches/series
- + eng/templates/1es-redirect.yml
- + eng/templates/image.yml
- + eng/templates/test-steps.yml
- pom.xml
- + src/main/java/com/microsoft/azure/proton/transport/proxy/HttpStatusLine.java
- src/main/java/com/microsoft/azure/proton/transport/proxy/ProxyHandler.java
- + src/main/java/com/microsoft/azure/proton/transport/proxy/ProxyResponse.java
- src/main/java/com/microsoft/azure/proton/transport/proxy/impl/Constants.java
- src/main/java/com/microsoft/azure/proton/transport/proxy/impl/DigestProxyChallengeProcessorImpl.java
- src/main/java/com/microsoft/azure/proton/transport/proxy/impl/ProxyHandlerImpl.java
- src/main/java/com/microsoft/azure/proton/transport/proxy/impl/ProxyImpl.java
- + src/main/java/com/microsoft/azure/proton/transport/proxy/impl/ProxyResponseImpl.java
- src/main/java/com/microsoft/azure/proton/transport/proxy/impl/StringUtils.java
- src/test/java/com/microsoft/azure/proton/transport/proxy/impl/DigestProxyChallengeProcessorImplTest.java
- + src/test/java/com/microsoft/azure/proton/transport/proxy/impl/HttpStatusLineTest.java
- src/test/java/com/microsoft/azure/proton/transport/proxy/impl/ProxyHandlerImplTest.java
- src/test/java/com/microsoft/azure/proton/transport/proxy/impl/ProxyImplTest.java
- + src/test/java/com/microsoft/azure/proton/transport/proxy/impl/ProxyResponseImplTest.java
- + src/test/java/com/microsoft/azure/proton/transport/proxy/impl/TestUtils.java


Changes:

=====================================
SECURITY.md
=====================================
@@ -0,0 +1,41 @@
+<!-- BEGIN MICROSOFT SECURITY.MD V0.0.8 BLOCK -->
+
+## Security
+
+Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).
+
+If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below.
+
+## Reporting Security Issues
+
+**Please do not report security vulnerabilities through public GitHub issues.**
+
+Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report).
+
+If you prefer to submit without logging in, send email to [secure at microsoft.com](mailto:secure at microsoft.com).  If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey).
+
+You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). 
+
+Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
+
+  * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
+  * Full paths of source file(s) related to the manifestation of the issue
+  * The location of the affected source code (tag/branch/commit or direct URL)
+  * Any special configuration required to reproduce the issue
+  * Step-by-step instructions to reproduce the issue
+  * Proof-of-concept or exploit code (if possible)
+  * Impact of the issue, including how an attacker might exploit the issue
+
+This information will help us triage your report more quickly.
+
+If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs.
+
+## Preferred Languages
+
+We prefer all communications to be in English.
+
+## Policy
+
+Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd).
+
+<!-- END MICROSOFT SECURITY.MD BLOCK -->


=====================================
ci.yml
=====================================
@@ -1,9 +1,3 @@
-resources:
-  repositories:
-    - repository: azure-sdk-build-tools
-      type: git
-      name: internal/azure-sdk-build-tools
-
 trigger:
   branches:
     include:
@@ -20,116 +14,140 @@ pr:
       - release/*
       - hotfix/*
 
-variables:
-  DefaultOptions: '--batch-mode --fail-at-end -Dmaven.wagon.http.pool=false'
-  LoggingOptions: '-Dorg.slf4j.simpleLogger.defaultLogLevel=error -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn'
-  MemoryOptions: '-Xmx3072m'
-
-stages:
-  - stage: Build
-    jobs:
-      - job: 'Build'
-        pool:
-          vmImage: 'ubuntu-18.04'
-        strategy:
-          matrix:
-            Java 8:
-              ArtifactName: 'java8-packages'
-              ProfileFlag: '-Djava8'
-              JavaVersion: '1.8'
-            # We name this 'packages' because it is the default version we want to ship with.
-            Java LTS:
-              ArtifactName: 'packages'
-              ProfileFlag: '-Djava-lts'
-              JavaVersion: '1.11'
-
-        steps:
-          - task: Maven at 3
-            displayName: 'Build and Package'
-            inputs:
-              mavenPomFile: 'pom.xml'
-              goals: 'deploy'
-              options: '$(DefaultOptions) $(ProfileFlag) -T 1C -DskipTests -Dgpg.skip -Dmaven.javadoc.skip=true -Dcheckstyle.skip=true -Dspotbugs.skip=true --settings eng/settings.xml -DaltDeploymentRepository="local::default::file:///${project.basedir}/output"'
-              mavenOptions: '$(LoggingOptions) $(MemoryOptions)'
-              javaHomeOption: 'JDKVersion'
-              jdkVersionOption: $(JavaVersion)
-              jdkArchitectureOption: 'x64'
-              publishJUnitResults: false
-
-          - task: Maven at 3
-            displayName: 'Run SpotBugs, Checkstyle, and Javadoc'
-            inputs:
-                mavenPomFile: pom.xml
-                options: '$(DefaultOptions) --no-transfer-progress -DskipTests -Dgpg.skip'
-                mavenOptions: '$(MemoryOptions)'
-                javaHomeOption: 'JDKVersion'
-                jdkVersionOption: $(JavaVersion)
-                jdkArchitectureOption: 'x64'
-                publishJUnitResults: false
-                goals: 'verify'
-
-          - script: |
-              cp output/com/microsoft/azure/qpid-proton-j-extensions/**/* $(Build.ArtifactStagingDirectory)
-              rm $(Build.ArtifactStagingDirectory)/*.sha1
-              rm $(Build.ArtifactStagingDirectory)/*.md5
-            displayName: Flatten and copy build outputs
-
-          - publish: $(Build.ArtifactStagingDirectory)
-            artifact: $(ArtifactName)
-            displayName: 'Publish outputs to $(ArtifactName) artifact'
-
-      - job: 'Test'
-        strategy:
-          matrix:
-            Linux - Java 8:
-              OSName: 'Linux'
-              OSVmImage: 'ubuntu-18.04'
-              ProfileFlag: '-Djava8'
-              JavaVersion: '1.8'
-            macOS - Java 8:
-              OSName: 'macOS'
-              OSVmImage: 'macOS-10.15'
-              ProfileFlag: '-Djava8'
-              JavaVersion: '1.8'
-            Windows - Java 8:
-              OSName: 'Windows'
-              OSVmImage: 'vs2017-win2016'
-              ProfileFlag: '-Djava8'
-              JavaVersion: '1.8'
-            Linux - Java LTS:
-              OSName: 'Linux'
-              OSVmImage: 'ubuntu-18.04'
-              ProfileFlag: '-Djava-lts'
-              JavaVersion: '1.11'
-            macOS - Java LTS:
-              OSName: 'macOS'
-              OSVmImage: 'macOS-10.15'
-              ProfileFlag: '-Djava-lts'
-              JavaVersion: '1.11'
-            Windows - Java LTS:
-              OSName: 'Windows'
-              OSVmImage: 'vs2017-win2016'
-              ProfileFlag: '-Djava-lts'
-              JavaVersion: '1.11'
-
-        pool:
-          vmImage: $(OSVmImage)
-
-        steps:
-          - task: Maven at 3
-            displayName: 'Run Tests'
-            inputs:
-              mavenPomFile: 'pom.xml'
-              options: '$(DefaultOptions) $(ProfileFlag) --settings eng/settings.xml'
-              mavenOptions: '-Xmx3072m $(LoggingOptions)'
-              javaHomeOption: 'JDKVersion'
-              jdkVersionOption: $(JavaVersion)
-              jdkArchitectureOption: 'x64'
-              publishJUnitResults: false
-              goals: 'test'
-
-          - task: PublishTestResults at 2
-            condition: succeededOrFailed()
-            inputs:
-              mergeTestResults: true
-              testRunTitle: '$(OSName) on Java $(JavaVersion)'
+extends:
+  template: /eng/templates/1es-redirect.yml
+  parameters:
+    stages:
+      - stage: Build
+        variables:
+          - name: DefaultOptions
+            value: '--batch-mode --fail-at-end -Dmaven.wagon.http.pool=false'
+          - name: LoggingOptions
+            value: '-Dorg.slf4j.simpleLogger.defaultLogLevel=error -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn'
+          - name: MemoryOptions
+            value: '-Xmx3072m'
+          - template: /eng/templates/image.yml
+        jobs:
+          - job: 'Build'
+            pool:
+              name: $(LINUXPOOL)
+              image: $(LINUXVMIMAGE)
+              os: linux
+
+            strategy:
+              matrix:
+                Java 8:
+                  ArtifactName: 'java8-packages'
+                  ProfileFlag: '-Djava8'
+                  JavaVersion: '1.8'
+                # We name this 'packages' because it is the default version we want to ship with.
+                Java LTS:
+                  ArtifactName: 'packages'
+                  ProfileFlag: '-Djava-lts'
+                  JavaVersion: '1.11'
+
+            steps:
+              - task: Maven at 3
+                displayName: 'Build and Package'
+                inputs:
+                  mavenPomFile: 'pom.xml'
+                  goals: 'deploy'
+                  options: '$(DefaultOptions) $(ProfileFlag) -T 1C -DskipTests -Dgpg.skip -Dcheckstyle.skip=true -Dspotbugs.skip=true --settings eng/settings.xml -DaltDeploymentRepository="local::default::file:///${project.basedir}/output"'
+                  mavenOptions: '$(LoggingOptions) $(MemoryOptions)'
+                  javaHomeOption: 'JDKVersion'
+                  jdkVersionOption: $(JavaVersion)
+                  jdkArchitectureOption: 'x64'
+                  publishJUnitResults: false
+
+              - task: Maven at 3
+                displayName: 'Run SpotBugs, Checkstyle, and Javadoc'
+                inputs:
+                    mavenPomFile: pom.xml
+                    options: '$(DefaultOptions) --no-transfer-progress -DskipTests -Dgpg.skip'
+                    mavenOptions: '$(MemoryOptions)'
+                    javaHomeOption: 'JDKVersion'
+                    jdkVersionOption: $(JavaVersion)
+                    jdkArchitectureOption: 'x64'
+                    publishJUnitResults: false
+                    goals: 'verify'
+
+              - script: |
+                  cp output/com/microsoft/azure/qpid-proton-j-extensions/**/* $(Build.ArtifactStagingDirectory)
+                  rm $(Build.ArtifactStagingDirectory)/*.sha1
+                  rm $(Build.ArtifactStagingDirectory)/*.md5
+                displayName: Flatten and copy build outputs
+
+              - task: 1ES.PublishPipelineArtifact at 1
+                displayName: 'Publish outputs to $(ArtifactName) artifact'
+                inputs:
+                  artifactName: $(ArtifactName)
+                  targetPath: $(Build.ArtifactStagingDirectory)
+
+          - job:
+            displayName: 'Test'
+            strategy:
+              matrix:
+                Linux - Java 8:
+                  OSName: 'Linux'
+                  OSVmImage: 'ubuntu-22.04'
+                  ProfileFlag: '-Djava8'
+                  JavaVersion: '1.8'
+                Linux - Java LTS:
+                  OSName: 'Linux'
+                  OSVmImage: 'ubuntu-22.04'
+                  ProfileFlag: '-Djava-lts'
+                  JavaVersion: '1.11'
+
+            pool:
+              name:  $(LINUXPOOL)
+              image: $(LINUXVMIMAGE)
+              os: linux
+
+            steps:
+              - template: /eng/templates/test-steps.yml
+
+          - job:
+            displayName: 'Test'
+            strategy:
+              matrix:
+                Windows - Java 8:
+                  OSName: 'Windows'
+                  OSVmImage: 'windows-2022'
+                  ProfileFlag: '-Djava8'
+                  JavaVersion: '1.8'
+                Windows - Java LTS:
+                  OSName: 'Windows'
+                  OSVmImage: 'windows-2022'
+                  ProfileFlag: '-Djava-lts'
+                  JavaVersion: '1.11'
+
+            pool:
+              name:  $(WINDOWSPOOL)
+              image: $(WINDOWSVMIMAGE)
+              os: windows
+
+            steps:
+              - template: /eng/templates/test-steps.yml
+
+          - job:
+            displayName: 'Test'
+            strategy:
+              matrix:
+                macOS - Java 8:
+                  OSName: 'macOS'
+                  OSVmImage: 'macOS-13'
+                  ProfileFlag: '-Djava8'
+                  JavaVersion: '1.8'
+                macOS - Java LTS:
+                  OSName: 'macOS'
+                  OSVmImage: 'macOS-13'
+                  ProfileFlag: '-Djava-lts'
+                  JavaVersion: '1.11'
+
+            pool:
+              name:  $(MACPOOL)
+              vmImage: $(MACVMIMAGE)
+              os: macOS
+
+            steps:
+              - template: /eng/templates/test-steps.yml
\ No newline at end of file


=====================================
debian/changelog
=====================================
@@ -1,3 +1,13 @@
+qpid-proton-j-extensions (1.2.5-1) unstable; urgency=medium
+
+  * Team upload
+  * New upstream version 1.2.5
+  * Add patch for deprecated Mockito method (Closes: #1086310)
+  * Freshen years in debian/copyright
+  * Bump Standards-Version to 4.7.0
+
+ -- tony mancill <tmancill at debian.org>  Sun, 24 Nov 2024 21:02:20 -0800
+
 qpid-proton-j-extensions (1.2.4-2) unstable; urgency=medium
 
   * Team upload


=====================================
debian/control
=====================================
@@ -11,7 +11,7 @@ Build-Depends-Indep:
  libapache-qpid-proton-j-java (>= 0.33.2),
  libslf4j-java (>= 1.7.28),
  libmockito-java <!nocheck>,
-Standards-Version: 4.6.2
+Standards-Version: 4.7.0
 Vcs-Git: https://salsa.debian.org/java-team/qpid-proton-j-extensions.git
 Vcs-Browser: https://salsa.debian.org/java-team/qpid-proton-j-extensions
 Homepage: https://github.com/Azure/qpid-proton-j-extensions


=====================================
debian/copyright
=====================================
@@ -3,11 +3,12 @@ Upstream-Name: qpid-proton-j-extensions
 Source: https://github.com/Azure/qpid-proton-j-extensions
 
 Files: *
-Copyright: 2023, Microsoft Corporation
+Copyright: 2023-2024, Microsoft Corporation
 License: MIT
 
 Files: debian/*
-Copyright: 2023, Joseph Nahmias <jello at debian.org>
+Copyright: 2023-2024, Joseph Nahmias <jello at debian.org>
+           2024, tony mancill <tmancill at debian.org>
 License: MIT
 
 License: MIT


=====================================
debian/patches/02-mockito-compat.patch
=====================================
@@ -0,0 +1,33 @@
+Description: Stop using deprecated Mockito.verifyZeroInteractions() 
+Author: tony mancill <tmancill at debian.org>
+Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1086310
+
+--- a/src/test/java/com/microsoft/azure/proton/transport/proxy/impl/ProxyAuthenticatorTests.java
++++ b/src/test/java/com/microsoft/azure/proton/transport/proxy/impl/ProxyAuthenticatorTests.java
+@@ -22,7 +22,7 @@
+ 
+ import static org.mockito.ArgumentMatchers.argThat;
+ import static org.mockito.Mockito.mock;
+-import static org.mockito.Mockito.verifyZeroInteractions;
++import static org.mockito.Mockito.verifyNoInteractions;
+ import static org.mockito.Mockito.when;
+ 
+ public class ProxyAuthenticatorTests {
+@@ -114,7 +114,7 @@
+         PasswordAuthentication authentication = proxyAuthenticator.getPasswordAuthentication(scheme, PROXY_ADDRESS);
+ 
+         // Assert
+-        verifyZeroInteractions(proxySelector);
++        verifyNoInteractions(proxySelector);
+ 
+         Assert.assertNotNull(authentication);
+         Assert.assertEquals(configuration.credentials().getUserName(), authentication.getUserName());
+@@ -148,7 +148,7 @@
+         PasswordAuthentication authentication = proxyAuthenticator.getPasswordAuthentication(scheme, PROXY_ADDRESS);
+ 
+         // Assert
+-        verifyZeroInteractions(proxySelector);
++        verifyNoInteractions(proxySelector);
+ 
+         Assert.assertNotNull(authentication);
+         Assert.assertEquals(configuration.credentials().getUserName(), authentication.getUserName());


=====================================
debian/patches/series
=====================================
@@ -1 +1,2 @@
 javac-source-target.patch
+02-mockito-compat.patch


=====================================
eng/templates/1es-redirect.yml
=====================================
@@ -0,0 +1,40 @@
+resources:
+  repositories:
+    - repository: 1ESPipelineTemplates
+      type: git
+      name: 1ESPipelineTemplates/1ESPipelineTemplates
+      ref: refs/tags/release
+
+parameters:
+- name: stages
+  type: stageList
+  default: []
+- name: UseOfficial
+  type: boolean
+  default: true
+
+extends:
+  ${{ if and(parameters.UseOfficial, eq(variables['System.TeamProject'], 'internal')) }}:
+    template: v1/1ES.Official.PipelineTemplate.yml at 1ESPipelineTemplates
+  ${{ else }}:
+    template: v1/1ES.Unofficial.PipelineTemplate.yml at 1ESPipelineTemplates
+  parameters:
+    settings:
+      skipBuildTagsForGitHubPullRequests: true
+    sdl:
+      sourceAnalysisPool:
+        name: azsdk-pool-mms-win-2022-general
+        image: azsdk-pool-mms-win-2022-1espt
+        os: windows
+      sourceRepositoriesToScan:
+        exclude:
+          - repository: azure-sdk-build-tools
+      eslint:
+        enabled: false
+        justificationForDisabling: 'ESLint injected task has failures because it uses an old version of mkdirp. We should not fail for tools not controlled by the repo. See: https://dev.azure.com/azure-sdk/internal/_build/results?buildId=3499746'
+      psscriptanalyzer:
+        compiled: true
+        break: true
+      policy: M365
+
+    stages: ${{ parameters.stages }}


=====================================
eng/templates/image.yml
=====================================
@@ -0,0 +1,24 @@
+# Default pool image selection. Set as variable so we can override at pipeline level
+
+variables:
+  - name: LINUXPOOL
+    value: azsdk-pool-mms-ubuntu-2204-general
+  - name: WINDOWSPOOL
+    value: azsdk-pool-mms-win-2022-general
+  - name: MACPOOL
+    value: Azure Pipelines
+
+  - name: LINUXVMIMAGE
+    value: azsdk-pool-mms-ubuntu-2204-1espt
+  - name: WINDOWSVMIMAGE
+    value: azsdk-pool-mms-win-2022-1espt
+  - name: MACVMIMAGE
+    value: macos-13
+
+  # Values required for pool.os field in 1es pipeline templates
+  - name: LINUXOS
+    value: linux
+  - name: WINDOWSOS
+    value: windows
+  - name: MACOS
+    value: macOS


=====================================
eng/templates/test-steps.yml
=====================================
@@ -0,0 +1,19 @@
+# relies on variable settings set from calling previous build steps
+steps:
+  - task: Maven at 3
+    displayName: 'Run Tests'
+    inputs:
+      mavenPomFile: 'pom.xml'
+      options: '$(DefaultOptions) $(ProfileFlag) --settings eng/settings.xml'
+      mavenOptions: '-Xmx3072m $(LoggingOptions)'
+      javaHomeOption: 'JDKVersion'
+      jdkVersionOption: $(JavaVersion)
+      jdkArchitectureOption: 'x64'
+      publishJUnitResults: false
+      goals: 'test'
+
+  - task: PublishTestResults at 2
+    condition: succeededOrFailed()
+    inputs:
+      mergeTestResults: true
+      testRunTitle: '$(OSName) on Java $(JavaVersion)'
\ No newline at end of file


=====================================
pom.xml
=====================================
@@ -12,7 +12,7 @@
   <modelVersion>4.0.0</modelVersion>
   <groupId>com.microsoft.azure</groupId>
   <artifactId>qpid-proton-j-extensions</artifactId>
-  <version>1.2.4</version>
+  <version>1.2.5</version>
 
   <licenses>
     <license>
@@ -45,7 +45,7 @@
     <packageOutputDirectory>${project.build.directory}</packageOutputDirectory>
 
     <!-- Product dependency versions -->
-    <proton-j-version>0.33.8</proton-j-version>
+    <proton-j-version>0.34.1</proton-j-version>
     <slf4j-version>1.7.28</slf4j-version>
 
     <!-- Test dependency versions -->


=====================================
src/main/java/com/microsoft/azure/proton/transport/proxy/HttpStatusLine.java
=====================================
@@ -0,0 +1,92 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.microsoft.azure.proton.transport.proxy;
+
+import java.util.Locale;
+import java.util.Objects;
+
+/**
+ * The first line in an HTTP 1.0/1.1 response. Consists of the HTTP protocol version, status code, and a reason phrase
+ * for the HTTP response.
+ *
+ * @see <a href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html">RFC 2616</a>
+ */
+public final class HttpStatusLine {
+    private final String httpVersion;
+    private final int statusCode;
+    private final String reason;
+
+    /**
+     * Creates a new instance of {@link HttpStatusLine}.
+     *
+     * @param protocolVersion The HTTP protocol version. For example, 1.0, 1.1.
+     * @param statusCode A numeric status code for the HTTP response.
+     * @param reason Textual phrase representing the HTTP status code.
+     */
+    private HttpStatusLine(String protocolVersion, int statusCode, String reason) {
+        this.httpVersion = Objects.requireNonNull(protocolVersion, "'httpVersion' cannot be null.");
+        this.statusCode = statusCode;
+        this.reason = Objects.requireNonNull(reason, "'reason' cannot be null.");
+    }
+
+    /**
+     * Parses the provided {@code statusLine} into an HTTP status line.
+     *
+     * @param line Line to parse into an HTTP status line.
+     * @return A new instance of {@link HttpStatusLine} representing the given {@code statusLine}.
+     * @throws IllegalArgumentException if {@code line} is not the correct format of an HTTP status line. If it
+     *         does not have a protocol version, status code, or reason component. Or, if the HTTP protocol version
+     *         cannot be parsed.
+     */
+    public static HttpStatusLine create(String line) {
+        final String[] components = line.split(" ", 3);
+        if (components.length != 3) {
+            throw new IllegalArgumentException(String.format(Locale.ROOT,
+                    "HTTP status-line is invalid. Line: %s", line));
+        }
+
+        final String[] protocol = components[0].split("/", 2);
+        if (protocol.length != 2) {
+            throw new IllegalArgumentException(String.format(Locale.ROOT,
+                    "Protocol is invalid, expected HTTP/{version}. Actual: %s", components[0]));
+        }
+
+        int statusCode;
+        try {
+            statusCode = Integer.parseInt(components[1]);
+        } catch (NumberFormatException e) {
+            throw new IllegalArgumentException(String.format(Locale.US,
+                    "HTTP Status code '%s' is not valid.", components[1]), e);
+        }
+
+        return new HttpStatusLine(protocol[1], statusCode, components[2]);
+    }
+
+    /**
+     * Gets the HTTP protocol version.
+     *
+     * @return The HTTP protocol version.
+     */
+    public String getProtocolVersion() {
+        return this.httpVersion;
+    }
+
+    /**
+     * Gets the HTTP status code.
+     *
+     * @return The HTTP status code.
+     */
+    public int getStatusCode() {
+        return this.statusCode;
+    }
+
+    /**
+     * Gets the textual representation for the HTTP status code.
+     *
+     * @return The textual representation for the HTTP status code.
+     */
+    public String getReason() {
+        return this.reason;
+    }
+}


=====================================
src/main/java/com/microsoft/azure/proton/transport/proxy/ProxyHandler.java
=====================================
@@ -3,7 +3,6 @@
 
 package com.microsoft.azure.proton.transport.proxy;
 
-import java.nio.ByteBuffer;
 import java.util.Map;
 
 /**
@@ -11,33 +10,6 @@ import java.util.Map;
  */
 public interface ProxyHandler {
 
-    /**
-     * Represents a response from the proxy.
-     */
-    class ProxyResponseResult {
-        private final Boolean isSuccess;
-        private final String error;
-
-        /**
-         * Creates a new response.
-         *
-         * @param isSuccess {@code true} if it was successful; {@code false} otherwise.
-         * @param error The error from the proxy. Or {@code null} if there was none.
-         */
-        public ProxyResponseResult(final Boolean isSuccess, final String error) {
-            this.isSuccess = isSuccess;
-            this.error = error;
-        }
-
-        public Boolean getIsSuccess() {
-            return isSuccess;
-        }
-
-        public String getError() {
-            return error;
-        }
-    }
-
     /**
      * Creates a CONNECT request to the provided {@code hostName} and adds {@code additionalHeaders} to the request.
      *
@@ -48,11 +20,11 @@ public interface ProxyHandler {
     String createProxyRequest(String hostName, Map<String, String> additionalHeaders);
 
     /**
-     * Verifies that {@code buffer} contains a successful CONNECT response.
+     * Verifies that {@code httpResponse} contains a successful CONNECT response.
+     *
+     * @param httpResponse HTTP response to validate for a successful CONNECT response.
+     * @return {@code true} if the HTTP response is successful and correct, and {@code false} otherwise.
      *
-     * @param buffer Buffer containing the HTTP response.
-     * @return Indicates if CONNECT response contained a success. If not, contains an error indicating why the call was
-     *         not successful.
      */
-    ProxyResponseResult validateProxyResponse(ByteBuffer buffer);
+    boolean validateProxyResponse(ProxyResponse httpResponse);
 }


=====================================
src/main/java/com/microsoft/azure/proton/transport/proxy/ProxyResponse.java
=====================================
@@ -0,0 +1,56 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.microsoft.azure.proton.transport.proxy;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Represents an HTTP response from a proxy.
+ */
+public interface ProxyResponse {
+    /**
+     * Gets the headers for the HTTP response.
+     *
+     * @return The headers for the HTTP response.
+     */
+    Map<String, List<String>> getHeaders();
+
+    /**
+     * Gets the HTTP status line.
+     *
+     * @return The HTTP status line.
+     */
+    HttpStatusLine getStatus();
+
+    /**
+     * Gets the HTTP response body.
+     *
+     * @return The HTTP response body.
+     */
+    ByteBuffer getContents();
+
+    /**
+     * Gets the HTTP response body as an error.
+     *
+     * @return If there is no HTTP response body, an empty string is returned.
+     */
+    String getError();
+
+    /**
+     * Gets whether or not the HTTP response is complete. An HTTP response is complete when the HTTP header and body are
+     * received.
+     *
+     * @return {@code true} if the HTTP response is complete, and {@code false} otherwise.
+     */
+    boolean isMissingContent();
+
+    /**
+     * Adds contents to the body if it is missing content.
+     *
+     * @param contents Contents to add to the HTTP body.
+     */
+    void addContent(ByteBuffer contents);
+}


=====================================
src/main/java/com/microsoft/azure/proton/transport/proxy/impl/Constants.java
=====================================
@@ -9,7 +9,7 @@ import java.util.Locale;
  * Package private constants.
  */
 class Constants {
-    static final String PROXY_AUTHENTICATE_HEADER = "Proxy-Authenticate:";
+    static final String PROXY_AUTHENTICATE = "Proxy-Authenticate";
     static final String PROXY_AUTHORIZATION = "Proxy-Authorization";
 
     static final String DIGEST = "Digest";
@@ -18,4 +18,11 @@ class Constants {
     static final String DIGEST_LOWERCASE = Constants.DIGEST.toLowerCase(Locale.ROOT);
 
     static final String CONNECT = "CONNECT";
+
+    static final String PROXY_CONNECT_FAILED = "Proxy connect request failed with error: ";
+    static final String PROXY_CONNECT_USER_ERROR = "User configuration error. Using non-matching proxy authentication.";
+
+    static final int PROXY_HANDSHAKE_BUFFER_SIZE = 4 * 1024; // buffers used only for proxy-handshake
+
+    static final String CONTENT_LENGTH = "Content-Length";
 }


=====================================
src/main/java/com/microsoft/azure/proton/transport/proxy/impl/DigestProxyChallengeProcessorImpl.java
=====================================
@@ -28,7 +28,7 @@ import static java.nio.charset.StandardCharsets.UTF_8;
  */
 public class DigestProxyChallengeProcessorImpl implements ProxyChallengeProcessor {
     static final String DEFAULT_ALGORITHM = "MD5";
-    private static final String PROXY_AUTH_DIGEST = Constants.PROXY_AUTHENTICATE_HEADER + " " + Constants.DIGEST;
+    private static final String PROXY_AUTH_DIGEST = Constants.DIGEST;
     private static final char[] HEX_CODE = "0123456789ABCDEF".toCharArray();
     private static final SecureRandom SECURE_RANDOM = new SecureRandom();
 


=====================================
src/main/java/com/microsoft/azure/proton/transport/proxy/impl/ProxyHandlerImpl.java
=====================================
@@ -3,16 +3,19 @@
 
 package com.microsoft.azure.proton.transport.proxy.impl;
 
+import com.microsoft.azure.proton.transport.proxy.HttpStatusLine;
 import com.microsoft.azure.proton.transport.proxy.Proxy;
 import com.microsoft.azure.proton.transport.proxy.ProxyHandler;
+import com.microsoft.azure.proton.transport.proxy.ProxyResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
-import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
 import java.util.Locale;
 import java.util.Map;
-import java.util.Scanner;
-import java.util.function.Predicate;
-import java.util.regex.Pattern;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 /**
  * Implementation class that handles connecting to the proxy.
@@ -27,8 +30,10 @@ public class ProxyHandlerImpl implements ProxyHandler {
     static final String CONNECT_REQUEST = "CONNECT %1$s HTTP/1.1%2$sHost: %1$s%2$sConnection: Keep-Alive%2$s";
     static final String HEADER_FORMAT = "%s: %s";
     static final String NEW_LINE = "\r\n";
-    private final Pattern successStatusLine = Pattern.compile("^http/1\\.(0|1) (?<statusCode>2[0-9]{2})", Pattern.CASE_INSENSITIVE);
-    private final Predicate<String> successStatusLinePredicate = successStatusLine.asPredicate();
+
+    private static final String CONNECTION_ESTABLISHED = "connection established";
+    private static final Set<String> SUPPORTED_VERSIONS = Stream.of("1.1", "1.0").collect(Collectors.toSet());
+    private final Logger logger = LoggerFactory.getLogger(ProxyHandlerImpl.class);
 
     /**
      * {@inheritDoc}
@@ -54,23 +59,17 @@ public class ProxyHandlerImpl implements ProxyHandler {
      * {@inheritDoc}
      */
     @Override
-    public ProxyResponseResult validateProxyResponse(ByteBuffer buffer) {
-        int size = buffer.remaining();
-        String response = null;
+    public boolean validateProxyResponse(ProxyResponse response) {
+        Objects.requireNonNull(response, "'response' cannot be null.");
 
-        if (size > 0) {
-            byte[] responseBytes = new byte[buffer.remaining()];
-            buffer.get(responseBytes);
-            response = new String(responseBytes, StandardCharsets.UTF_8);
-            final Scanner responseScanner = new Scanner(response);
-            if (responseScanner.hasNextLine()) {
-                final String firstLine = responseScanner.nextLine();
-                if (successStatusLinePredicate.test(firstLine)) {
-                    return new ProxyResponseResult(true, null);
-                }
-            }
+        final HttpStatusLine status = response.getStatus();
+        if (status == null) {
+            logger.error("Response does not contain a status line. {}", response);
+            return false;
         }
 
-        return new ProxyResponseResult(false, response);
+        return status.getStatusCode() == 200
+                && SUPPORTED_VERSIONS.contains(status.getProtocolVersion())
+                && CONNECTION_ESTABLISHED.equalsIgnoreCase(status.getReason());
     }
 }


=====================================
src/main/java/com/microsoft/azure/proton/transport/proxy/impl/ProxyImpl.java
=====================================
@@ -8,6 +8,7 @@ import com.microsoft.azure.proton.transport.proxy.ProxyAuthenticationType;
 import com.microsoft.azure.proton.transport.proxy.ProxyChallengeProcessor;
 import com.microsoft.azure.proton.transport.proxy.ProxyConfiguration;
 import com.microsoft.azure.proton.transport.proxy.ProxyHandler;
+import com.microsoft.azure.proton.transport.proxy.ProxyResponse;
 import org.apache.qpid.proton.engine.Transport;
 import org.apache.qpid.proton.engine.TransportException;
 import org.apache.qpid.proton.engine.impl.TransportImpl;
@@ -19,16 +20,23 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.nio.ByteBuffer;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Locale;
 import java.util.Map;
-import java.util.Scanner;
+import java.util.Optional;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
 import java.util.stream.Collectors;
 
 import static com.microsoft.azure.proton.transport.proxy.ProxyAuthenticationType.BASIC;
 import static com.microsoft.azure.proton.transport.proxy.ProxyAuthenticationType.DIGEST;
+import static com.microsoft.azure.proton.transport.proxy.impl.Constants.PROXY_AUTHENTICATE;
+import static com.microsoft.azure.proton.transport.proxy.impl.Constants.PROXY_CONNECT_FAILED;
+import static com.microsoft.azure.proton.transport.proxy.impl.Constants.PROXY_CONNECT_USER_ERROR;
+import static com.microsoft.azure.proton.transport.proxy.impl.Constants.PROXY_HANDSHAKE_BUFFER_SIZE;
 import static org.apache.qpid.proton.engine.impl.ByteBufferUtils.newWriteableBuffer;
 
 /**
@@ -40,9 +48,6 @@ import static org.apache.qpid.proton.engine.impl.ByteBufferUtils.newWriteableBuf
  */
 public class ProxyImpl implements Proxy, TransportLayer {
     private static final Logger LOGGER = LoggerFactory.getLogger(ProxyImpl.class);
-    private static final String PROXY_CONNECT_FAILED = "Proxy connect request failed with error: ";
-    private static final String PROXY_CONNECT_USER_ERROR = "User configuration error. Using non-matching proxy authentication.";
-    private static final int PROXY_HANDSHAKE_BUFFER_SIZE = 8 * 1024; // buffers used only for proxy-handshake
 
     private final ByteBuffer inputBuffer;
     private final ByteBuffer outputBuffer;
@@ -50,28 +55,27 @@ public class ProxyImpl implements Proxy, TransportLayer {
 
     private boolean tailClosed = false;
     private boolean headClosed = false;
-    private boolean isProxyConfigured;
     private String host = "";
     private Map<String, String> headers = null;
     private TransportImpl underlyingTransport;
-    private ProxyState proxyState = ProxyState.PN_PROXY_NOT_STARTED;
     private ProxyHandler proxyHandler;
 
+    private volatile boolean isProxyConfigured;
+    private volatile ProxyState proxyState = ProxyState.PN_PROXY_NOT_STARTED;
+
     /**
-     * Create proxy transport layer - which, after configuring using
-     * the {@link #configure(String, Map, ProxyHandler, Transport)} API
-     * is ready for layering in qpid-proton-j transport layers, using
-     * {@link org.apache.qpid.proton.engine.impl.TransportInternal#addTransportLayer(TransportLayer)} API.
+     * Create proxy transport layer - which, after configuring using the {@link #configure(String, Map, ProxyHandler,
+     * Transport)} API is ready for layering in qpid-proton-j transport layers, using {@link
+     * org.apache.qpid.proton.engine.impl.TransportInternal#addTransportLayer(TransportLayer)} API.
      */
     public ProxyImpl() {
         this(null);
     }
 
     /**
-     * Create proxy transport layer - which, after configuring using
-     * the {@link #configure(String, Map, ProxyHandler, Transport)} API
-     * is ready for layering in qpid-proton-j transport layers, using
-     * {@link org.apache.qpid.proton.engine.impl.TransportInternal#addTransportLayer(TransportLayer)} API.
+     * Create proxy transport layer - which, after configuring using the {@link #configure(String, Map, ProxyHandler,
+     * Transport)} API is ready for layering in qpid-proton-j transport layers, using {@link
+     * org.apache.qpid.proton.engine.impl.TransportInternal#addTransportLayer(TransportLayer)} API.
      *
      * @param configuration Proxy configuration to use.
      */
@@ -133,7 +137,7 @@ public class ProxyImpl implements Proxy, TransportLayer {
         return this.outputBuffer;
     }
 
-    protected Boolean getIsProxyConfigured() {
+    protected boolean getIsProxyConfigured() {
         return this.isProxyConfigured;
     }
 
@@ -180,10 +184,17 @@ public class ProxyImpl implements Proxy, TransportLayer {
         private final TransportOutput underlyingOutput;
         private final ByteBuffer head;
 
+        // Represents a response from a CONNECT request.
+        private final AtomicReference<ProxyResponse> proxyResponse = new AtomicReference<>();
+
+        /**
+         * Creates a transport wrapper that wraps the WebSocket transport input and output.
+         */
         ProxyTransportWrapper(TransportInput input, TransportOutput output) {
             underlyingInput = input;
             underlyingOutput = output;
             head = outputBuffer.asReadOnlyBuffer();
+            head.limit(0);
         }
 
         @Override
@@ -231,62 +242,78 @@ public class ProxyImpl implements Proxy, TransportLayer {
             switch (proxyState) {
                 case PN_PROXY_CONNECTING:
                     inputBuffer.flip();
-                    final ProxyHandler.ProxyResponseResult responseResult = proxyHandler.validateProxyResponse(inputBuffer);
-                    inputBuffer.compact();
-                    inputBuffer.clear();
 
+                    final ProxyResponse connectResponse = readProxyResponse(inputBuffer);
+
+                    if (connectResponse == null || connectResponse.isMissingContent()) {
+                        LOGGER.info("Request is missing content. Waiting for more bytes.");
+                        break;
+                    }
+                    //Clean up response to prepare for challenge
+                    proxyResponse.set(null);
+
+                    final boolean isSuccess = proxyHandler.validateProxyResponse(connectResponse);
                     // When connecting to proxy, it does not challenge us for authentication. If the user has specified
-                    // a configuration and it is not NONE, then we fail due to misconfiguration.
-                    if (responseResult.getIsSuccess()) {
+                    // a configuration, and it is not NONE, then we fail due to misconfiguration.
+                    if (isSuccess) {
                         if (proxyConfiguration == null || proxyConfiguration.authentication() == ProxyAuthenticationType.NONE) {
                             proxyState = ProxyState.PN_PROXY_CONNECTED;
                         } else {
                             if (LOGGER.isErrorEnabled()) {
                                 LOGGER.error("ProxyConfiguration mismatch. User configured: '{}', but authentication is not required",
-                                        proxyConfiguration.authentication());
+                                    proxyConfiguration.authentication());
                             }
-
                             closeTailProxyError(PROXY_CONNECT_USER_ERROR);
                         }
                         break;
                     }
 
-                    final String challenge = responseResult.getError();
-                    final Set<ProxyAuthenticationType> supportedTypes = getAuthenticationTypes(challenge);
+                    final Map<String, List<String>> headers = connectResponse.getHeaders();
+                    final Set<ProxyAuthenticationType> supportedTypes = getAuthenticationTypes(headers);
 
                     // The proxy did not successfully connect, user has specified that they want a particular
                     // authentication method, but it is not in list of supported authentication methods.
                     if (proxyConfiguration != null && !supportedTypes.contains(proxyConfiguration.authentication())) {
                         if (LOGGER.isErrorEnabled()) {
                             LOGGER.error("Proxy authentication required. User configured: '{}', but supported proxy authentication methods are: {}",
-                                    proxyConfiguration.authentication(),
-                                    supportedTypes.stream().map(type -> type.toString()).collect(Collectors.joining(",")));
+                                proxyConfiguration.authentication(),
+                                supportedTypes.stream().map(type -> type.toString()).collect(Collectors.joining(",")));
                         }
-
-                        closeTailProxyError(PROXY_CONNECT_USER_ERROR + PROXY_CONNECT_FAILED + challenge);
+                        closeTailProxyError(PROXY_CONNECT_USER_ERROR + PROXY_CONNECT_FAILED
+                                + connectResponse);
                         break;
                     }
 
+                    final List<String> challenges = headers.getOrDefault(PROXY_AUTHENTICATE, new ArrayList<>());
                     final ProxyChallengeProcessor processor = proxyConfiguration != null
-                            ? getChallengeProcessor(host, challenge, proxyConfiguration.authentication())
-                            : getChallengeProcessor(host, challenge, supportedTypes);
+                            ? getChallengeProcessor(host, challenges, proxyConfiguration.authentication())
+                            : getChallengeProcessor(host, challenges, supportedTypes);
 
                     if (processor != null) {
                         proxyState = ProxyState.PN_PROXY_CHALLENGE;
-                        headers = processor.getHeader();
+                        ProxyImpl.this.headers = processor.getHeader();
                     } else {
-                        closeTailProxyError(PROXY_CONNECT_FAILED + challenge);
+                        LOGGER.warn("Could not get ProxyChallengeProcessor for challenges.");
+                        closeTailProxyError(PROXY_CONNECT_FAILED + String.join(";", challenges));
                     }
                     break;
                 case PN_PROXY_CHALLENGE_RESPONDED:
                     inputBuffer.flip();
-                    final ProxyHandler.ProxyResponseResult result = proxyHandler.validateProxyResponse(inputBuffer);
-                    inputBuffer.compact();
+                    final ProxyResponse challengeResponse = readProxyResponse(inputBuffer);
 
-                    if (result.getIsSuccess()) {
+                    if (challengeResponse == null || challengeResponse.isMissingContent()) {
+                        LOGGER.warn("Request is missing content. Waiting for more bytes.");
+                        break;
+                    }
+                    //Clean up
+                    proxyResponse.set(null);
+
+                    final boolean result = proxyHandler.validateProxyResponse(challengeResponse);
+
+                    if (result) {
                         proxyState = ProxyState.PN_PROXY_CONNECTED;
                     } else {
-                        closeTailProxyError(PROXY_CONNECT_FAILED + result.getError());
+                        closeTailProxyError(PROXY_CONNECT_FAILED + challengeResponse);
                     }
                     break;
                 default:
@@ -353,6 +380,11 @@ public class ProxyImpl implements Proxy, TransportLayer {
             }
         }
 
+        /**
+         * Gets the beginning of the output buffer.
+         *
+         * @return The beginning of the byte buffer.
+         */
         @Override
         public ByteBuffer head() {
             if (getIsHandshakeInProgress()) {
@@ -368,6 +400,11 @@ public class ProxyImpl implements Proxy, TransportLayer {
             }
         }
 
+        /**
+         * Removes the first number of bytes from the output buffer.
+         *
+         * @param bytes The number of bytes to remove from the output buffer.
+         */
         @Override
         public void pop(int bytes) {
             if (getIsHandshakeInProgress()) {
@@ -392,6 +429,9 @@ public class ProxyImpl implements Proxy, TransportLayer {
             }
         }
 
+        /**
+         * Closes the output transport.
+         */
         @Override
         public void close_head() {
             headClosed = true;
@@ -402,18 +442,21 @@ public class ProxyImpl implements Proxy, TransportLayer {
          * Gets the ProxyChallengeProcessor based on authentication types supported. Prefers DIGEST authentication if
          * supported over BASIC. Returns null if it cannot match any supported types.
          */
-        private ProxyChallengeProcessor getChallengeProcessor(String host, String challenge,
+        private ProxyChallengeProcessor getChallengeProcessor(String host, List<String> challenges,
                                                               Set<ProxyAuthenticationType> authentication) {
+            final ProxyAuthenticationType authType;
             if (authentication.contains(DIGEST)) {
-                return getChallengeProcessor(host, challenge, DIGEST);
+                authType = DIGEST;
             } else if (authentication.contains(BASIC)) {
-                return getChallengeProcessor(host, challenge, BASIC);
+                authType = BASIC;
             } else {
                 return null;
             }
+
+            return getChallengeProcessor(host, challenges, authType);
         }
 
-        private ProxyChallengeProcessor getChallengeProcessor(String host, String challenge,
+        private ProxyChallengeProcessor getChallengeProcessor(String host, List<String> challenges,
                                                               ProxyAuthenticationType authentication) {
             final ProxyAuthenticator authenticator = proxyConfiguration != null
                     ? new ProxyAuthenticator(proxyConfiguration)
@@ -421,47 +464,45 @@ public class ProxyImpl implements Proxy, TransportLayer {
 
             switch (authentication) {
                 case DIGEST:
-                    return new DigestProxyChallengeProcessorImpl(host, challenge, authenticator);
+                    final Optional<String> matching = challenges.stream()
+                            .filter(challenge -> challenge.toLowerCase(Locale.ROOT).startsWith(Constants.DIGEST_LOWERCASE))
+                            .findFirst();
+
+                    return matching.map(c -> new DigestProxyChallengeProcessorImpl(host, c, authenticator))
+                            .orElse(null);
                 case BASIC:
                     return new BasicProxyChallengeProcessorImpl(host, authenticator);
                 default:
+                    LOGGER.warn("Authentication type does not have a challenge processor: {}", authentication);
                     return null;
             }
         }
 
         /**
-         * Gets the supported authentication types based on the {@code error}.
+         * Gets the supported authentication types based on the {@code headers}.
          *
-         * @param error Response from service call.
-         * @return The supported proxy authentication methods. Or, an empty array if the value of {@code error} is
-         *     {@code null}, an empty string. Also, if it does not contain {@link Constants#PROXY_AUTHENTICATE_HEADER} with
-         *     {@link Constants#BASIC_LOWERCASE} or {@link Constants#DIGEST_LOWERCASE}.
+         * @param headers HTTP proxy response headers from service call.
+         * @return The supported proxy authentication methods. Or, an empty set if the value of {@code error} is {@code
+         *         null}, an empty string. Or, if it does not contain{@link Constants#PROXY_AUTHENTICATE} with
+         *         {@link Constants#BASIC_LOWERCASE} or {@link Constants#DIGEST_LOWERCASE}.
          */
-        private Set<ProxyAuthenticationType> getAuthenticationTypes(String error) {
-            int index = error.indexOf(Constants.PROXY_AUTHENTICATE_HEADER);
-
-            if (index == -1) {
+        private Set<ProxyAuthenticationType> getAuthenticationTypes(Map<String, List<String>> headers) {
+            if (!headers.containsKey(PROXY_AUTHENTICATE)) {
                 return Collections.emptySet();
             }
 
-            Set<ProxyAuthenticationType> supportedTypes = new HashSet<>();
-
-            try (Scanner scanner = new Scanner(error)) {
-                while (scanner.hasNextLine()) {
-                    String line = scanner.nextLine().trim();
+            final Set<ProxyAuthenticationType> supportedTypes = new HashSet<>();
+            final List<String> authenticationTypes = headers.get(PROXY_AUTHENTICATE);
 
-                    if (!line.startsWith(Constants.PROXY_AUTHENTICATE_HEADER)) {
-                        continue;
-                    }
-
-                    String substring = line.substring(Constants.PROXY_AUTHENTICATE_HEADER.length())
-                            .trim().toLowerCase(Locale.ROOT);
+            for (String type : authenticationTypes) {
+                final String lowercase = type.toLowerCase(Locale.ROOT);
 
-                    if (substring.startsWith(Constants.BASIC_LOWERCASE)) {
-                        supportedTypes.add(BASIC);
-                    } else if (substring.startsWith(Constants.DIGEST_LOWERCASE)) {
-                        supportedTypes.add(DIGEST);
-                    }
+                if (lowercase.startsWith(Constants.BASIC_LOWERCASE)) {
+                    supportedTypes.add(BASIC);
+                } else if (lowercase.startsWith(Constants.DIGEST_LOWERCASE)) {
+                    supportedTypes.add(DIGEST);
+                } else {
+                    LOGGER.warn("Did not understand this authentication type: {}", type);
                 }
             }
 
@@ -472,5 +513,31 @@ public class ProxyImpl implements Proxy, TransportLayer {
             tailClosed = true;
             underlyingTransport.closed(new TransportException(errorMessage));
         }
+
+        /**
+         * Given a byte buffer, reads a HTTP proxy response from it.
+         *
+         * @param buffer The buffer to read HTTP proxy response from.
+         * @return The current HTTP proxy response. Or {@code null} if one could not be read from the buffer and there
+         *         is no current HTTP response.
+         */
+        private ProxyResponse readProxyResponse(ByteBuffer buffer) {
+            int size = buffer.remaining();
+            if (size <= 0) {
+                LOGGER.warn("InputBuffer is empty. Not reading any contents from it. Returning current response.");
+                return proxyResponse.get();
+            }
+
+            ProxyResponse current = proxyResponse.get();
+            if (current == null) {
+                proxyResponse.set(ProxyResponseImpl.create(buffer));
+            } else {
+                current.addContent(buffer);
+            }
+
+            buffer.compact();
+
+            return proxyResponse.get();
+        }
     }
 }


=====================================
src/main/java/com/microsoft/azure/proton/transport/proxy/impl/ProxyResponseImpl.java
=====================================
@@ -0,0 +1,196 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.microsoft.azure.proton.transport.proxy.impl;
+
+import com.microsoft.azure.proton.transport.proxy.HttpStatusLine;
+import com.microsoft.azure.proton.transport.proxy.ProxyResponse;
+import com.microsoft.azure.proton.transport.ws.WebSocket.WebSocketFrameReadState;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.AbstractMap;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import static com.microsoft.azure.proton.transport.proxy.impl.Constants.CONTENT_LENGTH;
+
+/**
+ * Represents an HTTP response from a proxy.
+ *
+ * @see <a href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html">RFC2616</a>
+ */
+public final class ProxyResponseImpl implements ProxyResponse {
+    private static final Logger LOGGER = LoggerFactory.getLogger(ProxyResponseImpl.class);
+
+    private final HttpStatusLine status;
+    private final Map<String, List<String>> headers;
+    private final ByteBuffer contents;
+
+    private ProxyResponseImpl(HttpStatusLine status, Map<String, List<String>> headers, ByteBuffer contents) {
+        this.status = status;
+        this.headers = headers;
+        this.contents = contents;
+    }
+
+    /**
+     * Create a proxy response from a given {@code buffer}. Assumes that the {@code buffer} has been flipped.
+     *
+     * @param buffer Buffer which could parse to a proxy response.
+     * @return A new instance of {@link ProxyResponseImpl} representing the given buffer.
+     * @throws IllegalArgumentException if {@code buffer} have no content to read.
+     */
+    public static ProxyResponse create(ByteBuffer buffer) {
+        // Because we've flipped the buffer, position = 0, and the limit = size of the content.
+        int size = buffer.remaining();
+
+        if (size <= 0) {
+            throw new IllegalArgumentException(String.format("Cannot create response with buffer have no content. "
+                + "Limit: %s. Position: %s. Cap: %s", buffer.limit(), buffer.position(), buffer.capacity()));
+        }
+
+        final byte[] responseBytes = new byte[size];
+        buffer.get(responseBytes);
+
+        final String response = new String(responseBytes, StandardCharsets.UTF_8);
+        final String[] lines = response.split(StringUtils.NEW_LINE);
+        final Map<String, List<String>> headers = new HashMap<>();
+
+        WebSocketFrameReadState frameReadState = WebSocketFrameReadState.INIT_READ;
+        HttpStatusLine statusLine = null;
+        ByteBuffer contents = ByteBuffer.allocate(0);
+
+        //Assume the full header message is in the first frame
+        for (String line : lines) {
+            switch (frameReadState) {
+                case INIT_READ:
+                    statusLine = HttpStatusLine.create(line);
+                    frameReadState = WebSocketFrameReadState.CHUNK_READ;
+                    break;
+                case CHUNK_READ:
+                    if (StringUtils.isNullOrEmpty(line)) {
+                        // Now that we're done reading all the headers, figure out the size of the HTTP body and
+                        // allocate an array of that size.
+                        int length = 0;
+                        if (headers.containsKey(CONTENT_LENGTH)) {
+                            final List<String> contentLength = headers.get(CONTENT_LENGTH);
+                            length = Integer.parseInt(contentLength.get(0));
+                        }
+
+                        boolean hasBody = length > 0;
+                        if (!hasBody) {
+                            LOGGER.info("There is no content in the response. Response: {}", response);
+                            return new ProxyResponseImpl(statusLine, headers, contents);
+                        }
+
+                        contents = ByteBuffer.allocate(length);
+                        frameReadState = WebSocketFrameReadState.HEADER_READ;
+                    } else {
+                        final Map.Entry<String, String> header = parseHeader(line);
+                        final List<String> value = headers.getOrDefault(header.getKey(), new ArrayList<>());
+
+                        value.add(header.getValue());
+                        headers.put(header.getKey(), value);
+                    }
+                    break;
+                case HEADER_READ:
+                    if (contents.position() == 0) {
+                        frameReadState = WebSocketFrameReadState.CONTINUED_FRAME_READ;
+                    }
+
+                    contents.put(line.getBytes(StandardCharsets.UTF_8));
+                    contents.mark();
+                    break;
+                case CONTINUED_FRAME_READ:
+                    contents.put(line.getBytes(StandardCharsets.UTF_8));
+                    contents.mark();
+                    break;
+                default:
+                    LOGGER.error("Unknown state: {}. Response: {}", frameReadState, response);
+                    frameReadState = WebSocketFrameReadState.READ_ERROR;
+                    break;
+            }
+        }
+
+
+        return new ProxyResponseImpl(statusLine, headers, contents);
+    }
+
+    private static Map.Entry<String, String> parseHeader(String contents) {
+        final String[] split = contents.split(":", 2);
+
+        if (split.length != 2) {
+            throw new IllegalStateException("Line is not a valid header. Contents: " + contents);
+        }
+
+        return new AbstractMap.SimpleEntry<>(split[0].trim(), split[1].trim());
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public HttpStatusLine getStatus() {
+        return status;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Map<String, List<String>> getHeaders() {
+        return headers;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ByteBuffer getContents() {
+        return contents.duplicate();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String getError() {
+        final ByteBuffer readonly = contents.asReadOnlyBuffer();
+        readonly.flip();
+        return StandardCharsets.UTF_8.decode(readonly).toString();
+    }
+
+    /**
+     * Gets whether or not the HTTP response is complete. An HTTP response is complete when the HTTP header and body are
+     * received.
+     *
+     * @return {@code true} if the HTTP response is complete, and {@code false} otherwise.
+     */
+    public boolean isMissingContent() {
+        return contents.hasRemaining();
+    }
+
+    /**
+     * Adds additional content to the HTTP response's body. Assumes that the {@code content} has been flipped.
+     *
+     * @param content Content to add to the body of the HTTP response.
+     * @throws NullPointerException if {@code content} is {@code null}.
+     * @throws IllegalArgumentException if {@code content} have no content to read.
+     */
+    public void addContent(ByteBuffer content) {
+        Objects.requireNonNull(content, "'content' cannot be null.");
+
+        int size = content.remaining();
+
+        if (size <= 0) {
+            throw new IllegalArgumentException("There was no content to add to current HTTP response.");
+        }
+
+        final byte[] responseBytes = new byte[content.remaining()];
+        content.get(responseBytes);
+
+        this.contents.put(responseBytes);
+    }
+
+}


=====================================
src/main/java/com/microsoft/azure/proton/transport/proxy/impl/StringUtils.java
=====================================
@@ -7,6 +7,8 @@ package com.microsoft.azure.proton.transport.proxy.impl;
  * Utility classes for strings.
  */
 class StringUtils {
+    static final String NEW_LINE = "\r\n";
+
     static boolean isNullOrEmpty(String string) {
         return string == null || string.isEmpty();
     }


=====================================
src/test/java/com/microsoft/azure/proton/transport/proxy/impl/DigestProxyChallengeProcessorImplTest.java
=====================================
@@ -180,10 +180,10 @@ public class DigestProxyChallengeProcessorImplTest {
     }
 
     private static String generateProxyChallenge(String realm, String nonce, String qop) {
-        final String digest = String.format("%s %s realm=\"%s\", nonce=\"%s\", qop=\"%s\", stale=false",
-                Constants.PROXY_AUTHENTICATE_HEADER, Constants.DIGEST, realm, nonce, qop);
-        final String basic = String.format("%s %s realm=\"%s\"",
-                Constants.PROXY_AUTHENTICATE_HEADER, Constants.BASIC, realm);
+        final String digest = String.format("%s realm=\"%s\", nonce=\"%s\", qop=\"%s\", stale=false",
+                Constants.DIGEST, realm, nonce, qop);
+        final String basic = String.format("%s realm=\"%s\"",
+                Constants.BASIC, realm);
 
         return String.join(NEW_LINE,
                 "HTTP/1.1 407 Proxy Authentication Required",


=====================================
src/test/java/com/microsoft/azure/proton/transport/proxy/impl/HttpStatusLineTest.java
=====================================
@@ -0,0 +1,46 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.microsoft.azure.proton.transport.proxy.impl;
+
+import com.microsoft.azure.proton.transport.proxy.HttpStatusLine;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+public class HttpStatusLineTest {
+
+    /**
+     * Verifies that it successfully parses a valid HTTP status line.
+     */
+    @Test
+    public void validStatusLine() {
+        // Arrange
+        final String line = "HTTP/1.1 200 Connection Established";
+
+        // Act
+        final HttpStatusLine actual = HttpStatusLine.create(line);
+
+        // Assert
+        Assert.assertNotNull(actual);
+
+        Assert.assertEquals(200, actual.getStatusCode());
+        Assert.assertEquals("1.1", actual.getProtocolVersion());
+        Assert.assertEquals("Connection Established", actual.getReason());
+    }
+
+
+    /**
+     * Verifies that status line length is invalid
+     */
+    @ParameterizedTest
+    @ValueSource(strings = {"HTTP/1.1 InvalidLength", "HTTP/1.1 Invalid Code", "HTTP/invalid protocol"})
+    public void invalidStatusLine(String line) {
+
+        // Act & Assert
+        Assert.assertThrows(IllegalArgumentException.class, () -> HttpStatusLine.create(line));
+    }
+
+
+}


=====================================
src/test/java/com/microsoft/azure/proton/transport/proxy/impl/ProxyHandlerImplTest.java
=====================================
@@ -3,16 +3,19 @@
 
 package com.microsoft.azure.proton.transport.proxy.impl;
 
-import com.microsoft.azure.proton.transport.proxy.ProxyHandler;
+import com.microsoft.azure.proton.transport.proxy.HttpStatusLine;
+import com.microsoft.azure.proton.transport.proxy.ProxyResponse;
 import org.junit.Assert;
 import org.junit.Test;
-import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.ValueSource;
 
 import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
 import java.util.HashMap;
 
+import static com.microsoft.azure.proton.transport.proxy.impl.StringUtils.NEW_LINE;
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
 public class ProxyHandlerImplTest {
     @Test
     public void testCreateProxyRequest() {
@@ -34,79 +37,63 @@ public class ProxyHandlerImplTest {
         Assert.assertEquals(expectedProxyRequest, actualProxyRequest);
     }
 
-    @ParameterizedTest
-    @ValueSource(ints = {200, 201, 202, 203, 204, 205, 206})
-    public void testValidateProxyResponseOnSuccess(int httpCode) {
-        final String validResponse = "HTTP/1.1 " + httpCode + "Connection Established\r\n"
-            + "FiddlerGateway: Direct\r\n"
-            + "StartTime: 13:08:21.574\r\n"
-            + "Connection: close";
-        final ByteBuffer buffer = ByteBuffer.allocate(1024);
-        buffer.put(validResponse.getBytes(StandardCharsets.UTF_8));
-        buffer.flip();
-
-        final ProxyHandlerImpl proxyHandler = new ProxyHandlerImpl();
-        ProxyHandler.ProxyResponseResult responseResult = proxyHandler.validateProxyResponse(buffer);
-
-        Assert.assertTrue(responseResult.getIsSuccess());
-        Assert.assertNull(responseResult.getError());
-
-        Assert.assertEquals(0, buffer.remaining());
-    }
-
     @Test
-    public void testValidateProxyResponseOnFailure() {
-        final String failResponse = String.join("\r\n", "HTTP/1.1 407 Proxy Auth Required",
-            "Connection: close",
-            "Proxy-Authenticate: Basic realm=\\\"FiddlerProxy (user: 1, pass: 1)\\",
-            "Content-Type: text/html",
-            "<html><body>[Fiddler] Proxy Authentication Required.<BR></body></html>\r\n");
-        final ByteBuffer buffer = ByteBuffer.allocate(1024);
-        buffer.put(failResponse.getBytes(StandardCharsets.UTF_8));
-        buffer.flip();
-
+    public void testValidateProxyResponseOnSuccess() {
+        // Arrange
+        final HttpStatusLine statusLine = HttpStatusLine.create("HTTP/1.1 200 Connection Established");
+        final ProxyResponse response = mock(ProxyResponse.class);
+        when(response.isMissingContent()).thenReturn(false);
+        when(response.getStatus()).thenReturn(statusLine);
         final ProxyHandlerImpl proxyHandler = new ProxyHandlerImpl();
-        ProxyHandler.ProxyResponseResult responseResult = proxyHandler.validateProxyResponse(buffer);
 
-        Assert.assertTrue(!responseResult.getIsSuccess());
-        Assert.assertEquals(failResponse, responseResult.getError());
+        // Act
+        final boolean result = proxyHandler.validateProxyResponse(response);
+
+        // Assert
+        Assert.assertTrue(result);
 
-        Assert.assertEquals(0, buffer.remaining());
     }
 
     @Test
-    public void testValidateProxyResponseOnInvalidResponse() {
-        final String invalidResponse = String.join("\r\n", "HTTP/1.1 abc Connection Established",
-            "HTTP/1.1 200 Connection Established",
-            "FiddlerGateway: Direct",
-            "StartTime: 13:08:21.574",
-            "Connection: close\r\n");
-        final ByteBuffer buffer = ByteBuffer.allocate(1024);
-        buffer.put(invalidResponse.getBytes(StandardCharsets.UTF_8));
-        buffer.flip();
+    public void testValidateProxyResponseOnFailure() {
+        // Arrange
+        final HttpStatusLine statusLine = HttpStatusLine.create("HTTP/1.1 407 Proxy Auth Required");
+        final String contents = "<html><body>[Fiddler] Proxy Authentication Required.<BR></body></html>";
+        final ByteBuffer encoded = UTF_8.encode(contents);
+        final ProxyResponse response = mock(ProxyResponse.class);
+        when(response.isMissingContent()).thenReturn(false);
+        when(response.getStatus()).thenReturn(statusLine);
+        when(response.getContents()).thenReturn(encoded);
+        when(response.getError()).thenReturn(contents);
 
         final ProxyHandlerImpl proxyHandler = new ProxyHandlerImpl();
-        ProxyHandler.ProxyResponseResult responseResult = proxyHandler.validateProxyResponse(buffer);
 
-        Assert.assertTrue(!responseResult.getIsSuccess());
-        Assert.assertEquals(invalidResponse, responseResult.getError());
+        // Act
+        final boolean result = proxyHandler.validateProxyResponse(response);
 
-        Assert.assertEquals(0, buffer.remaining());
+        // Assert
+        Assert.assertFalse(result);
     }
 
     @Test
     public void testValidateProxyResponseOnEmptyResponse() {
-        final String emptyResponse = "\r\n\r\n";
+        final String emptyResponse = NEW_LINE + NEW_LINE;
         final ByteBuffer buffer = ByteBuffer.allocate(1024);
-        buffer.put(emptyResponse.getBytes(StandardCharsets.UTF_8));
+        buffer.put(emptyResponse.getBytes(UTF_8));
         buffer.flip();
 
+        final ProxyResponse response = mock(ProxyResponse.class);
+        when(response.isMissingContent()).thenReturn(false);
+        when(response.getStatus()).thenReturn(null);
+        when(response.getContents()).thenReturn(buffer);
+        when(response.getError()).thenReturn(emptyResponse);
+
         final ProxyHandlerImpl proxyHandler = new ProxyHandlerImpl();
-        ProxyHandler.ProxyResponseResult responseResult = proxyHandler.validateProxyResponse(buffer);
 
-        Assert.assertTrue(!responseResult.getIsSuccess());
-        Assert.assertEquals(emptyResponse, responseResult.getError());
+        // Act
+        final boolean result = proxyHandler.validateProxyResponse(response);
 
-        Assert.assertEquals(0, buffer.remaining());
+        // Assert
+        Assert.assertFalse(result);
     }
 }


=====================================
src/test/java/com/microsoft/azure/proton/transport/proxy/impl/ProxyImplTest.java
=====================================
@@ -18,6 +18,9 @@ import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -36,10 +39,11 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Optional;
 
 import static com.microsoft.azure.proton.transport.proxy.impl.Constants.BASIC;
 import static com.microsoft.azure.proton.transport.proxy.impl.Constants.DIGEST;
-import static com.microsoft.azure.proton.transport.proxy.impl.Constants.PROXY_AUTHENTICATE_HEADER;
+import static com.microsoft.azure.proton.transport.proxy.impl.Constants.PROXY_AUTHENTICATE;
 import static com.microsoft.azure.proton.transport.proxy.impl.Constants.PROXY_AUTHORIZATION;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.argThat;
@@ -59,14 +63,20 @@ import static org.mockito.Mockito.when;
 public class ProxyImplTest {
     private static final InetSocketAddress PROXY_ADDRESS = InetSocketAddress.createUnresolved("my.host.name", 8888);
     private static final java.net.Proxy PROXY = new java.net.Proxy(java.net.Proxy.Type.HTTP, PROXY_ADDRESS);
-    private static final int BUFFER_SIZE = 8 * 1024;
+    private static final int BUFFER_SIZE = Constants.PROXY_HANDSHAKE_BUFFER_SIZE;
     private static final String USERNAME = "test-user";
     private static final String PASSWORD = "test-password!";
+    private static final String BASIC_HEADER = BASIC;
+    private static final String DIGEST_HEADER = String.format("%s realm=\"%s\", nonce=\"A randomly set nonce.\", qop=\"auth\", stale=false",
+                DIGEST, PROXY);
 
     private final Logger logger = LoggerFactory.getLogger(ProxyImplTest.class);
     private final Map<String, String> headers = new HashMap<>();
     private ProxySelector originalProxy;
 
+    @Captor
+    private ArgumentCaptor<Map<String, String>> additionalHeaders;
+
     private void initHeaders() {
         headers.put("header1", "value1");
         headers.put("header2", "value2");
@@ -75,6 +85,8 @@ public class ProxyImplTest {
 
     @Before
     public void setup() {
+        MockitoAnnotations.initMocks(this);
+
         originalProxy = ProxySelector.getDefault();
 
         ProxySelector.setDefault(new ProxySelector() {
@@ -107,6 +119,7 @@ public class ProxyImplTest {
 
     @After
     public void teardown() {
+        Mockito.framework().clearInlineMocks();
         ProxySelector.setDefault(originalProxy);
     }
 
@@ -326,13 +339,14 @@ public class ProxyImplTest {
         proxyImpl.configure(PROXY_ADDRESS.getHostName(), headers, mockHandler, mock(TransportImpl.class));
         TransportInput mockInput = mock(TransportInput.class);
         TransportWrapper transportWrapper = proxyImpl.wrap(mockInput, mock(TransportOutput.class));
-        ProxyHandler.ProxyResponseResult mockResponse = mock(ProxyHandler.ProxyResponseResult.class);
 
         when(mockHandler.createProxyRequest(any(), any())).thenReturn("proxy request");
 
-        when(mockResponse.getIsSuccess()).thenReturn(true);
-        when(mockResponse.getError()).thenReturn(null);
-        when(mockHandler.validateProxyResponse(any())).thenReturn(mockResponse);
+        when(mockHandler.validateProxyResponse(any())).thenReturn(true);
+
+        final String[] statusLine = new String[]{"HTTP/1.1", "200", "Connection Established"};
+        String response = getProxyResponse(statusLine, new ArrayList<>());
+        setInputBuffer(proxyImpl, response);
 
         Assert.assertEquals(Proxy.ProxyState.PN_PROXY_NOT_STARTED, proxyImpl.getProxyState());
         transportWrapper.pending();
@@ -351,13 +365,14 @@ public class ProxyImplTest {
         proxyImpl.configure(PROXY_ADDRESS.getHostName(), headers, mockHandler, mockTransport);
         TransportInput mockInput = mock(TransportInput.class);
         TransportWrapper transportWrapper = proxyImpl.wrap(mockInput, mock(TransportOutput.class));
-        ProxyHandler.ProxyResponseResult mockResponse = mock(ProxyHandler.ProxyResponseResult.class);
 
         when(mockHandler.createProxyRequest(any(), any())).thenReturn("proxy request");
 
-        when(mockResponse.getIsSuccess()).thenReturn(false);
-        when(mockResponse.getError()).thenReturn("proxy failure response");
-        when(mockHandler.validateProxyResponse(any())).thenReturn(mockResponse);
+        final String[] statusLine = new String[]{"HTTP/1.1", "500", "Internal Server Error"};
+        final List<String> authentications = new ArrayList<>();
+        String response = getProxyResponse(statusLine, authentications);
+        setInputBuffer(proxyImpl, response);
+        when(mockHandler.validateProxyResponse(any())).thenReturn(false);
 
         Assert.assertEquals(Proxy.ProxyState.PN_PROXY_NOT_STARTED, proxyImpl.getProxyState());
         transportWrapper.pending();
@@ -679,13 +694,15 @@ public class ProxyImplTest {
         proxyImpl.configure(PROXY_ADDRESS.getHostName(), headers, handler, underlyingTransport);
         TransportInput input = mock(TransportInput.class);
         TransportWrapper transportWrapper = proxyImpl.wrap(input, mock(TransportOutput.class));
-        ProxyHandler.ProxyResponseResult mockResponse = mock(ProxyHandler.ProxyResponseResult.class);
 
         when(handler.createProxyRequest(any(), any())).thenReturn("proxy request");
-        when(handler.validateProxyResponse(any())).thenReturn(mockResponse);
+        when(handler.validateProxyResponse(any())).thenReturn(false);
 
-        when(mockResponse.getIsSuccess()).thenReturn(false);
-        when(mockResponse.getError()).thenReturn(getProxyChallenge(true, false));
+        final String[] statusLine = new String[]{"HTTP/1.1", "407", "Proxy Authentication Required"};
+        final List<String> authentications = new ArrayList<>();
+        authentications.add(BASIC_HEADER);
+        final String response = getProxyResponse(statusLine, authentications);
+        setInputBuffer(proxyImpl, response);
 
         // Act and Assert
         Assert.assertEquals(Proxy.ProxyState.PN_PROXY_NOT_STARTED, proxyImpl.getProxyState());
@@ -699,9 +716,7 @@ public class ProxyImplTest {
     }
 
     /**
-     * Verifies that if we explicitly set ProxyAuthenticationType.NONE and the proxy asks for verification then we fail.
-     * This also covers the case where the proxy configuration suggests one auth method, but it is not supported in the
-     * proxy challenge.
+     * Verifies that if we configure proxy authentication type but the proxy does not ask for verification then we fail.
      */
     @Test
     public void authenticationNoAuthMismatchClosesTail() {
@@ -713,13 +728,14 @@ public class ProxyImplTest {
         proxyImpl.configure(PROXY_ADDRESS.getHostName(), headers, handler, underlyingTransport);
         TransportInput input = mock(TransportInput.class);
         TransportWrapper transportWrapper = proxyImpl.wrap(input, mock(TransportOutput.class));
-        ProxyHandler.ProxyResponseResult mockResponse = mock(ProxyHandler.ProxyResponseResult.class);
 
         when(handler.createProxyRequest(any(), any())).thenReturn("proxy request");
-        when(handler.validateProxyResponse(any())).thenReturn(mockResponse);
+        when(handler.validateProxyResponse(any())).thenReturn(true);
+
+        final String[] statusLine = new String[]{"HTTP/1.1", "200", "Connection Established"};
+        String response = getProxyResponse(statusLine, new ArrayList<>());
+        setInputBuffer(proxyImpl, response);
 
-        when(mockResponse.getIsSuccess()).thenReturn(true);
-        when(mockResponse.getError()).thenReturn(null);
 
         // Act and Assert
         Assert.assertEquals(Proxy.ProxyState.PN_PROXY_NOT_STARTED, proxyImpl.getProxyState());
@@ -737,7 +753,6 @@ public class ProxyImplTest {
      * configured auth method.
      */
     @Test
-    @SuppressWarnings({"unchecked", "rawtypes"})
     public void authenticationWithProxyConfiguration() {
         // Arrange
         ProxyConfiguration configuration = new ProxyConfiguration(ProxyAuthenticationType.BASIC, PROXY, USERNAME, PASSWORD);
@@ -747,13 +762,16 @@ public class ProxyImplTest {
         TransportOutput output = mock(TransportOutput.class);
         proxyImpl.configure(PROXY_ADDRESS.getHostName(), headers, handler, underlyingTransport);
         TransportWrapper transportWrapper = proxyImpl.wrap(mock(TransportInput.class), output);
-        ProxyHandler.ProxyResponseResult mockResponse = mock(ProxyHandler.ProxyResponseResult.class);
 
         when(handler.createProxyRequest(any(), any())).thenReturn("proxy request", "proxy request2");
-        when(handler.validateProxyResponse(any())).thenReturn(mockResponse);
+        when(handler.validateProxyResponse(any())).thenReturn(false, true);
 
-        when(mockResponse.getIsSuccess()).thenReturn(false, true);
-        when(mockResponse.getError()).thenReturn(getProxyChallenge(true, true), "Success.");
+        String[] statusLine = new String[]{"HTTP/1.1", "407", "Proxy Authentication Required"};
+        List<String> authentications = new ArrayList<>();
+        authentications.add(BASIC_HEADER);
+        authentications.add(DIGEST_HEADER);
+        String response = getProxyResponse(statusLine, authentications);
+        setInputBuffer(proxyImpl, response);
 
         // Act and Assert
         Assert.assertEquals(Proxy.ProxyState.PN_PROXY_NOT_STARTED, proxyImpl.getProxyState());
@@ -769,6 +787,10 @@ public class ProxyImplTest {
         clearOutputBuffer(proxyImpl);
         transportWrapper.pending();
 
+        statusLine = new String[]{"HTTP/1.1", "200", "Connection Established"};
+        response = getProxyResponse(statusLine, new ArrayList<>());
+        setInputBuffer(proxyImpl, response);
+
         Assert.assertTrue(proxyImpl.getIsHandshakeInProgress());
         Assert.assertEquals(Proxy.ProxyState.PN_PROXY_CHALLENGE_RESPONDED, proxyImpl.getProxyState());
         transportWrapper.process();
@@ -776,46 +798,42 @@ public class ProxyImplTest {
         Assert.assertFalse(proxyImpl.getIsHandshakeInProgress());
         Assert.assertEquals(Proxy.ProxyState.PN_PROXY_CONNECTED, proxyImpl.getProxyState());
 
-        ArgumentCaptor<Map> captor = ArgumentCaptor.forClass(Map.class);
         verify(handler, times(2)).createProxyRequest(
-                argThat(string -> string != null && string.equals(PROXY_ADDRESS.getHostName())), (Map<String, String>) captor.capture());
+                argThat(string -> string != null && string.equals(PROXY_ADDRESS.getHostName())), additionalHeaders.capture());
 
-        boolean foundHeader = false;
-        for (Map map : captor.getAllValues()) {
-            if (!map.containsKey(PROXY_AUTHORIZATION)) {
-                continue;
-            }
 
-            String value = (String) map.get(PROXY_AUTHORIZATION);
-            if (value.trim().startsWith(BASIC)) {
-                foundHeader = true;
-                break;
-            }
-        }
+        final Optional<Map<String, String>> matching = additionalHeaders.getAllValues()
+                .stream()
+                .filter(map -> map.containsKey(PROXY_AUTHORIZATION)
+                        && map.get(PROXY_AUTHORIZATION).trim().startsWith(BASIC))
+                .findFirst();
 
-        Assert.assertTrue(foundHeader);
+        Assert.assertTrue(matching.isPresent());
     }
 
     /**
-     * Verifies that when we use the system defaults and both are offered, then we will use the the DIGEST.
+     * Verifies that when we use the system defaults and both are offered, then we will use the DIGEST.
      */
     @Test
-    @SuppressWarnings({"unchecked", "rawtypes"})
     public void authenticationWithSystemDefaults() {
         // Arrange
         ProxyImpl proxyImpl = new ProxyImpl();
         ProxyHandler handler = mock(ProxyHandler.class);
         TransportImpl underlyingTransport = mock(TransportImpl.class);
         TransportOutput output = mock(TransportOutput.class);
+        TransportInput transportInput = mock(TransportInput.class);
         proxyImpl.configure(PROXY_ADDRESS.getHostName(), headers, handler, underlyingTransport);
-        TransportWrapper transportWrapper = proxyImpl.wrap(mock(TransportInput.class), output);
-        ProxyHandler.ProxyResponseResult mockResponse = mock(ProxyHandler.ProxyResponseResult.class);
+        TransportWrapper transportWrapper = proxyImpl.wrap(transportInput, output);
 
         when(handler.createProxyRequest(any(), any())).thenReturn("proxy request", "proxy request2");
-        when(handler.validateProxyResponse(any())).thenReturn(mockResponse);
+        when(handler.validateProxyResponse(any())).thenReturn(false, true);
 
-        when(mockResponse.getIsSuccess()).thenReturn(false, true);
-        when(mockResponse.getError()).thenReturn(getProxyChallenge(true, true), "Success.");
+        String[] statusLine = new String[]{"HTTP/1.1", "407", "Proxy Authentication Required"};
+        List<String> authentications = new ArrayList<>();
+        authentications.add(BASIC_HEADER);
+        authentications.add(DIGEST_HEADER);
+        String response = getProxyResponse(statusLine, authentications);
+        setInputBuffer(proxyImpl, response);
 
         // Act and Assert
         Assert.assertEquals(Proxy.ProxyState.PN_PROXY_NOT_STARTED, proxyImpl.getProxyState());
@@ -831,6 +849,10 @@ public class ProxyImplTest {
         clearOutputBuffer(proxyImpl);
         transportWrapper.pending();
 
+        statusLine = new String[]{"HTTP/1.1", "200", "Connection Established"};
+        response = getProxyResponse(statusLine, new ArrayList<>());
+        setInputBuffer(proxyImpl, response);
+
         Assert.assertTrue(proxyImpl.getIsHandshakeInProgress());
         Assert.assertEquals(Proxy.ProxyState.PN_PROXY_CHALLENGE_RESPONDED, proxyImpl.getProxyState());
         transportWrapper.process();
@@ -838,24 +860,87 @@ public class ProxyImplTest {
         Assert.assertFalse(proxyImpl.getIsHandshakeInProgress());
         Assert.assertEquals(Proxy.ProxyState.PN_PROXY_CONNECTED, proxyImpl.getProxyState());
 
-        ArgumentCaptor<Map> captor = ArgumentCaptor.forClass(Map.class);
         verify(handler, times(2)).createProxyRequest(
-                argThat(string -> string != null && string.equals(PROXY_ADDRESS.getHostName())), (Map<String, String>) captor.capture());
+                argThat(string -> string != null && string.equals(PROXY_ADDRESS.getHostName())), additionalHeaders.capture());
 
-        boolean foundHeader = false;
-        for (Map map : captor.getAllValues()) {
-            if (!map.containsKey(PROXY_AUTHORIZATION)) {
-                continue;
-            }
+        final Optional<Map<String, String>> matching = additionalHeaders.getAllValues()
+                .stream()
+                .filter(map -> map.containsKey(PROXY_AUTHORIZATION)
+                        && map.get(PROXY_AUTHORIZATION).trim().startsWith(DIGEST))
+                .findFirst();
 
-            String value = (String) map.get(PROXY_AUTHORIZATION);
-            if (value.trim().startsWith(DIGEST)) {
-                foundHeader = true;
-                break;
-            }
+        Assert.assertTrue(matching.isPresent());
+    }
+
+
+    /**
+     * Verifies that when proxy authentication response are transfer in multiple frames.
+     */
+    @Test
+    public void authenticationResponseWithMultipleFrames() {
+        // Arrange
+        ProxyImpl proxyImpl = new ProxyImpl();
+        ProxyHandler handler = mock(ProxyHandler.class);
+        TransportImpl underlyingTransport = mock(TransportImpl.class);
+        TransportOutput output = mock(TransportOutput.class);
+        TransportInput transportInput = mock(TransportInput.class);
+        proxyImpl.configure(PROXY_ADDRESS.getHostName(), headers, handler, underlyingTransport);
+        TransportWrapper transportWrapper = proxyImpl.wrap(transportInput, output);
+
+        when(handler.createProxyRequest(any(), any())).thenReturn("proxy request", "proxy request2");
+        when(handler.validateProxyResponse(any())).thenReturn(false, true);
+
+        String[] statusLine = new String[]{"HTTP/1.1", "407", "Proxy Authentication Required"};
+        List<String> authentications = new ArrayList<>();
+        authentications.add(BASIC_HEADER);
+        authentications.add(DIGEST_HEADER);
+        //Create a body which over buffer size so that it could be cut into multiple frames
+        //Here body is (buffer size * 2) bytes, and consider header size,
+        //it will create 3 frames for proxy response
+        String body = new String(new char[BUFFER_SIZE]).replace('\0', 't');
+        List<String> responses = getProxyResponseFrames(statusLine, authentications, body);
+
+        // Act and Assert
+        Assert.assertEquals(3, responses.size());
+
+        Assert.assertEquals(Proxy.ProxyState.PN_PROXY_NOT_STARTED, proxyImpl.getProxyState());
+        transportWrapper.pending();
+
+        for (String response : responses) {
+            setInputBuffer(proxyImpl, response);
+            Assert.assertTrue(proxyImpl.getIsHandshakeInProgress());
+            Assert.assertEquals(Proxy.ProxyState.PN_PROXY_CONNECTING, proxyImpl.getProxyState());
+            transportWrapper.process();
         }
 
-        Assert.assertTrue(foundHeader);
+
+        // At this point, we've gotten the correct challenger and set the header we want to respond with. We want to
+        // zero out the output buffer so that it'll write the headers when getting the request from the proxy handler.
+        Assert.assertEquals(Proxy.ProxyState.PN_PROXY_CHALLENGE, proxyImpl.getProxyState());
+        clearOutputBuffer(proxyImpl);
+        transportWrapper.pending();
+
+        statusLine = new String[]{"HTTP/1.1", "200", "Connection Established"};
+        String response = getProxyResponse(statusLine, new ArrayList<>());
+        setInputBuffer(proxyImpl, response);
+
+        Assert.assertTrue(proxyImpl.getIsHandshakeInProgress());
+        Assert.assertEquals(Proxy.ProxyState.PN_PROXY_CHALLENGE_RESPONDED, proxyImpl.getProxyState());
+        transportWrapper.process();
+
+        Assert.assertFalse(proxyImpl.getIsHandshakeInProgress());
+        Assert.assertEquals(Proxy.ProxyState.PN_PROXY_CONNECTED, proxyImpl.getProxyState());
+
+        verify(handler, times(2)).createProxyRequest(
+            argThat(string -> string != null && string.equals(PROXY_ADDRESS.getHostName())), additionalHeaders.capture());
+
+        final Optional<Map<String, String>> matching = additionalHeaders.getAllValues()
+            .stream()
+            .filter(map -> map.containsKey(PROXY_AUTHORIZATION)
+                && map.get(PROXY_AUTHORIZATION).trim().startsWith(DIGEST))
+            .findFirst();
+
+        Assert.assertTrue(matching.isPresent());
     }
 
     private static int getConnectRequestLength(String host, Map<String, String> headers) {
@@ -903,26 +988,53 @@ public class ProxyImplTest {
         }
     }
 
-    private static String getProxyChallenge(boolean includeBasic, boolean includeDigest) {
-        final String newLine = "\n";
-        StringBuilder builder = new StringBuilder("HTTP/1.1 407 Proxy Authentication Required");
-        builder.append(newLine);
-        builder.append("Date: Sun, 05 May 2019 07:28:00 GMT");
-        builder.append(newLine);
 
-        if (includeBasic) {
-            builder.append(String.join(" ", PROXY_AUTHENTICATE_HEADER, BASIC));
-            builder.append(newLine);
+    private void setInputBuffer(ProxyImpl proxyImpl, String value) {
+        final String inputBufferName = "inputBuffer";
+        try {
+            Field inputBuffer = ProxyImpl.class.getDeclaredField(inputBufferName);
+            inputBuffer.setAccessible(true);
+
+            ByteBuffer buffer = (ByteBuffer) inputBuffer.get(proxyImpl);
+            buffer.put(value.getBytes());
+        } catch (NoSuchFieldException e) {
+            if (logger.isErrorEnabled()) {
+                logger.error("Could not locate field '{}' on ProxyImpl class. Exception: {}", inputBufferName, e);
+            }
+        } catch (IllegalAccessException e) {
+            if (logger.isErrorEnabled()) {
+                logger.error("Could not fetch byte buffer from object.", e);
+            }
+        }
+    }
+
+    private String getProxyResponse(String[] statusLine, List<String> authentications) {
+        final Map<String, List<String>> headers = new HashMap<>();
+
+        if (!authentications.isEmpty()) {
+            headers.put(PROXY_AUTHENTICATE, authentications);
         }
 
-        if (includeDigest) {
-            builder.append(String.format("%s %s realm=\"%s\", nonce=\"A randomly set nonce.\", qop=\"auth\", stale=false",
-                    PROXY_AUTHENTICATE_HEADER, DIGEST, PROXY));
-            builder.append(newLine);
+        return TestUtils.createProxyResponse(statusLine, headers);
+    }
+
+    private List<String> getProxyResponseFrames(String[] statusLine, List<String> authentications, String body) {
+        final Map<String, List<String>> headers = new HashMap<>();
+
+        if (!authentications.isEmpty()) {
+            headers.put(PROXY_AUTHENTICATE, authentications);
         }
 
-        builder.append(newLine);
+        String response = TestUtils.createProxyResponse(statusLine, headers, body);
 
-        return builder.toString();
+        //Split response into frames base on buffer in characters size
+        int characters = BUFFER_SIZE / 2;
+        List<String> frames = new ArrayList<>();
+        for (int i = 0; i < response.length(); i += characters) {
+            frames.add(response.substring(i, Math.min(i + characters, response.length())));
+        }
+        return frames;
     }
+
+
 }


=====================================
src/test/java/com/microsoft/azure/proton/transport/proxy/impl/ProxyResponseImplTest.java
=====================================
@@ -0,0 +1,118 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.microsoft.azure.proton.transport.proxy.impl;
+
+import com.microsoft.azure.proton.transport.proxy.HttpStatusLine;
+import com.microsoft.azure.proton.transport.proxy.ProxyResponse;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.nio.ByteBuffer;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static com.microsoft.azure.proton.transport.proxy.impl.StringUtils.NEW_LINE;
+
+public class ProxyResponseImplTest {
+    /**
+     * Verifies that it successfully parses a valid HTTP response.
+     */
+    @Test
+    public void validResponse() {
+        // Arrange
+        final String[] statusLine = new String[]{"HTTP/1.1", "200", "Connection Established"};
+        final Map<String, List<String>> headers = new HashMap<>();
+        headers.put("FiddlerGateway", Collections.singletonList("Direct"));
+        headers.put("StartTime", Collections.singletonList("13:08:21.574"));
+        headers.put("Connection", Collections.singletonList("close"));
+
+        final String response = TestUtils.createProxyResponse(statusLine, headers);
+        final ByteBuffer contents = TestUtils.ENCODING.encode(response);
+
+        // Act
+        final ProxyResponse actual = ProxyResponseImpl.create(contents);
+
+        // Assert
+        Assert.assertNotNull(actual);
+
+        final HttpStatusLine status = actual.getStatus();
+        Assert.assertEquals("1.1", status.getProtocolVersion());
+        Assert.assertEquals(statusLine[2], status.getReason());
+        Assert.assertEquals(Integer.parseInt(statusLine[1]), status.getStatusCode());
+
+        Assert.assertFalse(actual.isMissingContent());
+
+        final Map<String, List<String>> actualHeaders = actual.getHeaders();
+        Assert.assertEquals(headers.size(), actualHeaders.size());
+
+        headers.forEach((key, value) -> {
+            final List<String> actualValue = actualHeaders.get(key);
+
+            Assert.assertTrue(actualHeaders.containsKey(key));
+            Assert.assertNotNull(actualValue);
+            Assert.assertEquals(1, actualValue.size());
+            Assert.assertEquals(value.get(0), actualValue.get(0));
+        });
+    }
+
+    /**
+     * Verifies that an exception is thrown when buffer is empty
+     */
+    @Test
+    public void invalidBuffer() {
+        // Arrange
+        final ByteBuffer contents = ByteBuffer.allocate(0);
+
+        // Act & Assert
+        Assert.assertThrows(IllegalArgumentException.class, () -> ProxyResponseImpl.create(contents));
+
+    }
+
+    /**
+     * Verifies that an exception is thrown when the header is invalid.
+     */
+    @Test
+    public void invalidHeader() {
+        // Arrange
+        final String[] statusLine = new String[]{"HTTP/1.1", "abc", "Connection Established"};
+        final Map<String, List<String>> headers = new HashMap<>();
+        headers.put("FiddlerGateway", Collections.singletonList("Direct"));
+        headers.put("StartTime", Collections.singletonList("13:08:21.574"));
+        headers.put("Connection", Collections.singletonList("close"));
+
+        final String response = TestUtils.createProxyResponse(statusLine, headers);
+        final ByteBuffer contents = TestUtils.ENCODING.encode(response);
+
+        // Act & Assert
+        IllegalArgumentException thrown = Assert.assertThrows(IllegalArgumentException.class,
+            () -> ProxyResponseImpl.create(contents));
+        Assert.assertEquals(NumberFormatException.class, thrown.getCause().getClass());
+
+    }
+
+    /**
+     * Verifies that we can parse an empty response.
+     */
+    @Test
+    public void emptyResponse() {
+        // Arrange
+        final String emptyResponse = NEW_LINE + NEW_LINE;
+        final ByteBuffer buffer = ByteBuffer.allocate(1024);
+        buffer.put(emptyResponse.getBytes(TestUtils.ENCODING));
+        buffer.flip();
+
+        // Act
+        final ProxyResponse response = ProxyResponseImpl.create(buffer);
+
+        // Assert
+        Assert.assertNotNull(response);
+        Assert.assertNull(response.getStatus());
+        Assert.assertFalse(response.isMissingContent());
+        Assert.assertNotNull(response.getHeaders());
+        Assert.assertEquals(0, response.getHeaders().size());
+        Assert.assertEquals(0, response.getContents().position());
+    }
+}


=====================================
src/test/java/com/microsoft/azure/proton/transport/proxy/impl/TestUtils.java
=====================================
@@ -0,0 +1,80 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+package com.microsoft.azure.proton.transport.proxy.impl;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import static com.microsoft.azure.proton.transport.proxy.impl.StringUtils.NEW_LINE;
+
+final class TestUtils {
+    /**
+     * Encoding for the HTTP proxy response.
+     */
+    static final Charset ENCODING = StandardCharsets.UTF_8;
+
+    private static final String CONTENT_TYPE = "Content-Type";
+    private static final String CONTENT_TYPE_TEXT = "text/plain";
+    private static final String CONTENT_LENGTH = "Content-Length";
+    private static final String HEADER_FORMAT = "%s: %s" + NEW_LINE;
+
+    private TestUtils() {
+    }
+
+    /**
+     * Creates a proxy HTTP response and returns it as a string.
+     *
+     * @param statusLine HTTP status line to create the proxy response with.
+     * @param headers A set of headers to add to the proxy response.
+     * @return A string representing the contents of the HTTP response.
+     */
+    static String createProxyResponse(String[] statusLine, Map<String, List<String>> headers) {
+        return createProxyResponse(statusLine, headers, null);
+    }
+
+    /**
+     * Creates a proxy HTTP response and returns it as a string. If there is content, {@link #CONTENT_LENGTH} and
+     * {@link #CONTENT_TYPE} headers are added to the {@code headers} parameter.
+     *
+     * @param statusLine HTTP status line to create the proxy response with.
+     * @param headers A set of headers to add to the proxy response.
+     * @param body Optional HTTP content body.
+     * @return A string representing the contents of the HTTP response.
+     */
+    static String createProxyResponse(String[] statusLine, Map<String, List<String>> headers, String body) {
+        final ByteBuffer encoded;
+        if (body != null) {
+            //Add empty line to end body
+            body += NEW_LINE;
+
+            encoded = ENCODING.encode(body);
+            final int size = encoded.remaining();
+
+            headers.put(CONTENT_TYPE, Collections.singletonList(CONTENT_TYPE_TEXT));
+            headers.put(CONTENT_LENGTH, Collections.singletonList(Integer.toString(size)));
+        }
+
+        final StringBuilder formattedHeaders = headers.entrySet()
+                .stream()
+                .collect(StringBuilder::new,
+                        (builder, entry) -> entry.getValue()
+                            .forEach(value -> builder.append(String.format(HEADER_FORMAT, entry.getKey(), value))),
+                        StringBuilder::append);
+
+        String response = String.join(NEW_LINE,
+                String.join(" ", statusLine),
+                formattedHeaders.toString(),
+                NEW_LINE); // The empty new line that ends the HTTP headers.
+
+        if (body != null) {
+            response += body;
+        }
+
+        return response;
+    }
+
+}



View it on GitLab: https://salsa.debian.org/java-team/qpid-proton-j-extensions/-/compare/4277ae136e5ccb537b0199fb83cf2afe0122c019...842738d275db8670660ab9a6865e7aae052406b2

-- 
View it on GitLab: https://salsa.debian.org/java-team/qpid-proton-j-extensions/-/compare/4277ae136e5ccb537b0199fb83cf2afe0122c019...842738d275db8670660ab9a6865e7aae052406b2
You're receiving this email because of your account on salsa.debian.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-java-commits/attachments/20241125/e74dcc92/attachment.htm>


More information about the pkg-java-commits mailing list