Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(middleware/cors): Add support for Access-Control-Allow-Private-Network #2908

Merged
merged 17 commits into from
Mar 22, 2024

Conversation

Behzad-Khokher
Copy link
Member

@Behzad-Khokher Behzad-Khokher commented Mar 9, 2024

Description

This pull request introduces support for the Access-Control-Allow-Private-Network header in the GoFiber CORS middleware. This addition allows Fiber applications to explicitly grant web applications access to resources on a user's private network as requested by @GalvinGao .

    app.Use(cors.New(cors.Config{
        AllowOrigins: "*",
        AllowMethods: "GET,POST,HEAD,PUT,DELETE,PATCH",
        AllowPrivateNetworkAccess: true,
    }))

Related to #2804

Changes introduced

  • Documentation Update: Detail the updates made to the documentation and links to the changed files.
  • API Alignment with Express: The AllowPrivateNetworkAccess configuration option does not have a direct equivalent in the Express.js CORS middleware at the time of this implementation.

Type of change

  • Enhancement (improvement to existing features and functionality)
  • Documentation update (changes to documentation)

Checklist

Before you submit your pull request, please make sure you meet these requirements:

  • Followed the inspiration of the Express.js framework for new functionalities, making them similar in usage.
  • Conducted a self-review of the code and provided comments for complex or critical parts.
  • Updated the documentation in the /docs/ directory for Fiber's documentation.
  • Added or updated unit tests to validate the effectiveness of the changes or new features.
  • Ensured that new and existing unit tests pass locally with the changes.
  • Verified that any new dependencies are essential and have been agreed upon by the maintainers/community.
  • Aimed for optimal performance with minimal allocations in the new code.
  • Provided benchmarks for the new code to analyze and improve upon.

Summary by CodeRabbit

  • New Features

    • Introduced a new configuration option AllowPrivateNetwork for CORS middleware, enabling support for the Access-Control-Allow-Private-Network header in requests from private networks.
  • Documentation

    • Updated CORS middleware documentation to include the AllowPrivateNetwork configuration option.
  • Tests

    • Added tests to validate the AllowPrivateNetwork functionality in the CORS middleware.

@Behzad-Khokher Behzad-Khokher self-assigned this Mar 9, 2024
@Behzad-Khokher Behzad-Khokher requested a review from a team as a code owner March 9, 2024 17:29
@Behzad-Khokher Behzad-Khokher requested review from gaby, sixcolors, ReneWerner87 and efectn and removed request for a team March 9, 2024 17:29
Copy link
Contributor

coderabbitai bot commented Mar 9, 2024

Warning

Rate Limit Exceeded

@sixcolors has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 21 minutes and 19 seconds before requesting another review.

How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.
Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.
Please see our FAQ for further information.

Commits Files that changed from the base of the PR and between 4b5515c and 394d962.

Walkthrough

The update introduces a new configuration option, AllowPrivateNetwork, to the CORS middleware, enabling the setting of the Access-Control-Allow-Private-Network header. This enhancement aligns with recent standards for private network access, providing developers with the tools to manage cross-origin requests more effectively within private networks.

Changes

Files Change Summary
helpers.go Added a new constant HeaderAccessControlAllowPrivateNetwork to the list of predefined header constants.
middleware/cors/cors.go Added a new field AllowPrivateNetwork in the Config struct to control setting the Access-Control-Allow-Private-Network header based on the configuration.
middleware/cors/cors_test.go Added a test function Test_CORS_AllowPrivateNetwork to validate the behavior of AllowPrivateNetwork in the CORS middleware, ensuring correct header settings under different configuration scenarios.

Related issues

🐇✨

In a world of code, so vast and wide,
A tiny change makes a grand stride.
Across private networks, our headers align,
With CORS now tamed, our APIs shine.
So hop along, on this digital quest,
For with each update, we aim to be best.

🌐🔒✨

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

Share

Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>.
    • Generate unit-tests for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit tests for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai generate interesting stats about this repository and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @coderabbitai read src/utils.ts and generate unit tests.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (invoked as PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger a review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • The JSON schema for the configuration file is available here.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/coderabbit-overrides.v2.json

CodeRabbit Discord Community

Join our Discord Community to get help, request features, and share feedback.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 0

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 0379cc5 and e9e9c97.
Files selected for processing (4)
  • docs/api/middleware/cors.md (2 hunks)
  • helpers.go (1 hunks)
  • middleware/cors/cors.go (2 hunks)
  • middleware/cors/cors_test.go (1 hunks)
Files skipped from review due to trivial changes (1)
  • helpers.go
Additional comments: 5
docs/api/middleware/cors.md (2)
  • 83-83: The addition of the AllowPrivateNetworkAccess configuration option is well-documented, providing clear guidance on its purpose and usage. This aligns with the PR's objective to enhance CORS middleware by supporting the Access-Control-Allow-Private-Network header.
  • 105-105: The documentation correctly reflects the default setting for AllowPrivateNetworkAccess in the ConfigDefault struct. This ensures developers are aware that the feature is disabled by default, promoting conscious activation of the feature.
middleware/cors/cors.go (2)
  • 66-71: The addition of the AllowPrivateNetworkAccess field to the Config struct is correctly implemented. The comment clearly explains the purpose of the field and its default behavior, which is essential for understanding how to use this new feature.
  • 236-240: The logic to conditionally set the Access-Control-Allow-Private-Network header based on the AllowPrivateNetworkAccess configuration is correctly implemented. This ensures that the header is only added when explicitly enabled, adhering to the principle of secure defaults.
middleware/cors/cors_test.go (1)
  • 692-727: The implementation of Test_CORS_AllowPrivateNetworkAccess correctly tests the new AllowPrivateNetworkAccess feature by verifying the presence or absence of the Access-Control-Allow-Private-Network header based on the configuration. The test is well-structured, with clear separation between the scenario where AllowPrivateNetworkAccess is enabled and the default scenario where it is disabled. This ensures comprehensive coverage of the new feature's behavior.

However, there are a few areas for potential improvement:

  1. Test Naming Convention: The test name is descriptive but could be more specific about the scenarios it covers. Consider splitting it into two separate tests or renaming it to reflect both scenarios being tested (enabled and disabled states).
  2. Code Duplication: There's some repetition in setting up the test contexts (fasthttp.RequestCtx). Extracting this setup into a helper function could reduce duplication and improve maintainability.
  3. Asserting Absence of Header: The test asserts the absence of the Access-Control-Allow-Private-Network header by checking for an empty string. While this is effective, using a more explicit assertion to check for the header's absence could improve readability and intent clarity.

Overall, the test function effectively validates the new feature, ensuring that the Access-Control-Allow-Private-Network header is correctly handled based on the middleware configuration. These suggestions aim to refine the test for better maintainability and clarity.

Consider refactoring the test to reduce code duplication and improve clarity in asserting the header's absence. Splitting the test or renaming it for specificity could also enhance readability.

Copy link

codecov bot commented Mar 9, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 82.68%. Comparing base (43dc60f) to head (394d962).
Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2908      +/-   ##
==========================================
+ Coverage   82.65%   82.68%   +0.02%     
==========================================
  Files         116      116              
  Lines        8385     8396      +11     
==========================================
+ Hits         6931     6942      +11     
  Misses       1112     1112              
  Partials      342      342              
Flag Coverage Δ
unittests 82.68% <100.00%> (+0.02%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

docs/api/middleware/cors.md Show resolved Hide resolved
helpers.go Outdated Show resolved Hide resolved
middleware/cors/cors.go Outdated Show resolved Hide resolved
middleware/cors/cors_test.go Outdated Show resolved Hide resolved
@ReneWerner87 ReneWerner87 added this to the v3 milestone Mar 10, 2024
@ReneWerner87
Copy link
Member

thx
@Behzad-Khokher pls check the hints from gaby and also golangci-lint

Copy link
Member

@sixcolors sixcolors left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is still a draft. It’s at the proposal stage and has not been accepted as a standard. Recommend we do not accept at this time.

https://wicg.github.io/private-network-access/

Status of this document

This specification was published by the Web Platform Incubator Community Group. It is not a W3C Standard nor is it on the W3C Standards Track. Please note that under the W3C Community Contributor License Agreement (CLA) there is a limited opt-out and other conditions apply. Learn more about W3C Community and Business Groups.

@sixcolors sixcolors changed the title 🔥 Feature: Add support for Access-Control-Allow-Private-Network in CORS middleware feat(middleware/cors): Add support for Access-Control-Allow-Private-Network Mar 12, 2024
@ReneWerner87
Copy link
Member

@gofiber/maintainers do we want to close the pull request and open it again later or how do we want to proceed here?

@gaby
Copy link
Member

gaby commented Mar 14, 2024

@gofiber/maintainers do we want to close the pull request and open it again later or how do we want to proceed here?

It seems Google has already roll-out this feature since Chrome 104. Several projects have already merged support for it

Examples:

I vote for merging it

middleware/cors/cors.go Outdated Show resolved Hide resolved
middleware/cors/cors_test.go Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 0

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between e9e9c97 and e928f79.
Files selected for processing (1)
  • middleware/cors/cors_test.go (1 hunks)
Files skipped from review as they are similar to previous changes (1)
  • middleware/cors/cors_test.go

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 0

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between e928f79 and 88cd688.
Files selected for processing (1)
  • helpers.go (1 hunks)
Files skipped from review as they are similar to previous changes (1)
  • helpers.go

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 0

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 1af71a3 and a3fa421.
Files selected for processing (2)
  • middleware/cors/cors.go (3 hunks)
  • middleware/cors/cors_test.go (1 hunks)
Files skipped from review as they are similar to previous changes (2)
  • middleware/cors/cors.go
  • middleware/cors/cors_test.go

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 0

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between a3fa421 and 1562aa0.
Files selected for processing (1)
  • middleware/cors/cors.go (3 hunks)
Files skipped from review as they are similar to previous changes (1)
  • middleware/cors/cors.go

@Behzad-Khokher
Copy link
Member Author

thx @Behzad-Khokher pls check the hints from gaby and also golangci-lint

I updated the PR and added more unit-tests

Okay thanks, will wrap this up today.

@gaby
Copy link
Member

gaby commented Mar 19, 2024

thx @Behzad-Khokher pls check the hints from gaby and also golangci-lint

I updated the PR and added more unit-tests

Okay thanks, will wrap this up today.

Don't worry, I already did them. :-) Just pending review from @gofiber/maintainers

@sixcolors
Copy link
Member

sixcolors commented Mar 19, 2024

@gaby we should merge #2922 first as there are a significant changes to CORS middleware in that PR.

@gaby
Copy link
Member

gaby commented Mar 19, 2024

@gaby we should merge #2922 first as there are a lot more lines.

Sounds good

@Behzad-Khokher
Copy link
Member Author

thx @Behzad-Khokher pls check the hints from gaby and also golangci-lint

I updated the PR and added more unit-tests

Okay thanks, will wrap this up today.

Don't worry, I already did them. :-) Just pending review from @gofiber/maintainers

Awsome! Thanks :)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 1

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 1562aa0 and 0ea8c85.
Files selected for processing (4)
  • docs/api/middleware/cors.md (2 hunks)
  • helpers.go (1 hunks)
  • middleware/cors/cors.go (3 hunks)
  • middleware/cors/cors_test.go (1 hunks)
Files skipped from review as they are similar to previous changes (3)
  • helpers.go
  • middleware/cors/cors.go
  • middleware/cors/cors_test.go
Additional comments: 1
docs/api/middleware/cors.md (1)
  • 121-129: > 📝 NOTE

This review was outside the diff hunks, and no overlapping diff hunk was found. Original lines [38-38]

Consider adding a space after the period to improve readability in the documentation.

docs/api/middleware/cors.md Outdated Show resolved Hide resolved
Copy link
Member

@sixcolors sixcolors left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Preflight requires the method OPTIONS along with Access-Control-Request-Method. Tests made incorrect assumptions.

fiber.HeaderAccessControlRequestPrivateNetwork should be defined as Access-Control-Request-Private-Network.

Preflight should Vary Access-Control-Request-Private-Network.

Tests will pass and CORS will function correctly after you commit the suggestions below:

middleware/cors/cors.go Outdated Show resolved Hide resolved
helpers.go Outdated Show resolved Hide resolved
middleware/cors/cors_test.go Outdated Show resolved Hide resolved
@sixcolors
Copy link
Member

sixcolors commented Mar 19, 2024

CORS.md ## Config needs fixing, table alignment is off and there is no default value listed.

## Config

| Property            | Type                       | Description                                                                                                                                                                                                                                                                                                                                                          | Default                            |
|:--------------------|:---------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------------------------|
| Next                | `func(fiber.Ctx) bool`     | Next defines a function to skip this middleware when returned true.                                                                                                                                                                                                                                                                                                  | `nil`                              |
| AllowOriginsFunc    | `func(origin string) bool` | `AllowOriginsFunc` is a function that dynamically determines whether to allow a request based on its origin. If this function returns `true`, the 'Access-Control-Allow-Origin' response header will be set to the request's 'origin' header. This function is only used if the request's origin doesn't match any origin in `AllowOrigins`.                         | `nil`                              |
| AllowOrigins        | `string`                   | AllowOrigins defines a comma-separated list of origins that may access the resource. This supports subdomain matching, so you can use a value like "https://*.example.com" to allow any subdomain of example.com to submit requests.                                                                                                                                 | `"*"`                              |
| AllowMethods        | `string`                   | AllowMethods defines a list of methods allowed when accessing the resource. This is used in response to a preflight request.                                                                                                                                                                                                                                         | `"GET,POST,HEAD,PUT,DELETE,PATCH"` |
| AllowHeaders        | `string`                   | AllowHeaders defines a list of request headers that can be used when making the actual request. This is in response to a preflight request.                                                                                                                                                                                                                          | `""`                               |
| AllowCredentials    | `bool`                     | AllowCredentials indicates whether or not the response to the request can be exposed when the credentials flag is true. When used as part of a response to a preflight request, this indicates whether or not the actual request can be made using credentials. Note: If true, AllowOrigins cannot be set to a wildcard (`"*"`) to prevent security vulnerabilities. | `false`                            |
| ExposeHeaders       | `string`                   | ExposeHeaders defines whitelist headers that clients are allowed to access.                                                                                                                                                                                                                                                                                          | `""`                               |
| MaxAge              | `int`                      | MaxAge indicates how long (in seconds) the results of a preflight request can be cached. If you pass MaxAge 0, the Access-Control-Max-Age header will not be added and the browser will use 5 seconds by default. To disable caching completely, pass MaxAge value negative. It will set the Access-Control-Max-Age header to 0.                                     | `0`                                |
| AllowPrivateNetwork | `bool`                     | Indicates whether the `Access-Control-Allow-Private-Network` response header should be set to `true`, allowing requests from private networks. This aligns with modern security practices for web applications interacting with private networks.                                                                                                                    | `false`                            |

I can't seem to make a committable suggestion since the alignment changes lines which are not in your PR.

@sixcolors
Copy link
Member

sixcolors commented Mar 20, 2024

Wouldn’t hurt to order the table alphabetically either.

## Config

| Property             | Type                        | Description                                                                                                                                                                                                                                                                                                                                                          | Default                            |
|:---------------------|:----------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------------------------|
| AllowCredentials     | `bool`                      | AllowCredentials indicates whether or not the response to the request can be exposed when the credentials flag is true. When used as part of a response to a preflight request, this indicates whether or not the actual request can be made using credentials. Note: If true, AllowOrigins cannot be set to a wildcard (`"*"`) to prevent security vulnerabilities. | `false`                            |
| AllowHeaders         | `string`                    | AllowHeaders defines a list of request headers that can be used when making the actual request. This is in response to a preflight request.                                                                                                                                                                                                                          | `""`                               |
| AllowMethods         | `string`                    | AllowMethods defines a list of methods allowed when accessing the resource. This is used in response to a preflight request.                                                                                                                                                                                                                                         | `"GET,POST,HEAD,PUT,DELETE,PATCH"` |
| AllowOrigins         | `string`                    | AllowOrigins defines a comma-separated list of origins that may access the resource. This supports subdomain matching, so you can use a value like "https://*.example.com" to allow any subdomain of example.com to submit requests.                                                                                                                                 | `"*"`                              |
| AllowOriginsFunc     | `func(origin string) bool`  | `AllowOriginsFunc` is a function that dynamically determines whether to allow a request based on its origin. If this function returns `true`, the 'Access-Control-Allow-Origin' response header will be set to the request's 'origin' header. This function is only used if the request's origin doesn't match any origin in `AllowOrigins`.                         | `nil`                              |
| AllowPrivateNetwork  | `bool`                      | Indicates whether the `Access-Control-Allow-Private-Network` response header should be set to `true`, allowing requests from private networks. This aligns with modern security practices for web applications interacting with private networks.                                                                                                                    | `false`                            |
| ExposeHeaders        | `string`                    | ExposeHeaders defines whitelist headers that clients are allowed to access.                                                                                                                                                                                                                                                                                          | `""`                               |
| MaxAge               | `int`                       | MaxAge indicates how long (in seconds) the results of a preflight request can be cached. If you pass MaxAge 0, the Access-Control-Max-Age header will not be added and the browser will use 5 seconds by default. To disable caching completely, pass MaxAge value negative. It will set the Access-Control-Max-Age header to 0.                                     | `0`                                |
| Next                 | `func(fiber.Ctx) bool`      | Next defines a function to skip this middleware when returned true.                                                                                                                                                                                                                                                                                                  | `nil`                              |

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 1

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 0ea8c85 and 7293cb9.
Files selected for processing (1)
  • middleware/cors/cors.go (3 hunks)
Additional comments: 1
middleware/cors/cors.go (1)
  • 66-71: The addition of AllowPrivateNetwork to the Config struct is well-implemented, with a safe default value of false. This aligns with the existing configuration structure and is a necessary addition for the new feature.

middleware/cors/cors.go Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 0

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 7293cb9 and 4b5515c.
Files selected for processing (1)
  • helpers.go (1 hunks)
Files skipped from review as they are similar to previous changes (1)
  • helpers.go

Copy link
Member

@gaby gaby left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! thanks for updating after the merge

@gaby
Copy link
Member

gaby commented Mar 22, 2024

@ReneWerner87 @efectn This is ready for review.

@sixcolors Are you backporting this to v2 after merge? Given the other changes to CORS in that branch.

@sixcolors
Copy link
Member

@ReneWerner87 @efectn This is ready for review.

@sixcolors Are you backporting this to v2 after merge? Given the other changes to CORS in that branch.

I can do that.

@ReneWerner87 ReneWerner87 enabled auto-merge (squash) March 22, 2024 11:15
@ReneWerner87 ReneWerner87 merged commit 5e8df0a into gofiber:main Mar 22, 2024
14 of 15 checks passed
Copy link

welcome bot commented Mar 22, 2024

Congrats on merging your first pull request! 🎉 We here at Fiber are proud of you! If you need help or want to chat with us, join us on Discord https://gofiber.io/discord

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

4 participants