-
Notifications
You must be signed in to change notification settings - Fork 0
acceptループで1クライアントずつ処理する電卓サーバーの作成 #1
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
Open
Fuminiton
wants to merge
1
commit into
main
Choose a base branch
from
trunk
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| CC = gcc | ||
| CFLAGS = -Wall -Wextra -pedantic -std=c11 \ | ||
| -D_POSIX_C_SOURCE=200809L \ | ||
| -Iinclude | ||
| CLIENT_CFLAGS = -Wall -Wextra -std=c11 -D_DEFAULT_SOURCE | ||
| TARGET = http_server | ||
| CLIENT = http_client | ||
|
|
||
| SRCS = src/main.c \ | ||
| src/utils/utils.c \ | ||
| src/core/http_request.c \ | ||
| src/core/http_response.c \ | ||
| src/core/calculation.c \ | ||
| src/core/server.c | ||
|
|
||
| OBJS = $(SRCS:.c=.o) | ||
|
|
||
| .PHONY: all test clean | ||
|
|
||
| all: $(TARGET) | ||
|
|
||
| $(TARGET): $(OBJS) | ||
| $(CC) $(CFLAGS) -o $@ $^ | ||
|
|
||
| %.o: %.c | ||
| $(CC) $(CFLAGS) -c -o $@ $< | ||
|
|
||
| $(CLIENT): tests/client.c | ||
| $(CC) $(CLIENT_CFLAGS) -o $@ $< | ||
|
|
||
| test: all $(CLIENT) | ||
| bash tests/test.sh | ||
|
|
||
| clean: | ||
| rm -f $(OBJS) $(TARGET) $(CLIENT) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| # Calculation Server | ||
|
|
||
| `GET /calc?query=2+10 HTTP/1.1`に対して、計算結果を返す HTTP Server | ||
|
|
||
| ## 前提 | ||
|
|
||
| - 計算は2つの自然数の加減乗除にのみに対応し、結果が少数になる場合は対応しない | ||
| - サーバーは1リクエストごとに終了する | ||
| - ポート番号は 1024~49151 の範囲で指定する | ||
|
|
||
| ## ファイル構造 | ||
|
|
||
| ``` | ||
| project/ | ||
| ├── include/ | ||
| │ ├── utils.h # ログ・メモリユーティリティ | ||
| │ ├── http_request.h # HTTPRequest 構造体と操作関数 | ||
| │ ├── http_response.h # レスポンス生成関数 | ||
| │ └── calculation.h # 計算機能 | ||
| ├── src/ | ||
| │ ├── utils/ | ||
| │ │ └── utils.c | ||
| │ ├── core/ | ||
| │ │ ├── http_request.c | ||
| │ │ ├── http_response.c | ||
| │ │ ├── calculation.c | ||
| │ │ └── server.c # ネットワーク・シグナル・ループ | ||
| │ └── main.c | ||
| ├── tests/ | ||
| │ ├── client.c # テスト用クライアント | ||
| │ └── test.sh | ||
| └── Makefile | ||
| ``` | ||
|
|
||
| ## 使用方法 | ||
|
|
||
| ### サーバー起動 | ||
|
|
||
| ```bash | ||
| ./http_server 8080 | ||
| ``` | ||
|
|
||
| ### クライアント起動 | ||
|
|
||
| ```bash | ||
| ./http_client 127.0.0.1 8080 | ||
| ``` | ||
|
|
||
| ## テスト | ||
|
|
||
| ```bash | ||
| make test | ||
| ``` | ||
|
|
||
|
|
||
| ## TODO | ||
|
|
||
| ### 必須 | ||
|
|
||
| - [x] clientからの接続を受け入れるserverを作成する | ||
| - [x] serverに接続できるclientを作成する | ||
| - [x] serverから文字列を送信して、clientが文字列を受け取れるようにする | ||
| - [x] clientがserverから受け取った文字列をserverに返せるようにする | ||
| - [x] 文字列"1+1"のような2つの文字の四則演算の結果を返す | ||
| - [x] serverがHTTPリクエスト情報をパースできるようにする | ||
| - [x] 計算結果をHTTPレスポンスで返却する | ||
|
|
||
| ### 任意 | ||
|
|
||
| - [ ] IPv4+v6 両対応 | ||
| - [ ] non-blocking 化 | ||
| - [ ] マルチスレッド化 | ||
| - [ ] 通信タイムアウトの設定 | ||
| - [x] signal を受け取ったら、全コネクションが正常終了して終了 | ||
| - [ ] SSL化 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| #ifndef HTTP_SERVER_CALCULATION_H | ||
| #define HTTP_SERVER_CALCULATION_H | ||
|
|
||
| #include <stddef.h> | ||
| #include "http_request.h" | ||
|
|
||
| #define URI_SIZE 1024 | ||
|
|
||
| typedef enum { | ||
| CALCULATION_OK = 0, | ||
| CALCULATION_ERROR_INVALID_ARG, /* NULLポインタ等の不正な引数 */ | ||
| CALCULATION_ERROR_DIV_ZERO, /* ゼロ除算 */ | ||
| CALCULATION_ERROR_OVERFLOW, /* 整数オーバーフロー */ | ||
| CALCULATION_ERROR_INVALID_OP, /* 不正な演算子 */ | ||
| } CalculationError; | ||
|
|
||
| int calculation_parse_query(const HTTPRequest *request, | ||
| int *out_operand1, char *out_op_char, int *out_operand2); | ||
| CalculationError calculation_compute(int operand1, char op_char, int operand2, int *result); | ||
| int calculation_handle_endpoint(const HTTPRequest *request, | ||
| char *response, size_t response_size); | ||
|
|
||
| #endif /* HTTP_SERVER_CALCULATION_H */ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| #ifndef HTTP_SERVER_HTTP_REQUEST_H | ||
| #define HTTP_SERVER_HTTP_REQUEST_H | ||
|
|
||
| #include <stddef.h> | ||
|
|
||
| typedef struct { | ||
| const char *buffer; /* 元のバッファへのポインタ */ | ||
| size_t method_len; | ||
| size_t uri_len; | ||
| size_t protocol_len; | ||
| const char *method; | ||
| const char *uri; | ||
| const char *protocol; | ||
| } HTTPRequest; | ||
|
|
||
| HTTPRequest *request_create(void); | ||
| int request_parse(HTTPRequest *request, const char *buffer); | ||
| void request_free(HTTPRequest *request); | ||
|
|
||
| #endif /* HTTP_SERVER_HTTP_REQUEST_H */ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| #ifndef HTTP_SERVER_HTTP_RESPONSE_H | ||
| #define HTTP_SERVER_HTTP_RESPONSE_H | ||
|
|
||
| #include <stddef.h> | ||
|
|
||
| #define MAX_RESPONSE_SIZE 8192 | ||
| #define MAX_RESULT_STR 32 | ||
|
|
||
| #define HTTP_OK 200 | ||
| #define HTTP_BAD_REQUEST 400 | ||
| #define HTTP_NOT_FOUND 404 | ||
| #define HTTP_INTERNAL_ERROR 500 | ||
|
|
||
| void response_build(char *buffer, size_t buffer_size, | ||
| int status_code, const char *status_text, | ||
| size_t content_length, const char *content); | ||
| void response_build_success(char *buffer, size_t buffer_size, int result); | ||
| void response_build_error(char *buffer, size_t buffer_size, | ||
| int status_code, const char *message); | ||
|
|
||
| #endif /* HTTP_SERVER_HTTP_RESPONSE_H */ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| #ifndef HTTP_SERVER_SERVER_H | ||
| #define HTTP_SERVER_SERVER_H | ||
|
|
||
| #define PORT_NUMBER_MIN 1024 | ||
| #define PORT_NUMBER_MAX 49151 | ||
|
|
||
| void server_run(int port); | ||
|
|
||
| #endif /* HTTP_SERVER_SERVER_H */ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| #ifndef HTTP_SERVER_UTILS_H | ||
| #define HTTP_SERVER_UTILS_H | ||
|
|
||
| #include <stddef.h> | ||
|
|
||
| void log_exit(const char *fmt, ...); | ||
| void log_error(const char *fmt, ...); | ||
| void log_info(const char *fmt, ...); | ||
| void *xmalloc(size_t size); | ||
|
|
||
| #endif /* HTTP_SERVER_UTILS_H */ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,120 @@ | ||
| #include <stdio.h> | ||
| #include <string.h> | ||
| #include <limits.h> | ||
| #include "utils.h" | ||
| #include "http_response.h" | ||
| #include "calculation.h" | ||
|
|
||
| int calculation_parse_query(const HTTPRequest *request, | ||
| int *out_operand1, char *out_op_char, int *out_operand2) { | ||
| int matched; | ||
| char uri_buffer[URI_SIZE]; | ||
|
|
||
| if (!request || !out_operand1 || !out_op_char || !out_operand2 || | ||
| request->uri_len >= URI_SIZE) { | ||
| return -1; | ||
| } | ||
|
|
||
| /* パスをnull終端の文字列にコピー */ | ||
| strncpy(uri_buffer, request->uri, request->uri_len); | ||
| uri_buffer[request->uri_len] = '\0'; | ||
|
|
||
| matched = sscanf(uri_buffer, "/calc?query=%d%c%d", | ||
| out_operand1, out_op_char, out_operand2); | ||
|
Comment on lines
+22
to
+23
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 例えば、 |
||
| if (matched != 3) { | ||
| log_error("Failed to parse calc query: %s", uri_buffer); | ||
| return -1; | ||
| } | ||
| if (strchr("+-*/", *out_op_char) == NULL) { | ||
| log_error("Invalid operator: %c", *out_op_char); | ||
| return -1; | ||
| } | ||
|
|
||
| return 0; | ||
| } | ||
|
|
||
| CalculationError calculation_compute(int operand1, char op_char, int operand2, int *result) { | ||
| if (!result) { | ||
| return CALCULATION_ERROR_INVALID_ARG; | ||
| } | ||
|
|
||
| switch (op_char) { | ||
| case '+': { | ||
| long long r = (long long)operand1 + operand2; | ||
| if (r > INT_MAX || r < INT_MIN) { | ||
| return CALCULATION_ERROR_OVERFLOW; | ||
| } | ||
| *result = (int)r; | ||
| break; | ||
| } | ||
| case '-': { | ||
| long long r = (long long)operand1 - operand2; | ||
| if (r > INT_MAX || r < INT_MIN) { | ||
| return CALCULATION_ERROR_OVERFLOW; | ||
| } | ||
| *result = (int)r; | ||
| break; | ||
| } | ||
| case '*': { | ||
| long long r = (long long)operand1 * operand2; | ||
| if (r > INT_MAX || r < INT_MIN) { | ||
| return CALCULATION_ERROR_OVERFLOW; | ||
| } | ||
| *result = (int)r; | ||
| break; | ||
| } | ||
| case '/': | ||
| if (operand2 == 0) { | ||
| return CALCULATION_ERROR_DIV_ZERO; | ||
| } | ||
| if (operand1 == INT_MIN && operand2 == -1) { | ||
| return CALCULATION_ERROR_OVERFLOW; | ||
| } | ||
| *result = operand1 / operand2; | ||
| break; | ||
| default: | ||
| return CALCULATION_ERROR_INVALID_OP; | ||
| } | ||
|
|
||
| return CALCULATION_OK; | ||
| } | ||
|
|
||
| int calculation_handle_endpoint(const HTTPRequest *request, | ||
| char *response, size_t response_size) { | ||
| int operand1, operand2; | ||
| char op_char; | ||
| int result; | ||
| CalculationError calculation_error; | ||
|
|
||
| if (calculation_parse_query(request, &operand1, &op_char, &operand2) != 0) { | ||
| response_build_error(response, response_size, HTTP_BAD_REQUEST, | ||
| "Invalid query format. Use: /calc?query=<num><op><num>"); | ||
| return -1; | ||
| } | ||
|
|
||
| calculation_error = calculation_compute(operand1, op_char, operand2, &result); | ||
| if (calculation_error != CALCULATION_OK) { | ||
| switch (calculation_error) { | ||
| case CALCULATION_ERROR_DIV_ZERO: | ||
| response_build_error(response, response_size, HTTP_BAD_REQUEST, | ||
| "Division by zero"); | ||
| break; | ||
| case CALCULATION_ERROR_INVALID_OP: | ||
| response_build_error(response, response_size, HTTP_BAD_REQUEST, | ||
| "Invalid operator"); | ||
| break; | ||
| case CALCULATION_ERROR_OVERFLOW: | ||
| response_build_error(response, response_size, HTTP_INTERNAL_ERROR, | ||
| "Integer overflow"); | ||
| break; | ||
| default: | ||
| response_build_error(response, response_size, HTTP_INTERNAL_ERROR, | ||
| "Internal calculation error"); | ||
| break; | ||
| } | ||
| return -1; | ||
| } | ||
|
|
||
| response_build_success(response, response_size, result); | ||
| return 0; | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| #include <stdlib.h> | ||
| #include <string.h> | ||
| #include "utils.h" | ||
| #include "http_request.h" | ||
|
|
||
| HTTPRequest *request_create(void) { | ||
| HTTPRequest *request = xmalloc(sizeof(HTTPRequest)); | ||
| return request; | ||
| } | ||
|
|
||
| int request_parse(HTTPRequest *request, const char *buffer) { | ||
| const char *p = buffer; | ||
| const char *first_space; | ||
| const char *second_space; | ||
| const char *first_newline; | ||
|
|
||
| if (!request || !buffer) { | ||
| return -1; | ||
| } | ||
|
|
||
| /* メソッドまでの最初のスペースを探す */ | ||
| first_space = strchr(p, ' '); | ||
| if (!first_space || first_space - p == 0) { | ||
| log_error("Invalid HTTP request format: no method"); | ||
| return -1; | ||
| } | ||
|
|
||
| /* パスまでの2番目のスペースを探す */ | ||
| second_space = strchr(first_space + 1, ' '); | ||
| if (!second_space || second_space - (first_space + 1) == 0) { | ||
| log_error("Invalid HTTP request format: no uri"); | ||
| return -1; | ||
| } | ||
|
|
||
| /* プロトコルまでの改行を探す */ | ||
| first_newline = strchr(second_space + 1, '\n'); | ||
| if (!first_newline) { | ||
| first_newline = strchr(second_space + 1, '\r'); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. HTTPの仕様だと、 |
||
| } | ||
| if (!first_newline || first_newline - (second_space + 1) == 0) { | ||
| log_error("Invalid HTTP request format: no protocol"); | ||
| return -1; | ||
| } | ||
|
|
||
| /* バッファとポインタを保存(ゼロコピー) */ | ||
| request->buffer = buffer; | ||
| request->method = p; | ||
| request->method_len = first_space - p; | ||
| request->uri = first_space + 1; | ||
| request->uri_len = second_space - (first_space + 1); | ||
| request->protocol = second_space + 1; | ||
| request->protocol_len = first_newline - (second_space + 1); | ||
|
|
||
| return 0; | ||
| } | ||
|
|
||
| void request_free(HTTPRequest *request) { | ||
| if (!request) { | ||
| return; | ||
| } | ||
| free(request); | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
queryが最初のquery parameterになるかは分からないですよね?