forked from Zelenov/SharpIpp
-
-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathSharpIppClient.cs
198 lines (169 loc) · 6.14 KB
/
SharpIppClient.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using SharpIpp.Exceptions;
using SharpIpp.Mapping;
using SharpIpp.Mapping.Profiles;
using SharpIpp.Models;
using SharpIpp.Protocol;
using SharpIpp.Protocol.Extensions;
using SharpIpp.Protocol.Models;
namespace SharpIpp;
public partial class SharpIppClient : ISharpIppClient
{
private static readonly Lazy<IMapper> MapperSingleton;
private readonly bool _disposeHttpClient;
private readonly HttpClient _httpClient;
private readonly IIppProtocol _ippProtocol;
static SharpIppClient()
{
MapperSingleton = new Lazy<IMapper>(MapperFactory);
}
public SharpIppClient() : this(new HttpClient(), new IppProtocol(), true)
{
}
public SharpIppClient(HttpClient httpClient) : this(httpClient, new IppProtocol(), false )
{
}
public SharpIppClient(HttpClient httpClient, IIppProtocol ippProtocol) : this( httpClient, ippProtocol, false )
{
}
internal SharpIppClient(HttpClient httpClient, IIppProtocol ippProtocol, bool disposeHttpClient)
{
_httpClient = httpClient;
_ippProtocol = ippProtocol;
_disposeHttpClient = disposeHttpClient;
}
private IMapper Mapper => MapperSingleton.Value;
/// <summary>
/// Status codes of <see cref="HttpResponseMessage" /> that are not successful,
/// but response still contains valid ipp-data in the body that can be parsed for better error description
/// Seems like they are printer specific
/// </summary>
private static readonly HttpStatusCode[] _plausibleHttpStatusCodes = [
HttpStatusCode.Continue,
HttpStatusCode.Unauthorized,
HttpStatusCode.Forbidden,
HttpStatusCode.UpgradeRequired,
];
/// <inheritdoc />
public async Task<IIppResponseMessage> SendAsync(
Uri printer,
IIppRequestMessage ippRequest,
CancellationToken cancellationToken = default)
{
var httpRequest = GetHttpRequestMessage( printer );
HttpResponseMessage? response;
using (Stream stream = new MemoryStream())
{
await _ippProtocol.WriteIppRequestAsync(ippRequest, stream, cancellationToken).ConfigureAwait(false);
stream.Seek(0, SeekOrigin.Begin);
httpRequest.Content = new StreamContent(stream) { Headers = { { "Content-Type", "application/ipp" } } };
response = await _httpClient.SendAsync(httpRequest, cancellationToken).ConfigureAwait(false);
}
Exception? httpException = null;
try
{
response.EnsureSuccessStatusCode();
}
catch (HttpRequestException ex)
{
if (!_plausibleHttpStatusCodes.Contains(response.StatusCode))
{
throw;
}
httpException = ex;
}
IIppResponseMessage? ippResponse;
try
{
using var responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
ippResponse = await _ippProtocol.ReadIppResponseAsync(responseStream, cancellationToken).ConfigureAwait(false);
if (!ippResponse.IsSuccessfulStatusCode())
throw new IppResponseException($"Printer returned error code", ippResponse);
}
catch
{
if (httpException == null)
{
throw;
}
throw httpException;
}
if (httpException == null)
{
return ippResponse;
}
throw new IppResponseException(httpException.Message, httpException, ippResponse);
}
public void Dispose()
{
if (_disposeHttpClient)
{
_httpClient.Dispose();
}
}
protected async Task<TOut> SendAsync<TIn, TOut>(
TIn data,
Func<TIn, IIppRequestMessage> constructRequestFunc,
Func<IIppResponseMessage, TOut> constructResponseFunc,
CancellationToken cancellationToken)
where TIn : IIppRequest
where TOut : IIppResponseMessage
{
var ippRequest = constructRequestFunc(data);
if (data.OperationAttributes == null || data.OperationAttributes.PrinterUri == null)
throw new Exception("PrinterUri is not set");
var ippResponse = await SendAsync(data.OperationAttributes.PrinterUri, ippRequest, cancellationToken).ConfigureAwait(false);
var res = constructResponseFunc(ippResponse);
return res;
}
private IppRequestMessage ConstructIppRequest<T>(T request)
{
if (request == null)
{
throw new ArgumentException($"{nameof(request)}");
}
var ippRequest = Mapper.Map<T, IppRequestMessage>(request);
return ippRequest;
}
public virtual T Construct<T>(IIppResponseMessage ippResponse) where T : IIppResponseMessage
{
try
{
var r = Mapper.Map<T>(ippResponse);
return r;
}
catch (Exception ex)
{
throw new IppResponseException("Ipp attributes mapping exception", ex, ippResponse);
}
}
private static HttpRequestMessage GetHttpRequestMessage( Uri printer )
{
var isSecured = printer.Scheme.Equals( "https", StringComparison.OrdinalIgnoreCase )
|| printer.Scheme.Equals( "ipps", StringComparison.OrdinalIgnoreCase );
var defaultPort = printer.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase)
? 443
: printer.Scheme.Equals("http", StringComparison.OrdinalIgnoreCase)
? 80
: 631;
var uriBuilder = new UriBuilder(isSecured ? "https" : "http", printer.Host, printer.Port == -1 ? defaultPort : printer.Port, printer.AbsolutePath)
{
Query = printer.Query
};
return new HttpRequestMessage( HttpMethod.Post, uriBuilder.Uri );
}
private static IMapper MapperFactory()
{
var mapper = new SimpleMapper();
var assembly = Assembly.GetAssembly(typeof(TypesProfile));
mapper.FillFromAssembly(assembly!);
return mapper;
}
}