• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Jericho / StrongGrid / 1208

26 May 2024 01:24PM UTC coverage: 73.751% (+0.04%) from 73.71%
1208

push

appveyor

Jericho
Merge branch 'release/0.108.0'

2835 of 3844 relevant lines covered (73.75%)

81.25 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.14
/Source/StrongGrid/Utilities/SendGridMultipartFormDataParser.cs
1
using HttpMultipartParser;
2
using System;
3
using System.Collections.Generic;
4
using System.IO;
5
using System.Linq;
6
using System.Text;
7
using System.Text.Json;
8
using System.Threading;
9
using System.Threading.Tasks;
10
using UtfUnknown;
11

12
namespace StrongGrid.Utilities
13
{
14
        internal class SendGridMultipartFormDataParser : IMultipartFormDataParser
15
        {
16
                private readonly List<FilePart> _files;
17
                private readonly List<ParameterPart> _parameters;
18
                private readonly List<KeyValuePair<string, string>> _charsets;
19

20
                public IReadOnlyList<FilePart> Files => _files.AsReadOnly();
2✔
21

22
                public IReadOnlyList<ParameterPart> Parameters => _parameters.AsReadOnly();
×
23

24
                public IReadOnlyList<KeyValuePair<string, string>> Charsets => _charsets.AsReadOnly();
6✔
25

26
                private SendGridMultipartFormDataParser()
27
                {
28
                        _files = new List<FilePart>();
6✔
29
                        _parameters = new List<ParameterPart>();
6✔
30
                        _charsets = new List<KeyValuePair<string, string>>();
6✔
31
                }
6✔
32

33
                public static SendGridMultipartFormDataParser Parse(Stream stream)
34
                {
35
                        var parser = MultipartFormBinaryDataParser.Parse(stream, Encoding.UTF8);
1✔
36
                        return ConvertToSendGridParser(parser);
1✔
37
                }
38

39
                public static async Task<SendGridMultipartFormDataParser> ParseAsync(Stream stream, CancellationToken cancellationToken = default)
40
                {
41
                        var parser = await MultipartFormBinaryDataParser.ParseAsync(stream, Encoding.UTF8, cancellationToken: cancellationToken).ConfigureAwait(false);
42
                        return ConvertToSendGridParser(parser);
43
                }
44

45
                public string GetParameterValue(string name, string defaultValue)
46
                {
47
                        var parameter = _parameters.FirstOrDefault(p => p.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
90✔
48
                        return parameter?.Data ?? defaultValue;
90✔
49
                }
50

51
                private static SendGridMultipartFormDataParser ConvertToSendGridParser(MultipartFormBinaryDataParser parser)
52
                {
53
                        var charsetsParameter = parser.Parameters.FirstOrDefault(p => p.Name.Equals("charsets", StringComparison.OrdinalIgnoreCase));
6✔
54
                        var charsets = JsonDocument.Parse(charsetsParameter?.ToString(Encoding.UTF8) ?? "{}")
6✔
55
                                .RootElement.EnumerateObject()
6✔
56
                                .Select(p => new KeyValuePair<string, string>(p.Name, p.Value.GetString()))
6✔
57
                                .ToArray();
6✔
58

59
                        var encodings = charsets.ToDictionary(p => p.Key, p => GetEncodingFromName(p.Value));
6✔
60

61
                        var sendGridParser = new SendGridMultipartFormDataParser();
6✔
62
                        foreach (var parameter in parser.Parameters)
154✔
63
                        {
64
                                // Get the encoding specified by SendGrid for this parameter
65
                                encodings.TryGetValue(parameter.Name, out Encoding encoding);
71✔
66

67
                                // If necessary, determine the encoding by looking at the content
68
                                if (encoding == null && (parameter.Data?.Any() ?? false))
71✔
69
                                {
70
                                        // Concatenate the lines of data.
71
                                        // Normally you would append a NewLine after each line but it's not necessary for this particular instance.
72
                                        // Besides, we don't know (yet) what encoding to use to convert the NewLine characters into bytes.
73
                                        var parameterData = parameter.Data
46✔
74
                                                .SelectMany(d => d)
46✔
75
                                                .ToArray();
46✔
76

77
                                        // Try to detect the encoding based on the content
78
                                        var result = CharsetDetector.DetectFromBytes(parameterData);
46✔
79
                                        encoding = result?.Detected?.Encoding;
46✔
80
                                }
81

82
                                // When all else fails, fallback to UTF8
83
                                encoding ??= Encoding.UTF8;
71✔
84

85
                                sendGridParser._parameters.Add(new ParameterPart(parameter.Name, parameter.ToString(encoding)));
71✔
86
                        }
87

88
                        sendGridParser._files.AddRange(parser.Files);
6✔
89
                        sendGridParser._charsets.AddRange(charsets);
6✔
90

91
                        return sendGridParser;
6✔
92
                }
93

94
                private static Encoding GetEncodingFromName(string encodingName)
95
                {
96
                        try
97
                        {
98
                                return Encoding.GetEncoding(encodingName);
27✔
99
                        }
100
                        catch
1✔
101
                        {
102
                                // ArgumentException is thrown when an "unusual" code page was used to encode a section of the email
103
                                // For example: {"to":"UTF-8","subject":"UTF-8","from":"UTF-8","text":"iso-8859-10"}
104
                                // We can see that 'iso-8859-10' was used to encode the "Text" but this encoding is not supported in
105
                                // .net (neither dotnet full nor dotnet core). Therefore we fallback on UTF-8. This is obviously not
106
                                // perfect because UTF-8 may or may not be able to handle all the encoded characters, but it's better
107
                                // than simply erroring out.
108
                                // See https://github.com/Jericho/StrongGrid/issues/341 for discussion.
109

110
                                // April 2024: return a null value instead of defaulting to UTF8 when the encoding name is invalid.
111
                                // This will allow us to subsequently use the actual content to attempt to determine which encoding
112
                                // was used to encode it.
113
                                return null;
1✔
114
                        }
115
                }
27✔
116
        }
117
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc