Redth / HttpTwo

A basic C# HTTP/2 client library
Apache License 2.0
119 stars 51 forks source link

Only the first request works #4

Open DocVirus opened 8 years ago

DocVirus commented 8 years ago

Hi! I'm trying to use HttpTwo with APNS (development server). Any first request (POST, Connect, Ping) is processed successfully, but, after that, any other request except Disconnect fails with error of broken connection from the remote party.

var store = new X509Store("MY", StoreLocation.CurrentUser);
            Http2Client request2 = null;
            try
            {
                store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
                var myCert =
                    store.Certificates.Find(X509FindType.FindBySubjectName, "Apple Development IOS Push Services", false)
                        .OfType<X509Certificate2>()
                        .FirstOrDefault();
                //var uri = new Uri("https://api.development.push.apple.com");
                var path =
                    new Uri(
                        "https://api.development.push.apple.com/3/device/somedeviceId");
                request2 = new Http2Client(path, myCert != null ? new X509Certificate2Collection(myCert) : null);                
                store.Close();
                var stream1 = new MemoryStream();
                var notif = new Notification {Aps = new Aps {Alert = "Hello", Sound = "default"}};
                var ser = new DataContractJsonSerializer(typeof (Notification));
                ser.WriteObject(stream1, notif);
                stream1.Position = 0;
                var response = await request2.Send(path, HttpMethod.Post, null, stream1);

Every new request in the same process (even made with new Http2Client instance) is refused by server. What do I do wrong?

MrKang commented 8 years ago

"Keep your connections with APNs open across multiple notifications; don’t repeatedly open and close connections. APNs treats rapid connection and disconnection as a denial-of-service attack. " maybe this is why your provider is refused by server.

franhoey commented 8 years ago

I'm having the same problem.

I've tried using a static Http2Client variable in the hope that would leave the connection open, but I get the same error on second attempt, then following attempts give me "illegal index value (62)".

Does Send() and Post() close the connection?

I'm guessing at some point Apple are returning a GOAWAY frame, with perhaps the cause of the problem in the body, is there any way of viewing it?

MrKang commented 8 years ago

Send() or Post() will close the connection, if you test your provider by browser.And maybe that is why your provider is refused by server. I found that by google: "HTTP/2 connections are persistent. For best performance, it is expected that clients will not close connections until it is determined that no further communication with a server is necessary (for example, when a user navigates away from a particular web page) or until the server closes the connection." I hope it helps you. Excuse my poor English :)

franhoey commented 8 years ago

Is there a way to leave the connections open using Send() and Post()?

MrKang commented 8 years ago

Maybe my poor English cause you misunderstood what I meant. Can you tell me how do you construct your provider?And how do you let your provider to request APNs to send a notification?I constructed my provider by ASP.NET Web API 2. I found that if I requested my provider by a browser ,it would close the connection after Post().If i used Charles(an HTTP debugging proxy server application),it would be fine.So maybe it was not your code cause the connection closed. You can set a breakpoint in Connect() at if (IsConnected ()) to watch the connection is still opened or not.

I really want to help you ,but my English is not so good :) .If you have any problems, I will be happy to answer your questions.

DocVirus commented 8 years ago

Can you tell me how do you construct your provider?

Well, I actually don't create any provider explicitly. I just use the Http2Client class.

And how do you let your provider to request APNs to send a notification?

By providing Http2Client with the URL of the APNS server. I've qouted my code in the starting message. It's pretty simple, so maybe it lacks something neccessary but I have no idea, what exactly. I'm running my code using MS Visual Studio, so the browser is not relevant here and I don't intentionally break the connection.

meisterm commented 8 years ago

Are there any solutions yet? I have the same problem firing more than one request to the Server. For example the first Post Command: 11:09:06.326 PM: -> [Frame: SETTINGS, Id=0, Ack=False, HeaderTableSize=, EnablePush=, MaxConcurrentStreams=, InitialWindowSize=, MaxFrameSize=, MaxHeaderListSize=] 11:09:06.332 PM: <- [Frame: SETTINGS, Id=0, Ack=False, HeaderTableSize=, EnablePush=, MaxConcurrentStreams=, InitialWindowSize=, MaxFrameSize=, MaxHeaderListSize=] 11:09:06.348 PM: -> [Frame: SETTINGS, Id=0, Ack=True, HeaderTableSize=, EnablePush=, MaxConcurrentStreams=, InitialWindowSize=, MaxFrameSize=, MaxHeaderListSize=] 11:09:06.355 PM: -> [Frame: HEADERS, Id=1, EndStream=False, EndHeaders=True, Priority=False, Weight=0, Padded=False, PadLength=0, HeaderBlockFragmentLength=144] 11:09:06.357 PM: -> [Frame: DATA, Id=1, EndStream=True, Padded=False, PadLength=0, PayloadLength=96] 11:09:06.499 PM: <- [Frame: SETTINGS, Id=0, Ack=True, HeaderTableSize=, EnablePush=, MaxConcurrentStreams=, InitialWindowSize=, MaxFrameSize=, MaxHeaderListSize=] 11:09:06.519 PM: <- [Frame: HEADERS, Id=1, EndStream=False, EndHeaders=True, Priority=False, Weight=0, Padded=False, PadLength=0, HeaderBlockFragmentLength=37] 11:09:06.520 PM: <- [Frame: DATA, Id=1, EndStream=True, Padded=False, PadLength=0, PayloadLength=27] 11:09:06.533 PM: -> [Frame: WINDOW_UPDATE, Id=0, WindowSizeIncrement=27] 11:09:06.540 PM: -> [Frame: GOAWAY, Id=0, ErrorCode=0, LastStreamId=0, AdditionalDebugData=]

This is a connection to Apple Push Notification Service. The Server sends me a goaway frame. But why?

The next post request looks like this:

11:09:29.147 PM: <- [Frame: SETTINGS, Id=0, Ack=False, HeaderTableSize=, EnablePush=, MaxConcurrentStreams=, InitialWindowSize=, MaxFrameSize=, MaxHeaderListSize=] 11:09:29.149 PM: -> [Frame: SETTINGS, Id=0, Ack=True, HeaderTableSize=, EnablePush=, MaxConcurrentStreams=, InitialWindowSize=, MaxFrameSize=, MaxHeaderListSize=] 11:09:29.150 PM: -> [Frame: SETTINGS, Id=0, Ack=False, HeaderTableSize=, EnablePush=, MaxConcurrentStreams=, InitialWindowSize=, MaxFrameSize=, MaxHeaderListSize=] 11:09:29.150 PM: -> [Frame: HEADERS, Id=1, EndStream=False, EndHeaders=True, Priority=False, Weight=0, Padded=False, PadLength=0, HeaderBlockFragmentLength=144] 11:09:29.150 PM: -> [Frame: DATA, Id=1, EndStream=True, Padded=False, PadLength=0, PayloadLength=96] 11:09:29.311 PM: <- [Frame: GOAWAY, Id=0, ErrorCode=1, LastStreamId=0, AdditionalDebugData=First received frame was not SETTINGS. Hex dump for first 5 bytes: 0000000401]

Mazeror commented 6 years ago

Hello! I spent couple of days, trying to make this lib working. Core problems of this lib: 1) wrong using of TPL-pattern (lock, symaphore, events, configure.await - all together!). Fristofall, rewrite it to event-based OR tpl model (as you wish). 2) Re-esteblishing new connection for every push message. It's wrong, and apple doesn't like it (as mentioned above). (StreamManager.cs, method Get) 3) http2 library drops hpack's dynamic table every frame (Utils.cs, method UnpackHeaders) => can't parse headers from second and next packets. 4) no re-connect when GOAWAY o RSTCHANNEL frames are received.

This will be enought for working solution.

Performance also is very pure, but it's much easyer: change Lists to byte arrays in Frames.