committed by
GitHub
34 changed files with 684618 additions and 16 deletions
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -0,0 +1,30 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
|||
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
|||
<xs:element name="Weavers"> |
|||
<xs:complexType> |
|||
<xs:all> |
|||
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
|||
<xs:complexType> |
|||
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:all> |
|||
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
|||
<xs:annotation> |
|||
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:schema> |
|||
@ -0,0 +1,65 @@ |
|||
// Copyright 2023 The Ip2Region Authors. All rights reserved.
|
|||
// Use of this source code is governed by a Apache2.0-style
|
|||
// license that can be found in the LICENSE file.
|
|||
// @Author Alan <lzh.shap@gmail.com>
|
|||
// @Date 2023/07/25
|
|||
|
|||
using System; |
|||
using System.Buffers; |
|||
using System.IO; |
|||
|
|||
namespace IP2Region.Net.Internal.Abstractions; |
|||
|
|||
internal abstract class AbstractCacheStrategy |
|||
{ |
|||
protected const int HeaderInfoLength = 256; |
|||
protected const int VectorIndexRows = 256; |
|||
protected const int VectorIndexCols = 256; |
|||
protected const int VectorIndexSize = 8; |
|||
|
|||
protected readonly Stream XdbStream; |
|||
private const int BufferSize = 4096; |
|||
|
|||
internal int IoCount { get; private set; } |
|||
|
|||
protected AbstractCacheStrategy(Stream xdbStream) |
|||
{ |
|||
XdbStream = xdbStream; |
|||
} |
|||
|
|||
protected int GetVectorIndexStartPos(uint ip) |
|||
{ |
|||
var il0 = ip >> 24 & 0xFF; |
|||
var il1 = ip >> 16 & 0xFF; |
|||
var idx = il0 * VectorIndexCols * VectorIndexSize + il1 * VectorIndexSize; |
|||
return (int)idx; |
|||
} |
|||
|
|||
internal abstract ReadOnlyMemory<byte> GetVectorIndex(uint ip); |
|||
|
|||
internal virtual ReadOnlyMemory<byte> GetData(int offset, int length) |
|||
{ |
|||
byte[] buffer = ArrayPool<byte>.Shared.Rent(length); |
|||
int totalBytesRead = 0; |
|||
try |
|||
{ |
|||
XdbStream.Seek(offset, SeekOrigin.Begin); |
|||
|
|||
int bytesRead; |
|||
do |
|||
{ |
|||
int bytesToRead = Math.Min(BufferSize, length - totalBytesRead); |
|||
bytesRead = XdbStream.Read(buffer, totalBytesRead, bytesToRead); |
|||
totalBytesRead += bytesRead; |
|||
|
|||
IoCount++; |
|||
} while (bytesRead > 0 && totalBytesRead < length); |
|||
} |
|||
finally |
|||
{ |
|||
ArrayPool<byte>.Shared.Return(buffer); |
|||
} |
|||
|
|||
return new ReadOnlyMemory<byte>(buffer, 0, totalBytesRead); |
|||
} |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
// Copyright 2023 The Ip2Region Authors. All rights reserved.
|
|||
// Use of this source code is governed by a Apache2.0-style
|
|||
// license that can be found in the LICENSE file.
|
|||
// @Author Alan <lzh.shap@gmail.com>
|
|||
// @Date 2023/07/25
|
|||
|
|||
using IP2Region.Net.Internal.Abstractions; |
|||
using IP2Region.Net.XDB; |
|||
using System; |
|||
using System.IO; |
|||
|
|||
namespace IP2Region.Net.Internal; |
|||
|
|||
internal class CacheStrategyFactory |
|||
{ |
|||
private readonly Stream _xdbStream; |
|||
|
|||
public CacheStrategyFactory(Stream xdbStream) |
|||
{ |
|||
_xdbStream = xdbStream; |
|||
} |
|||
|
|||
public AbstractCacheStrategy CreateCacheStrategy(CachePolicy cachePolicy) |
|||
{ |
|||
return cachePolicy switch |
|||
{ |
|||
CachePolicy.Content => new ContentCacheStrategy(_xdbStream), |
|||
CachePolicy.VectorIndex => new VectorIndexCacheStrategy(_xdbStream), |
|||
CachePolicy.File => new FileCacheStrategy(_xdbStream), |
|||
_ => throw new ArgumentException(nameof(cachePolicy)) |
|||
}; |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
// Copyright 2023 The Ip2Region Authors. All rights reserved.
|
|||
// Use of this source code is governed by a Apache2.0-style
|
|||
// license that can be found in the LICENSE file.
|
|||
// @Author Alan <lzh.shap@gmail.com>
|
|||
// @Date 2023/07/25
|
|||
|
|||
using IP2Region.Net.Internal.Abstractions; |
|||
using System; |
|||
using System.IO; |
|||
|
|||
namespace IP2Region.Net.Internal; |
|||
|
|||
internal class ContentCacheStrategy : AbstractCacheStrategy |
|||
{ |
|||
private readonly ReadOnlyMemory<byte> _cacheData; |
|||
|
|||
public ContentCacheStrategy(Stream xdbStream) : base(xdbStream) |
|||
{ |
|||
_cacheData = base.GetData(0, (int)XdbStream.Length); |
|||
XdbStream.Close(); |
|||
XdbStream.Dispose(); |
|||
} |
|||
|
|||
internal override ReadOnlyMemory<byte> GetVectorIndex(uint ip) |
|||
{ |
|||
int idx = GetVectorIndexStartPos(ip); |
|||
return _cacheData.Slice(HeaderInfoLength + idx, VectorIndexSize); |
|||
} |
|||
|
|||
internal override ReadOnlyMemory<byte> GetData(int offset, int length) |
|||
{ |
|||
return _cacheData.Slice(offset, length); |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
// Copyright 2023 The Ip2Region Authors. All rights reserved.
|
|||
// Use of this source code is governed by a Apache2.0-style
|
|||
// license that can be found in the LICENSE file.
|
|||
// @Author Alan <lzh.shap@gmail.com>
|
|||
// @Date 2023/07/25
|
|||
|
|||
using IP2Region.Net.Internal.Abstractions; |
|||
using System; |
|||
using System.IO; |
|||
|
|||
namespace IP2Region.Net.Internal; |
|||
|
|||
internal class FileCacheStrategy : AbstractCacheStrategy |
|||
{ |
|||
public FileCacheStrategy(Stream xdbStream) : base(xdbStream) |
|||
{ |
|||
} |
|||
|
|||
internal override ReadOnlyMemory<byte> GetVectorIndex(uint ip) |
|||
{ |
|||
var idx = GetVectorIndexStartPos(ip); |
|||
return GetData(HeaderInfoLength + idx, VectorIndexSize); |
|||
} |
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
// Copyright 2023 The Ip2Region Authors. All rights reserved.
|
|||
// Use of this source code is governed by a Apache2.0-style
|
|||
// license that can be found in the LICENSE file.
|
|||
// @Author Alan <lzh.shap@gmail.com>
|
|||
// @Date 2023/07/25
|
|||
|
|||
using IP2Region.Net.Internal.Abstractions; |
|||
using System; |
|||
using System.IO; |
|||
|
|||
namespace IP2Region.Net.Internal; |
|||
|
|||
internal class VectorIndexCacheStrategy : AbstractCacheStrategy |
|||
{ |
|||
private readonly ReadOnlyMemory<byte> _vectorIndex; |
|||
|
|||
public VectorIndexCacheStrategy(Stream xdbStream) : base(xdbStream) |
|||
{ |
|||
var vectorLength = VectorIndexRows * VectorIndexCols * VectorIndexSize; |
|||
_vectorIndex = base.GetData(HeaderInfoLength, vectorLength); |
|||
} |
|||
|
|||
internal override ReadOnlyMemory<byte> GetVectorIndex(uint ip) |
|||
{ |
|||
var idx = GetVectorIndexStartPos(ip); |
|||
return _vectorIndex.Slice(idx, VectorIndexSize); |
|||
} |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\..\configureawait.props" /> |
|||
<Import Project="..\..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFrameworks>netstandard2.0;netstandard2.1;net8.0</TargetFrameworks> |
|||
<AssemblyName>LINGYUN.Abp.IP2Region</AssemblyName> |
|||
<PackageId>LINGYUN.Abp.IP2Region</PackageId> |
|||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> |
|||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> |
|||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> |
|||
<Nullable>enable</Nullable> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<None Remove="LINGYUN\Abp\IP2Region\Resources\ip2region.xdb" /> |
|||
<EmbeddedResource Include="LINGYUN\Abp\IP2Region\Resources\ip2region.xdb" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.VirtualFileSystem" /> |
|||
<PackageReference Include="IP2Region.Net" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,28 @@ |
|||
using IP2Region.Net.Abstractions; |
|||
using IP2Region.Net.XDB; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.VirtualFileSystem; |
|||
|
|||
namespace LINGYUN.Abp.IP2Region; |
|||
|
|||
[DependsOn(typeof(AbpVirtualFileSystemModule))] |
|||
public class AbpIP2RegionModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
Configure<AbpVirtualFileSystemOptions>(options => |
|||
{ |
|||
options.FileSets.AddEmbedded<AbpIP2RegionModule>(); |
|||
}); |
|||
|
|||
context.Services.AddSingleton<ISearcher, AbpSearcher>((serviceProvider) => |
|||
{ |
|||
var virtualFileProvider = serviceProvider.GetRequiredService<IVirtualFileProvider>(); |
|||
var xdbFile = virtualFileProvider.GetFileInfo("/LINGYUN/Abp/IP2Region/Resources/ip2region.xdb"); |
|||
var searcher = new AbpSearcher(CachePolicy.File, xdbFile.CreateReadStream()); |
|||
|
|||
return searcher; |
|||
}); |
|||
} |
|||
} |
|||
@ -0,0 +1,80 @@ |
|||
using IP2Region.Net.Abstractions; |
|||
using IP2Region.Net.Internal; |
|||
using IP2Region.Net.Internal.Abstractions; |
|||
using IP2Region.Net.XDB; |
|||
using System.IO; |
|||
using System.Net; |
|||
using System.Runtime.InteropServices; |
|||
using System.Text; |
|||
|
|||
namespace LINGYUN.Abp.IP2Region; |
|||
public class AbpSearcher : ISearcher |
|||
{ |
|||
const int SegmentIndexSize = 14; |
|||
|
|||
private readonly AbstractCacheStrategy _cacheStrategy; |
|||
public int IoCount => _cacheStrategy.IoCount; |
|||
|
|||
public AbpSearcher(CachePolicy cachePolicy, Stream xdbStream) |
|||
{ |
|||
var factory = new CacheStrategyFactory(xdbStream); |
|||
_cacheStrategy = factory.CreateCacheStrategy(cachePolicy); |
|||
} |
|||
|
|||
public string? Search(string ipStr) |
|||
{ |
|||
var ip = Util.IpAddressToUInt32(ipStr); |
|||
return Search(ip); |
|||
} |
|||
|
|||
public string? Search(IPAddress ipAddress) |
|||
{ |
|||
var ip = Util.IpAddressToUInt32(ipAddress); |
|||
return Search(ip); |
|||
} |
|||
|
|||
public string? Search(uint ip) |
|||
{ |
|||
var index = _cacheStrategy.GetVectorIndex(ip); |
|||
uint sPtr = MemoryMarshal.Read<uint>(index.Span); |
|||
uint ePtr = MemoryMarshal.Read<uint>(index.Span.Slice(4)); |
|||
|
|||
var dataLen = 0; |
|||
uint dataPtr = 0; |
|||
uint l = 0; |
|||
uint h = (ePtr - sPtr) / SegmentIndexSize; |
|||
|
|||
while (l <= h) |
|||
{ |
|||
var mid = Util.GetMidIp(l, h); |
|||
var pos = sPtr + mid * SegmentIndexSize; |
|||
|
|||
var buffer = _cacheStrategy.GetData((int)pos, SegmentIndexSize); |
|||
uint sip = MemoryMarshal.Read<uint>(buffer.Span); |
|||
uint eip = MemoryMarshal.Read<uint>(buffer.Span.Slice(4)); |
|||
|
|||
if (ip < sip) |
|||
{ |
|||
h = mid - 1; |
|||
} |
|||
else if (ip > eip) |
|||
{ |
|||
l = mid + 1; |
|||
} |
|||
else |
|||
{ |
|||
dataLen = MemoryMarshal.Read<ushort>(buffer.Span.Slice(8)); |
|||
dataPtr = MemoryMarshal.Read<uint>(buffer.Span.Slice(10)); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
if (dataLen == 0) |
|||
{ |
|||
return default; |
|||
} |
|||
|
|||
var regionBuff = _cacheStrategy.GetData((int)dataPtr, dataLen); |
|||
return Encoding.UTF8.GetString(regionBuff.Span.ToArray()); |
|||
} |
|||
} |
|||
Binary file not shown.
@ -0,0 +1,62 @@ |
|||
# IP2Region.Net |
|||
|
|||
.NET client library for IP2Region |
|||
|
|||
## Installation |
|||
|
|||
Install the package with [NuGet](https://www.nuget.org/packages/IP2Region.Net) |
|||
|
|||
```bash |
|||
Install-Package IP2Region.Net |
|||
``` |
|||
|
|||
## Usage |
|||
|
|||
```csharp |
|||
using IP2Region.Net.Abstractions; |
|||
using IP2Region.Net.XDB; |
|||
|
|||
ISearcher searcher = new Searcher(CachePolicy , "your xdb file path"); |
|||
``` |
|||
### Cache Policy Description |
|||
| Cache Policy | Description | Thread Safe | |
|||
|-------------------------|------------------------------------------------------------------------------------------------------------|-------------| |
|||
| CachePolicy.Content | Cache the entire `xdb` data. | Yes | |
|||
| CachePolicy.VectorIndex | Cache `vecotorIndex` to speed up queries and reduce system io pressure by reducing one fixed IO operation. | Yes | |
|||
| CachePolicy.File | Completely file-based queries | Yes | |
|||
### XDB File Description |
|||
Generate using [maker](https://github.com/lionsoul2014/ip2region/tree/master/maker/csharp), or [download](https://github.com/lionsoul2014/ip2region/blob/master/data/ip2region.xdb) pre-generated xdb files |
|||
|
|||
## ASP.NET Core Usage |
|||
|
|||
```csharp |
|||
services.AddSingleton<ISearcher>(new Searcher(CachePolicy , "your xdb file path")); |
|||
``` |
|||
|
|||
## Performance |
|||
|
|||
``` ini |
|||
|
|||
BenchmarkDotNet=v0.13.2, OS=macOS 13.4.1 (c) (22F770820d) [Darwin 22.5.0] |
|||
Apple M1, 1 CPU, 8 logical and 8 physical cores |
|||
.NET SDK=7.0.306 |
|||
[Host] : .NET 6.0.20 (6.0.2023.32017), Arm64 RyuJIT AdvSIMD |
|||
DefaultJob : .NET 6.0.20 (6.0.2023.32017), Arm64 RyuJIT AdvSIMD |
|||
|
|||
|
|||
``` |
|||
| Method | Mean | Error | StdDev | |
|||
|-------------------------|-----------:|---------:|---------:| |
|||
| CachePolicy_Content | 155.7 ns | 0.46 ns | 0.39 ns | |
|||
| CachePolicy_File | 2,186.8 ns | 34.27 ns | 32.06 ns | |
|||
| CachePolicy_VectorIndex | 1,570.3 ns | 27.53 ns | 22.99 ns | |
|||
|
|||
|
|||
|
|||
## Contributing |
|||
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. |
|||
|
|||
Please make sure to update tests as appropriate. |
|||
|
|||
## License |
|||
[Apache License 2.0](https://github.com/lionsoul2014/ip2region/blob/master/LICENSE.md) |
|||
@ -0,0 +1,48 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace LINGYUN.Abp.Identity.Session.AspNetCore; |
|||
public class AbpIdentitySessionAspNetCoreOptions |
|||
{ |
|||
/// <summary>
|
|||
/// 是否解析IP地理信息
|
|||
/// </summary>
|
|||
public bool IsParseIpLocation { get; set; } |
|||
/// <summary>
|
|||
/// 不做处理的省份
|
|||
/// </summary>
|
|||
public IList<string> IgnoreProvinces { get; set; } |
|||
/// <summary>
|
|||
/// 地理信息解析
|
|||
/// </summary>
|
|||
public Func<LocationInfo, string> LocationParser { get; set; } |
|||
|
|||
public AbpIdentitySessionAspNetCoreOptions() |
|||
{ |
|||
IsParseIpLocation = false; |
|||
IgnoreProvinces = new List<string> |
|||
{ |
|||
// 中国直辖市不显示省份数据
|
|||
"北京", "上海", "天津", "重庆" |
|||
}; |
|||
LocationParser = (locationInfo) => |
|||
{ |
|||
var location = ""; |
|||
|
|||
if (!locationInfo.Province.IsNullOrWhiteSpace() && |
|||
!IgnoreProvinces.Contains(locationInfo.Province)) |
|||
{ |
|||
location += locationInfo.Province + " "; |
|||
} |
|||
if (!locationInfo.City.IsNullOrWhiteSpace()) |
|||
{ |
|||
location += locationInfo.City; |
|||
} |
|||
if (location.IsNullOrWhiteSpace() && !locationInfo.Country.IsNullOrWhiteSpace()) |
|||
{ |
|||
location = locationInfo.Country; |
|||
} |
|||
return location; |
|||
}; |
|||
} |
|||
} |
|||
@ -0,0 +1,36 @@ |
|||
using IP2Region.Net.Abstractions; |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace LINGYUN.Abp.Identity.Session.AspNetCore; |
|||
public class IP2RegionLocationInfoProvider : IIpLocationInfoProvider, ISingletonDependency |
|||
{ |
|||
protected static readonly LocationInfo _nullCache = null; |
|||
|
|||
protected ISearcher Searcher { get; } |
|||
public IP2RegionLocationInfoProvider(ISearcher searcher) |
|||
{ |
|||
Searcher = searcher; |
|||
} |
|||
|
|||
public virtual Task<LocationInfo> GetLocationInfoAsync(string ipAddress) |
|||
{ |
|||
var region = Searcher.Search(ipAddress); |
|||
if (region.IsNullOrWhiteSpace()) |
|||
{ |
|||
return Task.FromResult(_nullCache); |
|||
} |
|||
|
|||
var regions = region.Split('|'); |
|||
// | 0 | 1 | 2 |3| 4 | 5 | 6 |
|
|||
// 39.128.0.0|39.128.31.255|中国|0|云南省|昆明市|移动
|
|||
var locationInfo = new LocationInfo |
|||
{ |
|||
Country = regions.Length >= 3 && !string.Equals(regions[2], "0") ? regions[2] : null, |
|||
Province = regions.Length >= 5 && !string.Equals(regions[4], "0") ? regions[4] : null, |
|||
City = regions.Length >= 6 && !string.Equals(regions[5], "0") ? regions[5] : null, |
|||
}; |
|||
return Task.FromResult(locationInfo); |
|||
} |
|||
} |
|||
@ -1,7 +1,9 @@ |
|||
namespace LINGYUN.Abp.Identity.Session; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.Identity.Session; |
|||
public interface IDeviceInfoProvider |
|||
{ |
|||
DeviceInfo DeviceInfo { get; } |
|||
Task<DeviceInfo> GetDeviceInfoAsync(); |
|||
|
|||
string ClientIpAddress { get; } |
|||
} |
|||
|
|||
@ -0,0 +1,14 @@ |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.Identity.Session; |
|||
public interface IIpLocationInfoProvider |
|||
{ |
|||
/// <summary>
|
|||
/// 通过ip地址获取地理信息
|
|||
/// </summary>
|
|||
/// <param name="ipAddress">ip地址</param>
|
|||
/// <returns>
|
|||
/// 如果解析成功返回地理信息,否则返回null
|
|||
/// </returns>
|
|||
Task<LocationInfo> GetLocationInfoAsync(string ipAddress); |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
namespace LINGYUN.Abp.Identity.Session; |
|||
public class LocationInfo |
|||
{ |
|||
public string Country { get; set; } |
|||
public string Province { get; set; } |
|||
public string City { get; set; } |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace LINGYUN.Abp.Identity.Session; |
|||
|
|||
[Dependency(ServiceLifetime.Singleton, TryRegister = true)] |
|||
public class NoneIpLocationInfoProvider : IIpLocationInfoProvider |
|||
{ |
|||
protected static readonly LocationInfo _nullCache = null; |
|||
public Task<LocationInfo> GetLocationInfoAsync(string ipAddress) |
|||
{ |
|||
return Task.FromResult(_nullCache); |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net8.0</TargetFramework> |
|||
<RootNamespace /> |
|||
<IsPackable>false</IsPackable> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Microsoft.NET.Test.Sdk" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\framework\common\LINGYUN.Abp.IP2Region\LINGYUN.Abp.IP2Region.csproj" /> |
|||
<ProjectReference Include="..\LINGYUN.Abp.TestBase\LINGYUN.Abp.TestsBase.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<None Update="LINGYUN\Abp\IP2Region\ip.merge.txt"> |
|||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> |
|||
</None> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,6 @@ |
|||
using LINGYUN.Abp.Tests; |
|||
|
|||
namespace LINGYUN.Abp.IP2Region; |
|||
public class AbpIP2RegionTestBase : AbpTestsBase<AbpIP2RegionTestModule> |
|||
{ |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
using LINGYUN.Abp.Tests; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.IP2Region; |
|||
|
|||
[DependsOn( |
|||
typeof(AbpIP2RegionModule), |
|||
typeof(AbpTestsBaseModule))] |
|||
public class AbpIP2RegionTestModule : AbpModule |
|||
{ |
|||
} |
|||
@ -0,0 +1,93 @@ |
|||
using IP2Region.Net.XDB; |
|||
using System; |
|||
using System.IO; |
|||
using System.Linq; |
|||
using Volo.Abp.VirtualFileSystem; |
|||
using Xunit; |
|||
|
|||
namespace LINGYUN.Abp.IP2Region; |
|||
public class SearcherTest : AbpIP2RegionTestBase |
|||
{ |
|||
private readonly Stream _xdbStream; |
|||
|
|||
public SearcherTest() |
|||
{ |
|||
var virtualFileProvider = GetRequiredService<IVirtualFileProvider>(); |
|||
_xdbStream = virtualFileProvider.GetFileInfo("/LINGYUN/Abp/IP2Region/Resources/ip2region.xdb").CreateReadStream(); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData("114.114.114.114")] |
|||
[InlineData("119.29.29.29")] |
|||
[InlineData("223.5.5.5")] |
|||
[InlineData("180.76.76.76")] |
|||
[InlineData("8.8.8.8")] |
|||
public void TestSearchCacheContent(string ip) |
|||
{ |
|||
var contentSearcher = new AbpSearcher(CachePolicy.Content, _xdbStream); |
|||
var region = contentSearcher.Search(ip); |
|||
Console.WriteLine(region); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData("114.114.114.114")] |
|||
[InlineData("119.29.29.29")] |
|||
[InlineData("223.5.5.5")] |
|||
[InlineData("180.76.76.76")] |
|||
[InlineData("8.8.8.8")] |
|||
public void TestSearchCacheVector(string ip) |
|||
{ |
|||
var vectorSearcher = new AbpSearcher(CachePolicy.VectorIndex, _xdbStream); |
|||
var region = vectorSearcher.Search(ip); |
|||
Console.WriteLine(region); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData("114.114.114.114")] |
|||
[InlineData("119.29.29.29")] |
|||
[InlineData("223.5.5.5")] |
|||
[InlineData("180.76.76.76")] |
|||
[InlineData("8.8.8.8")] |
|||
public void TestSearchCacheFile(string ip) |
|||
{ |
|||
var fileSearcher = new AbpSearcher(CachePolicy.File, _xdbStream); |
|||
var region = fileSearcher.Search(ip); |
|||
Console.WriteLine(region); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(CachePolicy.Content)] |
|||
[InlineData(CachePolicy.VectorIndex)] |
|||
[InlineData(CachePolicy.File)] |
|||
public void TestBenchSearch(CachePolicy cachePolicy) |
|||
{ |
|||
var searcher = new AbpSearcher(cachePolicy, _xdbStream); |
|||
var srcPath = Path.Combine(AppContext.BaseDirectory, "ip.merge.txt"); |
|||
|
|||
foreach (var line in File.ReadLines(srcPath)) |
|||
{ |
|||
var ps = line.Trim().Split("|", 3); |
|||
|
|||
if (ps.Length != 3) |
|||
{ |
|||
throw new ArgumentException($"invalid ip segment line {line}", nameof(line)); |
|||
} |
|||
|
|||
var sip = Util.IpAddressToUInt32(ps[0]); |
|||
var eip = Util.IpAddressToUInt32(ps[1]); |
|||
var mip = Util.GetMidIp(sip, eip); |
|||
|
|||
uint[] temp = { sip, Util.GetMidIp(sip, mip), mip, Util.GetMidIp(mip, eip), eip }; |
|||
|
|||
foreach (var ip in temp) |
|||
{ |
|||
var region = searcher.Search(ip); |
|||
|
|||
if (region != ps[2]) |
|||
{ |
|||
throw new Exception($"failed search {ip} with ({region}!={ps[2]})"); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
File diff suppressed because it is too large
Loading…
Reference in new issue