C言語　HTTP通信　ヘッダの中身を表示

## 生データの中身を表示
携帯で接続。
404が出ているが、ヘッダの中身が見れたので気にしない。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4054974/b706c0cd-8f99-438a-8559-94fb5547903b.png)
```
ubuntu@ubuntu:~/kaihatsu/net$ gcc server.c -o server
ubuntu@ubuntu:~/kaihatsu/net$ ./server
ソケット作成成功
ソケットオプション設定
ポート 8080 にバインド成功
接続待機開始...

新しい接続 [192.168.68.163:60116]
▼▼▼ 生リクエストデータ ▼▼▼
GET / HTTP/1.1
Host: 192.168.68.96:8080
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Mobile Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,ja-CN;q=0.8,ja;q=0.7


▲▲▲ 生リクエストデータ ▲▲▲

解析結果:
メソッド: GET
リクエストパス: /

初期パスに変換: /index.html
アクセスファイル: public/index.html
ファイル情報:
サイズ: 207 bytes
タイプ: text/html

▼▼▼ レスポンスヘッダ ▼▼▼
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 207

▲▲▲ レスポンスヘッダ ▲▲▲

接続を閉じました

新しい接続 [192.168.68.163:60114]
▼▼▼ 生リクエストデータ ▼▼▼
GET /style.css HTTP/1.1
Host: 192.168.68.96:8080
Connection: keep-alive
User-Agent: Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Mobile Safari/537.36
Accept: text/css,*/*;q=0.1
Referer: http://192.168.68.96:8080/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,ja-CN;q=0.8,ja;q=0.7


▲▲▲ 生リクエストデータ ▲▲▲

解析結果:
メソッド: GET
リクエストパス: /style.css

アクセスファイル: public/style.css
ファイル開くのに失敗: No such file or directory
▼▼▼ レスポンスヘッダ ▼▼▼
HTTP/1.1 404 OK
Content-Type: text/plain
Content-Length: 9

▲▲▲ レスポンスヘッダ ▲▲▲

接続を閉じました

新しい接続 [192.168.68.163:60118]
▼▼▼ 生リクエストデータ ▼▼▼
GET /app.js HTTP/1.1
Host: 192.168.68.96:8080
Connection: keep-alive
User-Agent: Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Mobile Safari/537.36
Accept: */*
Referer: http://192.168.68.96:8080/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,ja-CN;q=0.8,ja;q=0.7


▲▲▲ 生リクエストデータ ▲▲▲

解析結果:
メソッド: GET
リクエストパス: /app.js

アクセスファイル: public/app.js
ファイル開くのに失敗: No such file or directory
▼▼▼ レスポンスヘッダ ▼▼▼
HTTP/1.1 404 OK
Content-Type: text/plain
Content-Length: 9

▲▲▲ レスポンスヘッダ ▲▲▲

接続を閉じました
```

## Cコード
```server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <fcntl.h>

#define PORT 8080
#define BUFFER_SIZE 2048
#define ROOT_DIR "public/"

// 内容を判定する関数
const char* get_content_type(const char *path) {
    const char *dot = strrchr(path, '.');
    if (dot) {
        if (strcmp(dot, ".css") == 0) return "text/css";
        if (strcmp(dot, ".js") == 0) return "application/javascript";
    }
    return "text/html";
}

// レスポンスを送信する関数
void send_response(int client_fd, int status, const char *content_type, const char *body, int content_length) {
    char header[BUFFER_SIZE];
    snprintf(header, sizeof(header), 
        "HTTP/1.1 %d OK\r\n"
        "Content-Type: %s\r\n"
        "Content-Length: %d\r\n\r\n",
        status, content_type, content_length);
    
    // レスポンスヘッダを表示
    printf("▼▼▼ レスポンスヘッダ ▼▼▼\n");
    printf("%s", header);
    printf("▲▲▲ レスポンスヘッダ ▲▲▲\n\n");
    
    write(client_fd, header, strlen(header));
    write(client_fd, body, content_length);
}

// リクエストを処理する関数
void handle_request(int client_fd) {
    char buffer[BUFFER_SIZE] = {0};
    ssize_t bytes_read = read(client_fd, buffer, sizeof(buffer));
    
    if (bytes_read < 0) {
        perror("リクエストの読み込み失敗");
        return;
    }
    
    // 生のリクエストデータを表示
    printf("▼▼▼ 生リクエストデータ ▼▼▼\n%.*s\n", (int)bytes_read, buffer);
    printf("▲▲▲ 生リクエストデータ ▲▲▲\n\n");
    
    // リクエストヘッダを解析
    char method[16], path[256];
    sscanf(buffer, "%s %s", method, path);
    printf("解析結果:\n");
    printf("メソッド: %s\nリクエストパス: %s\n\n", method, path);
    
    // デフォルトパスの処理
    if (strcmp(path, "/") == 0) {
        strcpy(path, "/index.html");
        printf("初期パスに変換: %s\n", path);
    }
    
    // ローカルファイルパスを構築
    char full_path[512];
    snprintf(full_path, sizeof(full_path), "%s%s", ROOT_DIR, path + 1);
    printf("アクセスファイル: %s\n", full_path);
    
    // ファイルを開く
    int file_fd = open(full_path, O_RDONLY);
    if (file_fd == -1) {
        perror("ファイル開くのに失敗");
        send_response(client_fd, 404, "text/plain", "Not Found", 9);
        return;
    }
    
    // ファイル情報を取得
    struct stat st;
    fstat(file_fd, &st);
    char *content = malloc(st.st_size);
    read(file_fd, content, st.st_size);
    printf("ファイル情報:\nサイズ: %ld bytes\nタイプ: %s\n\n", st.st_size, get_content_type(path));
    
    send_response(client_fd, 200, get_content_type(path), content, st.st_size);
    free(content);
    close(file_fd);
}

int main() {
    int server_fd, client_fd;
    struct sockaddr_in address;
    
    // ソケット作成
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        perror("ソケット作成失敗");
        exit(EXIT_FAILURE);
    }
    printf("ソケット作成成功\n");
    
    // ソケットオプション設定
    int opt = 1;
    setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    printf("ソケットオプション設定\n");
    
    // アドレスバインド
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(PORT);
    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
        perror("バインド失敗");
        exit(EXIT_FAILURE);
    }
    printf("ポート %d にバインド成功\n", PORT);
    
    // リスニング開始
    if (listen(server_fd, 3) < 0) {
        perror("リスニング失敗");
        exit(EXIT_FAILURE);
    }
    printf("接続待機開始...\n\n");
    
    // メインループ
    while (1) {
        socklen_t addrlen = sizeof(address);
        client_fd = accept(server_fd, (struct sockaddr *)&address, &addrlen);
        if (client_fd < 0) {
            perror("接続受付失敗");
            continue;
        }
        
        // クライアント情報を表示
        char client_ip[INET_ADDRSTRLEN];
        inet_ntop(AF_INET, &address.sin_addr, client_ip, INET_ADDRSTRLEN);
        printf("新しい接続 [%s:%d]\n", client_ip, ntohs(address.sin_port));
        
        handle_request(client_fd);
        close(client_fd);
        printf("接続を閉じました\n\n");
    }
    
    return 0;
}
```