{"action":"created","comment":{"url":"https://api.github.com/repos/wxWidgets/wxWidgets/pulls/comments/229886124","pull_request_review_id":170490431,"id":229886124,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIyOTg4NjEyNA==","diff_hunk":"@@ -0,0 +1,611 @@\n+/////////////////////////////////////////////////////////////////////////////\n+// Name:        webrequest.h\n+// Created:     2018-10-14\n+// Copyright:   (c) 2018 wxWidgets development team\n+// Licence:     wxWindows licence\n+/////////////////////////////////////////////////////////////////////////////\n+\n+/**\n+    @class wxWebRequest\n+\n+    This class allows for simple HTTP requests using the operating systems\n+    components as implementation.\n+\n+    The latest features of the operating system will be used if available\n+    (e.g. HTTP/2, TLS 1.3).\n+    System-wide configuration like proxy and SSL certificates will be used\n+    when possible.\n+\n+    Instances of wxWebRequest are created by using\n+    wxWebSession::CreateRequest().\n+\n+    The requests are handled asynchronously and event handlers are used to\n+    communicate the request status. The response data may be stored in\n+    memory, to a file or processed directly, see SetStorage() for details.\n+\n+    Example usage:\n+\n+    @code\n+        // Create the request object\n+        wxObjectDataPtr<wxWebRequest> request(\n+            wxWebSession::GetDefault().CreateRequest(\"https://www.wxwidgets.org/downloads/logos/blocks.png\"));\n+\n+        // Bind state event\n+        request->Bind(wxEVT_WEBREQUEST_STATE, [](wxWebRequestEvent& evt) {\n+            switch (evt.GetState())\n+            {\n+                // Request completed\n+                case wxWebRequest::State_Completed:\n+                {\n+                    wxImage logoImage(*evt->GetResponse()->GetStream());\n+                    if (logoImage.IsOK())\n+                        wxLogInfo(\"Image loaded\");\n+                    break;\n+                }\n+                // Request failed\n+                case wxWebRequest::State_Failed:\n+                    wxLogError(\"Could not load logo: %s\", evt.GetErrorDescription());\n+                    break;\n+            }\n+\n+        });\n+\n+        // Start the request\n+        request->Start();\n+    @endcode\n+\n+    @section descriptions Implementation Descriptions\n+\n+    The following APIs are used per platform, additional documentation\n+    about supported features may be found in their documentation\n+\n+    Available features by implementation and minimum version:\n+    <table>\n+    <tr><th>Operating System</th><th>API</th><th>HTTPS</th><th>HTTP/2</th></tr>\n+    <tr>\n+        <td>Windows</td>\n+        <td>\n+            <a target=_new href=\"https://docs.microsoft.com/en-us/windows/desktop/WinHttp/about-winhttp\">WinHTTP</a>\n+        </td>\n+        <td>Yes</td>\n+        <td>Windows 10 1607</td>\n+    </tr>\n+    <tr>\n+        <td>macOS</td>\n+        <td>\n+            <a target=_new href=\"https://developer.apple.com/documentation/foundation/urlsession\">NSURLSession</a>\n+        </td>\n+        <td>macOS 10.9</td>\n+        <td>macOS 10.11</td>\n+    </tr>\n+    <tr>\n+        <td>iOS</td>\n+        <td>\n+            <a target=_new href=\"https://developer.apple.com/documentation/foundation/urlsession\">NSURLSession</a>\n+        </td>\n+        <td>iOS 7.0</td>\n+        <td>iOS 9.0</td>\n+    </tr>\n+    <tr>\n+        <td>Linux</td>\n+        <td>\n+            <a target=_new href=\"https://curl.haxx.se/libcurl/\">libcurl</a>\n+        </td>\n+        <td>Yes</td>\n+        <td>7.47.0</td>\n+    </tr>\n+    </table>\n+\n+    @beginEventEmissionTable{wxWebRequestEvent}\n+    @event{wxEVT_WEBREQUEST_STATE(id, func)}\n+        The request state changed.\n+    @event{wxEVT_WEBREQUEST_DATA(id, func)}\n+        A new block of data has been downloaded.\n+    @endEventTable\n+\n+    @since 3.1.2\n+\n+    @library{wxnet}\n+    @category{net}\n+\n+    @see wxWebResponse, wxWebSession\n+*/\n+class wxWebRequest: public wxEvtHandler, public wxRefCounter\n+{\n+public:\n+    /**\n+        Possible request states returned by GetState().\n+    */\n+    enum State\n+    {\n+        /// The request has just been created and Start() has not been called\n+        State_Idle,\n+\n+        /// The request has been started and is transferring data\n+        State_Active,\n+\n+        /**\n+            The request is currently unauthorized. Calling GetAuthChallenge()\n+            returns a challenge object with further details.\n+        */\n+        State_Unauthorized,\n+\n+        /// The request completed successfully and all data has been received.\n+        State_Completed,\n+\n+        /// The request failed\n+        State_Failed,\n+\n+        /// The request has been cancelled before completion by calling Cancel()\n+        State_Cancelled\n+    };\n+\n+    /**\n+        Possible storage types. Set by SetStorage().\n+    */\n+    enum Storage\n+    {\n+        /// All data is collected in memory until the request is complete\n+        Storage_Memory,\n+\n+        /// The data is written to a file on disk\n+        Storage_File,\n+\n+        /**\n+            The data is not stored by the request and is only available\n+            via events.\n+        */\n+        Storage_None\n+    };\n+\n+    /**\n+        Send the request to the server asynchronously.\n+\n+        Events will be triggered on success or failure.\n+\n+        @see Cancel()\n+    */\n+    void Start();\n+\n+    /**\n+        Cancel an active request.\n+    */\n+    void Cancel();\n+\n+    /**\n+        Returns a response object after a successful request.\n+\n+        Before sending a request or after a failed request this will return\n+        @c NULL.\n+    */\n+    wxWebResponse* GetResponse();\n+\n+    /**\n+        Returns the current authentication challenge object while the request\n+        is in @c State_Unauthorized.\n+    */\n+    wxWebAuthChallenge* GetAuthChallenge();\n+\n+    /**\n+        Returns the id specified while creating this request.\n+    */\n+    int GetId() const;\n+\n+    /** @name Request options\n+        Methods that set options before starting the request\n+    */\n+    ///@{\n+    /**\n+        Sets a request header which will be sent to the server by this request.\n+\n+        The header will be added if it hasn't been set before or replaced\n+        otherwise.\n+\n+        @param name\n+            Name of the header\n+        @param value\n+            String value of the header. An empty string will remove the header.\n+    */\n+    void SetHeader(const wxString& name, const wxString& value);\n+\n+    /**\n+        Set <a href=\"http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html\">common</a>\n+        or expanded HTTP method.\n+\n+        The default method is GET unless request data is provided in which\n+        case POST is the default.\n+\n+        @param method\n+            HTTP method name, e.g. \"GET\".\n+    */\n+    void SetMethod(const wxString& method);\n+\n+    /**\n+        Set the text to be posted to the server.\n+\n+        After a successful call to this method, the request will use HTTP @c\n+        POST instead of the default @c GET when it's executed.\n+\n+        @param text\n+            The text data to post.\n+        @param contentType\n+            The value of HTTP \"Content-Type\" header, e.g. \"text/html;\n+            charset=UTF-8\".\n+        @param conv\n+            Conversion used when sending the text to the server\n+    */\n+    void SetData(const wxString& text, const wxString& contentType,\n+        const wxMBConv& conv = wxConvUTF8);\n+\n+    /**\n+        Set the binary data to be posted to the server.\n+\n+        The next request will be a HTTP @c POST instead of the default HTTP\n+        @c GET and the given @a dataStream will be posted as the body of\n+        this request.\n+\n+        @param dataStream\n+            The data in this stream will be posted as the request body\n+        @param contentType\n+            The value of HTTP \"Content-Type\" header, e.g.\n+            \"application/octet-stream\".\n+        @param dataSize\n+            Amount of data which is sent to the server. If set to\n+            @c wxInvalidOffset all stream data is sent.\n+\n+    */\n+    void SetData(wxSharedPtr<wxInputStream> dataStream,\n+        const wxString& contentType, wxFileOffset dataSize = wxInvalidOffset);\n+\n+    /**\n+        Instructs the request to ignore server error status codes.\n+\n+        Per default, server side errors (status code 400-599) will enter\n+        the State_Failed state just like network errors, but\n+        if the response is still required in such cases (e.g. to get more\n+        details from the response body), set this option to ignore all errors.\n+        If ignored, wxWebResponse::GetStatus() has to be checked\n+        from the State_Completed event handler.\n+    */\n+    void SetIgnoreServerErrorStatus(bool ignore);\n+\n+    /**\n+        Sets how response data will be stored.\n+\n+        The default storage method @c Storage_Memory collects all response data\n+        in memory until the request is completed. This is fine for most usage\n+        scenarios like API calls, loading images, etc. For larger downloads or\n+        if the response data will be used permanently @c Storage_File instructs\n+        the request to write the response to a temporary file. This temporary\n+        file may then be read or moved after the request is complete. The file\n+        will be downloaded to the system temp directory as returned by\n+        wxStandardPaths::GetTempDir(). To specify a different directory use\n+        wxWebSession::SetTempDir().\n+\n+        Sometimes response data needs to be processed while its downloaded from\n+        the server. For example if the response is in a format that can be\n+        parsed piece by piece like XML, JSON or an archive format like ZIP.\n+        In these cases storing the data in memory or a file before being able\n+        to process it might not be ideal and @c Storage_None should be set.\n+        With this storage method the data is only available during the\n+        @c wxEVT_WEBREQUEST_DATA event calls as soon as it's received from the\n+        server.\n+    */\n+    void SetStorage(Storage storage);\n+    ///@}\n+\n+    /** @name Progress methods\n+        Methods that describe the requests progress\n+    */\n+    ///@{\n+    /**\n+        Returns the current state of the request.\n+    */\n+    State GetState() const { return m_state; }\n+\n+    /// Returns the number of bytes sent to the server.\n+    wxFileOffset GetBytesSent() const;\n+\n+    /// Returns the number of bytes expected to be send to the server.\n+    wxFileOffset GetBytesExpectedToSend() const;\n+\n+    /// Returns the number of bytes received from the server.\n+    wxFileOffset GetBytesReceived() const;\n+\n+    /**\n+        Returns the number of bytes expected to be received from the server.\n+\n+        This value is based on the @c Content-Length header, if none is found\n+        it will return -1.\n+    */\n+    wxFileOffset GetBytesExpectedToReceive() const;\n+    ///@}\n+};\n+\n+/**\n+    Authentication challenge information available via\n+    wxWebRequest::GetAuthChallenge().\n+\n+    Use SetCredentials() to provide user credentials.\n+*/\n+class wxWebAuthChallenge\n+{\n+public:\n+    enum Source\n+    {\n+        /// The server requested authentication\n+        Source_Server,\n+\n+        /// A proxy requested authentication\n+        Source_Proxy\n+    };\n+\n+    /**\n+        Returns which source requested credentials with this challenge.\n+    */\n+    Source GetSource() const { return m_source; }\n+\n+    /**\n+        Used to provide user credentials to the authentication challenge.\n+\n+        @param user\n+            User name.\n+        @param password\n+            The users password.\n+    */\n+    void SetCredentials(const wxString& user, const wxString& password);\n+};\n+\n+/**\n+    A wxWebResponse allows access to the response sent by the server.\n+\n+    @since 3.1.2\n+\n+    @library{wxnet}\n+    @category{net}\n+\n+    @see wxWebRequest\n+*/\n+class wxWebResponse\n+{\n+public:\n+    /**\n+        Returns the final URL.\n+        This URL might be different than the request URL when a redirection\n+        occurred.\n+    */\n+    wxString GetURL() const;\n+\n+    /**\n+        Returns a header from the response or an empty string if the header\n+        could not be found.\n+\n+        @param name Name of the header field\n+    */\n+    wxString GetHeader(const wxString& name) const;\n+\n+    /**\n+        Returns the MIME type of the response (if available).\n+    */\n+    wxString GetMimeType() const;\n+\n+    /**\n+        Returns the status code returned by the server.\n+    */\n+    int GetStatus() const;\n+\n+    /**\n+        Returns the status text of the response.\n+    */\n+    wxString GetStatusText() const;\n+\n+    /**\n+        Returns a stream which represents the response data sent by the server.\n+    */\n+    wxInputStream* GetStream();\n+\n+    /**\n+        Returns a suggested filename for the response data.\n+    */\n+    wxString GetSuggestedFileName() const;\n+\n+    /**\n+        Returns all response data as a string.\n+\n+        @param conv wxMBConv used to convert the response to a string.\n+                    If @c NULL, the conversion will be determined by\n+                    response headers. The default is UTF-8.\n+    */\n+    wxString AsString(wxMBConv* conv = NULL) const;\n+};\n+\n+/**\n+    @class wxWebSession\n+\n+    This class handles session-wide parameters and data used by wxWebRequest\n+    instances.\n+\n+    Usually the default session available via wxWebSession::GetDefault()\n+    should be used. Additional instances might be useful if session separation\n+    is required. Instances <b>must not</b> be deleted before every active web\n+    request has finished.\n+\n+    Every wxWebRequest sharing the same session object will use the same\n+    cookies. Additionally, an underlying network connection might be kept\n+    alive to achieve faster additional responses.\n+\n+    @since 3.1.2\n+\n+    @library{wxnet}\n+    @category{net}\n+\n+    @see wxWebRequest\n+*/\n+class wxWebSession\n+{\n+public:\n+    /**\n+        Constructor for the session\n+\n+        All requests created by a call to CreateRequest() will use this session\n+        for communication and to store cookies.\n+    */\n+    wxWebSession();\n+\n+    /**\n+        Create a new request for the specified URL\n+\n+        @param url\n+            The URL of the HTTP resource for this request\n+        @param id\n+            Optional id sent with events\n+    */\n+    wxWebRequest* CreateRequest(const wxString& url, int id = wxID_ANY);\n+\n+    /**\n+        Sets a request header in every wxWebRequest created from this session\n+        after is has been set.\n+\n+        A good example for a session-wide request header is the @c User-Agent\n+        header.\n+\n+        @param name Name of the header\n+        @param value String value of the header\n+    */\n+    void SetHeader(const wxString& name, const wxString& value);\n+\n+    /**\n+        Override the default temporary directory that may be used by the\n+        session implemention, when required.\n+    */\n+    void SetTempDir(const wxString& name);\n+\n+    /**\n+        Returns the current temporary directory.\n+\n+        @see SetTempDir()\n+    */\n+    const wxString& GetTempDir() const;\n+\n+    /**\n+        Returns the default session\n+    */\n+    static wxWebSession& GetDefault();\n+\n+    /**\n+        Creates a new wxWebSession object.\n+\n+        @param backend\n+            The backend web session implementation to use.\n+\n+        @return\n+            The created wxWebSession\n+    */\n+    static wxWebSession* New(const wxString& backend = wxWebSessionBackendDefault);\n+\n+    /**\n+        Allows the registering of new backend for wxWebSession.\n+\n+        backend can be used as an argument to New().\n+\n+        @param backend The name for the new backend to be registered under\n+        @param factory A shared pointer to the factory which creates the appropriate backend.\n+    */\n+    static void RegisterFactory(const wxString& backend, wxSharedPtr<wxWebSessionFactory> factory);\n+\n+    /**\n+        Allows to check if the specified backend is available at runtime.\n+\n+        Usually the default backend should always be available, but e.g. macOS\n+        before 10.9 does not have the @c NSURLSession implementation available.\n+    */\n+    static bool IsBackendAvailable(const wxString& backend);\n+};\n+\n+/**\n+    @class wxWebSessionFactory\n+\n+    An abstract factory class for creation wxWebSession backends.\n+\n+    Each implementation of wxWebSession should have its own factory.\n+\n+    @since 3.1.2\n+\n+    @library{wxnet}\n+    @category{net}\n+\n+    @see wxWebSession\n+*/\n+class wxWebSessionFactory\n+{\n+public:\n+    /**\n+        Creates a new web session object\n+    */\n+    virtual wxWebSession* Create() = 0;\n+};\n+\n+/**\n+    @class wxWebRequestEvent\n+\n+    A web request event sent during or after server communication.\n+\n+    @since 3.1.2\n+\n+    @library{wxnet}\n+    @category{net}\n+\n+    @see wxWebRequest\n+*/\n+class wxWebRequestEvent : public wxEvent\n+{\n+public:\n+    wxWebRequestEvent();\n+    wxWebRequestEvent(wxEventType type, int id, wxWebRequest::State state,\n+        wxWebResponse* response = NULL, const wxString& errorDesc = \"\");","path":"interface/wx/webrequest.h","position":565,"original_position":565,"commit_id":"d8e84af99216db480653a6b7525d79f7c90a0806","original_commit_id":"d8e84af99216db480653a6b7525d79f7c90a0806","user":{"login":"MaartenBent","id":8088070,"node_id":"MDQ6VXNlcjgwODgwNzA=","avatar_url":"https://avatars3.githubusercontent.com/u/8088070?v=4","gravatar_id":"","url":"https://api.github.com/users/MaartenBent","html_url":"https://github.com/MaartenBent","followers_url":"https://api.github.com/users/MaartenBent/followers","following_url":"https://api.github.com/users/MaartenBent/following{/other_user}","gists_url":"https://api.github.com/users/MaartenBent/gists{/gist_id}","starred_url":"https://api.github.com/users/MaartenBent/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/MaartenBent/subscriptions","organizations_url":"https://api.github.com/users/MaartenBent/orgs","repos_url":"https://api.github.com/users/MaartenBent/repos","events_url":"https://api.github.com/users/MaartenBent/events{/privacy}","received_events_url":"https://api.github.com/users/MaartenBent/received_events","type":"User","site_admin":false},"body":"Is using `\"\"` correct (also in headers)? Other headers use `wxEmptyString` or `wxString()`.","created_at":"2018-10-31T22:15:16Z","updated_at":"2018-10-31T22:47:41Z","html_url":"https://github.com/wxWidgets/wxWidgets/pull/977#discussion_r229886124","pull_request_url":"https://api.github.com/repos/wxWidgets/wxWidgets/pulls/977","author_association":"CONTRIBUTOR","_links":{"self":{"href":"https://api.github.com/repos/wxWidgets/wxWidgets/pulls/comments/229886124"},"html":{"href":"https://github.com/wxWidgets/wxWidgets/pull/977#discussion_r229886124"},"pull_request":{"href":"https://api.github.com/repos/wxWidgets/wxWidgets/pulls/977"}}},"pull_request":{"url":"https://api.github.com/repos/wxWidgets/wxWidgets/pulls/977","id":222741257,"node_id":"MDExOlB1bGxSZXF1ZXN0MjIyNzQxMjU3","html_url":"https://github.com/wxWidgets/wxWidgets/pull/977","diff_url":"https://github.com/wxWidgets/wxWidgets/pull/977.diff","patch_url":"https://github.com/wxWidgets/wxWidgets/pull/977.patch","issue_url":"https://api.github.com/repos/wxWidgets/wxWidgets/issues/977","number":977,"state":"open","locked":false,"title":"Modern HTTP(s) Support","user":{"login":"TcT2k","id":5075894,"node_id":"MDQ6VXNlcjUwNzU4OTQ=","avatar_url":"https://avatars3.githubusercontent.com/u/5075894?v=4","gravatar_id":"","url":"https://api.github.com/users/TcT2k","html_url":"https://github.com/TcT2k","followers_url":"https://api.github.com/users/TcT2k/followers","following_url":"https://api.github.com/users/TcT2k/following{/other_user}","gists_url":"https://api.github.com/users/TcT2k/gists{/gist_id}","starred_url":"https://api.github.com/users/TcT2k/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/TcT2k/subscriptions","organizations_url":"https://api.github.com/users/TcT2k/orgs","repos_url":"https://api.github.com/users/TcT2k/repos","events_url":"https://api.github.com/users/TcT2k/events{/privacy}","received_events_url":"https://api.github.com/users/TcT2k/received_events","type":"User","site_admin":false},"body":"This is my proposal for a simple HTTP request class which would allow modern HTTP and HTTP/s connections based on APIs provided by the operating system.\r\n\r\nPlease have a look at the interface file to see the complete documentation.\r\n\r\n* [Documentation Page](https://github.com/TcT2k/wxWidgets/blob/http_request/interface/wx/webrequest.h)\r\n* [Sample source code](https://github.com/TcT2k/wxWidgets/blob/http_request/samples/webrequest/webrequest.cpp)\r\n* [Test source code](https://github.com/TcT2k/wxWidgets/blob/http_request/tests/net/webrequest.cpp)\r\n\r\nUsage would look like this:\r\n```c++\r\n// Create the request object\r\nwxObjectDataPtr<wxWebRequest> request(\r\n    wxWebSession::GetDefault().CreateRequest(\"https://www.wxwidgets.org/downloads/logos/blocks.png\"));\r\n// Bind events\r\nrequest->Bind(wxEVT_WEBREQUEST_STATE, [](wxWebRequestEvent& evt) {\r\n    switch (evt.GetState())\r\n    {\r\n        // Request completed\r\n        case wxWebRequest::State_Completed:\r\n        {\r\n            wxImage logoImage(*evt->GetResponse()->GetStream());\r\n            if (logoImage.IsOK())\r\n                wxLogInfo(\"Image loaded\");\r\n            break;\r\n        }\r\n        // Request failed\r\n        case wxWebRequest::State_Failed:\r\n            wxLogError(\"Could not load logo: %s\", evt.GetErrorDescription());\r\n            break;\r\n    }\r\n});\r\n// Start the request\r\nrequest->Start();\r\n```\r\n\r\nAs always any **feedback** is welcome. I don't have an **ETA** on when or if I would implement this, but I've experience with the proposed underlying APIs and it should be easy to get started.\r\n\r\nInitial todo for a basic (but functional) implementation:\r\n- [x] Interface Documentation\r\n- [x] Finalize API\r\n  - [x] `Storage_File` event params\r\n  - [x] `Storage_None` event params\r\n  - [x] Authentification params and event\r\n- [x] Add generic `wxCredentialEntryDialog`\r\n- [x] Sample application with common usage scenarios\r\n- [ ] WinHTTP implementation\r\n- [ ] NSURLSession implementation\r\n- [ ] libcurl implementation\r\n\r\nFurther improvements in **the future** could be:\r\n* More detailed connection options (timeouts, TLS versions, insecure HTTPs, etc)\r\n* Use Vista+ [CredUIPromptForWindowsCredentialsW](https://docs.microsoft.com/en-us/windows/desktop/api/wincred/nf-wincred-creduipromptforwindowscredentialsw) for `wxCredentialEntryDialog`\r\n\r\n\r\nThe following APIs would be used:\r\n <table>\r\n    <tr><th>Operating System</th><th>API</th><th>HTTPS</th><th>HTTP/2</th></tr>\r\n    <tr>\r\n        <td>Windows</td>\r\n        <td>\r\n            <a target=_new href=\"https://docs.microsoft.com/en-us/windows/desktop/WinHttp/about-winhttp\">WinHTTP</a>\r\n        </td>\r\n        <td>Yes</td>\r\n        <td>Windows 10 1607</td>\r\n    </tr>\r\n    <tr>\r\n        <td>macOS</td>\r\n        <td>\r\n            <a target=_new href=\"https://developer.apple.com/documentation/foundation/urlsession\">NSURLSession</a>\r\n        </td>\r\n        <td>macOS 10.9</td>\r\n        <td>macOS 10.11</td>\r\n    </tr>\r\n    <tr>\r\n        <td>iOS</td>\r\n        <td>\r\n            <a target=_new href=\"https://developer.apple.com/documentation/foundation/urlsession\">NSURLSession</a>\r\n        </td>\r\n        <td>iOS 7.0</td>\r\n        <td>iOS 9.0</td>\r\n    </tr>\r\n    <tr>\r\n        <td>Linux</td>\r\n        <td>\r\n            <a target=_new href=\"https://curl.haxx.se/libcurl/\">libcurl</a>\r\n        </td>\r\n        <td>Yes</td>\r\n        <td>7.47.0</td>\r\n    </tr>\r\n    </table>\r\n\r\nSample App Screenshots\r\n---\r\n![image](https://user-images.githubusercontent.com/5075894/47272413-91e6f880-d585-11e8-9c7a-359ea9599df0.png)\r\n![image](https://user-images.githubusercontent.com/5075894/47315019-aa1d4d00-d643-11e8-9d0b-3d506bf228c7.png)\r\n","created_at":"2018-10-14T20:34:30Z","updated_at":"2018-10-31T22:47:40Z","closed_at":null,"merged_at":null,"merge_commit_sha":"5f4508524cbd3e481233e6acf54818833346489b","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/wxWidgets/wxWidgets/pulls/977/commits","review_comments_url":"https://api.github.com/repos/wxWidgets/wxWidgets/pulls/977/comments","review_comment_url":"https://api.github.com/repos/wxWidgets/wxWidgets/pulls/comments{/number}","comments_url":"https://api.github.com/repos/wxWidgets/wxWidgets/issues/977/comments","statuses_url":"https://api.github.com/repos/wxWidgets/wxWidgets/statuses/d8e84af99216db480653a6b7525d79f7c90a0806","head":{"label":"TcT2k:http_request","ref":"http_request","sha":"d8e84af99216db480653a6b7525d79f7c90a0806","user":{"login":"TcT2k","id":5075894,"node_id":"MDQ6VXNlcjUwNzU4OTQ=","avatar_url":"https://avatars3.githubusercontent.com/u/5075894?v=4","gravatar_id":"","url":"https://api.github.com/users/TcT2k","html_url":"https://github.com/TcT2k","followers_url":"https://api.github.com/users/TcT2k/followers","following_url":"https://api.github.com/users/TcT2k/following{/other_user}","gists_url":"https://api.github.com/users/TcT2k/gists{/gist_id}","starred_url":"https://api.github.com/users/TcT2k/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/TcT2k/subscriptions","organizations_url":"https://api.github.com/users/TcT2k/orgs","repos_url":"https://api.github.com/users/TcT2k/repos","events_url":"https://api.github.com/users/TcT2k/events{/privacy}","received_events_url":"https://api.github.com/users/TcT2k/received_events","type":"User","site_admin":false},"repo":{"id":25212183,"node_id":"MDEwOlJlcG9zaXRvcnkyNTIxMjE4Mw==","name":"wxWidgets","full_name":"TcT2k/wxWidgets","private":false,"owner":{"login":"TcT2k","id":5075894,"node_id":"MDQ6VXNlcjUwNzU4OTQ=","avatar_url":"https://avatars3.githubusercontent.com/u/5075894?v=4","gravatar_id":"","url":"https://api.github.com/users/TcT2k","html_url":"https://github.com/TcT2k","followers_url":"https://api.github.com/users/TcT2k/followers","following_url":"https://api.github.com/users/TcT2k/following{/other_user}","gists_url":"https://api.github.com/users/TcT2k/gists{/gist_id}","starred_url":"https://api.github.com/users/TcT2k/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/TcT2k/subscriptions","organizations_url":"https://api.github.com/users/TcT2k/orgs","repos_url":"https://api.github.com/users/TcT2k/repos","events_url":"https://api.github.com/users/TcT2k/events{/privacy}","received_events_url":"https://api.github.com/users/TcT2k/received_events","type":"User","site_admin":false},"html_url":"https://github.com/TcT2k/wxWidgets","description":"Read-only mirror of the wxWidgets SVN repo (automatically updated). Report issues here: http://trac.wxwidgets.org/","fork":true,"url":"https://api.github.com/repos/TcT2k/wxWidgets","forks_url":"https://api.github.com/repos/TcT2k/wxWidgets/forks","keys_url":"https://api.github.com/repos/TcT2k/wxWidgets/keys{/key_id}","collaborators_url":"https://api.github.com/repos/TcT2k/wxWidgets/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/TcT2k/wxWidgets/teams","hooks_url":"https://api.github.com/repos/TcT2k/wxWidgets/hooks","issue_events_url":"https://api.github.com/repos/TcT2k/wxWidgets/issues/events{/number}","events_url":"https://api.github.com/repos/TcT2k/wxWidgets/events","assignees_url":"https://api.github.com/repos/TcT2k/wxWidgets/assignees{/user}","branches_url":"https://api.github.com/repos/TcT2k/wxWidgets/branches{/branch}","tags_url":"https://api.github.com/repos/TcT2k/wxWidgets/tags","blobs_url":"https://api.github.com/repos/TcT2k/wxWidgets/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/TcT2k/wxWidgets/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/TcT2k/wxWidgets/git/refs{/sha}","trees_url":"https://api.github.com/repos/TcT2k/wxWidgets/git/trees{/sha}","statuses_url":"https://api.github.com/repos/TcT2k/wxWidgets/statuses/{sha}","languages_url":"https://api.github.com/repos/TcT2k/wxWidgets/languages","stargazers_url":"https://api.github.com/repos/TcT2k/wxWidgets/stargazers","contributors_url":"https://api.github.com/repos/TcT2k/wxWidgets/contributors","subscribers_url":"https://api.github.com/repos/TcT2k/wxWidgets/subscribers","subscription_url":"https://api.github.com/repos/TcT2k/wxWidgets/subscription","commits_url":"https://api.github.com/repos/TcT2k/wxWidgets/commits{/sha}","git_commits_url":"https://api.github.com/repos/TcT2k/wxWidgets/git/commits{/sha}","comments_url":"https://api.github.com/repos/TcT2k/wxWidgets/comments{/number}","issue_comment_url":"https://api.github.com/repos/TcT2k/wxWidgets/issues/comments{/number}","contents_url":"https://api.github.com/repos/TcT2k/wxWidgets/contents/{+path}","compare_url":"https://api.github.com/repos/TcT2k/wxWidgets/compare/{base}...{head}","merges_url":"https://api.github.com/repos/TcT2k/wxWidgets/merges","archive_url":"https://api.github.com/repos/TcT2k/wxWidgets/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/TcT2k/wxWidgets/downloads","issues_url":"https://api.github.com/repos/TcT2k/wxWidgets/issues{/number}","pulls_url":"https://api.github.com/repos/TcT2k/wxWidgets/pulls{/number}","milestones_url":"https://api.github.com/repos/TcT2k/wxWidgets/milestones{/number}","notifications_url":"https://api.github.com/repos/TcT2k/wxWidgets/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/TcT2k/wxWidgets/labels{/name}","releases_url":"https://api.github.com/repos/TcT2k/wxWidgets/releases{/id}","deployments_url":"https://api.github.com/repos/TcT2k/wxWidgets/deployments","created_at":"2014-10-14T15:00:30Z","updated_at":"2018-10-17T00:35:07Z","pushed_at":"2018-10-31T14:58:49Z","git_url":"git://github.com/TcT2k/wxWidgets.git","ssh_url":"git@github.com:TcT2k/wxWidgets.git","clone_url":"https://github.com/TcT2k/wxWidgets.git","svn_url":"https://github.com/TcT2k/wxWidgets","homepage":"https://www.wxwidgets.org/","size":311759,"stargazers_count":3,"watchers_count":3,"language":"C++","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":2,"mirror_url":null,"archived":false,"open_issues_count":0,"license":null,"forks":2,"open_issues":0,"watchers":3,"default_branch":"master"}},"base":{"label":"wxWidgets:master","ref":"master","sha":"8890a25db3d82d12798e14f5cfec13d3e7917f0d","user":{"login":"wxWidgets","id":791023,"node_id":"MDEyOk9yZ2FuaXphdGlvbjc5MTAyMw==","avatar_url":"https://avatars0.githubusercontent.com/u/791023?v=4","gravatar_id":"","url":"https://api.github.com/users/wxWidgets","html_url":"https://github.com/wxWidgets","followers_url":"https://api.github.com/users/wxWidgets/followers","following_url":"https://api.github.com/users/wxWidgets/following{/other_user}","gists_url":"https://api.github.com/users/wxWidgets/gists{/gist_id}","starred_url":"https://api.github.com/users/wxWidgets/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/wxWidgets/subscriptions","organizations_url":"https://api.github.com/users/wxWidgets/orgs","repos_url":"https://api.github.com/users/wxWidgets/repos","events_url":"https://api.github.com/users/wxWidgets/events{/privacy}","received_events_url":"https://api.github.com/users/wxWidgets/received_events","type":"Organization","site_admin":false},"repo":{"id":1764646,"node_id":"MDEwOlJlcG9zaXRvcnkxNzY0NjQ2","name":"wxWidgets","full_name":"wxWidgets/wxWidgets","private":false,"owner":{"login":"wxWidgets","id":791023,"node_id":"MDEyOk9yZ2FuaXphdGlvbjc5MTAyMw==","avatar_url":"https://avatars0.githubusercontent.com/u/791023?v=4","gravatar_id":"","url":"https://api.github.com/users/wxWidgets","html_url":"https://github.com/wxWidgets","followers_url":"https://api.github.com/users/wxWidgets/followers","following_url":"https://api.github.com/users/wxWidgets/following{/other_user}","gists_url":"https://api.github.com/users/wxWidgets/gists{/gist_id}","starred_url":"https://api.github.com/users/wxWidgets/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/wxWidgets/subscriptions","organizations_url":"https://api.github.com/users/wxWidgets/orgs","repos_url":"https://api.github.com/users/wxWidgets/repos","events_url":"https://api.github.com/users/wxWidgets/events{/privacy}","received_events_url":"https://api.github.com/users/wxWidgets/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/wxWidgets/wxWidgets","description":"Cross-Platform GUI Library - Report issues here: https://trac.wxwidgets.org/","fork":false,"url":"https://api.github.com/repos/wxWidgets/wxWidgets","forks_url":"https://api.github.com/repos/wxWidgets/wxWidgets/forks","keys_url":"https://api.github.com/repos/wxWidgets/wxWidgets/keys{/key_id}","collaborators_url":"https://api.github.com/repos/wxWidgets/wxWidgets/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/wxWidgets/wxWidgets/teams","hooks_url":"https://api.github.com/repos/wxWidgets/wxWidgets/hooks","issue_events_url":"https://api.github.com/repos/wxWidgets/wxWidgets/issues/events{/number}","events_url":"https://api.github.com/repos/wxWidgets/wxWidgets/events","assignees_url":"https://api.github.com/repos/wxWidgets/wxWidgets/assignees{/user}","branches_url":"https://api.github.com/repos/wxWidgets/wxWidgets/branches{/branch}","tags_url":"https://api.github.com/repos/wxWidgets/wxWidgets/tags","blobs_url":"https://api.github.com/repos/wxWidgets/wxWidgets/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/wxWidgets/wxWidgets/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/wxWidgets/wxWidgets/git/refs{/sha}","trees_url":"https://api.github.com/repos/wxWidgets/wxWidgets/git/trees{/sha}","statuses_url":"https://api.github.com/repos/wxWidgets/wxWidgets/statuses/{sha}","languages_url":"https://api.github.com/repos/wxWidgets/wxWidgets/languages","stargazers_url":"https://api.github.com/repos/wxWidgets/wxWidgets/stargazers","contributors_url":"https://api.github.com/repos/wxWidgets/wxWidgets/contributors","subscribers_url":"https://api.github.com/repos/wxWidgets/wxWidgets/subscribers","subscription_url":"https://api.github.com/repos/wxWidgets/wxWidgets/subscription","commits_url":"https://api.github.com/repos/wxWidgets/wxWidgets/commits{/sha}","git_commits_url":"https://api.github.com/repos/wxWidgets/wxWidgets/git/commits{/sha}","comments_url":"https://api.github.com/repos/wxWidgets/wxWidgets/comments{/number}","issue_comment_url":"https://api.github.com/repos/wxWidgets/wxWidgets/issues/comments{/number}","contents_url":"https://api.github.com/repos/wxWidgets/wxWidgets/contents/{+path}","compare_url":"https://api.github.com/repos/wxWidgets/wxWidgets/compare/{base}...{head}","merges_url":"https://api.github.com/repos/wxWidgets/wxWidgets/merges","archive_url":"https://api.github.com/repos/wxWidgets/wxWidgets/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/wxWidgets/wxWidgets/downloads","issues_url":"https://api.github.com/repos/wxWidgets/wxWidgets/issues{/number}","pulls_url":"https://api.github.com/repos/wxWidgets/wxWidgets/pulls{/number}","milestones_url":"https://api.github.com/repos/wxWidgets/wxWidgets/milestones{/number}","notifications_url":"https://api.github.com/repos/wxWidgets/wxWidgets/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/wxWidgets/wxWidgets/labels{/name}","releases_url":"https://api.github.com/repos/wxWidgets/wxWidgets/releases{/id}","deployments_url":"https://api.github.com/repos/wxWidgets/wxWidgets/deployments","created_at":"2011-05-18T05:54:03Z","updated_at":"2018-10-31T22:28:27Z","pushed_at":"2018-10-31T22:28:17Z","git_url":"git://github.com/wxWidgets/wxWidgets.git","ssh_url":"git@github.com:wxWidgets/wxWidgets.git","clone_url":"https://github.com/wxWidgets/wxWidgets.git","svn_url":"https://github.com/wxWidgets/wxWidgets","homepage":"https://www.wxwidgets.org/","size":311686,"stargazers_count":2136,"watchers_count":2136,"language":"C++","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":825,"mirror_url":null,"archived":false,"open_issues_count":39,"license":null,"forks":825,"open_issues":39,"watchers":2136,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/wxWidgets/wxWidgets/pulls/977"},"html":{"href":"https://github.com/wxWidgets/wxWidgets/pull/977"},"issue":{"href":"https://api.github.com/repos/wxWidgets/wxWidgets/issues/977"},"comments":{"href":"https://api.github.com/repos/wxWidgets/wxWidgets/issues/977/comments"},"review_comments":{"href":"https://api.github.com/repos/wxWidgets/wxWidgets/pulls/977/comments"},"review_comment":{"href":"https://api.github.com/repos/wxWidgets/wxWidgets/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/wxWidgets/wxWidgets/pulls/977/commits"},"statuses":{"href":"https://api.github.com/repos/wxWidgets/wxWidgets/statuses/d8e84af99216db480653a6b7525d79f7c90a0806"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"MaartenBent"}}