Skip to content

Commit 78860c7

Browse files
committed
Refactored .NET HttpCommandExecutor for future HTTP client expansion
1 parent 782a3b1 commit 78860c7

File tree

2 files changed

+110
-75
lines changed

2 files changed

+110
-75
lines changed

dotnet/src/webdriver/Remote/CommandInfo.cs

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// <copyright file="CommandInfo.cs" company="WebDriver Committers">
1+
// <copyright file="CommandInfo.cs" company="WebDriver Committers">
22
// Licensed to the Software Freedom Conservancy (SFC) under one
33
// or more contributor license agreements. See the NOTICE file
44
// distributed with this work for additional information
@@ -75,14 +75,16 @@ public string Method
7575
}
7676

7777
/// <summary>
78-
/// Creates a web request for your command
78+
/// Creates the full URI associated with this command, substituting command
79+
/// parameters for tokens in the URI template.
7980
/// </summary>
80-
/// <param name="baseUri">Uri that will have the command run against</param>
81-
/// <param name="commandToExecute">Command to execute</param>
82-
/// <returns>A web request of what has been run</returns>
83-
public HttpWebRequest CreateWebRequest(Uri baseUri, Command commandToExecute)
81+
/// <param name="baseUri">The base URI associated with the command.</param>
82+
/// <param name="commandToExecute">The command containing the parameters with which
83+
/// to substitute the tokens in the template.</param>
84+
/// <returns>The full URI for the command, with the parameters of the command
85+
/// substituted for the tokens in the template.</returns>
86+
public Uri CreateCommandUri(Uri baseUri, Command commandToExecute)
8487
{
85-
HttpWebRequest request = null;
8688
string[] urlParts = this.resourcePath.Split(new string[] { "/" }, StringSplitOptions.RemoveEmptyEntries);
8789
for (int i = 0; i < urlParts.Length; i++)
8890
{
@@ -97,17 +99,12 @@ public HttpWebRequest CreateWebRequest(Uri baseUri, Command commandToExecute)
9799
string relativeUrlString = string.Join("/", urlParts);
98100
Uri relativeUri = new Uri(relativeUrlString, UriKind.Relative);
99101
bool uriCreateSucceeded = Uri.TryCreate(baseUri, relativeUri, out fullUri);
100-
if (uriCreateSucceeded)
101-
{
102-
request = HttpWebRequest.Create(fullUri) as HttpWebRequest;
103-
request.Method = this.method;
104-
}
105-
else
102+
if (!uriCreateSucceeded)
106103
{
107104
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Unable to create URI from base {0} and relative path {1}", baseUri == null ? string.Empty : baseUri.ToString(), relativeUrlString));
108105
}
109106

110-
return request;
107+
return fullUri;
111108
}
112109

113110
private static string GetCommandPropertyValue(string propertyName, Command commandToExecute)

dotnet/src/webdriver/Remote/HttpCommandExecutor.cs

Lines changed: 99 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// <copyright file="HttpCommandExecutor.cs" company="WebDriver Committers">
1+
// <copyright file="HttpCommandExecutor.cs" company="WebDriver Committers">
22
// Licensed to the Software Freedom Conservancy (SFC) under one
33
// or more contributor license agreements. See the NOTICE file
44
// distributed with this work for additional information
@@ -17,6 +17,7 @@
1717
// </copyright>
1818

1919
using System;
20+
using System.Diagnostics;
2021
using System.Globalization;
2122
using System.IO;
2223
using System.Net;
@@ -30,8 +31,10 @@ namespace OpenQA.Selenium.Remote
3031
internal class HttpCommandExecutor : ICommandExecutor
3132
{
3233
private const string JsonMimeType = "application/json";
33-
private const string ContentTypeHeader = JsonMimeType + ";charset=utf-8";
34-
private const string RequestAcceptHeader = JsonMimeType + ", image/png";
34+
private const string PngMimeType = "image/png";
35+
private const string CharsetType = "charset=utf-8";
36+
private const string ContentTypeHeader = JsonMimeType + ";" + CharsetType;
37+
private const string RequestAcceptHeader = JsonMimeType + ", " + PngMimeType;
3538
private Uri remoteServerUri;
3639
private TimeSpan serverResponseTimeout;
3740
private bool enableKeepAlive;
@@ -71,6 +74,7 @@ public HttpCommandExecutor(Uri addressOfRemoteServer, TimeSpan timeout, bool ena
7174
this.enableKeepAlive = enableKeepAlive;
7275

7376
ServicePointManager.Expect100Continue = false;
77+
ServicePointManager.DefaultConnectionLimit = 2000;
7478

7579
// In the .NET Framework, HttpWebRequest responses with an error code are limited
7680
// to 64k by default. Since the remote server error responses include a screenshot,
@@ -80,12 +84,12 @@ public HttpCommandExecutor(Uri addressOfRemoteServer, TimeSpan timeout, bool ena
8084
{
8185
HttpWebRequest.DefaultMaximumErrorResponseLength = -1;
8286
}
83-
}
87+
}
8488

85-
/// <summary>
86-
/// Gets the repository of objects containin information about commands.
87-
/// </summary>
88-
public CommandInfoRepository CommandInfoRepository
89+
/// <summary>
90+
/// Gets the repository of objects containin information about commands.
91+
/// </summary>
92+
public CommandInfoRepository CommandInfoRepository
8993
{
9094
get { return this.commandInfoRepository; }
9195
}
@@ -103,22 +107,11 @@ public virtual Response Execute(Command commandToExecute)
103107
}
104108

105109
CommandInfo info = this.commandInfoRepository.GetCommandInfo(commandToExecute.Name);
106-
HttpWebRequest request = info.CreateWebRequest(this.remoteServerUri, commandToExecute);
107-
request.Timeout = (int)this.serverResponseTimeout.TotalMilliseconds;
108-
request.Accept = RequestAcceptHeader;
109-
request.KeepAlive = this.enableKeepAlive;
110-
request.ServicePoint.ConnectionLimit = 2000;
111-
if (request.Method == CommandInfo.PostCommand)
112-
{
113-
string payload = commandToExecute.ParametersAsJsonString;
114-
byte[] data = Encoding.UTF8.GetBytes(payload);
115-
request.ContentType = ContentTypeHeader;
116-
Stream requestStream = request.GetRequestStream();
117-
requestStream.Write(data, 0, data.Length);
118-
requestStream.Close();
119-
}
110+
HttpRequestInfo requestInfo = new HttpRequestInfo(this.remoteServerUri, commandToExecute, info);
120111

121-
Response toReturn = this.CreateResponse(request);
112+
HttpResponseInfo responseInfo = this.MakeHttpRequest(requestInfo);
113+
114+
Response toReturn = this.CreateResponse(responseInfo);
122115
if (commandToExecute.Name == DriverCommand.NewSession && toReturn.IsSpecificationCompliant)
123116
{
124117
// If we are creating a new session, sniff the response to determine
@@ -153,9 +146,25 @@ private static string GetTextOfWebResponse(HttpWebResponse webResponse)
153146
return responseString;
154147
}
155148

156-
private Response CreateResponse(WebRequest request)
149+
private HttpResponseInfo MakeHttpRequest(HttpRequestInfo requestInfo)
157150
{
158-
Response commandResponse = new Response();
151+
HttpWebRequest request = HttpWebRequest.Create(requestInfo.FullUri) as HttpWebRequest;
152+
request.Method = requestInfo.HttpMethod;
153+
request.Timeout = (int)this.serverResponseTimeout.TotalMilliseconds;
154+
request.Accept = RequestAcceptHeader;
155+
request.KeepAlive = this.enableKeepAlive;
156+
request.ServicePoint.ConnectionLimit = 2000;
157+
if (request.Method == CommandInfo.PostCommand)
158+
{
159+
string payload = requestInfo.RequestBody;
160+
byte[] data = Encoding.UTF8.GetBytes(payload);
161+
request.ContentType = ContentTypeHeader;
162+
Stream requestStream = request.GetRequestStream();
163+
requestStream.Write(data, 0, data.Length);
164+
requestStream.Close();
165+
}
166+
167+
HttpResponseInfo responseInfo = new HttpResponseInfo();
159168
HttpWebResponse webResponse = null;
160169
try
161170
{
@@ -182,58 +191,87 @@ private Response CreateResponse(WebRequest request)
182191
}
183192
else
184193
{
185-
string responseString = GetTextOfWebResponse(webResponse);
186-
if (webResponse.ContentType != null && webResponse.ContentType.StartsWith(JsonMimeType, StringComparison.OrdinalIgnoreCase))
187-
{
188-
commandResponse = Response.FromJson(responseString);
189-
}
190-
else
194+
responseInfo.Body = GetTextOfWebResponse(webResponse);
195+
responseInfo.ContentType = webResponse.ContentType;
196+
responseInfo.StatusCode = webResponse.StatusCode;
197+
}
198+
199+
return responseInfo;
200+
}
201+
202+
private Response CreateResponse(HttpResponseInfo stuff)
203+
{
204+
Response commandResponse = new Response();
205+
string responseString = stuff.Body;
206+
if (stuff.ContentType != null && stuff.ContentType.StartsWith(JsonMimeType, StringComparison.OrdinalIgnoreCase))
207+
{
208+
commandResponse = Response.FromJson(responseString);
209+
}
210+
else
211+
{
212+
commandResponse.Value = responseString;
213+
}
214+
215+
if (this.commandInfoRepository.SpecificationLevel < 1 && (stuff.StatusCode < HttpStatusCode.OK || stuff.StatusCode >= HttpStatusCode.BadRequest))
216+
{
217+
// 4xx represents an unknown command or a bad request.
218+
if (stuff.StatusCode >= HttpStatusCode.BadRequest && stuff.StatusCode < HttpStatusCode.InternalServerError)
191219
{
192-
commandResponse.Value = responseString;
220+
commandResponse.Status = WebDriverResult.UnhandledError;
193221
}
194-
195-
if (this.commandInfoRepository.SpecificationLevel < 1 && (webResponse.StatusCode < HttpStatusCode.OK || webResponse.StatusCode >= HttpStatusCode.BadRequest))
222+
else if (stuff.StatusCode >= HttpStatusCode.InternalServerError)
196223
{
197-
// 4xx represents an unknown command or a bad request.
198-
if (webResponse.StatusCode >= HttpStatusCode.BadRequest && webResponse.StatusCode < HttpStatusCode.InternalServerError)
224+
// 5xx represents an internal server error. The response status should already be set, but
225+
// if not, set it to a general error code. The exception is a 501 (NotImplemented) response,
226+
// which indicates that the command hasn't been implemented on the server.
227+
if (stuff.StatusCode == HttpStatusCode.NotImplemented)
199228
{
200-
commandResponse.Status = WebDriverResult.UnhandledError;
229+
commandResponse.Status = WebDriverResult.UnknownCommand;
201230
}
202-
else if (webResponse.StatusCode >= HttpStatusCode.InternalServerError)
231+
else
203232
{
204-
// 5xx represents an internal server error. The response status should already be set, but
205-
// if not, set it to a general error code. The exception is a 501 (NotImplemented) response,
206-
// which indicates that the command hasn't been implemented on the server.
207-
if (webResponse.StatusCode == HttpStatusCode.NotImplemented)
208-
{
209-
commandResponse.Status = WebDriverResult.UnknownCommand;
210-
}
211-
else
233+
if (commandResponse.Status == WebDriverResult.Success)
212234
{
213-
if (commandResponse.Status == WebDriverResult.Success)
214-
{
215-
commandResponse.Status = WebDriverResult.UnhandledError;
216-
}
235+
commandResponse.Status = WebDriverResult.UnhandledError;
217236
}
218237
}
219-
else
220-
{
221-
commandResponse.Status = WebDriverResult.UnhandledError;
222-
}
223238
}
224-
225-
if (commandResponse.Value is string)
239+
else
226240
{
227-
// First, collapse all \r\n pairs to \n, then replace all \n with
228-
// System.Environment.NewLine. This ensures the consistency of
229-
// the values.
230-
commandResponse.Value = ((string)commandResponse.Value).Replace("\r\n", "\n").Replace("\n", System.Environment.NewLine);
241+
commandResponse.Status = WebDriverResult.UnhandledError;
231242
}
243+
}
232244

233-
webResponse.Close();
245+
if (commandResponse.Value is string)
246+
{
247+
// First, collapse all \r\n pairs to \n, then replace all \n with
248+
// System.Environment.NewLine. This ensures the consistency of
249+
// the values.
250+
commandResponse.Value = ((string)commandResponse.Value).Replace("\r\n", "\n").Replace("\n", System.Environment.NewLine);
234251
}
235252

236253
return commandResponse;
237254
}
255+
256+
private class HttpRequestInfo
257+
{
258+
public HttpRequestInfo(Uri serverUri, Command commandToExecute, CommandInfo commandInfo)
259+
{
260+
this.FullUri = commandInfo.CreateCommandUri(serverUri, commandToExecute);
261+
this.HttpMethod = commandInfo.Method;
262+
this.RequestBody = commandToExecute.ParametersAsJsonString;
263+
}
264+
265+
public Uri FullUri { get; set; }
266+
public string HttpMethod { get; set; }
267+
public string RequestBody { get; set; }
268+
}
269+
270+
private class HttpResponseInfo
271+
{
272+
public HttpStatusCode StatusCode { get; set; }
273+
public string Body { get; set; }
274+
public string ContentType { get; set; }
275+
}
238276
}
239277
}

0 commit comments

Comments
 (0)