কীভাবে সি # তে একটি সহজ প্রক্সি তৈরি করবেন?


143

আমি কয়েক সপ্তাহ আগে প্রিভোক্সি ডাউনলোড করেছি এবং মজাদার জন্য এটির একটি সাধারণ সংস্করণ কীভাবে করা যায় তা জানতে আগ্রহী ছিলাম।

আমি বুঝতে পারি যে প্রক্সিটিতে অনুরোধ প্রেরণের জন্য আমার ব্রাউজারটি (ক্লায়েন্ট) কনফিগার করতে হবে। প্রক্সি ওয়েবে অনুরোধটি প্রেরণ করে (এটি একটি HTTP প্রক্সি বলে দিন)। প্রক্সিটি উত্তরটি গ্রহণ করবে ... তবে কীভাবে প্রক্সিটি ব্রাউজারে (ক্লায়েন্ট) অনুরোধটি প্রেরণ করতে পারে?

আমি সি # এবং এইচটিপি প্রক্সি জন্য ওয়েবে অনুসন্ধান করেছি কিন্তু এমন কিছু পাই নি যা আমাকে বুঝতে দেয় যে এটি কীভাবে দৃশ্যের পিছনে কাজ করে। (আমি বিশ্বাস করি যে আমি একটি বিপরীত প্রক্সি চাই না তবে আমি নিশ্চিত নই)।

আপনার কারওর কি কিছু ব্যাখ্যা বা কিছু তথ্য আছে যা আমাকে এই ছোট প্রকল্পটি চালিয়ে যেতে দেবে?

হালনাগাদ

এটি আমি বুঝতে পারি (নীচে গ্রাফিক দেখুন)।

পদক্ষেপ 1 আমি প্রক্সি শোনার বন্দরে 127.0.0.1 এ সমস্ত অনুরোধ প্রেরণের জন্য ক্লায়েন্টকে (ব্রাউজার) কনফিগার করছি। এইভাবে, অনুরোধটি সরাসরি ইন্টারনেটে প্রেরণ করা হবে না তবে প্রক্সি দ্বারা প্রক্রিয়া করা হবে।

পদক্ষেপ 2 প্রক্সিটি একটি নতুন সংযোগ দেখতে পাবে, এইচটিটিপি শিরোনামটি পড়ুন এবং তার অনুরোধটি সম্পাদন করতে হবে দেখুন। তিনি অনুরোধটি কার্যকর করেন।

স্টিপি 3 অনুরোধ থেকে প্রক্সি একটি উত্তর পেয়েছে। এখন তাকে অবশ্যই ওয়েব থেকে উত্তর ক্লায়েন্টের কাছে পাঠাতে হবে তবে কীভাবে ???

বিকল্প পাঠ

দরকারী লিঙ্ক

মেন্টালিস প্রক্সি : আমি এই প্রকল্পটি খুঁজে পেয়েছি যা প্রক্সি (তবে আরও বেশি যা আমি চাই)। আমি উত্সটি যাচাই করতে পারি তবে ধারণাটি আরও বুঝতে আমি কিছুটা বেসিক চাইছিলাম।

এএসপি প্রক্সি : আমি এখানেও কিছু তথ্য পেতে সক্ষম হতে পারি।

অনুরোধ প্রতিফলক : এটি একটি সহজ উদাহরণ।

এখানে একটি সাধারণ এইচটিটিপি প্রক্সি সহ একটি গিট হাব সংগ্রহস্থল


২০১৫ সালে আমার স্ক্রিনশট নেই Sorry দুঃখিত।
প্যাট্রিক দেশজার্ডিনস

প্রকৃতপক্ষে, দেখা যাচ্ছে যে সংরক্ষণাগার.অর্গ এটি আছে । আপনাকে বিরক্ত করার জন্য দুঃখিত.
ইলমারি করোনেন

উত্তর:


35

HttpListenerআগত অনুরোধগুলি শোনার জন্য ক্লাসের সাথে একটি তৈরি করতে এবং অনুরোধগুলি HttpWebRequestরিলে করার জন্য ক্লাসটি তৈরি করতে পারেন ।


আমি কোথায় রিলে করব? আমি কীভাবে জানতে পারি যে তথ্য কোথায় পাঠাতে হবে? ব্রাউজারটি প্রেরণে 127.0.0.1:9999 কে বলে 9999 নম্বরে ক্লায়েন্টটি অনুরোধটি পেয়ে ওয়েবে প্রেরণ করে। একটি উত্তর পান ... ক্লায়েন্ট কি করে? কোন ঠিকানায় পাঠাবেন?
প্যাট্রিক দেশজার্ডিনস

2
আপনি যদি HTTPListener ব্যবহার করে থাকেন তবে আপনি কেবল এইচটিপিপ্লাইটার.গেটকন্টেক্সট () এ প্রতিক্রিয়া লিখবেন Resp প্রতিক্রিয়া.আউটপুট স্ট্রিম। ঠিকানাটির যত্ন নেওয়ার দরকার নেই।
অরেগনহোস্ট

আকর্ষণীয়, আমি এইভাবে চেক করব।
প্যাট্রিক দেশজার্ডিন্স

8
আমি এটির জন্য এইচটিপিপ্লাইটার ব্যবহার করব না। পরিবর্তে, একটি ASP.NET অ্যাপ্লিকেশন তৈরি করুন এবং আইআইএসের মধ্যে এটি হোস্ট করুন। HTTPListener ব্যবহার করার সময়, আপনি আইআইএস দ্বারা সরবরাহিত প্রক্রিয়া মডেলটি ছেড়ে দিচ্ছেন। এর অর্থ আপনি প্রক্রিয়া পরিচালনা (স্টার্টআপ, ব্যর্থতা সনাক্তকরণ, পুনর্ব্যবহারযোগ্য), থ্রেড পুল পরিচালনা ইত্যাদির মতো জিনিসগুলি হারাবেন।
মৌরিসিও শেফার

2
এটি হ'ল, যদি আপনি এটি অনেক ক্লায়েন্ট কম্পিউটারের জন্য ব্যবহার করতে চান ... খেলনা প্রক্সিটির জন্য এইচটিপিপ্লাইস্টার ঠিক আছে ...
মরিসিও শেফার

93

আমি এইচটিপিপ্লাইস্টার বা এর মতো কিছু ব্যবহার করবো না, আপনি এতগুলি সমস্যার মুখোমুখি হবেন।

সর্বাধিক গুরুত্বপূর্ণ এটি সমর্থন করতে একটি বিশাল ব্যথা হবে:

  • প্রক্সি কিপ-অ্যালাইভস
  • এসএসএল কাজ করবে না (সঠিক উপায়ে আপনি পপআপগুলি পাবেন)
  • । নেট লাইব্রেরিগুলি কঠোরভাবে আরএফসিগুলিকে অনুসরণ করে যার ফলে কিছু অনুরোধ ব্যর্থ হয় (যদিও আই, এফএফ এবং বিশ্বের যে কোনও ব্রাউজার কাজ করবে।)

আপনার যা করা দরকার তা হ'ল:

  • টিসিপি বন্দর শুনুন
  • ব্রাউজার অনুরোধ পার্স করুন
  • টিসিপি স্তরে হোস্টটির সাথে হোস্টটি সংযোগ করুন ract
  • আপনি কাস্টম শিরোনাম ইত্যাদি যোগ না করতে চাইলে সব কিছু সামনে এবং সামনে ফরোয়ার্ড করুন

আমি বিভিন্ন প্রয়োজনীয়তার সাথে .NET এ 2 টি পৃথক এইচটিটিপি প্রক্সি লিখেছি এবং আমি আপনাকে বলতে পারি যে এটি করার সর্বোত্তম উপায়।

মেন্টালিস এটি করছে, তবে তাদের কোডটি "ডেলিগেট স্প্যাগেটি", গোটো এর চেয়েও খারাপ :)


1
টিসিপি সংযোগগুলির জন্য আপনি কোন শ্রেণি (এস) ব্যবহার করেছেন?
ক্যামেরন

8
@ ক্যামেরন টিসিপিলিস্টনার এবং এসএসলস্ট্রিম।
ড। খারাপ

2
আপনি দয়া করে কেন এইচটিটিপিএস কাজ করবেন না তার সমাপ্তি ভাগ করে নিতে পারেন?
রেস্টুটা

10
টিএসপি স্তরে এটি স্পর্শ না করে আপনার সংযোগটি ফরোয়ার্ড করা উচিত এসএসএলের জন্য @ রিস্তুটা কাজ করার জন্য এবং এইচটিটিপিলেস্টনার এটি করতে পারে না। এসএসএল কীভাবে কাজ করে তা আপনি পড়তে পারেন এবং লক্ষ্য সার্ভারে প্রমাণীকরণের জন্য এটির প্রয়োজন দেখতে পাবেন। সুতরাং ক্লায়েন্টের সাথে সংযোগ করার চেষ্টা করবে google.com কিন্তু আসলে সংযুক্ত করবে আপনার Httplistener যা নয় google.com এবং যা নিশ্চিতভাবে ঘটবে মেলেনি ভুল পান হবে এবং আপনার শ্রোতা স্বাক্ষরিত যা নিশ্চিতভাবে ঘটবে ব্যবহার করা হবে না যেহেতু, ভুল যা নিশ্চিতভাবে ঘটবে ইত্যাদি আপনি ঠিক করতে পারবো পাবেন এটি কম্পিউটারে সিএ ইনস্টল করে ক্লায়েন্ট যদিও ব্যবহার করবে। এটি একটি সুন্দর নোংরা সমাধান।
ড। মন্দ

1
@ ডিআরআইভিল: +++ আশ্চর্যজনক টিপসের জন্য ধন্যবাদ, তবে আমি কীভাবে ক্লায়েন্টকে (ব্রাউজারে) ডেটা ফেরত পাঠাতে পারি তা জানতে আগ্রহী, আমি বলতে পারি যে আমি কীভাবে ক্লায়েন্টকে প্রতিক্রিয়া পাঠাব?
সাবের

26

TcpListener এবং TcpClient ব্যবহার করে আমি সম্প্রতি # # নেট এ হালকা ওজনের প্রক্সি লিখেছি

https://github.com/titanium007/Titanium-Web-Proxy

এটি সুরক্ষিত HTTP- র সঠিক উপায়ে সমর্থন করে, ক্লায়েন্ট মেশিনকে প্রক্সি দ্বারা ব্যবহৃত রুট শংসাপত্রের উপর বিশ্বাস করা দরকার। ওয়েবসকেট রিলে সমর্থন করে। HTTP 1.1 এর সমস্ত বৈশিষ্ট্য পাইপলাইনিং ব্যতীত সমর্থিত। পাইপলাইনিং বেশিরভাগ আধুনিক ব্রাউজার দ্বারা ব্যবহৃত হয় না। উইন্ডোজ প্রমাণীকরণ (প্লেইন, ডাইজেস্ট) সমর্থন করে।

আপনি প্রকল্পটি উল্লেখ করে আপনার অ্যাপ্লিকেশনটি হুক করতে পারেন এবং তারপরে সমস্ত ট্র্যাফিক দেখতে এবং সংশোধন করতে পারেন। (অনুরোধ এবং প্রতিক্রিয়া)

যতদূর পারফরম্যান্স, আমি এটি আমার মেশিনে পরীক্ষা করেছি এবং কোনও লক্ষণীয় দেরি না করে কাজ করেছি works


এবং এখনও 2020 এ বজায় রেখেছেন, ভাগ করে নেওয়ার জন্য ধন্যবাদ :)
মার্ক অ্যাডামসন

19

প্রক্সি নিম্নলিখিত পদ্ধতিতে কাজ করতে পারে।

পদক্ষেপ 1, প্রক্সিহোস্ট: প্রক্সিপোর্ট ব্যবহার করতে ক্লায়েন্টকে কনফিগার করুন।

প্রক্সি একটি টিসিপি সার্ভার যা প্রক্সিহোস্ট: প্রক্সিপোর্টে শুনছে। ব্রাউজার প্রক্সিটির সাথে সংযোগ খুলবে এবং এইচটিটিপি অনুরোধ প্রেরণ করে। প্রক্সি এই অনুরোধটিকে বিশ্লেষণ করে এবং "হোস্ট" শিরোনাম সনাক্ত করার চেষ্টা করে। এই শিরোনামটি প্রক্সিটি কোথায় সংযোগ খুলবেন তা বলবে।

পদক্ষেপ 2: প্রক্সি "হোস্ট" শিরোনামে উল্লিখিত ঠিকানার সাথে সংযোগ খুলবে। তারপরে এটি সেই দূরবর্তী সার্ভারে HTTP অনুরোধ প্রেরণ করে। প্রতিক্রিয়া পড়ে।

পদক্ষেপ 3: প্রত্যন্ত HTTP সার্ভার থেকে প্রতিক্রিয়াটি পড়ার পরে, প্রক্সিটি ব্রাউজারের সাথে পূর্বের খোলা টিসিপি সংযোগের মাধ্যমে প্রতিক্রিয়া পাঠায়।

পরিকল্পিতভাবে এটি দেখতে এটি দেখতে পাবেন:

Browser                            Proxy                     HTTP server
  Open TCP connection  
  Send HTTP request  ----------->                       
                                 Read HTTP header
                                 detect Host header
                                 Send request to HTTP ----------->
                                 Server
                                                      <-----------
                                 Read response and send
                   <-----------  it back to the browser
Render content

14

আপনি যদি কেবল ট্র্যাফিককেই বাধা দিতে চান, তবে আপনি একটি প্রক্সি তৈরি করতে ফিডলার কোরটি ব্যবহার করতে পারেন ...

http://fiddler.wikidot.com/fiddlercore

এটি কী করে তা দেখতে প্রথমে ফিডলারের সাথে ইউআই দিয়ে চালান, এটি একটি প্রক্সি যা আপনাকে http / https ট্র্যাফিক ডিবাগ করতে দেয়। এটি সি # তে লেখা এবং এর একটি কোর রয়েছে যা আপনি নিজের অ্যাপ্লিকেশনগুলিতে তৈরি করতে পারেন।

মনে রাখবেন ফিডলারকোর বাণিজ্যিক অ্যাপ্লিকেশনগুলির জন্য বিনামূল্যে নয়।


6

ওউইন এবং ওয়েবএপিআই দিয়ে জিনিসগুলি সত্যিই সহজ হয়ে গেছে। একটি সি # প্রক্সি সার্ভারের জন্য অনুসন্ধানে, আমি এই পোস্টটি জুড়ে এসেছি http://blog.kloud.com.au/2013/11/24/do-it-yourself-web-api-proxy/ । এই আমি যাচ্ছি রাস্তা হবে।


5

ডঃ অশুভের সাথে সম্মত হন আপনি যদি HTTPListener ব্যবহার করেন তবে আপনার অনেক সমস্যা হবে, আপনাকে অনুরোধগুলি বিশ্লেষণ করতে হবে এবং শিরোনামে নিযুক্ত থাকবেন এবং ...

  1. ব্রাউজারের অনুরোধগুলি শুনতে টিসিপি শ্রোতাদের ব্যবহার করুন
  2. অনুরোধের প্রথম প্রথম লাইনটি বিশ্লেষণ করুন এবং সংযোগের জন্য হোস্ট ডোমেন এবং পোর্টটি পান
  3. ব্রাউজার অনুরোধের প্রথম লাইনে পাওয়া হোস্টকে ঠিক কাঁচা অনুরোধটি প্রেরণ করুন
  4. লক্ষ্য সাইট থেকে ডেটা গ্রহণ (আমার এই বিভাগে সমস্যা আছে)
  5. হোস্ট থেকে প্রাপ্ত সঠিক তথ্যটি ব্রাউজারে প্রেরণ করুন

আপনি দেখতে পাচ্ছেন যে ব্রাউজারের অনুরোধটিতে কী আছে এবং এটি বিশ্লেষণ করার দরকার নেই, কেবল প্রথম লাইনের প্রথম লাইন থেকে লক্ষ্য সাইটের ঠিকানা পান সাধারণত এই জিইটি http://google.com HTTP1.1 বা সংযুক্ত ফেসবুক.কম পছন্দ করুন : 443 (এটি এসএসএল অনুরোধের জন্য)


4

মোজা 4 বাস্তবায়ন করার জন্য একটি খুব সাধারণ প্রোটোকল। আপনি প্রাথমিক সংযোগটি শোনেন, ক্লায়েন্ট কর্তৃক অনুরোধ করা হোস্ট / পোর্টের সাথে সংযোগ দিন, সাফল্যের কোডটি ক্লায়েন্টকে প্রেরণ করুন তারপরে সকেটগুলিতে বহির্গামী এবং আগত প্রবাহগুলি ফরোয়ার্ড করুন।

আপনি যদি HTTP- র সাথে যান তবে আপনাকে কিছু HTTP শিরোনাম পড়তে হবে এবং সেট করতে হবে / সরিয়ে ফেলতে হবে যাতে এটি আরও কিছুটা কাজ।

যদি আমি সঠিকভাবে মনে রাখি তবে এসএসএল এইচটিটিপি এবং মোজা প্রক্সি জুড়ে কাজ করবে। এইচটিটিপি প্রক্সির জন্য আপনি সংযোগ ক্রিয়াটি প্রয়োগ করেন যা উপরে বর্ণিত মোজা 4 এর মতো কাজ করে, তারপরে ক্লায়েন্ট প্রক্সাইড টিসিপি স্ট্রিম জুড়ে এসএসএল সংযোগটি খোলে।


2

ব্রাউজারটি প্রক্সিটির সাথে সংযুক্ত থাকে তাই ওয়েব সার্ভার থেকে প্রক্সিটি যে ডেটা পায় সেটি ঠিক একই সংযোগের মাধ্যমে প্রেরণ করা হয় যা ব্রাউজার প্রক্সিটিতে প্রবর্তিত হয়েছিল।


1

এটির মূল্যের জন্য, এইচটিপিপ্লাইটার এবং এইচটিপিপিপ্লায়েন্টের উপর ভিত্তি করে একটি সি # নমুনা অ্যাসিঙ্ক বাস্তবায়ন (আমি এটি আইআইএস এক্সপ্রেসের সাথে অ্যান্ড্রয়েড ডিভাইসে ক্রোম সংযোগ করতে সক্ষম হতে ব্যবহার করি, এটিই আমি খুঁজে পেয়েছি ...)।

এবং যদি আপনার এইচটিটিপিএস সমর্থন প্রয়োজন হয় তবে এর জন্য আরও কোডের প্রয়োজন হবে না, কেবল শংসাপত্রের কনফিগারেশন: এইচটিটিপিএস সমর্থন সহ এইচটিপিপ্লাস্টার

// define http://localhost:5000 and http://127.0.0.1:5000/ to be proxies for http://localhost:53068
using (var server = new ProxyServer("http://localhost:53068", "http://localhost:5000/", "http://127.0.0.1:5000/"))
{
    server.Start();
    Console.WriteLine("Press ESC to stop server.");
    while (true)
    {
        var key = Console.ReadKey(true);
        if (key.Key == ConsoleKey.Escape)
            break;
    }
    server.Stop();
}

....

public class ProxyServer : IDisposable
{
    private readonly HttpListener _listener;
    private readonly int _targetPort;
    private readonly string _targetHost;
    private static readonly HttpClient _client = new HttpClient();

    public ProxyServer(string targetUrl, params string[] prefixes)
        : this(new Uri(targetUrl), prefixes)
    {
    }

    public ProxyServer(Uri targetUrl, params string[] prefixes)
    {
        if (targetUrl == null)
            throw new ArgumentNullException(nameof(targetUrl));

        if (prefixes == null)
            throw new ArgumentNullException(nameof(prefixes));

        if (prefixes.Length == 0)
            throw new ArgumentException(null, nameof(prefixes));

        RewriteTargetInText = true;
        RewriteHost = true;
        RewriteReferer = true;
        TargetUrl = targetUrl;
        _targetHost = targetUrl.Host;
        _targetPort = targetUrl.Port;
        Prefixes = prefixes;

        _listener = new HttpListener();
        foreach (var prefix in prefixes)
        {
            _listener.Prefixes.Add(prefix);
        }
    }

    public Uri TargetUrl { get; }
    public string[] Prefixes { get; }
    public bool RewriteTargetInText { get; set; }
    public bool RewriteHost { get; set; }
    public bool RewriteReferer { get; set; } // this can have performance impact...

    public void Start()
    {
        _listener.Start();
        _listener.BeginGetContext(ProcessRequest, null);
    }

    private async void ProcessRequest(IAsyncResult result)
    {
        if (!_listener.IsListening)
            return;

        var ctx = _listener.EndGetContext(result);
        _listener.BeginGetContext(ProcessRequest, null);
        await ProcessRequest(ctx).ConfigureAwait(false);
    }

    protected virtual async Task ProcessRequest(HttpListenerContext context)
    {
        if (context == null)
            throw new ArgumentNullException(nameof(context));

        var url = TargetUrl.GetComponents(UriComponents.SchemeAndServer, UriFormat.Unescaped);
        using (var msg = new HttpRequestMessage(new HttpMethod(context.Request.HttpMethod), url + context.Request.RawUrl))
        {
            msg.Version = context.Request.ProtocolVersion;

            if (context.Request.HasEntityBody)
            {
                msg.Content = new StreamContent(context.Request.InputStream); // disposed with msg
            }

            string host = null;
            foreach (string headerName in context.Request.Headers)
            {
                var headerValue = context.Request.Headers[headerName];
                if (headerName == "Content-Length" && headerValue == "0") // useless plus don't send if we have no entity body
                    continue;

                bool contentHeader = false;
                switch (headerName)
                {
                    // some headers go to content...
                    case "Allow":
                    case "Content-Disposition":
                    case "Content-Encoding":
                    case "Content-Language":
                    case "Content-Length":
                    case "Content-Location":
                    case "Content-MD5":
                    case "Content-Range":
                    case "Content-Type":
                    case "Expires":
                    case "Last-Modified":
                        contentHeader = true;
                        break;

                    case "Referer":
                        if (RewriteReferer && Uri.TryCreate(headerValue, UriKind.Absolute, out var referer)) // if relative, don't handle
                        {
                            var builder = new UriBuilder(referer);
                            builder.Host = TargetUrl.Host;
                            builder.Port = TargetUrl.Port;
                            headerValue = builder.ToString();
                        }
                        break;

                    case "Host":
                        host = headerValue;
                        if (RewriteHost)
                        {
                            headerValue = TargetUrl.Host + ":" + TargetUrl.Port;
                        }
                        break;
                }

                if (contentHeader)
                {
                    msg.Content.Headers.Add(headerName, headerValue);
                }
                else
                {
                    msg.Headers.Add(headerName, headerValue);
                }
            }

            using (var response = await _client.SendAsync(msg).ConfigureAwait(false))
            {
                using (var os = context.Response.OutputStream)
                {
                    context.Response.ProtocolVersion = response.Version;
                    context.Response.StatusCode = (int)response.StatusCode;
                    context.Response.StatusDescription = response.ReasonPhrase;

                    foreach (var header in response.Headers)
                    {
                        context.Response.Headers.Add(header.Key, string.Join(", ", header.Value));
                    }

                    foreach (var header in response.Content.Headers)
                    {
                        if (header.Key == "Content-Length") // this will be set automatically at dispose time
                            continue;

                        context.Response.Headers.Add(header.Key, string.Join(", ", header.Value));
                    }

                    var ct = context.Response.ContentType;
                    if (RewriteTargetInText && host != null && ct != null &&
                        (ct.IndexOf("text/html", StringComparison.OrdinalIgnoreCase) >= 0 ||
                        ct.IndexOf("application/json", StringComparison.OrdinalIgnoreCase) >= 0))
                    {
                        using (var ms = new MemoryStream())
                        {
                            using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
                            {
                                await stream.CopyToAsync(ms).ConfigureAwait(false);
                                var enc = context.Response.ContentEncoding ?? Encoding.UTF8;
                                var html = enc.GetString(ms.ToArray());
                                if (TryReplace(html, "//" + _targetHost + ":" + _targetPort + "/", "//" + host + "/", out var replaced))
                                {
                                    var bytes = enc.GetBytes(replaced);
                                    using (var ms2 = new MemoryStream(bytes))
                                    {
                                        ms2.Position = 0;
                                        await ms2.CopyToAsync(context.Response.OutputStream).ConfigureAwait(false);
                                    }
                                }
                                else
                                {
                                    ms.Position = 0;
                                    await ms.CopyToAsync(context.Response.OutputStream).ConfigureAwait(false);
                                }
                            }
                        }
                    }
                    else
                    {
                        using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
                        {
                            await stream.CopyToAsync(context.Response.OutputStream).ConfigureAwait(false);
                        }
                    }
                }
            }
        }
    }

    public void Stop() => _listener.Stop();
    public override string ToString() => string.Join(", ", Prefixes) + " => " + TargetUrl;
    public void Dispose() => ((IDisposable)_listener)?.Dispose();

    // out-of-the-box replace doesn't tell if something *was* replaced or not
    private static bool TryReplace(string input, string oldValue, string newValue, out string result)
    {
        if (string.IsNullOrEmpty(input) || string.IsNullOrEmpty(oldValue))
        {
            result = input;
            return false;
        }

        var oldLen = oldValue.Length;
        var sb = new StringBuilder(input.Length);
        bool changed = false;
        var offset = 0;
        for (int i = 0; i < input.Length; i++)
        {
            var c = input[i];

            if (offset > 0)
            {
                if (c == oldValue[offset])
                {
                    offset++;
                    if (oldLen == offset)
                    {
                        changed = true;
                        sb.Append(newValue);
                        offset = 0;
                    }
                    continue;
                }

                for (int j = 0; j < offset; j++)
                {
                    sb.Append(input[i - offset + j]);
                }

                sb.Append(c);
                offset = 0;
            }
            else
            {
                if (c == oldValue[0])
                {
                    if (oldLen == 1)
                    {
                        changed = true;
                        sb.Append(newValue);
                    }
                    else
                    {
                        offset = 1;
                    }
                    continue;
                }

                sb.Append(c);
            }
        }

        if (changed)
        {
            result = sb.ToString();
            return true;
        }

        result = input;
        return false;
    }
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.